Add C test for separate thread closing
This commit is contained in:
parent
68a95fd258
commit
9e0aab54f2
|
@ -1,9 +1,11 @@
|
|||
# Compiler tools, commands, and flags
|
||||
COMPILE := gcc
|
||||
BUILD_DIR := build
|
||||
JDK_HOME := $(shell if [ "`uname`" = "Darwin" ]; then echo "`/usr/libexec/java_home`"; else echo "$$JDK_HOME"; fi)
|
||||
INCLUDES := -I"../../main/c/Posix" -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/linux" -I"$(JDK_HOME)/include/darwin" -I"$(JDK_HOME)/include/solaris"
|
||||
CFLAGS := -fPIC -Os -flto -static-libgcc -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -fuse-linker-plugin
|
||||
LDFLAGS := -Os -flto -static-libgcc -shared -fuse-linker-plugin -s
|
||||
CFLAGS := -fPIC -Os -flto -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 $(shell if [ "`uname`" != "Darwin" ]; then echo "-static-libgcc -fuse-linker-plugin"; fi)
|
||||
LDFLAGS := -Os -flto -fuse-linker-plugin $(shell if [ "`uname`" != "Darwin" ]; then echo "-static-libgcc -s"; fi)
|
||||
LIBRARIES := $(shell if [ "`uname`" = "Darwin" ]; then echo "-framework Cocoa -framework IOKit"; else echo "-pthread"; fi)
|
||||
DELETE := @rm
|
||||
MKDIR := @mkdir -p
|
||||
COPY := @cp
|
||||
|
@ -28,7 +30,7 @@ $(BUILD_DIR) :
|
|||
|
||||
# Build rules for all tests
|
||||
testOpenClose : $(BUILD_DIR)/testOpenClose.o $(BUILD_DIR)/PosixHelperFunctions.o
|
||||
$(COMPILE) $(LDFLAGS) -o $@ $<
|
||||
$(COMPILE) $(LDFLAGS) $(LIBRARIES) -o $@ $^
|
||||
|
||||
# Suffix rules to get from *.c -> *.o
|
||||
$(BUILD_DIR)/%.o : %.c
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -30,8 +31,8 @@
|
|||
|
||||
|
||||
// Global static variables
|
||||
static long portHandle = -1;
|
||||
static char* comPort = NULL;
|
||||
static volatile long portHandle = -1, readBufferLength = 0;
|
||||
static char *comPort = NULL, *readBuffer = NULL;
|
||||
|
||||
|
||||
// JNI functionality
|
||||
|
@ -44,36 +45,36 @@ bool configTimeouts(long serialPortFD, int timeoutMode, int readTimeout, int wri
|
|||
tcgetattr(serialPortFD, &options);
|
||||
|
||||
// Set updated port timeouts
|
||||
if (((timeoutMode & 0x1) > 0) && (readTimeout > 0)) // Read Semi-blocking with timeout
|
||||
if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING) > 0) && (readTimeout > 0)) // Read Semi-blocking with timeout
|
||||
{
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = readTimeout / 100;
|
||||
}
|
||||
else if ((timeoutMode & 0x1) > 0) // Read Semi-blocking without timeout
|
||||
else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING) > 0) // Read Semi-blocking without timeout
|
||||
{
|
||||
options.c_cc[VMIN] = 1;
|
||||
options.c_cc[VTIME] = 0;
|
||||
options.c_cc[VMIN] = 1;
|
||||
options.c_cc[VTIME] = 0;
|
||||
}
|
||||
else if (((timeoutMode & 0x10) > 0) && (readTimeout > 0)) // Read Blocking with timeout
|
||||
else if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) && (readTimeout > 0)) // Read Blocking with timeout
|
||||
{
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = readTimeout / 100;
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = readTimeout / 100;
|
||||
}
|
||||
else if ((timeoutMode & 0x10) > 0) // Read Blocking without timeout
|
||||
else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) // Read Blocking without timeout
|
||||
{
|
||||
options.c_cc[VMIN] = 1;
|
||||
options.c_cc[VTIME] = 0;
|
||||
options.c_cc[VMIN] = 1;
|
||||
options.c_cc[VTIME] = 0;
|
||||
}
|
||||
else if ((timeoutMode & 0x1000) > 0) // Scanner Mode
|
||||
else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_SCANNER) > 0) // Scanner Mode
|
||||
{
|
||||
options.c_cc[VMIN] = 1;
|
||||
options.c_cc[VTIME] = 1;
|
||||
options.c_cc[VMIN] = 1;
|
||||
options.c_cc[VTIME] = 1;
|
||||
}
|
||||
else // Non-blocking
|
||||
else // Non-blocking
|
||||
{
|
||||
flags = O_NONBLOCK;
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = 0;
|
||||
flags = O_NONBLOCK;
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = 0;
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
|
@ -108,10 +109,14 @@ bool configPort(long serialPortFD)
|
|||
unsigned char rs485RxDuringTx = false;
|
||||
unsigned char isDtrEnabled = true;
|
||||
unsigned char isRtsEnabled = true;
|
||||
char xonStartChar = 17;
|
||||
char xoffStopChar = 19;
|
||||
|
||||
// Clear any serial port flags and set up raw non-canonical port parameters
|
||||
struct termios options = { 0 };
|
||||
tcgetattr(serialPortFD, &options);
|
||||
options.c_cc[VSTART] = (unsigned char)xonStartChar;
|
||||
options.c_cc[VSTOP] = (unsigned char)xoffStopChar;
|
||||
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | INPCK | IGNPAR | IGNCR | ICRNL | IXON | IXOFF);
|
||||
options.c_oflag &= ~OPOST;
|
||||
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
|
@ -119,23 +124,23 @@ bool configPort(long serialPortFD)
|
|||
|
||||
// Update the user-specified port parameters
|
||||
tcflag_t byteSize = (byteSizeInt == 5) ? CS5 : (byteSizeInt == 6) ? CS6 : (byteSizeInt == 7) ? CS7 : CS8;
|
||||
tcflag_t parity = (parityInt == 0) ? 0 : (parityInt == 1) ? (PARENB | PARODD) : (parityInt == 2) ? PARENB : (parityInt == 3) ? (PARENB | CMSPAR | PARODD) : (PARENB | CMSPAR);
|
||||
tcflag_t parity = (parityInt == com_fazecast_jSerialComm_SerialPort_NO_PARITY) ? 0 : (parityInt == com_fazecast_jSerialComm_SerialPort_ODD_PARITY) ? (PARENB | PARODD) : (parityInt == com_fazecast_jSerialComm_SerialPort_EVEN_PARITY) ? PARENB : (parityInt == com_fazecast_jSerialComm_SerialPort_MARK_PARITY) ? (PARENB | CMSPAR | PARODD) : (PARENB | CMSPAR);
|
||||
options.c_cflag |= (byteSize | parity | CLOCAL | CREAD);
|
||||
if (!isDtrEnabled || !isRtsEnabled)
|
||||
options.c_cflag &= ~HUPCL;
|
||||
if (!rs485ModeEnabled)
|
||||
options.c_iflag |= BRKINT;
|
||||
if (stopBitsInt == 3)
|
||||
if (stopBitsInt == com_fazecast_jSerialComm_SerialPort_TWO_STOP_BITS)
|
||||
options.c_cflag |= CSTOPB;
|
||||
if (((flowControl & 0x00000010) > 0) || ((flowControl & 0x00000001) > 0))
|
||||
if (((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_CTS_ENABLED) > 0) || ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_RTS_ENABLED) > 0))
|
||||
options.c_cflag |= CRTSCTS;
|
||||
if (byteSizeInt < 8)
|
||||
options.c_iflag |= ISTRIP;
|
||||
if (parityInt != 0)
|
||||
options.c_iflag |= (INPCK | IGNPAR);
|
||||
if ((flowControl & 0x10000) > 0)
|
||||
if ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_IN_ENABLED) > 0)
|
||||
options.c_iflag |= IXOFF;
|
||||
if ((flowControl & 0x100000) > 0)
|
||||
if ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED) > 0)
|
||||
options.c_iflag |= IXON;
|
||||
|
||||
// Set baud rate and apply changes
|
||||
|
@ -225,7 +230,7 @@ long openPortNative(void)
|
|||
return serialPortFD;
|
||||
}
|
||||
|
||||
bool closePortNative(long serialPortFD)
|
||||
long closePortNative(long serialPortFD)
|
||||
{
|
||||
// Unblock, unlock, and close the port
|
||||
fsync(serialPortFD);
|
||||
|
@ -236,10 +241,133 @@ bool closePortNative(long serialPortFD)
|
|||
while (close(serialPortFD) && (errno == EINTR))
|
||||
errno = 0;
|
||||
serialPortFD = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int readBytes(long serialPortFD, char* buffer, long bytesToRead, long offset, int timeoutMode, int readTimeout)
|
||||
{
|
||||
// Ensure that the allocated read buffer is large enough
|
||||
int numBytesRead, numBytesReadTotal = 0, bytesRemaining = bytesToRead, ioctlResult = 0;
|
||||
if (bytesToRead > readBufferLength)
|
||||
{
|
||||
char *newMemory = (char*)realloc(readBuffer, bytesToRead);
|
||||
if (!newMemory)
|
||||
return -1;
|
||||
readBuffer = newMemory;
|
||||
readBufferLength = bytesToRead;
|
||||
}
|
||||
|
||||
// Infinite blocking mode specified, don't return until we have completely finished the read
|
||||
if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) && (readTimeout == 0))
|
||||
{
|
||||
// While there are more bytes we are supposed to read
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
// Attempt to read some number of bytes from the serial port
|
||||
do { errno = 0; numBytesRead = read(serialPortFD, readBuffer + numBytesReadTotal, bytesRemaining); } while ((numBytesRead < 0) && (errno == EINTR));
|
||||
if ((numBytesRead == -1) || ((numBytesRead == 0) && (ioctl(serialPortFD, FIONREAD, &ioctlResult) == -1)))
|
||||
break;
|
||||
|
||||
// Fix index variables
|
||||
numBytesReadTotal += numBytesRead;
|
||||
bytesRemaining -= numBytesRead;
|
||||
}
|
||||
}
|
||||
else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) // Blocking mode, but not indefinitely
|
||||
{
|
||||
// Get current system time
|
||||
struct timeval expireTime = { 0 }, currTime = { 0 };
|
||||
gettimeofday(&expireTime, NULL);
|
||||
expireTime.tv_usec += (readTimeout * 1000);
|
||||
if (expireTime.tv_usec > 1000000)
|
||||
{
|
||||
expireTime.tv_sec += (expireTime.tv_usec * 0.000001);
|
||||
expireTime.tv_usec = (expireTime.tv_usec % 1000000);
|
||||
}
|
||||
|
||||
// While there are more bytes we are supposed to read and the timeout has not elapsed
|
||||
do
|
||||
{
|
||||
do { errno = 0; numBytesRead = read(serialPortFD, readBuffer + numBytesReadTotal, bytesRemaining); } while ((numBytesRead < 0) && (errno == EINTR));
|
||||
if ((numBytesRead == -1) || ((numBytesRead == 0) && (ioctl(serialPortFD, FIONREAD, &ioctlResult) == -1)))
|
||||
break;
|
||||
|
||||
// Fix index variables
|
||||
numBytesReadTotal += numBytesRead;
|
||||
bytesRemaining -= numBytesRead;
|
||||
|
||||
// Get current system time
|
||||
gettimeofday(&currTime, NULL);
|
||||
} while ((bytesRemaining > 0) && ((expireTime.tv_sec > currTime.tv_sec) || ((expireTime.tv_sec == currTime.tv_sec) && (expireTime.tv_usec > currTime.tv_usec))));
|
||||
}
|
||||
else // Semi- or non-blocking specified
|
||||
{
|
||||
// Read from the port
|
||||
do { errno = 0; numBytesRead = read(serialPortFD, readBuffer, bytesToRead); } while ((numBytesRead < 0) && (errno == EINTR));
|
||||
if (numBytesRead > 0)
|
||||
numBytesReadTotal = numBytesRead;
|
||||
}
|
||||
|
||||
// Return number of bytes read if successful
|
||||
memcpy(buffer, readBuffer, numBytesReadTotal);
|
||||
return (numBytesRead == -1) ? -1 : numBytesReadTotal;
|
||||
}
|
||||
|
||||
void* readingThread(void *arg)
|
||||
{
|
||||
// Read forever in a loop while the port is open
|
||||
char readBuffer[2048];
|
||||
while (portHandle > 0)
|
||||
{
|
||||
printf("\nBeginning blocking read...\n");
|
||||
int numBytesRead = readBytes(portHandle, readBuffer, sizeof(readBuffer), 0, com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING, 0);
|
||||
printf("Read %d bytes\n", numBytesRead);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int testCloseSeparateThread(void)
|
||||
{
|
||||
// Open the serial port
|
||||
if ((portHandle = openPortNative()) <= 0)
|
||||
{
|
||||
printf("ERROR: Could not open port: %s\n", comPort);
|
||||
return -1;
|
||||
}
|
||||
printf("Port opened: %s\n", comPort);
|
||||
|
||||
// Configure the serial port for indefinitely blocking reads
|
||||
if (!configTimeouts(portHandle, com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING, 0, 0, 0))
|
||||
{
|
||||
printf("ERROR: Could not configure port timeouts\n");
|
||||
return -2;
|
||||
}
|
||||
printf("Blocking read timeouts successfully configured\n");
|
||||
|
||||
// Start a new thread to continuously read from the serial port for 5 seconds
|
||||
pthread_t pid;
|
||||
if (pthread_create(&pid, NULL, &readingThread, NULL))
|
||||
{
|
||||
printf("ERROR: Could not create a reading thread\n");
|
||||
return -3;
|
||||
}
|
||||
sleep(5);
|
||||
|
||||
// Close the serial port
|
||||
printf("\nAttempting to close serial port from a separate thread...\n");
|
||||
if ((portHandle = closePortNative(portHandle)) > 0)
|
||||
{
|
||||
printf("ERROR: Could not close port: %s\n", comPort);
|
||||
return -4;
|
||||
}
|
||||
printf("Port closed\n");
|
||||
|
||||
// Wait for the reading thread to return
|
||||
pthread_join(pid, NULL);
|
||||
printf("Reading thread successfully returned\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int testFull(void)
|
||||
int testSimpleOpenClose(void)
|
||||
{
|
||||
// Open the serial port
|
||||
if ((portHandle = openPortNative()) <= 0)
|
||||
|
@ -250,8 +378,7 @@ int testFull(void)
|
|||
printf("Port opened\n");
|
||||
|
||||
// Close the serial port
|
||||
closePortNative(portHandle);
|
||||
if (portHandle > 0)
|
||||
if ((portHandle = closePortNative(portHandle)) > 0)
|
||||
{
|
||||
printf("ERROR: Could not close port: %s\n", comPort);
|
||||
return -3;
|
||||
|
@ -271,5 +398,6 @@ int main(int argc, char *argv[])
|
|||
comPort = argv[1];
|
||||
|
||||
// Perform one of the above open/close tests
|
||||
return testFull();
|
||||
return testCloseSeparateThread();
|
||||
//return testSimpleOpenClose();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue