Add C test for separate thread closing

This commit is contained in:
Will Hedgecock 2021-11-30 11:16:12 -06:00
parent 68a95fd258
commit 9e0aab54f2
2 changed files with 162 additions and 32 deletions

View File

@ -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

View File

@ -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();
}