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
*
* Created on: Feb 25, 2012
* Last Updated on: Feb 14, 2022
* Last Updated on: Feb 15, 2022
* Author: Will Hedgecock
*
* Copyright (C) 2012-2022 Fazecast, Inc.
@ -79,8 +79,9 @@ jfieldID readTimeoutField;
jfieldID writeTimeoutField;
jfieldID eventFlagsField;
// List of available serial ports
// Global list of available serial ports
char portsEnumerated = 0;
pthread_mutex_t criticalSection;
serialPortVector serialPorts = { NULL, 0, 0 };
// JNI exception handler
@ -240,30 +241,36 @@ void* eventReadingThread2(void *serialPortPointer)
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
enumeratePorts();
// Create a Java-based port listing
jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialCommClass, 0);
if (checkJniError(env, __LINE__ - 1)) return arrayObject;
for (int i = 0; i < serialPorts.length; ++i)
char stopLooping = checkJniError(env, __LINE__ - 1) ? 1 : 0;
for (int i = 0; !stopLooping && (i < serialPorts.length); ++i)
{
// Create a new SerialComm object containing the enumerated values
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));
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));
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));
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));
if (checkJniError(env, __LINE__ - 1)) return arrayObject;
if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
// Add new SerialComm object to array
(*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;
}
@ -354,6 +361,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_initializeLibrar
sigaction(SIGUSR2, &ignoreAction, NULL);
sigaction(SIGTTOU, &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)
@ -366,6 +376,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibr
// Delete the cached global reference
(*env)->DeleteGlobalRef(env, serialCommClass);
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)
@ -377,25 +390,33 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDeta
if (checkJniError(env, __LINE__ - 1)) return;
// Ensure that the serial port exists
char continueRetrieval = 1;
pthread_mutex_lock(&criticalSection);
if (!portsEnumerated)
enumeratePorts();
serialPort *port = fetchPort(&serialPorts, portName);
if (!port)
{
(*env)->ReleaseStringUTFChars(env, portNameJString, portName);
checkJniError(env, __LINE__ - 1);
return;
}
continueRetrieval = 0;
// Fill in the Java-side port details
(*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewStringUTF(env, port->portDescription));
if (checkJniError(env, __LINE__ - 1)) return;
(*env)->SetObjectField(env, obj, friendlyNameField, (*env)->NewStringUTF(env, port->friendlyName));
if (checkJniError(env, __LINE__ - 1)) return;
(*env)->SetObjectField(env, obj, portLocationField, (*env)->NewStringUTF(env, port->portLocation));
if (checkJniError(env, __LINE__ - 1)) return;
if (continueRetrieval)
{
(*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewStringUTF(env, port->portDescription));
if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
}
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
pthread_mutex_unlock(&criticalSection);
(*env)->ReleaseStringUTFChars(env, portNameJString, portName);
checkJniError(env, __LINE__ - 1);
}
@ -405,8 +426,6 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
// Retrieve the serial port parameter fields
jstring portNameJString = (jstring)(*env)->GetObjectField(env, obj, comPortField);
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);
if (checkJniError(env, __LINE__ - 1)) return 0;
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;
unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField);
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
pthread_mutex_lock(&criticalSection);
serialPort *port = fetchPort(&serialPorts, portName);
if (!port)
{
// Create port representation and add to serial port listing
port = pushBack(&serialPorts, portName, "User-Specified Port", "User-Specified Port", "0-0");
}
pthread_mutex_unlock(&criticalSection);
if (!port || (port->handle > 0))
{
(*env)->ReleaseStringUTFChars(env, portNameJString, portName);
@ -437,17 +460,21 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
verifyAndSetUserPortGroup(portName);
// Try to open the serial port with read/write access
pthread_mutex_lock(&criticalSection);
port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1;
if ((port->handle = open(portName, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC)) > 0)
{
// Ensure that multiple root users cannot access the device simultaneously
pthread_mutex_unlock(&criticalSection);
if (!disableExclusiveLock && flock(port->handle, LOCK_EX | LOCK_NB))
{
port->errorLineNumber = lastErrorLineNumber = __LINE__ - 2;
port->errorNumber = lastErrorNumber = errno;
while (close(port->handle) && (errno == EINTR))
errno = 0;
pthread_mutex_lock(&criticalSection);
port->handle = -1;
pthread_mutex_unlock(&criticalSection);
}
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);
while (close(port->handle) && (errno == EINTR))
errno = 0;
pthread_mutex_lock(&criticalSection);
port->handle = -1;
pthread_mutex_unlock(&criticalSection);
}
else if (autoFlushIOBuffers)
{
@ -466,7 +495,10 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
}
}
else
{
port->errorNumber = lastErrorNumber = errno;
pthread_mutex_unlock(&criticalSection);
}
// Return a pointer to the serial port data structure
(*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);
while (close(port->handle) && (errno == EINTR))
errno = 0;
pthread_mutex_lock(&criticalSection);
port->handle = -1;
pthread_mutex_unlock(&criticalSection);
return 0;
}

View File

@ -2,7 +2,7 @@
* SerialPort_Windows.c
*
* Created on: Feb 25, 2012
* Last Updated on: Jan 28, 2022
* Last Updated on: Feb 15, 2022
* Author: Will Hedgecock
*
* Copyright (C) 2012-2022 Fazecast, Inc.
@ -78,8 +78,9 @@ jfieldID eventFlagsField;
typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(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;
CRITICAL_SECTION criticalSection;
serialPortVector serialPorts = { NULL, 0, 0 };
// JNI exception handler
@ -320,31 +321,37 @@ static void enumeratePorts(void)
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
enumeratePorts();
// Get relevant SerialComm methods and fill in com port array
wchar_t comPort[128];
jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialCommClass, 0);
if (checkJniError(env, __LINE__ - 1)) return arrayObject;
for (int i = 0; i < serialPorts.length; ++i)
char stopLooping = checkJniError(env, __LINE__ - 1) ? 1 : 0;
for (int i = 0; !stopLooping && (i < serialPorts.length); ++i)
{
// Create new SerialComm object containing the enumerated values
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)));
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)));
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)));
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)));
if (checkJniError(env, __LINE__ - 1)) return arrayObject;
if (checkJniError(env, __LINE__ - 1)) stopLooping = 1;
// Add new SerialComm object to array
(*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;
}
@ -412,6 +419,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_initializeLibrar
if (checkJniError(env, __LINE__ - 1)) return;
eventFlagsField = (*env)->GetFieldID(env, serialCommClass, "eventFlags", "I");
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)
@ -424,6 +434,9 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibr
// Delete the cached global reference
(*env)->DeleteGlobalRef(env, serialCommClass);
checkJniError(env, __LINE__ - 1);
// Delete the critical section lock
DeleteCriticalSection(&criticalSection);
}
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;
// Ensure that the serial port exists
char continueRetrieval = 1;
EnterCriticalSection(&criticalSection);
if (!portsEnumerated)
enumeratePorts();
serialPort *port = fetchPort(&serialPorts, portName);
if (!port)
{
(*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName);
checkJniError(env, __LINE__ - 1);
return;
}
continueRetrieval = 0;
// Fill in the Java-side port details
(*env)->SetObjectField(env, obj, friendlyNameField, (*env)->NewString(env, (jchar*)port->friendlyName, wcslen(port->friendlyName)));
if (checkJniError(env, __LINE__ - 1)) return;
(*env)->SetObjectField(env, obj, portDescriptionField, (*env)->NewString(env, (jchar*)port->portDescription, wcslen(port->portDescription)));
if (checkJniError(env, __LINE__ - 1)) return;
(*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, friendlyNameField, (*env)->NewString(env, (jchar*)port->friendlyName, wcslen(port->friendlyName)));
if (checkJniError(env, __LINE__ - 1)) continueRetrieval = 0;
}
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
LeaveCriticalSection(&criticalSection);
(*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName);
checkJniError(env, __LINE__ - 1);
}
@ -463,22 +484,24 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
// Retrieve the serial port parameter fields
jstring portNameJString = (jstring)(*env)->GetObjectField(env, obj, comPortField);
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);
if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char disableAutoConfig = (*env)->GetBooleanField(env, obj, disableConfigField);
if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField);
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
EnterCriticalSection(&criticalSection);
serialPort *port = fetchPort(&serialPorts, portName);
if (!port)
{
// Create port representation and add to serial port listing
port = pushBack(&serialPorts, portName, L"User-Specified Port", L"User-Specified Port", L"0-0");
}
LeaveCriticalSection(&criticalSection);
if (!port || (port->handle != INVALID_HANDLE_VALUE))
{
(*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName);
@ -492,9 +515,12 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
reduceLatencyToMinimum(portName + 4, requestElevatedPermissions);
// 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)
{
// Configure the port parameters and timeouts
LeaveCriticalSection(&criticalSection);
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
@ -502,15 +528,17 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(
CancelIoEx(port->handle, NULL);
SetCommMask(port->handle, 0);
CloseHandle(port->handle);
EnterCriticalSection(&criticalSection);
port->handle = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&criticalSection);
}
else if (autoFlushIOBuffers)
Java_com_fazecast_jSerialComm_SerialPort_flushRxTxBuffers(env, obj, (jlong)(intptr_t)port);
}
else
{
port->errorLineNumber = lastErrorLineNumber = __LINE__ - 15;
port->errorNumber = lastErrorNumber = GetLastError();
LeaveCriticalSection(&criticalSection);
}
// 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
PurgeComm(port->handle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
CancelIoEx(port->handle, NULL);
FlushFileBuffers(port->handle);
SetCommMask(port->handle, 0);
// Close the port
port->eventListenerRunning = 0;
port->errorLineNumber = lastErrorLineNumber = __LINE__ + 1;
port->errorNumber = lastErrorNumber = (!CloseHandle(port->handle) ? GetLastError() : 0);
EnterCriticalSection(&criticalSection);
port->handle = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&criticalSection);
return 0;
}

View File

@ -2,7 +2,7 @@
* SerialPort.java
*
* Created on: Feb 25, 2012
* Last Updated on: Feb 14, 2022
* Last Updated on: Feb 15, 2022
* Author: Will Hedgecock
*
* Copyright (C) 2012-2022 Fazecast, Inc.
@ -444,10 +444,16 @@ public final class SerialPort
* <p>
* The serial ports can be accessed by iterating through each of the SerialPort objects in this array.
* <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.
* <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
* interested in in your own application code.
* <p>
@ -501,9 +507,7 @@ public final class SerialPort
serialPort.friendlyName = "User-Specified Port";
serialPort.portDescription = "User-Specified Port";
serialPort.portLocation = "0-0";
synchronized (SerialPort.class) {
serialPort.retrievePortDetails();
}
serialPort.retrievePortDetails();
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,
// and this method may alter or read a global class structure in native code
synchronized (SerialPort.class) {
portHandle = openPortNative();
}
// Start an event-based listener if registered and the port is open
// Natively open the serial port, and start an event-based listener if registered
portHandle = openPortNative();
if ((portHandle != 0) && (serialEventListener != null))
serialEventListener.startListening();
return (portHandle != 0);
@ -689,14 +688,9 @@ public final class SerialPort
if (serialEventListener != null)
serialEventListener.stopListening();
// Natively close the port, and synchronize to the class scope since port enumeration methods are class-based,
// and this method may alter or read a global class structure in native code
// Natively close the port
if (portHandle != 0)
{
synchronized (SerialPort.class) {
portHandle = closePortNative(portHandle);
}
}
portHandle = closePortNative(portHandle);
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
* the native code for this port.
* <p>
* This function must be called while the port is still open as soon as an error is encountered,
* or it may return an incorrect source code line location.
* This function must be called as soon as an error is encountered, or it may return an incorrect source
* code line location.
*
* @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.
* <p>
* This function must be called while the port is still open as soon as an error is encountered,
* or it may return an incorrect or invalid error code.
* This function must be called as soon as an error is encountered, or it may return an incorrect or
* invalid error code.
*
* @return Error number of the latest native code error.
*/