Skip to content

Protect transportWrite with writeMutex#43

Merged
tomthuneby merged 1 commit intomasterfrom
emdj/lock-in-writeFrame
Jul 15, 2025
Merged

Protect transportWrite with writeMutex#43
tomthuneby merged 1 commit intomasterfrom
emdj/lock-in-writeFrame

Conversation

@Emil-Juhl
Copy link

Guard usage of the transportWrite and encoding frames into the writeBuffer by locking the writeMutex within writeFrame rather than only within write.

The write function relies on repeated call to read that need to be performed by a separate thread from the user of hdlcpp. This is because write will block until a frameAck or frameNack is received or a timeout is hit.
The hdlcpp implementation, however, also performs writes on the transport layer from within the read function itself. It does so in the process of either sending a frameAck or frameNack upon receival of a data frame.

From this it is aparent that any user of hdlcpp is basically forced into a race condition that can potentially mangle frames on the wire. The race can be hit by simply receiving data frames while trying to transmit data frames as well.
Since the function which actually encodes data into the writeBuffer and calls the transportWrite callback is writeFrame the write mutex should be locked here and not on the write function. This makes sure that if the thread handling read were to attempt transmitting a frameAck while another thread was attempting to transmit a data frame, then these two frames don't get mixed together.

Of course, one can argue that now the write API is not "thread safe" since it doesn't have its own distinct mutex. However, it should be fair to place the burden of thread safety for calls into write on the user of the respective hdlcpp instance.

@Emil-Juhl Emil-Juhl force-pushed the emdj/lock-in-writeFrame branch from c01de16 to 4ca08ae Compare July 14, 2025 12:19
@Emil-Juhl Emil-Juhl requested a review from jeppefrandsen July 14, 2025 12:20
@Emil-Juhl
Copy link
Author

@jeppefrandsen thanks for the review! After some discussion with @tomthuneby we decided on introducing a new mutex instead. Our client code may be calling into hdlcpp->write from multiple threads and others might be doing the same. I think my previous approach was maybe a bit too heavy handed with regards to changing the semantics of Hdlcpp::write with respect to the thread_safety around sequence number logic etc.

@jeppefrandsen
Copy link
Contributor

@jeppefrandsen thanks for the review! After some discussion with @tomthuneby we decided on introducing a new mutex instead. Our client code may be calling into hdlcpp->write from multiple threads and others might be doing the same. I think my previous approach was maybe a bit too heavy handed with regards to changing the semantics of Hdlcpp::write with respect to the thread_safety around sequence number logic etc.

I also think this is more safe. Did not check the current client side code (only on mobile 😅)

Guard usage of the `transportWrite` and encoding frames into the
`writeBuffer` by adding a `writeFrameMutex` to be locked within
`writeFrame`.

The `write` function relies on repeated calls to `read` that need to be
performed by a separate thread from the user of hdlcpp. This is because
`write` will block until a `frameAck` or `frameNack` is received or a
timeout is hit.
The hdlcpp implementation, however, also performs writes on the
transport layer from within the `read` function itself. It does so in
the process of either sending a `frameAck` or `frameNack` upon receival
of a data frame.

From this it is aparent that any user of hdlcpp is basically forced
into a race condition that can potentially mangle frames on the wire.
The race can be hit by simply receiving data frames while trying to
transmit data frames as well.
Since the function which actually encodes data into the `writeBuffer`
and calls the `transportWrite` callback is `writeFrame`, adding a mutex
to specifically guard this function can prevent the race. This makes
sure that if the thread handling `read` were to attempt transmitting a
`frameAck` while another thread was attempting to transmit a data frame,
then these two frames don't get mixed together.

By introducing a new mutex rather than just moving the existing
`writeMutex`, the thread safety of the sequence numbering increments
within `write` is kept as-is.
@Emil-Juhl Emil-Juhl force-pushed the emdj/lock-in-writeFrame branch from 4ca08ae to 00793dd Compare July 14, 2025 12:38
@Emil-Juhl Emil-Juhl requested a review from tomthuneby July 14, 2025 12:38
@tomthuneby tomthuneby merged commit eab9c13 into master Jul 15, 2025
1 check passed
@tomthuneby tomthuneby deleted the emdj/lock-in-writeFrame branch July 15, 2025 09:52
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

Comments