Comments (4)
No, and I'm not inclined to add such a feature. I think it would be more appropriate to build it on top of the existing API using existing channels as building blocks. I'm on mobile right now, but let me know if you want me to explain more.
from chan.
Sure, if you don't mind.
from chan.
OK, so one of the main problems with providing a "broadcast feature" is that there are many different ways to do it. Does the producer block until all consumers have received a value? Do all available consumers get sent a value or do only consumers who are ready to receive a value get sent a value? Merging this type of API with the existing Sender
/Receiver
types complicates things even more. In particular, in order to broadcast a value, it must be copyable, which is a restriction that isn't needed for simple ownership transfer like what the existing channels use.
My first recommendation therefore is explore the specific problem you're trying to solve. What semantics do you need? A really simple broadcaster is pretty easy to write. Here's one:
extern crate chan;
use std::sync::RwLock;
use std::thread;
pub struct Broadcaster<T> {
registry: RwLock<Vec<chan::Sender<T>>>,
}
impl<T: Clone> Broadcaster<T> {
pub fn new() -> Self {
Broadcaster { registry: RwLock::new(vec![]) }
}
/// Create a new listener of broadcasted values.
///
/// Every listener created by this method will receive all values sent by
/// the `broadcast` method.
pub fn listener(&self) -> chan::Receiver<T> {
let (send, recv) = chan::sync(0);
let mut registry = self.registry.write().unwrap();
registry.push(send);
recv
}
/// Broadcast `value` to all listeners.
///
/// This call blocks until all listeners have received a copy of `value`.
pub fn broadcast(&self, value: T) {
let registry = self.registry.read().unwrap();
for sender in &*registry {
sender.send(value.clone());
}
}
}
fn main() {
let broadcaster = Broadcaster::new();
let mut handles = vec![];
for i in 0..10 {
let listener = broadcaster.listener();
handles.push(thread::spawn(move || {
println!("[thread {}] received: {}", i, listener.recv().unwrap());
}));
}
broadcaster.broadcast(42);
for h in handles {
h.join().unwrap();
}
}
This broadcaster will block until all listeners have received the sent value.
There are lots of variations available here. For example, the above code lets you work with chan::Receiver
, which means the chan_select!
macro is still useful. But it does not let you work with chan::Sender
, since you must send values using the special broadcast
method. Another approach could change that. For example:
#[macro_use]
extern crate chan;
use std::thread;
pub struct Broadcaster<T> {
writer: chan::Sender<T>,
register: chan::Sender<chan::Sender<T>>,
}
impl<T: Clone + Send + 'static> Broadcaster<T> {
pub fn new() -> Self {
let (writer_tx, writer_rx) = chan::sync::<T>(0);
let (register_tx, register_rx) = chan::sync(0);
thread::spawn(move || {
let mut registry: Vec<chan::Sender<T>> = vec![];
loop {
chan_select! {
register_rx.recv() -> sender => {
match sender {
None => break,
Some(sender) => registry.push(sender),
}
},
writer_rx.recv() -> value => {
match value {
None => break,
Some(value) => {
for sender in ®istry {
sender.send(value.clone());
}
}
}
},
}
}
});
Broadcaster {
writer: writer_tx,
register: register_tx,
}
}
pub fn channel(&self) -> (chan::Sender<T>, chan::Receiver<T>) {
let (send, recv) = chan::sync(0);
self.register.send(send);
(self.writer.clone(), recv)
}
}
fn main() {
let broadcaster = Broadcaster::new();
let mut handles = vec![];
for i in 0..10 {
let (_, listener) = broadcaster.channel();
handles.push(thread::spawn(move || {
println!("[thread {}] received: {}", i, listener.recv().unwrap());
}));
}
broadcaster.channel().0.send(42);
for h in handles {
h.join().unwrap();
}
}
This one uses a thread for each broadcaster and handles synchronization between channels. Notice that the channel
method returns the ends of two different channels. Another difference is that sends no longer block on all receivers having received a value. (Semantically, the sender has a buffer of size 1, since subsequent sends cannot begin until all receivers have received a value.) Other valid semantics include using non-blocking sends to receivers.
A major downside (IMO) of the above two approaches is that the Receiver
values returned by listener
and channel
are still independently cloneable. Those clones will not have broadcast semantics. (Which could actually be desirable! It is yet another variation.)
I think the key point I'm trying to convey is that since there are so many ways to do it, we have two realistic options:
- Don't include it. Let people devise their own solutions depending on their specific problem.
- Spend a bunch of time trying to craft an API that covers most use cases. I think it will be hard to make this API easy to understand.
I'm more inclined toward (1). If there is a simple solution to (2), then I might be willing to accept a PR.
from chan.
Thanks for the writeup! I see your reasoning. Closing this in favor of writing my own broadcaster.
from chan.
Related Issues (19)
- remove hyper dev dependency HOT 4
- Possible memory leak in `tick` HOT 2
- [feature request] re-expose try_recv() HOT 8
- How to chan_select over multiple chans that will terminate? HOT 1
- Using chan_select! requires values to be 'static HOT 3
- Add timeout-able methods HOT 1
- doesn't seem to work with `fork()` HOT 2
- Select integration with std lib mpsc api HOT 4
- Allow chan_select to generate no-op None variants HOT 2
- Sender::send(t) -> Result<(), SendError> HOT 16
- Deadlock with select-send and select-recv HOT 2
- crossbeam-channel – EOL chan HOT 6
- Return value from `chan_select!` expression HOT 2
- chan::async() conflicts with Rust 2018 Edition HOT 6
- I think it's butter to return a Result for invalid add HOT 5
- Relicense under dual MIT/Apache-2.0 HOT 8
- Async channel is blocking program HOT 9
- All Notifier subscriptions made in select.rs appear to be leaked HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from chan.