I’m interested in using Rust for realtime audio stuff. In these use cases the audio rendering thread has to be absolutely non-blocking and can start at any moment, potentially descheduling all running threads, so your data structures have to be in a valid state at all times. As such, you’d generally use constructs based on (lock-free!) atomics. For example, a volume slider simply communicates a single value so you’d use a AtomicFloat. Locks/mutexes are generally avoided, often like the plague, as anything blocking your audio thread causes trouble.
This gets tricky whenever the data you’re interested in gets too large to update atomically (generally >64 bits), such as an audio sample. Whenever your non-audio thread changes the sample, you want your audio callback to read either AAAAAAAA or BBBBBBBB, but never AAABBBBB (which could happen if one of your threads is descheduled). You can do this by creating an indirection using an atomic pointer. This points to your object and now your realtime callback always reads either object A or object B, never some halfway in between state. A more in-depth and much better explanation can be found here:
Atomic pointers work well, but several decades of experience have taught us that handling raw pointers is a very bad idea. In my program I have a lot of structures that I can reference in multiple places, so I would like to use reference counting. Now I’m trying to create some sort of Arc/AtomicPtr hybrid, that uses reference counting for non-realtime threads and allows wait-free, atomic “fleeting” references on realtime threads:
- Non-realtime threads should interact with the value as if it was an Arc (e.g. reference counted)
- A realtime thread should be able to get a valid reference from get_fleeting at any time, without waiting
- This object should not be deleted while the realtime thread is using it
- The destructors can only be run on non-realtime threads
This playground shows more or less the interface I have in mind (RtUsableArc) and a toy problem, but I still have two problems:
- There is a data race in load(), as thread 1 could get a pointer, thread 2 could delete that Arc/object and thread 1 would then try to transmute the now invalid data into an Arc again
- I need a way to not run the destructor in safe_delete until all fleeting references are gone (currently it simply waits a few ms, although I plan on moving that to some form of epoch-based as in crossbeam)
Any ideas on how to best tackle this problem?