Move all global/class sync to native code

This commit is contained in:
Will Hedgecock 2022-02-15 15:35:50 -06:00
parent 60ed45bb5d
commit 7330e76913
33 changed files with 129 additions and 72 deletions

View File

@ -2,7 +2,7 @@
* SerialPort_Posix.c * SerialPort_Posix.c
* *
* Created on: Feb 25, 2012 * Created on: Feb 25, 2012
* Last Updated on: Feb 14, 2022 * Last Updated on: Feb 15, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2022 Fazecast, Inc. * Copyright (C) 2012-2022 Fazecast, Inc.
@ -79,8 +79,9 @@ jfieldID readTimeoutField;
jfieldID writeTimeoutField; jfieldID writeTimeoutField;
jfieldID eventFlagsField; jfieldID eventFlagsField;
// List of available serial ports // Global list of available serial ports
char portsEnumerated = 0; char portsEnumerated = 0;
pthread_mutex_t criticalSection;
serialPortVector serialPorts = { NULL, 0, 0 }; serialPortVector serialPorts = { NULL, 0, 0 };
// JNI exception handler // JNI exception handler
@ -240,30 +241,36 @@ void* eventReadingThread2(void *serialPortPointer)
JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm) JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm)
{ {
// Mark this entire function as a critical section
pthread_mutex_lock(&criticalSection);
// Enumerate all ports on the current system // Enumerate all ports on the current system
enumeratePorts(); enumeratePorts();
// Create a Java-based port listing // Create a Java-based port listing
jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialCommClass, 0); jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialCommClass, 0);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; char stopLooping = checkJniError(env, __LINE__ - 1) ? 1 : 0;
for (int i = 0; i < serialPorts.length; ++i) for (int i = 0; !stopLooping && (i < serialPorts.length); ++i)
{ {
// Create a new SerialComm object containing the enumerated values // Create a new SerialComm object containing the enumerated values
jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor); jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, portDescriptionField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portDescription)); (*env)->SetObjectField(env, serialCommObject, portDescriptionField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portDescription));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, friendlyNameField, (*env)->NewStringUTF(env, serialPorts.ports[i]->friendlyName)); (*env)->SetObjectField(env, serialCommObject, friendlyNameField, (*env)->NewStringUTF(env, serialPorts.ports[i]->friendlyName));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portPath)); (*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portPath));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, portLocationField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portLocation)); (*env)->SetObjectField(env, serialCommObject, portLocationField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portLocation));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
// Add new SerialComm object to array // Add new SerialComm object to array
(*env)->SetObjectArrayElement(env, arrayObject, i, serialCommObject); (*env)->SetObjectArrayElement(env, arrayObject, i, serialCommObject);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
} }
// Exit critical section and return the com port array
pthread_mutex_unlock(&criticalSection);
return arrayObject; return arrayObject;
} }
@ -354,6 +361,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_initializeLibrar
sigaction(SIGUSR2, &ignoreAction, NULL); sigaction(SIGUSR2, &ignoreAction, NULL);
sigaction(SIGTTOU, &ignoreAction, NULL); sigaction(SIGTTOU, &ignoreAction, NULL);
sigaction(SIGTTIN, &ignoreAction, NULL); sigaction(SIGTTIN, &ignoreAction, NULL);
// Initialize the critical section lock
pthread_mutex_init(&criticalSection, NULL);
} }
JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibrary(JNIEnv *env, jclass serialComm) JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibrary(JNIEnv *env, jclass serialComm)
@ -366,6 +376,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibr
// Delete the cached global reference // Delete the cached global reference
(*env)->DeleteGlobalRef(env, serialCommClass); (*env)->DeleteGlobalRef(env, serialCommClass);
checkJniError(env, __LINE__ - 1); checkJniError(env, __LINE__ - 1);
// Delete the critical section lock
pthread_mutex_destroy(&criticalSection);
} }
JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDetails(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDetails(JNIEnv *env, jobject obj)
@ -377,25 +390,33 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDeta
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) return;
// Ensure that the serial port exists // Ensure that the serial port exists
char continueRetrieval = 1;
pthread_mutex_lock(&criticalSection);
if (!portsEnumerated) if (!portsEnumerated)
enumeratePorts(); enumeratePorts();
serialPort *port = fetchPort(&serialPorts, portName); serialPort *port = fetchPort(&serialPorts, portName);
if (!port) if (!port)
{ continueRetrieval = 0;
(*env)->ReleaseStringUTFChars(env, portNameJString, portName);
checkJniError(env, __LINE__ - 1);
return;
}
// Fill in the Java-side port details // Fill in the Java-side port details
(*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewStringUTF(env, port->portDescription)); if (continueRetrieval)
if (checkJniError(env, __LINE__ - 1)) return; {
(*env)->SetObjectField(env, obj, friendlyNameField, (*env)->NewStringUTF(env, port->friendlyName)); (*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewStringUTF(env, port->portDescription));
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
(*env)->SetObjectField(env, obj, portLocationField, (*env)->NewStringUTF(env, port->portLocation)); }
if (checkJniError(env, __LINE__ - 1)) return; if (continueRetrieval)
{
(*env)->SetObjectField(env, obj, friendlyNameField, (*env)->NewStringUTF(env, port->friendlyName));
if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
}
if (continueRetrieval)
{
(*env)->SetObjectField(env, obj, portLocationField, (*env)->NewStringUTF(env, port->portLocation));
if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
}
// Release all JNI structures // Release all JNI structures
pthread_mutex_unlock(&criticalSection);
(*env)->ReleaseStringUTFChars(env, portNameJString, portName); (*env)->ReleaseStringUTFChars(env, portNameJString, portName);
checkJniError(env, __LINE__ - 1); checkJniError(env, __LINE__ - 1);
} }
@ -405,8 +426,6 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
// Retrieve the serial port parameter fields // Retrieve the serial port parameter fields
jstring portNameJString = (jstring)(*env)->GetObjectField(env, obj, comPortField); jstring portNameJString = (jstring)(*env)->GetObjectField(env, obj, comPortField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
const char *portName = (*env)->GetStringUTFChars(env, portNameJString, NULL);
if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char disableExclusiveLock = (*env)->GetBooleanField(env, obj, disableExclusiveLockField); unsigned char disableExclusiveLock = (*env)->GetBooleanField(env, obj, disableExclusiveLockField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char requestElevatedPermissions = (*env)->GetBooleanField(env, obj, requestElevatedPermissionsField); unsigned char requestElevatedPermissions = (*env)->GetBooleanField(env, obj, requestElevatedPermissionsField);
@ -415,14 +434,18 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField); unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
const char *portName = (*env)->GetStringUTFChars(env, portNameJString, NULL);
if (checkJniError(env, __LINE__ - 1)) return 0;
// Ensure that the serial port still exists and is not already open // Ensure that the serial port still exists and is not already open
pthread_mutex_lock(&criticalSection);
serialPort *port = fetchPort(&serialPorts, portName); serialPort *port = fetchPort(&serialPorts, portName);
if (!port) if (!port)
{ {
// Create port representation and add to serial port listing // Create port representation and add to serial port listing
port = pushBack(&serialPorts, portName, "User-Specified Port", "User-Specified Port", "0-0"); port = pushBack(&serialPorts, portName, "User-Specified Port", "User-Specified Port", "0-0");
} }
pthread_mutex_unlock(&criticalSection);
if (!port || (port->handle > 0)) if (!port || (port->handle > 0))
{ {
(*env)->ReleaseStringUTFChars(env, portNameJString, portName); (*env)->ReleaseStringUTFChars(env, portNameJString, portName);
@ -437,17 +460,21 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
verifyAndSetUserPortGroup(portName); verifyAndSetUserPortGroup(portName);
// Try to open the serial port with read/write access // Try to open the serial port with read/write access
pthread_mutex_lock(&criticalSection);
port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1; port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1;
if ((port->handle = open(portName, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC)) > 0) if ((port->handle = open(portName, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC)) > 0)
{ {
// Ensure that multiple root users cannot access the device simultaneously // Ensure that multiple root users cannot access the device simultaneously
pthread_mutex_unlock(&criticalSection);
if (!disableExclusiveLock && flock(port->handle, LOCK_EX | LOCK_NB)) if (!disableExclusiveLock && flock(port->handle, LOCK_EX | LOCK_NB))
{ {
port->errorLineNumber = lastErrorLineNumber = __LINE__ - 2; port->errorLineNumber = lastErrorLineNumber = __LINE__ - 2;
port->errorNumber = lastErrorNumber = errno; port->errorNumber = lastErrorNumber = errno;
while (close(port->handle) && (errno == EINTR)) while (close(port->handle) && (errno == EINTR))
errno = 0; errno = 0;
pthread_mutex_lock(&criticalSection);
port->handle = -1; port->handle = -1;
pthread_mutex_unlock(&criticalSection);
} }
else if (!disableAutoConfig && !Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj, (jlong)(intptr_t)port)) else if (!disableAutoConfig && !Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj, (jlong)(intptr_t)port))
{ {
@ -455,7 +482,9 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
fcntl(port->handle, F_SETFL, O_NONBLOCK); fcntl(port->handle, F_SETFL, O_NONBLOCK);
while (close(port->handle) && (errno == EINTR)) while (close(port->handle) && (errno == EINTR))
errno = 0; errno = 0;
pthread_mutex_lock(&criticalSection);
port->handle = -1; port->handle = -1;
pthread_mutex_unlock(&criticalSection);
} }
else if (autoFlushIOBuffers) else if (autoFlushIOBuffers)
{ {
@ -466,7 +495,10 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
} }
} }
else else
{
port->errorNumber = lastErrorNumber = errno; port->errorNumber = lastErrorNumber = errno;
pthread_mutex_unlock(&criticalSection);
}
// Return a pointer to the serial port data structure // Return a pointer to the serial port data structure
(*env)->ReleaseStringUTFChars(env, portNameJString, portName); (*env)->ReleaseStringUTFChars(env, portNameJString, portName);
@ -808,7 +840,9 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_closePortNative
flock(port->handle, LOCK_UN | LOCK_NB); flock(port->handle, LOCK_UN | LOCK_NB);
while (close(port->handle) && (errno == EINTR)) while (close(port->handle) && (errno == EINTR))
errno = 0; errno = 0;
pthread_mutex_lock(&criticalSection);
port->handle = -1; port->handle = -1;
pthread_mutex_unlock(&criticalSection);
return 0; return 0;
} }

View File

@ -2,7 +2,7 @@
* SerialPort_Windows.c * SerialPort_Windows.c
* *
* Created on: Feb 25, 2012 * Created on: Feb 25, 2012
* Last Updated on: Jan 28, 2022 * Last Updated on: Feb 15, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2022 Fazecast, Inc. * Copyright (C) 2012-2022 Fazecast, Inc.
@ -78,8 +78,9 @@ jfieldID eventFlagsField;
typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(LPDWORD); typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(LPDWORD);
typedef int (__stdcall *FT_GetDeviceInfoListFunction)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD); typedef int (__stdcall *FT_GetDeviceInfoListFunction)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD);
// List of available serial ports // Global list of available serial ports
char portsEnumerated = 0; char portsEnumerated = 0;
CRITICAL_SECTION criticalSection;
serialPortVector serialPorts = { NULL, 0, 0 }; serialPortVector serialPorts = { NULL, 0, 0 };
// JNI exception handler // JNI exception handler
@ -320,31 +321,37 @@ static void enumeratePorts(void)
JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm) JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm)
{ {
// Mark this entire function as a critical section
EnterCriticalSection(&criticalSection);
// Enumerate all ports on the current system // Enumerate all ports on the current system
enumeratePorts(); enumeratePorts();
// Get relevant SerialComm methods and fill in com port array // Get relevant SerialComm methods and fill in com port array
wchar_t comPort[128]; wchar_t comPort[128];
jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialCommClass, 0); jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialCommClass, 0);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; char stopLooping = checkJniError(env, __LINE__ - 1) ? 1 : 0;
for (int i = 0; i < serialPorts.length; ++i) for (int i = 0; !stopLooping && (i < serialPorts.length); ++i)
{ {
// Create new SerialComm object containing the enumerated values // Create new SerialComm object containing the enumerated values
jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor); jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portPath, wcslen(serialPorts.ports[i]->portPath))); (*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portPath, wcslen(serialPorts.ports[i]->portPath)));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, friendlyNameField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->friendlyName, wcslen(serialPorts.ports[i]->friendlyName))); (*env)->SetObjectField(env, serialCommObject, friendlyNameField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->friendlyName, wcslen(serialPorts.ports[i]->friendlyName)));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, portDescriptionField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portDescription, wcslen(serialPorts.ports[i]->portDescription))); (*env)->SetObjectField(env, serialCommObject, portDescriptionField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portDescription, wcslen(serialPorts.ports[i]->portDescription)));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
(*env)->SetObjectField(env, serialCommObject, portLocationField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portLocation, wcslen(serialPorts.ports[i]->portLocation))); (*env)->SetObjectField(env, serialCommObject, portLocationField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portLocation, wcslen(serialPorts.ports[i]->portLocation)));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
// Add new SerialComm object to array // Add new SerialComm object to array
(*env)->SetObjectArrayElement(env, arrayObject, i, serialCommObject); (*env)->SetObjectArrayElement(env, arrayObject, i, serialCommObject);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
} }
// Exit critical section and return the com port array
LeaveCriticalSection(&criticalSection);
return arrayObject; return arrayObject;
} }
@ -412,6 +419,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_initializeLibrar
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) return;
eventFlagsField = (*env)->GetFieldID(env, serialCommClass, "eventFlags", "I"); eventFlagsField = (*env)->GetFieldID(env, serialCommClass, "eventFlags", "I");
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) return;
// Initialize the critical section lock
InitializeCriticalSection(&criticalSection);
} }
JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibrary(JNIEnv *env, jclass serialComm) JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibrary(JNIEnv *env, jclass serialComm)
@ -424,6 +434,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibr
// Delete the cached global reference // Delete the cached global reference
(*env)->DeleteGlobalRef(env, serialCommClass); (*env)->DeleteGlobalRef(env, serialCommClass);
checkJniError(env, __LINE__ - 1); checkJniError(env, __LINE__ - 1);
// Delete the critical section lock
DeleteCriticalSection(&criticalSection);
} }
JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDetails(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDetails(JNIEnv *env, jobject obj)
@ -435,25 +448,33 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDeta
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) return;
// Ensure that the serial port exists // Ensure that the serial port exists
char continueRetrieval = 1;
EnterCriticalSection(&criticalSection);
if (!portsEnumerated) if (!portsEnumerated)
enumeratePorts(); enumeratePorts();
serialPort *port = fetchPort(&serialPorts, portName); serialPort *port = fetchPort(&serialPorts, portName);
if (!port) if (!port)
{ continueRetrieval = 0;
(*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName);
checkJniError(env, __LINE__ - 1);
return;
}
// Fill in the Java-side port details // Fill in the Java-side port details
(*env)->SetObjectField(env, obj, friendlyNameField, (*env)->NewString(env, (jchar*)port->friendlyName, wcslen(port->friendlyName))); if (continueRetrieval)
if (checkJniError(env, __LINE__ - 1)) return; {
(*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewString(env, (jchar*)port->portDescription, wcslen(port->portDescription))); (*env)->SetObjectField(env, obj, friendlyNameField, (*env)->NewString(env, (jchar*)port->friendlyName, wcslen(port->friendlyName)));
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
(*env)->SetObjectField(env, obj, portLocationField, (*env)->NewString(env, (jchar*)port->portLocation, wcslen(port->portLocation))); }
if (checkJniError(env, __LINE__ - 1)) return; if (continueRetrieval)
{
(*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewString(env, (jchar*)port->portDescription, wcslen(port->portDescription)));
if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
}
if (continueRetrieval)
{
(*env)->SetObjectField(env, obj, portLocationField, (*env)->NewString(env, (jchar*)port->portLocation, wcslen(port->portLocation)));
if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
}
// Release all JNI structures // Release all JNI structures
LeaveCriticalSection(&criticalSection);
(*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName); (*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName);
checkJniError(env, __LINE__ - 1); checkJniError(env, __LINE__ - 1);
} }
@ -463,22 +484,24 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
// Retrieve the serial port parameter fields // Retrieve the serial port parameter fields
jstring portNameJString = (jstring)(*env)->GetObjectField(env, obj, comPortField); jstring portNameJString = (jstring)(*env)->GetObjectField(env, obj, comPortField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
const wchar_t *portName = (wchar_t*)(*env)->GetStringChars(env, portNameJString, NULL);
if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char requestElevatedPermissions = (*env)->GetBooleanField(env, obj, requestElevatedPermissionsField); unsigned char requestElevatedPermissions = (*env)->GetBooleanField(env, obj, requestElevatedPermissionsField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char disableAutoConfig = (*env)->GetBooleanField(env, obj, disableConfigField); unsigned char disableAutoConfig = (*env)->GetBooleanField(env, obj, disableConfigField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField); unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
const wchar_t *portName = (wchar_t*)(*env)->GetStringChars(env, portNameJString, NULL);
if (checkJniError(env, __LINE__ - 1)) return 0;
// Ensure that the serial port still exists and is not already open // Ensure that the serial port still exists and is not already open
EnterCriticalSection(&criticalSection);
serialPort *port = fetchPort(&serialPorts, portName); serialPort *port = fetchPort(&serialPorts, portName);
if (!port) if (!port)
{ {
// Create port representation and add to serial port listing // Create port representation and add to serial port listing
port = pushBack(&serialPorts, portName, L"User-Specified Port", L"User-Specified Port", L"0-0"); port = pushBack(&serialPorts, portName, L"User-Specified Port", L"User-Specified Port", L"0-0");
} }
LeaveCriticalSection(&criticalSection);
if (!port || (port->handle != INVALID_HANDLE_VALUE)) if (!port || (port->handle != INVALID_HANDLE_VALUE))
{ {
(*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName); (*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName);
@ -492,9 +515,12 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
reduceLatencyToMinimum(portName + 4, requestElevatedPermissions); reduceLatencyToMinimum(portName + 4, requestElevatedPermissions);
// Try to open the serial port with read/write access // Try to open the serial port with read/write access
EnterCriticalSection(&criticalSection);
port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1;
if ((port->handle = CreateFileW(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, NULL)) != INVALID_HANDLE_VALUE) if ((port->handle = CreateFileW(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, NULL)) != INVALID_HANDLE_VALUE)
{ {
// Configure the port parameters and timeouts // Configure the port parameters and timeouts
LeaveCriticalSection(&criticalSection);
if (!disableAutoConfig && !Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj, (jlong)(intptr_t)port)) if (!disableAutoConfig && !Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj, (jlong)(intptr_t)port))
{ {
// Close the port if there was a problem setting the parameters // Close the port if there was a problem setting the parameters
@ -502,15 +528,17 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
CancelIoEx(port->handle, NULL); CancelIoEx(port->handle, NULL);
SetCommMask(port->handle, 0); SetCommMask(port->handle, 0);
CloseHandle(port->handle); CloseHandle(port->handle);
EnterCriticalSection(&criticalSection);
port->handle = INVALID_HANDLE_VALUE; port->handle = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&criticalSection);
} }
else if (autoFlushIOBuffers) else if (autoFlushIOBuffers)
Java_com_fazecast_jSerialComm_SerialPort_flushRxTxBuffers(env, obj, (jlong)(intptr_t)port); Java_com_fazecast_jSerialComm_SerialPort_flushRxTxBuffers(env, obj, (jlong)(intptr_t)port);
} }
else else
{ {
port->errorLineNumber = lastErrorLineNumber = __LINE__ - 15;
port->errorNumber = lastErrorNumber = GetLastError(); port->errorNumber = lastErrorNumber = GetLastError();
LeaveCriticalSection(&criticalSection);
} }
// Return a pointer to the serial port data structure // Return a pointer to the serial port data structure
@ -790,14 +818,15 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_closePortNative
// Purge any outstanding port operations // Purge any outstanding port operations
PurgeComm(port->handle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); PurgeComm(port->handle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
CancelIoEx(port->handle, NULL); CancelIoEx(port->handle, NULL);
FlushFileBuffers(port->handle);
SetCommMask(port->handle, 0); SetCommMask(port->handle, 0);
// Close the port // Close the port
port->eventListenerRunning = 0; port->eventListenerRunning = 0;
port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1; port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1;
port->errorNumber = lastErrorNumber = (!CloseHandle(port->handle) ? GetLastError() : 0); port->errorNumber = lastErrorNumber = (!CloseHandle(port->handle) ? GetLastError() : 0);
EnterCriticalSection(&criticalSection);
port->handle = INVALID_HANDLE_VALUE; port->handle = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&criticalSection);
return 0; return 0;
} }

View File

@ -2,7 +2,7 @@
* SerialPort.java * SerialPort.java
* *
* Created on: Feb 25, 2012 * Created on: Feb 25, 2012
* Last Updated on: Feb 14, 2022 * Last Updated on: Feb 15, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2022 Fazecast, Inc. * Copyright (C) 2012-2022 Fazecast, Inc.
@ -444,10 +444,16 @@ public final class SerialPort
* <p> * <p>
* The serial ports can be accessed by iterating through each of the SerialPort objects in this array. * The serial ports can be accessed by iterating through each of the SerialPort objects in this array.
* <p> * <p>
* Note that the {@link #openPort()} method must be called before any attempts to read from or write to the port. * Note that the array will also include any serial ports that your application currently has open, even if
* the devices attached to those ports become disconnected. As such, it is important that you always call
* {@link #closePort()} on a SerialPort object if it becomes disconnected, which is detectable by inspecting
* the return values from the various read calls or by registering a {@link SerialPortDataListener} for the
* {@link SerialPort#LISTENING_EVENT_PORT_DISCONNECTED} event.
* <p>
* The {@link #openPort()} method must be called before any attempts to read from or write to the port.
* Likewise, {@link #closePort()} should be called when you are finished accessing the port. * Likewise, {@link #closePort()} should be called when you are finished accessing the port.
* <p> * <p>
* Also note that repeated calls to this function will re-enumerate all serial ports and will return a completely * Note that repeated calls to this function will re-enumerate all serial ports and will return a completely
* unique set of array objects. As such, you should store a reference to the serial port object(s) you are * unique set of array objects. As such, you should store a reference to the serial port object(s) you are
* interested in in your own application code. * interested in in your own application code.
* <p> * <p>
@ -501,9 +507,7 @@ public final class SerialPort
serialPort.friendlyName = "User-Specified Port"; serialPort.friendlyName = "User-Specified Port";
serialPort.portDescription = "User-Specified Port"; serialPort.portDescription = "User-Specified Port";
serialPort.portLocation = "0-0"; serialPort.portLocation = "0-0";
synchronized (SerialPort.class) { serialPort.retrievePortDetails();
serialPort.retrievePortDetails();
}
return serialPort; return serialPort;
} }
@ -629,13 +633,8 @@ public final class SerialPort
} }
} }
// Natively open the serial port, and synchronize to the class scope since port enumeration methods are class-based, // Natively open the serial port, and start an event-based listener if registered
// and this method may alter or read a global class structure in native code portHandle = openPortNative();
synchronized (SerialPort.class) {
portHandle = openPortNative();
}
// Start an event-based listener if registered and the port is open
if ((portHandle != 0) && (serialEventListener != null)) if ((portHandle != 0) && (serialEventListener != null))
serialEventListener.startListening(); serialEventListener.startListening();
return (portHandle != 0); return (portHandle != 0);
@ -689,14 +688,9 @@ public final class SerialPort
if (serialEventListener != null) if (serialEventListener != null)
serialEventListener.stopListening(); serialEventListener.stopListening();
// Natively close the port, and synchronize to the class scope since port enumeration methods are class-based, // Natively close the port
// and this method may alter or read a global class structure in native code
if (portHandle != 0) if (portHandle != 0)
{ portHandle = closePortNative(portHandle);
synchronized (SerialPort.class) {
portHandle = closePortNative(portHandle);
}
}
return (portHandle == 0); return (portHandle == 0);
} }
@ -751,8 +745,8 @@ public final class SerialPort
* Returns the source code line location of the latest error encountered during execution of * Returns the source code line location of the latest error encountered during execution of
* the native code for this port. * the native code for this port.
* <p> * <p>
* This function must be called while the port is still open as soon as an error is encountered, * This function must be called as soon as an error is encountered, or it may return an incorrect source
* or it may return an incorrect source code line location. * code line location.
* *
* @return Source line of latest native code error. * @return Source line of latest native code error.
*/ */
@ -761,8 +755,8 @@ public final class SerialPort
/** /**
* Returns the error number returned by the most recent native source code line that failed execution. * Returns the error number returned by the most recent native source code line that failed execution.
* <p> * <p>
* This function must be called while the port is still open as soon as an error is encountered, * This function must be called as soon as an error is encountered, or it may return an incorrect or
* or it may return an incorrect or invalid error code. * invalid error code.
* *
* @return Error number of the latest native code error. * @return Error number of the latest native code error.
*/ */