1
0
mirror of https://github.com/mik3y/usb-serial-for-android synced 2025-06-07 16:06:10 +00:00

read w/o timeout now only throws exception on connection lost

partly revert f4166f34, as there might be unkown reasons for empty response
This commit is contained in:
kai-morich 2021-04-18 19:53:44 +02:00
parent 128d1a00b1
commit 5f94a47b63
3 changed files with 31 additions and 39 deletions

View File

@ -54,6 +54,9 @@ import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
@ -985,7 +988,7 @@ public class DeviceTest {
@Test @Test
public void readBufferSize() throws Exception { public void readBufferSize() throws Exception {
// looks like devices perform USB read with full mReadEndpoint.getMaxPacketSize() size (32, 64, 512) // looks like devices perform USB read with full mReadEndpoint.getMaxPacketSize() size (32, 64, 512)
// if the Java byte[] is shorter than the received result, it is silently lost with read timeout! // if the buffer is smaller than the received result, it is silently lost
// //
// for buffer > packet size, but not multiple of packet size, the same issue happens, but typically // for buffer > packet size, but not multiple of packet size, the same issue happens, but typically
// only the last (partly filled) packet is lost. // only the last (partly filled) packet is lost.
@ -1012,7 +1015,7 @@ public class DeviceTest {
else if (usb.serialDriver instanceof ProlificSerialDriver) else if (usb.serialDriver instanceof ProlificSerialDriver)
Assert.assertTrue("expected > 0 and < 16 byte, got " + data.length, data.length > 0 && data.length < 16); Assert.assertTrue("expected > 0 and < 16 byte, got " + data.length, data.length > 0 && data.length < 16);
else // ftdi, ch340, cp2105 else // ftdi, ch340, cp2105
fail("buffer to small exception expected"); Assert.assertEquals(0, data.length);
} catch (IOException ignored) { } catch (IOException ignored) {
} }
if (purge) { if (purge) {
@ -1474,22 +1477,8 @@ public class DeviceTest {
@Test @Test
public void readTimeout() throws Exception { public void readTimeout() throws Exception {
final Boolean[] closed = {Boolean.FALSE}; ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> future;
Runnable closeThread = () -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
usb.close();
closed[0] = true;
};
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
byte[] writeBuf = new byte[]{1}; byte[] writeBuf = new byte[]{1};
byte[] readBuf = new byte[1]; byte[] readBuf = new byte[1];
if (usb.serialDriver instanceof FtdiSerialDriver) if (usb.serialDriver instanceof FtdiSerialDriver)
@ -1497,25 +1486,29 @@ public class DeviceTest {
int len,i,j; int len,i,j;
long time; long time;
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
// w/o timeout // w/o timeout
telnet.write(writeBuf); telnet.write(writeBuf);
len = usb.serialPort.read(readBuf, 0); // not blocking because data is available len = usb.serialPort.read(readBuf, 0); // not blocking because data is available
assertEquals(1, len); assertEquals(1, len);
time = System.currentTimeMillis(); time = System.currentTimeMillis();
closed[0] = false; future = scheduler.schedule(() -> usb.close(), 100, TimeUnit.MILLISECONDS);
Executors.newSingleThreadExecutor().submit(closeThread);
try { try {
usb.serialPort.read(readBuf, 0); // blocking until close() len = usb.serialPort.read(readBuf, 0); // blocking until close()
fail("read error expected"); assertEquals(0, len);
} catch (IOException ignored) {} } catch (IOException ignored) {
assertTrue(System.currentTimeMillis()-time >= 100); // typically no exception as read request canceled at the beginning of close()
// wait for usbClose // and most cases the connection is still valid in testConnection()
for(i=0; i<100; i++) { } catch (Exception ignored) {
if(closed[0]) break; // can fail with NPE if connection is closed between closed check and queueing/waiting for request
Thread.sleep(1);
} }
assertTrue("not closed in time", closed[0]); assertTrue(System.currentTimeMillis()-time >= 100);
future.get(); // wait until close finished
scheduler.shutdown();
// with timeout // with timeout
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD)); usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
@ -2083,8 +2076,8 @@ public class DeviceTest {
b = usb.read(1); b = usb.read(1);
long t3 = System.currentTimeMillis(); long t3 = System.currentTimeMillis();
ftdiSerialPort.setLatencyTimer(lt); ftdiSerialPort.setLatencyTimer(lt);
assertEquals("latency 1", 99, Math.max(t2-t1, 99)); // looks strange, but shows actual value assertTrue("latency 1: expected < 100, got "+ (t2-t1), (t2-t1) < 100);
assertEquals("latency 100", 99, Math.min(t3-t2, 99)); assertTrue("latency 100: expected > 100, got " + (t3-t2), (t3-t2) > 100);
usb.deviceConnection.close(); usb.deviceConnection.close();
try { try {

View File

@ -152,7 +152,7 @@ public class UsbWrapper implements SerialInputOutputManager.Listener {
if(!flags.contains(OpenCloseFlags.NO_IOMANAGER_THREAD)) { if(!flags.contains(OpenCloseFlags.NO_IOMANAGER_THREAD)) {
ioManager = new SerialInputOutputManager(serialPort, this); ioManager = new SerialInputOutputManager(serialPort, this);
if(!flags.contains(OpenCloseFlags.NO_IOMANAGER_START)) if(!flags.contains(OpenCloseFlags.NO_IOMANAGER_START))
Executors.newSingleThreadExecutor().submit(ioManager); ioManager.start();
} }
synchronized (readBuffer) { synchronized (readBuffer) {
readBuffer.clear(); readBuffer.clear();

View File

@ -149,7 +149,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
byte[] buf = new byte[2]; byte[] buf = new byte[2];
int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200); int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200);
if(len < 0) if(len < 0)
throw new IOException("Connection lost, USB get_status request failed"); throw new IOException("USB get_status request failed");
} }
@Override @Override
@ -177,7 +177,8 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
long endTime = testConnection ? MonotonicClock.millis() + timeout : 0; long endTime = testConnection ? MonotonicClock.millis() + timeout : 0;
int readMax = Math.min(dest.length, MAX_READ_SIZE); int readMax = Math.min(dest.length, MAX_READ_SIZE);
nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout); nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout);
// Android error propagation is improvable, nread == -1 can be: timeout, connection lost, buffer to small // Android error propagation is improvable:
// nread == -1 can be: timeout, connection lost, buffer to small, ???
if(nread == -1 && testConnection && MonotonicClock.millis() < endTime) if(nread == -1 && testConnection && MonotonicClock.millis() < endTime)
testConnection(); testConnection();
@ -191,12 +192,10 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
throw new IOException("Waiting for USB request failed"); throw new IOException("Waiting for USB request failed");
} }
nread = buf.position(); nread = buf.position();
// Android error propagation is improvable:
// response != null & nread == 0 can be: connection lost, buffer to small, ???
if(nread == 0) { if(nread == 0) {
if(dest.length % mReadEndpoint.getMaxPacketSize() != 0) { testConnection();
throw new IOException("Connection lost or buffer to small");
} else {
throw new IOException("Connection lost");
}
} }
} }
return Math.max(nread, 0); return Math.max(nread, 0);