Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- run: mvn -P "${{ matrix.profile }}" --batch-mode

macos:
runs-on: [macos-latest]
runs-on: [macos-14]
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -106,4 +106,4 @@ jobs:
java-version: 11
distribution: temurin

- run: mvn -P "${{ matrix.profile }}" --batch-mode
- run: mvn -P "${{ matrix.profile }}" --batch-mode
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.5)
Comment thread
tresf marked this conversation as resolved.
cmake_policy(SET CMP0048 NEW)
cmake_policy(SET CMP0042 NEW)

Expand Down
41 changes: 40 additions & 1 deletion src/main/cpp/_nix_based/jssc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,13 +907,52 @@ const jint events[] = {INTERRUPT_BREAK,
//EV_RXFLAG, //Not supported
EV_TXEMPTY};


/* OK */
/*
* Collecting data for EventListener class (Linux have no implementation of "WaitCommEvent" function from Windows)
*
*/
JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_waitEvents
(JNIEnv *env, jobject, jlong portHandle) {
( JNIEnv*env, jobject, jlong portHandle, jint waitEventsTimeoutMs) {
int err;

/* Code in `LinuxEventThread.run()` (in `SerialPort.java`) calls us
* in an infinite-loop. As a work-around, it uses a (very) small
* sleep, to not utilize a full CPU all the time. But still, this
* permanently wastes a lot of CPU cycles (that many, that it is a
* problem in our production use-case). The win32 code uses
* `OVERLAPPED` structs and `WaitSingleObject()` which already
* provide that kind of "wait" mechanism. But we do not have a
* win32-API here. As this impl here returns immediately, we'll
* first ask `poll()` (if available and enabled). This way we can
* "emulate" to actually wait if nothing is ready.
* See also JavaDoc of `SerialPort.setWaitEventsTimeoutMs(int)`. */
int const isFeatureEnabled = (waitEventsTimeoutMs >= 1);
if( isFeatureEnabled ){
#if !HAVE_POLL
static unsigned cnt = 0;
if( ((cnt++) & 0xFFFF) == 0 ){
fprintf(stderr, "WARN: waitEventsTimeoutMs not available on your platform, as `poll()` not available.\n");
}
#else
struct pollfd pfd = {0};
pfd.fd = portHandle;
pfd.events = POLLIN | POLLPRI | POLLRDHUP;
err = poll(&pfd, 1, waitEventsTimeoutMs);
if( err == -1 ) switch( errno ){
case EINTR:
/* Got interrupted by signal. Go report events we have so far. */
break;
default:
/* some error occurred. */
err = errno; /* bkup `errno` before calling into `FindClass()` */
jclass exClz = env->FindClass("java/lang/RuntimeException");
if( exClz ) env->ThrowNew(exClz, strerror(err));
return NULL;
}
#endif
}

jclass intClass = env->FindClass("[I");
if( intClass == NULL ) return NULL;
Expand Down
6 changes: 3 additions & 3 deletions src/main/cpp/jssc_SerialNativeInterface.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/main/cpp/windows/jssc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,12 +447,13 @@ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak
return returnValue;
}


/*
* Wait event
* portHandle - port handle
*/
JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_waitEvents
(JNIEnv *env, jobject, jlong portHandle) {
( JNIEnv*env, jobject, jlong portHandle, jint/*unused on windows*/ ){
HANDLE hComm = (HANDLE)portHandle;
DWORD lpEvtMask = 0;
DWORD lpNumberOfBytesTransferred = 0;
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/jssc/SerialNativeInterface.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,23 @@ public static String getLibraryVersion() {
*/
public native int getEventsMask(long handle);

/**
* Convenience overload for {@link #waitEvents(long, int)}, NOT using
* the {@link SerialPort#setWaitEventsTimeoutMs(int)} feature.
*/
public int[][] waitEvents(long handle){ return waitEvents(handle, -1); }

Comment on lines +232 to +237

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather remove this overload. First, it is unused in SerialPort class, which is generally what the library users use to interact with native code, not SerialNativeInterface (serialInterface is a private field in SerialPort, how to even get a reference to it in normal code)?

Secondly it's unnecessary if we have an option to reset waitEventsTimeoutMs (see other comment).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is unused in SerialPort class

Agreed, this is more of a @Deprecated API for those wanting a native drop-in, which I'm on board for not supporting, but I don't want to minimize others' use-cases, quoting:

This project, not intentionally but more indirectly seems to depends on deploying the binary through another way than the java parts (agree, ugly). Due to way too complicated reasons. There's a way too long chain of technical details why this is the case 😔 So yes, the java part in unlucky circumstances can be a mismatching version compared to the shared objects 🙁

/**
* Wait events
*
* @param handle handle of opened port
*
* @param waitEventsTimeoutMs See {@link SerialPort#setWaitEventsTimeoutMs(int)}.
*
* @return Method returns two-dimensional array containing event types and their values
* (<b>events[i][0] - event type</b>, <b>events[i][1] - event value</b>).
*/
public native int[][] waitEvents(long handle);
public native int[][] waitEvents(long handle, int waitEventsTimeoutMs);

/**
* Change RTS line state
Expand Down
31 changes: 30 additions & 1 deletion src/main/java/jssc/SerialPort.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class SerialPort {
private final String portName;
private volatile boolean portOpened = false;
private boolean maskAssigned = false;
private int waitEventsTimeoutMs = -1;

//since 2.2.0 ->
private volatile Method methodErrorOccurred = null;
Expand Down Expand Up @@ -920,6 +921,34 @@ public boolean setFlowControlMode(int mask) throws SerialPortException {
return serialInterface.setFlowControlMode(portHandle, mask);
}

/**
* Reduce busy-waiting CPU load in {@link #waitEvents()}.
*
* (This is irrelevant for windows)
*
* The {@link #waitEvents()} implementation on non-windows systems
* usually returns immediately. This is unfortunate for the callers
* which want to await events in an infinite-loop. As doing so would
* burn lot of CPU time.
*
* This setting can be used to reduce that load. For regular
* incoming data events this does not cause any further delays.
* {@link #waitEvents()} still will reports most of the events as
* soon they become available, even before the specified timeout got
* reached.
*
* BUT BE AWARE: Enabling this might delay delivery of some special
* serial-events (like 'DCD line changed' or 'RI line changed') by
* the amount of time specified. So you have to decide yourself if
* you can/will afford this trade.
*/
public void setWaitEventsTimeoutMs(int waitEventsTimeoutMs) {
if (waitEventsTimeoutMs <= 0) {
throw new IllegalArgumentException(String.valueOf(waitEventsTimeoutMs));
}
this.waitEventsTimeoutMs = waitEventsTimeoutMs;
}
Comment on lines +945 to +950

@pietrygamat pietrygamat Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bothers me. This method fails on <=0, native codes tests for >= 1, and the default value is -1. The inconsistent condition aside, it feels like the more natural approach would be to allow 0/negative to disable the feature. If it can be enabled in runtime, there should also be a way to disable it.


/**
* Get flow control mode
*
Expand Down Expand Up @@ -951,7 +980,7 @@ public boolean sendBreak(int duration)throws SerialPortException {
}

private int[][] waitEvents() {
return serialInterface.waitEvents(portHandle);
return serialInterface.waitEvents(portHandle, waitEventsTimeoutMs);
}

/**
Expand Down
Loading