Crate blinkcast

source ·
Expand description

Fast, Bounded, Lossy broadcast channel with no_std support. This is implemented with ring buffer and atomic operations, it may be considered lock-free, as we don’t use Lock premitive, but the implementation may spin waiting for a contending writer/reader to finish accessing a specific node. Its very rare, but maybe I won’t call it lock-free in the strict sense.

The API of the blinkcast is similar to that of the std::sync::mpsc channels. However, there are some differences:

  • It allows for multiple consumers (receivers) and multiple prodocuers (senders).
  • The channel broadcasts every send to every consumer.
  • Lossy, the sender will overwrite old data, so receivers must be quick or they will lose the old data (don’ t blink).
  • Implemented for no_std environments.

The data sent must implment Clone, because it will be kept in the buffer, and readers can read it multiple times.

The original object will remain in the buffer until its overwritten, at that point it will be dropped. Thus be careful if the value is a large allocation for example big Arc. One of the clones (original) will be kept by the buffer and will result in a delayed deallocation if that was not expected by the user. See issue #1

§Example

Single sender multiple receivers

use blinkcast::alloc::channel;

let (sender, mut receiver1) = channel::<i32>(4);
sender.send(1);
sender.send(2);

let mut receiver2 = receiver1.clone();

assert_eq!(receiver1.recv(), Some(1));
assert_eq!(receiver1.recv(), Some(2));
assert_eq!(receiver1.recv(), None);

assert_eq!(receiver2.recv(), Some(1));
assert_eq!(receiver2.recv(), Some(2));
assert_eq!(receiver2.recv(), None);

Multiple senders multiple receivers

use blinkcast::alloc::channel;
use std::thread;
let (sender1, mut receiver1) = channel::<i32>(100);
let sender2 = sender1.clone();

let t1 = thread::spawn(move || {
    for i in 0..50 {
        sender1.send(i);
    }
});
let t2 = thread::spawn(move || {
    for i in 0..50 {
        sender2.send(i);
    }
});

t1.join().unwrap();
t2.join().unwrap();

let mut receiver2 = receiver1.clone();

let mut sum1 = 0;
let mut sum2 = 0;
for i in 0..100 {
   let v1 = receiver1.recv().unwrap();
   let v2 = receiver2.recv().unwrap();
   sum1 += v1;
   sum2 += v2;
   assert_eq!(v1, v2);
}
assert_eq!(sum1, 49 * 50);
assert_eq!(sum2, 49 * 50);

Another example using the static_mem module (no allocation)

use blinkcast::static_mem::Sender;

let sender = Sender::<i32, 4>::new();
let mut receiver1 = sender.new_receiver();
sender.send(1);
sender.send(2);

let mut receiver2 = receiver1.clone();

assert_eq!(receiver1.recv(), Some(1));
assert_eq!(receiver1.recv(), Some(2));
assert_eq!(receiver1.recv(), None);

assert_eq!(receiver2.recv(), Some(1));
assert_eq!(receiver2.recv(), Some(2));
assert_eq!(receiver2.recv(), None);

Modules§

  • A channel implemented with heap allocated buffers (require alloc feature).
  • A channel implemented with static memory.

Constants§

  • The maximum length of the buffer allowed on this platform It will be 2^16 - 1 on 32bit platforms and 2^32 - 1 on 64bit platforms