Skip to content

Fix unsound unsafe impl Send + Sync for Display (issue #2)#4

Open
moneppo wants to merge 1 commit into
mainfrom
fix/thread-safe-display
Open

Fix unsound unsafe impl Send + Sync for Display (issue #2)#4
moneppo wants to merge 1 commit into
mainfrom
fix/thread-safe-display

Conversation

@moneppo
Copy link
Copy Markdown
Collaborator

@moneppo moneppo commented May 12, 2026

Addresses #2 - unsafe impl Send + Sync are not safe

Replace the unsound unsafe impl Sync for Display with an internal Mutex<SendableDevice>. Concurrent calls to set_mode and redraw are now serialized by the lock, making Display genuinely thread-safe. Display gains Send + Sync automatically from Mutex<SendableDevice> without any explicit unsafe impl on Display itself.

The remaining unsafe impl Send for SendableDevice is isolated in a small newtype with a safety comment explaining why HidDevice is safe to send across threads.

Replace the unsound `unsafe impl Sync for Display` with an internal
`Mutex<SendableDevice>`. Concurrent calls to `set_mode` and `redraw`
are now serialized by the lock, making `Display` genuinely thread-safe.
`Display` gains `Send + Sync` automatically from `Mutex<SendableDevice>`
without any explicit `unsafe impl` on `Display` itself.

The remaining `unsafe impl Send for SendableDevice` is isolated in a
small newtype with a safety comment explaining why `HidDevice` is safe
to send across threads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cceckman cceckman left a comment

Choose a reason for hiding this comment

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

Thanks for taking a stab at this!

I've not had a chance to test this yet; in fact, I had some issues earlier (before this PR) getting a response, but that might be on my end. I'll take another stab at it when I get a chance.

In the mean time, I think we might be OK with just the Mutex; it looks like HidDevice is already Send?

Comment thread src/lib.rs
/// Safety: The C hidapi library serializes concurrent access to a device handle
/// internally, so it is safe to send an HidDevice to another thread.
struct SendableDevice(HidDevice);
unsafe impl Send for SendableDevice {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like HidDevice should already be Send.

The inner device has its own unsafe impl:

https://github.com/ruabmbua/hidapi-rs/blob/7c8358fd275b394df588cc7ad513693b46d4d908/src/hidapi.rs#L160

and it's a restriction on HidDeviceBackend:

https://github.com/ruabmbua/hidapi-rs/blob/7c8358fd275b394df588cc7ad513693b46d4d908/src/lib.rs#L121

So I think HidDevice should already be Send. If we remove this unsafe impl, does everything still compile?


In general, the unsafe code should only be in the crate that does the unsafe->safe bridge, FFI and whatnot. (In this case,hidapi.) It tells us what guarantees are provided.

Comment thread src/lib.rs
Comment on lines +137 to +138
/// Safety: The C hidapi library serializes concurrent access to a device handle
/// internally, so it is safe to send an HidDevice to another thread.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I didn't see this in the C code or the docs, but I'm less of an expert there; do you have a link to where this gets implemented / how it's accomplished?

If it's internally synchronized, it should implement Sync as well. But given the hidapi crate doesn't implement Sync for HidDevice, I'm suspicious that it doesn't actually have internal synchronization.

(Even if it does implement Sync, we might want a Mutex on our accessor to synchronize the Glider-level transactions.)

Comment thread src/lib.rs
#[pyclass(frozen)]
pub struct Display {
device: HidDevice,
device: Mutex<SendableDevice>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
device: Mutex<SendableDevice>,
// The Glider API has a request/response protocol. We want to make sure we capture the response for a given request, so we lock the device for each transaction.
device: Mutex<SendableDevice>,

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.

2 participants