4 ms·
Rust doesn't help with implementing low level primitives like this, you'd have to use unsafe code.
by eloff 4y ago
Rust doesn't help with implementing low level primitives like this, you'd have to use unsafe code.
- cesarb 4y ago> Rust doesn't help with implementing low level primitives like this A charitable reading of the parent comment might be that Rust already implements these particular low-level primitives, so that by using Rust, you are not using the problematic glibc pthread code. See https://blog.rust-lang.org/2022/06/30/Rust-1.62.0.html#thinner-faster-mutexes-on-linux https://blog.rust-lang.org/2022/06/30/Rust-1.62.0.html#thinn... for the relevant announcement: "Previously, Mutex, Condvar, and RwLock were backed by the pthreads library on Linux. [...] Rust's standard library now ships with a raw futex-based implementation of these locks on Linux, [...]"
- jstimpfle 4y agoIn either case you need to use some implementation of synchronisation primitives, and that could contain bugs. Where's the difference?
- asveikau 4y agoWhich means they have plenty of opportunities to introduce their own bugs.
- dundarious 4y agoglibc's impl is a "raw futex-based" one as well. I'm no great fan of glibc as an organization or body of code, but the comparative benefits of Rust here are not well explained.
- tialaramex 4y agoAlthough glibc's implementation is indeed "raw futex-based" it is not merely a raw futex, of course. What you get from glibc isn't a futex (tricky to use properly) but instead a C structure type named pthread_mutex_t. It's quite big. 40 bytes! All Rust wanted was a futex, which for reference is just any 32-bit aligned value. The pthread_mutex_t is less useful to Rust, yet it's ten times bigger! So, Mara's new Mutex<T> in the Linux builds of Rust is just a futex (32-bits) plus a poison boolean plus your type T which is protected by the mutex. If your type is Zero Size then this rounds up to 8 bytes, five times smaller than pthread_mutex_t. If your type was just a u16 (unsigned 16-bit integer) then it rounds up to the same size, but you're also protecting your 16-bit unsigned integer which is nice. Now, if you're protecting huge data structures with mutexes, you don't care, it makes no measurable difference. But if you heard futex is small (it is) and protected loads of tiny objects with one each (say, individual 32-bit floating point X,Y co-ordinate pairs) Rust now makes your choice cost roughly what you expected instead of bloating your program for no reason. It's also a bit faster (but probably not enough that you should care) and simplifies some other book keeping in Rust itself. [ Above I wrote about Mutex, for Condvar the argument is similar but I haven't spent lots of time understanding the innards of Condvar whereas I spent a week reading Mutex stuff ]
- gpderetta 4y agoCondvar and mutexes are for very different use cases though, and condvars are notoriously hard to get right, especially when supporting all the full set of posix behaviours with good performance.
- tialaramex 4y agoYou need a mutex to actually use condition variables. So, even if Rust's Condvar were just as bulky and complicated as the POSIX one offered by glibc, you saved on the associated mutex. However from a quick glance Rust's Condvar is much smaller than pthread_cond_t Condition variables per se don't seem that difficult. Mara did the Linux and Windows condition variables, both are pretty elegant. As with a graceful swan, there is lots happening out of sight (in the futex code Rust needs to cope with the fact that when people do futexes for $notLinux including OpenBSD and Fuchsia they inevitably put a spin on the idea, adding something they thought was cool or ignoring a hard problem they didn't fancy solving) but the general idea is very simple. In contrast the glibc implementation is fiendishly complicated, but that's not because of POSIX, it's because they decided to do something fiendishly complicated, it hardly seems fair to demand that everybody who didn't need that complexity should respect how difficult it was anyway. Example, glibc promises if six threads A, B, C, D, E, F wait on a condition variable in order and then it is signalled exactly four times, threads A, B, C and D are definitely woken and E, and F are definitely not woken. That's potentially useful for some purposes, but it's expensive, what if I don't care about this? Too bad, the price is baked in even though POSIX says nothing about this feature. Rust doesn't care, so, if A, B, C, D, E, F wait on the CondVar and then it gets signalled exactly four times, well, four threads wake, but it could be any four. If A always waits again, signalling four times could just wake A four times, and each time it goes back to sleep. That seems useless, but if you wrote A, that's on you.
- gpderetta 4y agoFor what is worth, which threads are guaranteed to be unblocked is defined by the standard, at least under realtime scheduling. I'm sure the rust devs got their condvar implementation exactly right at the first try, including cancellation, RT support, support for robust mutexes, wait morphing, process shared cond vars, etc. For human programmers like the glibc ones, it took many decades, and there are very few that are confident enough to make large changes. As I understand, there was no posix correct cond var for windows for many years untill after vista MS decided to expose the right primitives.