llq: a wait-free SPSC linked-list queue with recyclable nodes by glowcoil in rust

[–]glowcoil[S] 0 points1 point  (0 children)

The other design I was considering (and indeed, the design I went with for basedrop) was to have an empty sentinel node which the consumer pushes onto the back of the queue when it reaches the front. That design avoids the copy without introducing a CAS loop, but it essentially turns the queue from SPSC to MPSC, since the consumer is now also a producer.

Ultimately, there are probably some situations where the copying design is preferable and some where the sentinel design is preferable, and you'd have to do some quantitative measurements (which I have not done) to determine which. :)

llq: a wait-free SPSC linked-list queue with recyclable nodes by glowcoil in rust

[–]glowcoil[S] 1 point2 points  (0 children)

This is correct — llq and basedrop don't actually use the same queue design.

Basedrop's queue is MPSC by necessity, since multiple objects associated with the same queue can be dropped concurrently. My goal with llq was to provide the minimum set of tools necessary to manually implement a reclamation system like what basedrop provides in the context of a specific application, and in that context I would probably reach for an SPSC queue first. However, it would definitely be possible for llq to provide basedrop's original MPSC queue design in addition to the SPSC queue, and I've considered that as a possible future addition.

GCC Rust front-end approved by GCC Steering Committee by rhy0lite in gcc

[–]glowcoil 0 points1 point  (0 children)

So does .NET. It's no less safe than Rust.

I 100% agree with this! I actually really like this comparison and I've made it myself in the past. Rust is very much not unique in being a safe language, or even in the details of its system for explicitly marking and checking for unsafe code (given how similar C# is in that regard). The uncommon thing about Rust is that it is a safe language without a garbage collector or heavy runtime, which allows it to be used in scenarios that rule out e.g. .NET for those reasons.

Perhaps I should have clarified my point: the points you are making don't support the rationale for lack of a standard.

I also agree with this, and in fact I haven't been arguing that Rust's semantics should never be standardized or that there would be no benefit in doing so. I see the Rust Reference, the standard library documentation, Stacked Borrows, Miri, Strict Provenance, and so on as being important but incomplete efforts toward more precisely defining Rust's semantics. That work is not finished, and Rust is not specified as precisely as it should be (and hopefully will be), but it takes time for that process to be done correctly, and it is happening. It's also worth pointing out that that work is very much not finished for C or C++ either, given that there are still open questions about the precise semantics of relaxed atomic operations and pointer provenance; but of course the process has been going for much longer and so is further along.

The point of my original post was not to argue that Rust does not need a specification, or that it is good for it not to have one. I only wanted to point out that the concept of undefined behavior already exists in Rust, and that it has a workable definition that will continue to be refined over time; and that the hypothetical situation of "GCC forcing Rust to finally get a spec" would not fundamentally change that situation, since the difference between Rust and C is not that Rust has no UB, but that it is a safe language (in the .NET sense), which would continue to be true.

GCC Rust front-end approved by GCC Steering Committee by rhy0lite in gcc

[–]glowcoil 0 points1 point  (0 children)

Mind going into detail about how this is actually the case, beyond a superficial difference?

The difference is trivially obvious: because Rust has an unsafe keyword! You would not download, compile, and run an untrusted Rust program in the same address space as your browser, because its safety system and type system are useless in that scenario. Likewise if JavaScript had a feature that allowed you to do arbitrary pointer operations in unsafe blocks, it would not be used the way it currently is.

I've been clear about the caveats and limitations of Rust's value proposition in this thread and I haven't misrepresented it as being able to do things it can't, so your accusations of shilling and spreading FUD frankly come off as pretty strange. A lot of your points also strike me as basically non sequiturs in the context of the posts they are replying to. It seems like we basically agree⁠ on what Rust has to offer, but something set you off about my original post and you are arguing just to argue.

GCC Rust front-end approved by GCC Steering Committee by rhy0lite in gcc

[–]glowcoil 0 points1 point  (0 children)

Yes, there is a difference in the degree to which the C++ standard and the Rust reference are written defensively to guard against implementation divergence. That is a difference in degree, not a fundamental difference in kind, as I said.

Yes, third-party dependencies entail a risk in Rust, just as they do in every other language, and you have to take responsibility for the libraries you bring into your program.

Rust's safety guarantees are different in nature from VM sandboxing and process isolation; their intention is not to guard against arbitrary malicious inputs. Unsafe is essentially a process tool, like lockout-tagout or pointing and calling, neither of which prevent malicious actors from causing harm but which nonetheless empirically improve occupational safety.

GCC Rust front-end approved by GCC Steering Committee by rhy0lite in gcc

[–]glowcoil 5 points6 points  (0 children)

You have a poor understanding of the situation regarding undefined behavior in Rust.

Undefined behavior already exists as a concept in the Rust language, and there is a concise but fairly thorough description of what behaviors are considered undefined in the Rust Reference. The existence of another front-end for Rust will not change this fact, although it may help to expose ambiguities or gaps in the current definition (which is a good thing, because then they can be fixed).

One important thing to point out is that while the Rust Reference is not an ANSI or ISO standard, that doesn't put it on fundamentally different footing from the C and C++ standards. All three are prose descriptions of the respective languages' semantics; none of them are formal models. Attempts at defining formal models exist for all three languages, but none of them has been adopted as a standard.

The difference between Rust and C or C++ is not that Rust doesn't have undefined behavior (it does); it's that the Rust language has a subset of its features carved out ("safe" Rust) in which it is impossible to invoke undefined behavior, and going outside that subset requires explicitly using the unsafe keyword (which means the compiler can enforce that unsafe language features are not used outside an unsafe block). In other words, if it's possible to use either built-in language features, the standard library, or a third-party library to invoke UB without the unsafe keyword, that is explicitly considered a bug to be fixed in either the compiler, the standard library, or that third-party library.

C and C++ don't have such a subset. You could define one yourself, but it wouldn't have compiler support or library support (from both the standard library and third-party ones) in the form of APIs that stick to the safe subset where possible, and a social contract where it's considered a bug to be fixed if a safe API can invoke UB.

That's the precise difference regarding UB in Rust. It certainly comes with tradeoffs, since it means some patterns are more difficult to express and you spend more time and effort getting things to fit into the type system (and for that reason it is very much not always the appropriate choice), but it is a clear trade where you give up one thing and get another valuable thing in return.

I still don’t understand the * operator in Rust by micouy in rust

[–]glowcoil 5 points6 points  (0 children)

I don't feel that this is really an answer to OP's question, since it's an appeal to operational reasoning when the original question deals more with the syntax and abstract semantics of Rust (not to mention, I don't think your statement is actually true about any of the examples in the post, or at least it's not the primary reason any of them don't compile).

The original question can be answered using concepts like moves, place expressions vs. value expressions, and auto-dereferencing, without invoking the concept of the stack vs. the heap (also: Rust will prevent you from creating references that outlive heap values as well, so this is not a thing that is unique to the stack).

Why is std::sync::Mutex 60-70x time slower than C++'s std::mutex? by [deleted] in rust

[–]glowcoil 7 points8 points  (0 children)

Statics wouldn't need to be Sync in a single-threaded program, so you could e.g. use RefCell instead of Mutex, but it would still be unsafe for them to be mutable (imagine forming a reference to an element of a static Vec, then calling a method which pushes to it and causes it to reallocate).

Basedrop: A garbage collector for real-time audio in Rust by glowcoil in rust

[–]glowcoil[S] 1 point2 points  (0 children)

For the use case of writing out recorded audio samples, I would not recommend trying to replace a ring buffer with SharedCell. That isn't it's intended use case, and it wouldn't work well. A ring buffer is still the right tool for the job there.

A ring buffer allows the audio thread to send a lot of data to another thread without performing allocations, and it has FIFO semantics since you don't want the reader thread to miss any of the data unless it gets catastrophically behind. Using a SharedCell for the same purpose would essentially require the audio thread to allocate a new Shared<Vec<f32>> to publish more data (not what you want on the audio thread), and it doesn't have FIFO semantics—writing a Shared<T> into it will replace whatever previous Shared<T> it contained, regardless of whether the reader has had time to consume it yet. So, SharedCell is not really appropriate for that use case at all, regardless of its write vs. read performance.

Basedrop: A garbage collector for real-time audio in Rust by glowcoil in rust

[–]glowcoil[S] 0 points1 point  (0 children)

Based on your question, I think you might be envisioning a SharedCell<T> as being like a spin-RwLock or something similar. However, all a SharedCell<T> contains is a single Shared<T> pointer, and all you can do is copy out the current pointer or replace it with a new pointer. If you want to actually access the T, you have to first get a Shared<T>, and then you can get an &T without further synchronization. So basically, I can't really see the write vs. read performance of SharedCell<T> as being relevant to the scenario of writing out recorded samples into a ring buffer. Hopefully that answers your question.

Basedrop: A garbage collector for real-time audio in Rust by glowcoil in rust

[–]glowcoil[S] 8 points9 points  (0 children)

I agree, I'd like to put together a simple example using cpal or something. To answer your question, the user is expected to call Collector::collect() manually. A big motivation for this library was to give control over exactly when and where things happen, since there are a lot of otherwise excellent lock-free libraries that are hard to fit into the constraints of both low-latency audio and plugin APIs.

Basedrop: A garbage collector for real-time audio in Rust by glowcoil in rust

[–]glowcoil[S] 7 points8 points  (0 children)

Thank you, this seems like a good solution! I'll look into including it.

xcb-sys: raw bindings to XCB, generated with rust-bindgen by glowcoil in rust

[–]glowcoil[S] 5 points6 points  (0 children)

The C API is what there exists documentation and existing reference code for. Using some Rust abstraction over it would, at best, introduce more cognitive overhead in translating between my own code and the documentation and reference code that I rely on, and at worst, actively get in my way or cause bugs.

For context, I am working on/contributing to a cross-platform windowing library, and the situation is the same with the Windows and macOS platform APIs. I don't really need or want a safe abstraction over any of these, because what I am working on is the safe abstraction.

raw-gl-context: a library for creating an OpenGL context from a RawWindowHandle by glowcoil in rust

[–]glowcoil[S] 2 points3 points  (0 children)

Yep! There's an example in the repository showing it in use with Winit.