I have written a basic implementation and proposal and am looking for feedback about usefulness. It does have one glaring feature missing which is the 'select' or 'alt' blocks.
>> How is this compare to Boot Fiber channels and John Bandela's Channels (cppcon '16)?
For the Boost Fiber Channels (BFC), the proposed implementation is more inline with implementations of channels as they are known outside of C++.
I don't know whether Go-style channels are implementable in present-day C++; you might need to get more coroutine-awareness in the language first somehow (and in fact I would be pleasantly shocked if Gor's coroutines proposal was anywhere close to aware enough).
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a157469b-d311-447a-917b-724ea3cb5123%40isocpp.org.
>> - Channel is bidirectional only. I would expect an additional producer channel that would have a make_consumer member function enforcing single producer multiple consumers pattern (not certain if multiple producers single consumer would also be useful).I don't quite understand this statement. I can say that channels do not and should not have any concept of directionality - they are always bidirectional. A thread given a channel can either read from it or write to it and that is by-design. I can definitely see the potential benefit from a wrapper class that enforces a single direction, but that would be an extension to a fully bidirectional channel.
On Wednesday, October 12, 2016 at 10:53:40 PM UTC-7, Aaron R Robinson wrote:>> - Channel is bidirectional only. I would expect an additional producer channel that would have a make_consumer member function enforcing single producer multiple consumers pattern (not certain if multiple producers single consumer would also be useful).I don't quite understand this statement. I can say that channels do not and should not have any concept of directionality - they are always bidirectional. A thread given a channel can either read from it or write to it and that is by-design. I can definitely see the potential benefit from a wrapper class that enforces a single direction, but that would be an extension to a fully bidirectional channel.But _why_ is it by design?
Bidirectionality is trivially implemented (efficiently) using two unidirectional channels, but a unidirectional channel built over a bidirectional channel is nothing more than a limitation imposed with no memory usage improvements. It seems to me that unidirectional channels are the simplest and least error-prone building block.
[...] This again pointing out that unidirectional channels are safer building blocks. They're far harder to use incorrectly, especially if you leverage the type system appropriately in your interface e.g. creating a unidirectional channel should provide two separate objects - a producer end and a consumer end - while a bidirectional channel is just two objects that each contain a producer and a consumer end (from two separate channels).
What Golang calls a "channel" is a unidirectional SPSC concurrent bounded queue with capacity 1, in that reads and writes are guaranteed to block — and again the producer and consumer "ends" are logically separated. (I intuitively believe that these channels implement the coroutine switching I described above, but I admit I don't know for sure.)Golang also supports "buffered channels", a.k.a. unidirectional SPSC concurrent bounded queues of any specified capacity.
a misuse of the term "channel" as Boost.Fiber's.
2016-10-21 21:37 GMT+02:00 Arthur O'Dwyer <arthur....@gmail.com>:What Golang calls a "channel" is a unidirectional SPSC concurrent bounded queue with capacity 1, in that reads and writes are guaranteed to block — and again the producer and consumer "ends" are logically separated. (I intuitively believe that these channels implement the coroutine switching I described above, but I admit I don't know for sure.)Golang also supports "buffered channels", a.k.a. unidirectional SPSC concurrent bounded queues of any specified capacity.
I'm wondering because my understanding was that Go channels are per default unbuffered with capacity 0.
A push operation is blocked (goroutine gets suspended) till another goroutine consumes the pushed data; a pop operation will block (suspending the goroutine) if no producer is waiting or immediately return otherwise.
Buffered Go channels are created by specifying a capacity (at least 1) at creation.
Go channels are per default bidirectional (push and pop operations can be invoked on the channel by the same goroutine) but can created as unidirectional: 'c chan<- int' == only push operations on channel c allowed, 'c <-chan int' == only pop operations allowed.
What I'm missing?
a misuse of the term "channel" as Boost.Fiber's.in which sense?
I don't believe that the term "channel" should be used to refer to a "concurrent queue" object. The point of a channel, in my mind, is that it is SPSC and preferably capacity-1 (or capacity-0 in Go's terms); this is what lets you make it fast. If you add features until you've got a bidirectional MPMC concurrent bounded queue — or in Boost.Fiber's case a bidirectional MPMC concurrent unbounded queue — then not only do you lose a lot of performance,
At the very least, if you're designing a data structure whose public API is exactly the public API of std::queue, please call it some variation on "queue" or "concurrent_queue" so that people can find and understand it! If you call it "channel", I for one am going to assume that it's got semantics that are somehow different from a queue's.
You're correct that Golang's own documentation describes its channels as "capacity 0"; but in the terminology I was using, that would mean you could never put anything into the channel because it would always be full! Whether you think of it as "0 capacity, 1 element allowed in-flight as a special case" or "1 capacity, 0 elements allowed in-flight as a special case" is basically just an implementation detail of the theory AFAIC. :)
Please remember that the concept Aaron is describing is not a "channel" as implemented by Golang, etc.; it's a "concurrent bounded queue", as implemented by TBB, Lawrence Crowl's P0260, etc. Let me explain what I'm thinking...
If a function needs both ends, it can take both as parameters, or use a wrapper object, such as a `bichannel<T, U>` that offers `operator<<(T const&)` and `operator>>(U&)`.
--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/PFsRPzrUrJ8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CA%2Bwfc18%3DH1Rwkjz%3D2Bvy8gVekLXbyyzvq5%3Dvc1cdbPWNqcDO%3DQ%40mail.gmail.com.
On Sun, Oct 23, 2016 at 12:18 AM, Oliver Kowalke <oliver....@gmail.com> wrote:2016-10-23 0:18 GMT+02:00 Sean Middleditch <sean.mid...@gmail.com>:If a function needs both ends, it can take both as parameters, or use a wrapper object, such as a `bichannel<T, U>` that offers `operator<<(T const&)` and `operator>>(U&)`.so operator<<() == push() and operator>>() == pop(), that means no try-push() and try_pop(),.
are operator<<() and operator>>() blocking in general (for instance producer waits till item has been consumed) or does it block only at the slow path?Just a note, that is a question more to other parts of the thread. I'd personally prefer push/try_push; I was just using the stream interface proposed earlier to illustrate an unrelated point. I personally think the overloaded operators here are bonkers.
What about following:Despite of this the question is how queues are distinguished from channels or is it a synonym for the same object.A channel is used for synchronization via message passing but a (concurrent) queue does serve this too.
- a concurrent_queue provides asynchronous mp
- a (C++) channel provides synchronous message passing, e.g acts as an rendezvous point
There has been a lot of comments about Promises/Futures in this proposal. I can't stress enough that Promises/Futures are solving a different problem and not related to channels, although they can be used to create more complex solutions. Channels give a guarantee that they will block when requesting data but the channel does not have data this is the intent. Returning a Promise breaks that fundamental model. However, there is nothing saying you can't have a channel of Promises/Futures, that is definitely possibly, but goes against the intent of CSP.
So the bottom line is that I still can't imagine how the proposed channel would bring me some benefits and make my life easier when thinking about and implementing solutions to certain problem types.
Thanks for the feedback Domen.
On Wednesday, October 26, 2016 at 11:52:24 AM UTC-7, Domen Vrankar wrote:So the bottom line is that I still can't imagine how the proposed channel would bring me some benefits and make my life easier when thinking about and implementing solutions to certain problem types.I think the above statement is really important and probably were a lot of confusion is coming from.
The slew of use cases you provide all have one thing in common and that is the threading model is always implied. Futures/Promises/coroutines, all of them have an implied concurrency concept that in most cases is non-trivial to break out of. The channel type is much more akin to a mutex then it is to a Future or Promise as it is merely a synchronization primitive that allows multiple threads/coroutines/processes/etc to coordinate in a unified manner. In fact in the CSP model the term processes is used, not threads. In Limbo you could communicate through a channel with other processes or spawned threads. They were all treated equally and this allowed a large amount of flexibility since it was irrelevant what was on the other end of the channel, just that when you read from a channel either you waited or were given data/message.