1

Al-Qudsi: Implementing truly safe semaphores in rust

 1 year ago
source link: https://lwn.net/Articles/910417/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Al-Qudsi: Implementing truly safe semaphores in rust

[Posted October 5, 2022 by corbet]
Mahmoud Al-Qudsi provides extensive details on what it takes to implement a safe semaphore type in the Rust language.
The problem is that with n > 1, there’s no concept of a “privileged” owning thread and all threads that have “obtained” the semaphore do so equally. Therefore, a rust semaphore can only ever provide read-only (&T) access to an underlying resource, limiting the usefulness of such a semaphore almost to the point of having no utility. As such, the only safe “owning” semaphore with read-write access that can exist in the rust world would be Semaphore<()>, or one that actually owns no data and can only be used for its side effect of limiting concurrency while the semaphore is “owned,” so to speak.

(Log in to post comments)

Al-Qudsi: Implementing truly safe semaphores in rust

Posted Oct 5, 2022 17:37 UTC (Wed) by jezuch (subscriber, #52988) [Link]

> Therefore, a rust semaphore can only ever provide read-only (&T) access to an underlying resource, limiting the usefulness of such a semaphore almost to the point of having no utility.

Well ok then, what is it good for?

Al-Qudsi: Implementing truly safe semaphores in rust

Posted Oct 5, 2022 19:33 UTC (Wed) by excors (subscriber, #95769) [Link]

The article claims it's *not* good for anything, which is why it doesn't exist in the standard library.

I suspect the article is wrong though. "&T" does not mean "read-only". It means the language won't let you assign to the object's fields directly (because it's not an exclusive reference, so the compiler can't be sure you won't assign in two threads concurrently and cause a data race), but the object may still support interior mutability - e.g. it can contain an AtomicU32 which has functions that can safely mutate it through a &T with no risk of data races.

More usefully it could contain a Mutex (which converts a shared reference "&Mutex<T>" to an exclusive reference "&mut T", but only for one thread at a time). You'd use the semaphore so that only N threads can perform some high-level operation concurrently (maybe you only want 4 HTTP requests at once), while the &T uses fine-grained mutexes around any shared mutable data as normal.

I haven't read the article very carefully but I'm not sure why they don't that. As far as I can see, their design has no explicit link between the semaphore and the thing protected by the semaphore (e.g. the object providing the HTTP request API), even though the thing protected by the semaphore must still be accessed by all the threads through some shared &T (which internally uses atomics/mutexes/etc) because that's the only way to share things between threads in Rust. You could (I think) store that &T inside the semaphore object and return it from the wait() function, and that would make it harder to accidentally write code that forgets to acquire the semaphore before using the associated &T. It wouldn't matter in terms of Rust's safety guarantees, but it seems like that'd be an easier-to-use API.

Al-Qudsi: Implementing truly safe semaphores in rust

Posted Oct 5, 2022 19:58 UTC (Wed) by Karellen (subscriber, #67644) [Link]

There may be cases where limiting a resource to having n concurrent readers (and zero writers) has some greater-than-zero utility, which the naive rust semaphore would support.

Al-Qudsi: Implementing truly safe semaphores in rust

Posted Oct 5, 2022 19:40 UTC (Wed) by riking (subscriber, #95706) [Link]

A comment elsewhere notes that a Semaphore can actually be truly useful if it controls access to an array where the elements are interchangeable - Semaphore<T, 5> conceptually owning a [T; 5] and giving out &mut T.

Al-Qudsi: Implementing truly safe semaphores in rust

Posted Oct 6, 2022 12:23 UTC (Thu) by willy (subscriber, #9762) [Link]

That doesn't make sense to me -- you need to know which one is free, not how many other owners there are. Unless you can control that they're stacked (ie nested).

Al-Qudsi: Implementing truly safe semaphores in rust

Posted Oct 6, 2022 1:40 UTC (Thu) by nyanpasu64 (subscriber, #135579) [Link]

> While a strictly binary semaphore (max concurrency == 1) can guarantee that there will never be multiple writers accessing a memory region, there’s not much theoretical benefit to such a binary semaphore over a mutex – in fact, they’re interchangeable.

Binary semaphores aren't useful for purely locking data (wrapping and protecting state), but I still want them for coordinating between threads (transmitting state in of themselves). Unlike mutexes, they allow any thread to release a token, not just a thread (if any) that just acquired one, which is useful in situations where you'd otherwise use a condition variable. Condition variables are actually surprisingly tricky to use right, and using one to simulate a binary semaphore will lead to race conditions if you don't keep a separate condition boolean (alongside a mutex *and* condition variable) and only read it while holding the mutex (https://pvk.ca/Blog/2019/01/09/preemption-is-gc-for-memor...).


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK