[HN Gopher] Crossfire: High-performance lockless spsc/mpsc/mpmc ...
       ___________________________________________________________________
        
       Crossfire: High-performance lockless spsc/mpsc/mpmc channels for
       Rust
        
       Author : 0x1997
       Score  : 93 points
       Date   : 2025-11-02 03:07 UTC (19 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | AceJohnny2 wrote:
       | Because I had to look it up:
       | 
       | SPSC = Single Producer Single Consumer
       | 
       | MPMC = Multiple Producer Multiple Consumer
       | 
       | (I'll let you deduce the other abbreviations)
        
         | endorphine wrote:
         | S = Single
         | 
         | M = Multi
         | 
         | ---
         | 
         | C = Consumer
         | 
         | P = Producer
        
         | wartywhoa23 wrote:
         | They could have named that 1:1, 1:n, n:m, n:1, but deemed it
         | too old and unfashionable.
        
           | surajrmal wrote:
           | Which side is producer and which is consumer? I don't think
           | fashion has anything to do with it.
        
             | wartywhoa23 wrote:
             | How do you read "X:Y": "X to Y" or "Y to X"? And how do you
             | treat "to" - as direction from an origin to a destination
             | or vice versa? As in "I send it to you".
        
       | enricozb wrote:
       | When reading this project's wiki [0], it mentions that Kanal
       | (another channel implementation) uses an optimization that "makes
       | [the] async API not cancellation-safe". I wonder if this is the
       | same / related issue to the recent HN thread on "future lock"
       | [1]. I hadn't heard of this cancellation safety issue prior to
       | that other HN thread.
       | 
       | [0]: https://github.com/frostyplanet/crossfire-rs/wiki#kanal [1]:
       | https://news.ycombinator.com/item?id=45774086
        
         | paholg wrote:
         | Futurelock is not about cancellation safety (cancellation is
         | actually one solution to futurelock), though the related issues
         | that are linked in that post are.
        
         | andrepd wrote:
         | Cancellation safety is another thing entirely, but one about
         | which there's also an oxide RFD
         | https://rfd.shared.oxide.computer/rfd/400
        
       | truth_seeker wrote:
       | For Java/JVM :
       | 
       | https://github.com/JCTools/JCTools
        
       | Hamuko wrote:
       | I feel like testing if it'd be faster than tokio::sync::mpsc in
       | my project, but in the context of a websocket client, the
       | performance of just using tokio is already pretty good. Existing
       | CPU usage is already negligible (under a minute of CPU time in
       | >110 hours real time).
        
       | efskap wrote:
       | Using stuff like this, does it make sense to use Rust in a
       | golang-style where instead of async and its function colouring,
       | you spawn coroutines and synchronize over channels?
        
         | thrance wrote:
         | It doesn't matter if you use channels or mutexes to communicate
         | between tasks, you still need your function to be async to
         | spawn it as a coroutine. Your only choice is between coroutines
         | (async tasks spawned on an executor) or regular OS threads.
         | Channels work with both, the rule of thumb is to use async when
         | your workload is IO-bound, and threads when it is compute-
         | bound. Then, it's up to you whether you communicate by sharing
         | memory or share memory by communicating.
        
           | surajrmal wrote:
           | It does matter. Using channels makes control flow much more
           | difficult to understand, but allows you to avoid wrapping
           | everything in its own mutex (or RefCell) and local reasoning
           | is easier to understand. There is also a difference in
           | latency and cpu utilization, both of which still matter in io
           | bound workloads. I honestly don't think it's one or the other
           | but optimal usage is a mix of both depending on specifics of
           | the use case. Channels are great for things that you want to
           | be decoupled from each other but it needs to hit a certain
           | level of abstraction/complexity before it's worth it.
           | 
           | Even folks who write modern go try to avoid overusing
           | channels. It's quite common to see go codebases with few or
           | no channels.
        
             | thrance wrote:
             | I meant, when it comes to choosing between threads or
             | async/await, it doesn't matter if you use channels or
             | something else. Both can be used with them. My original
             | comment wasn't very clear it seems.
             | 
             | Of course it matters what synchronization primitives you
             | choose, for the reasons you gave.
        
           | nicoburns wrote:
           | > Your only choice is between coroutines (async tasks spawned
           | on an executor) or regular OS threads.
           | 
           | Thats not true. There are stackgul coroutine libraries in
           | Rust too. I believe there's one called "may". They are
           | admittedly not that widely used, but they are available.
        
             | steveklabnik wrote:
             | May has unresolvable soundness issues, which is part of why
             | it's not popular.
        
         | mamcx wrote:
         | If you are more like "parallel" than "async" totally yes!
         | 
         | here "parallel" is used in the most broad sense where you have
         | (probably unrelated) tasks that are mostly independent for each
         | other and run to completion. In that case "async" is an anti-
         | pattern. So if you work more process-based that switch-based
         | go!
        
       | reitzensteinm wrote:
       | I'm a little nervous about the correctness of the memory
       | orderings in this project, e.g.
       | 
       | Two acquires back to back are unnecessary here. In general,
       | fetch_sub and fetch_add should give enough guarantees for this
       | file in Relaxed. https://github.com/frostyplanet/crossfire-
       | rs/blob/master/src...
       | 
       | Congest is never written to with release, so the Acquire is never
       | used to form a release chain:
       | https://github.com/frostyplanet/crossfire-rs/blob/dd4a646ca9...
       | 
       | The queue appears to close the channel twice (once per rx/tx),
       | which is discordant with the apparent care taken with the
       | fencing. https://github.com/frostyplanet/crossfire-
       | rs/blob/dd4a646ca9...
       | 
       | The author also suggests an incorrect optimization to Tokio here
       | which suggests a lack of understanding of what the specific
       | guarantees given are: https://github.com/tokio-rs/tokio/pull/7622
       | 
       | The tests do not appear to simulate the queue in Loom, which
       | would be a very, very good idea.
       | 
       | This stuff is _hard_. I almost certainly made a mistake in what I
       | 've written above (edit: I did!). In practice, the queue is
       | probably fine to use, but I wouldn't be shocked if there's a
       | heisenbug lurking in this codebase that manifests something like:
       | it all works fine now, but in the next LLVM version an
       | optimization pass is added which breaks it on ARM in release
       | mode, and after that the queue yields duplicate values in a busy
       | loop every few million reads which is only triggered on Graviton
       | processors.
       | 
       | Or something. Like I said, this stuff is _hard_. I wrote a very
       | detailed simulator for the Rust /C++ memory model, have
       | implemented dozens of lockless algorithms, and I still make a
       | mistake every time I go to write code. You need to simulate it
       | with something like Loom to have any hope of a robust
       | implementation.
       | 
       | For anyone interested in learning about Rust's memory model, I
       | can't recommend enough Rust Atomics and Locks:
       | 
       | https://marabos.nl/atomics/
        
         | embedding-shape wrote:
         | > The tests do not appear to simulate the queue in Loom, which
         | would be a very, very good idea.
         | 
         | Loom is apparently this: https://github.com/tokio-rs/loom I've
         | used tokio a bit in the past, but wasn't aware of that tool at
         | all, looks really useful and probably I'm not alone in never
         | hearing about it before. Any tips&tricks or gotchas with it one
         | should know beforehand?
        
       | tontinton wrote:
       | Can I select multiple receivers concurrently, similar to a select
       | in Linux?
        
       ___________________________________________________________________
       (page generated 2025-11-02 23:01 UTC)