Thread-local storage global Arc CEC connection handling#4
Open
trinitronx wants to merge 4 commits intomanio:masterfrom
Open
Thread-local storage global Arc CEC connection handling#4trinitronx wants to merge 4 commits intomanio:masterfrom
trinitronx wants to merge 4 commits intomanio:masterfrom
Conversation
…nnection>>>
Thread-local wasn't working to truly share the _same_ connection across threads
because the mutable borrow was failing. To complicate matters, `CecConnection`
and `CecConnectionCfg` don't implement Copy or Clone traits, nor are they Sync.
`Option<Arc<..>>` implements the Clone trait, so relying on this to store an
Arc to be shared across threads works. Add to this using `OnceLock` for the
global static variable to be initialized only once. Thus, we create a singleton
pattern: the connection initialized in `main()` once becomes the single shared
connection instance, for use in the `on_command_received()` handler function
across libcec threads.
Note: LazyLock was tried, but the initializer proc/closure was always failing to
borrow and reliably access thread-local variables. Since initialization may have
been called from multiple threads, any dereferencing call will block the calling
thread if another initialization routine is currently running.
The way that lower-level cec_rs and libcec-sys libraries implement the callback
functions (e.g. `ICECCallbacks::commandReceived`) is to execute them in another
thread. However, due to the way that Rust type & borrow checked code is run,
we would otherwise lose access to the same `CecConnection` object across the ffi
boundary:
Rust main() -> libcec ffi C code -> thread(s?) -> on_command_received()
Using this `OnceLock` singleton pattern with `Option<Arc<...>>` seems to work.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a bit of an overall refactor to use a singleton
CecConnectionwhich can be shared across threads.The rationale behind doing this is to be able to share the
CecConnectionacross the FFI boundary to any callback handlers (e.g. functions passed toCecConnectionCfg.command_received_callback()). These callback functions (e.g.on_command_received) were previously not able to utilize theCecConnectionattached to the shared hardware CEC adapter. So they could not use it to respond to any unhandled CEC commands (libcecdoes respond automatically to most, but there are some exceptions).In any case, the underlying lower level
libcec_sysandlibcecC code (used through FFI) will spawn any callback functions in separate threads. That poses the issue of how to share aCecConnectionobject in Rust across this boundary. Using this singleton pattern works and passes Rust's borrow and type checking, thanks toArcbeing a thread-safe reference-counting pointer.This should enable us to handle responding to CEC commands in the future within the
on_command_received()callback.Changes:
cargo: Add arrayvec 0.7.1
cargo fmt & Initial implementation of thread-local storage CecConnection handling
debug: Add global atomic thread counter
connection: Refactor
connhandling w/staticOnceLock<Option<Arc<CecConnection>>>Thread-local wasn't working to truly share the same connection across threads
because the mutable borrow was failing. To complicate matters,
CecConnectionand
CecConnectionCfgdon't implementCopyorClonetraits, nor are theySync.Option<Arc<..>>implements theClonetrait, so relying on this to store anArcto be shared across threads works. Add to this usingOnceLockfor theglobal
staticvariable to be initialized only once. Thus, we create a singletonpattern: the connection initialized in
main()once becomes the single sharedconnection instance, for use in the
on_command_received()handler functionacross
libcecthreads.Note:
LazyLockwas tried, but the initializer proc/closure was always failing toborrow and reliably access thread-local variables. Since initialization may have
been called from multiple threads, any dereferencing call will block the calling
thread if another initialization routine is currently running.
The way that lower-level
cec_rsandlibcec-syslibraries implement the callbackfunctions (e.g.
ICECCallbacks::commandReceived) is to execute them in anotherthread. However, due to the way that Rust type & borrow checked code is run,
we would otherwise lose access to the same
CecConnectionobject across the ffiboundary:
Using this
OnceLocksingleton pattern withOption<Arc<...>>seems to work.