fpagliughi / rust-industrial-io

Rust interface to the Linux Industrial I/O subsystem
MIT License
45 stars 21 forks source link

Thread safety #16

Open pcercuei opened 3 years ago

pcercuei commented 3 years ago

The readme states that "the contexts and devices in the underlying libiio are not thread safe."

They actually are, though. You can't obviously access some resources at the same time (e.g. trying to refill a buffer in two different threads at the same time), but you can read/write attributes in one thread and manipulate the buffer in another thread, it works just fine.

tomstokes commented 3 years ago

Analog doesn't guarantee thread-safety of the libiio functions, even if some of them happen to be usable across threads in their current implementation ( https://ez.analog.com/linux-software-drivers/f/q-a/90775/safe-to-assume-that-libiio-calls-are-thread-safe/197587#197587 )

The unreleased master branch exposes iio_context_clone to allow cloning contexts across threads: https://github.com/fpagliughi/rust-industrial-io/commit/14de9c2179ddfad3537ceb6bff6c22ebef80615e#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5

pcercuei commented 3 years ago

Thread safety is guaranteed in the libiio functions. Libiio has been developed from the ground up with the idea that it should be thread-safe.

fpagliughi commented 3 years ago

@pcercuei Thanks for bringing this up! I have been in the same boat as @tomstokes; after reading some old blog posts, etc, was under the assumption that the library was not thread safe.

The problem with Rust (oops... I mean the great thing about Rust) is that it requires you to be completely explicit about things in regard to thread safety. So the statement "you can't access ... some resources at the same time" gets a little tricky. But I will definitely look into it for the next version.

fpagliughi commented 2 years ago

Having thought about this for a while, I'll likely split the difference...

Having believed that the contexts were not thread-safe, I made all the Rust objects !Send and !Sync, meaning that individual objects can not be shared across threads (!Sync) and they can not even be moved from one thread to another (!Send). This means that all objects derived from a context are forced to live in the same thread as that context.

Ha! Try that in C. :smile:

But, with this new information, I'll start relaxing those constraints, starting by making Device movable across threads (i.e. Send). This would mean that the Context needs to be shared across those threads, and thus needs to be Sync.

As a first step, it would mean that a Device still couldn't be shared across threads, and that a Buffer object would still need to live in the same thread as the Device. With some further information about what's thread safe and not, I assume that this could also be relaxed.

But certainly it sounds like the Buffer::refill() function should take a mutable reference to self which would prevent it from being called by two threads simultaneously even if it were shared between them.

I'll take these one step at a time, with a release in between to try and get it right.

...and I'll update the README.