namib-project / libcoap-rs

Rust bindings to and wrapper around the libcoap C library
BSD 2-Clause "Simplified" License
6 stars 1 forks source link

Multithreading support and memory management rework #22

Open pulsastrix opened 1 month ago

pulsastrix commented 1 month ago

Now that libcoap seems to be getting multithreading support, we should add support for it too. This is also a great opportunity to rework the memory management a bit, as changes to it will be necessary for multithreading anyways.

This issue is also a prerequisite for #2.

Implementation

As libcoap's thread-safety is optional, we need to support both non-thread-safe and thread-safe libcoap. To optimize performance, we might need to adjust the memory model accordingly.

For thread-safe libcoap

General thread-safety model of libcoap

See https://libcoap.net/doc/reference/develop/coap__threadsafe__internal_8h_source.html for a general description of how libcoap's thread safety works. In essence, libcoap currently works using a global lock that is held for all operations that may affect a context or any of its associated structures (sessions, resources, ...)

Proposed memory model of libcoap-rs

Our current app_data are replaced with parking_lot::ReentrantMutexes, which then contain a RefCell to the wrapper struct.

do_io (and any other methods that might invoke a callback) will drop their borrow of the wrapper struct before actually performing the operation, which allows callbacks to acquire and access the wrapper struct. Accesses to the underlying coap_context_t are also possible, as libcoap's global lock also allows re-locking from within callbacks.

Care needs to be taken to ensure that no deadlocks may occur if more than one wrapped struct is needed at the same time (e.g. CoapSessions and CoapContexts). This may require additional changes that allow obtaining the raw context pointer without first locking the session. At the minimum, we need to define a consistent order in which structures must be locked.

For non-thread-safe libcoap

We can simplify our implementation a bit by replacing our "lendable" smart pointers with simple Rc<RefCell<T>>s. Due to the changes above, we will always release borrows before possible callbacks can occur, and therefore lending is not necessary.