Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix macOS segfault on close after hardware error #211

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

MrDOS
Copy link
Contributor

@MrDOS MrDOS commented Feb 1, 2021

This fixes the JVM crash experienced on macOS after a hardware error occurs. The crash was introduced along with the core hardware-error-detection functionality in #172.

In order to generate the OUTPUT_BUFFER_EMPTY event, the monitor thread event loop periodically checks the status of the serial port output buffer, just as it checks for other state changes. On platforms which have it, and on Windows where it's emulated, it uses the TIOCSERGETLSR ioctl to get the information. On platforms which don't have that ioctl (macOS and FreeBSD <10), the monitor thread starts up another “drain thread” under the monitor thread which loops on tcdrain(3), and generates the event whenever that call unblocks. That separate drain thread is usually terminated by RXTXPort.interruptEventListener(). However, because the monitor thread event loop now internally terminates upon encountering a hardware error, the drain thread cleanup in that interruption method isn't being performed. The fix is to have the event loop call the interruption method when it detects a hardware failure. That ensures all of the normal monitor thread cleanup happens.

However, doing that revealed another, rare race condition where the application thread could encounter the port failure and invoke RXTXPort.close() before the monitor thread began its shutdown (e.g., if the application is sitting on a tight loop around available(), as in the case of my DisconnectTest test case). This manifested in another segfault. The obvious solution was some locking around the internal state of the monitor thread. This already kind of existed in the form of the RXTXPort.MonitorThreadLock boolean and the RXTXPort.waitForTheNativeCodeSilly() method which waited for the flag to become unset in a 5-second sleep loop. I took this as an opportunity to replace that nonsense with a real lock. I chose a read/write lock rather than just synchronized blocks around an object because it more closely matches the semantics of the old behaviour: all of the I/O functions checked that the flag was clear, which is akin to sharing a read lock, and all of the notification reconfiguration methods set/cleared the flag, which is akin to an exclusive write lock.

This should fix the issue @d5smith raised in #197. It probably doesn't fix the original hang-on-close()/disconnect() issue @mvalla is facing, unless the new locking has accidentally cleaned up another concurrency issue.

Just cleaning up the event info struct isn't enough: one some platforms,
there's a “drain loop” thread which runs underneath the monitor thread
to watch for an empty output buffer. This thread needs to be stopped,
too, or it'll segfault and bring down the whole JVM after the event info
struct is freed.
The old `MonitorThreadLock` boolean field was only checked at a very
slow interval (5s!), and, itself not being synchronized, was prone to
race conditions. More importantly, it wasn't set during the monitor
thread's self-cleanup after hardware failure, so under typical access
patterns, the monitor thread and the application thread would both try
to clean up the monitor thread simultaneously. This race condition could
occasionally lead to a segfault (only reproduced on macOS, but I've no
doubt it could happen elsewhere).

I've also attempted to clean up some redundant flag fields, and
consolidate setting the remaining fields to further avoid
concurrency/reentrancy issues.
@d5smith
Copy link

d5smith commented Feb 1, 2021

I can confirm this fixes the behaviour i was seeing on macOS. Thanks!

@MrDOS
Copy link
Contributor Author

MrDOS commented Feb 1, 2021

Great! I have not yet tried this build on any platform other than macOS, nor have I tested behaviour other than failures (have not even run any data through it), so I want to do some more QC before merging. But thank you for trying it so quickly.

@claui
Copy link

claui commented Feb 8, 2024

@MrDOS According to preliminary tests, this appears to be fixing the crashes in interruptEventLoop on our x86_64 Linux boxes.

If combined with PR #249, all the crashes we’ve been getting at disconnect time no longer occur.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants