5 ms·
Real world programs I've written in CSP style have made use of Alt (or select in Go) on mixed collections of receive and send channels. One simple use case is
by dfawcus 3y ago
Real world programs I've written in CSP style have made use of Alt (or select in Go) on mixed collections of receive and send channels.
One simple use case is for a dynamic graph of channels, where one wishes to be able to 'cancel' a blocking send, that being done by having a 'cancel' read channel which one can select upon. Then one uses an Alt over both the send and the receive, with the expectation that usually it is the send which completes.
I've used that pattern in recent Go code.
- Rochus 3y agoThanks. But do you really need another channel for "cancel", i.e. couldn't you just close the blocking channel? Of course I can add yet another built-in procedure, e.g. SELECT() which accepts both in and out channels; but since Oberon tries to be as simple as possible, I shouldn't do so as long as there are feasible work-arounds.
- dfawcus 3y agoYes one needs another channel. No, one can not just close the channel, because it is the sending end (which can close the channel) which is blocked, and the desire is to tear down the graph upon which its send is blocked. Certainly in Go, if one closes the channel upon which the sender is blocked, then it panics due to sending on a closed channel. It doesn't matter if one closes the channel before the send, or closes from another goroutine during the blocked send, the sender will still panic. So the only way to safely unblock the sender, is for its blocking send to be part of a select containing a read channel. Then when woken up, the send channel can be closed.
- Rochus 3y agoOk, I see, thanks. I will therefore add a SELECT procedure. The interface would look like in SEND and RECEIVE, but I could add an integer as the first parameter for the number of in channels, then one could first list all in and then all out channels. What do you think? Is there an added value for a non-blocking variang (i.e. like default in the Go select statement)? I could do that e.g. with a boolean parameter. EDIT: so far my SEND would not panic if the channel closes or is already closed, but just return 0; I thought that there could be yet another process instead of a cancel channel and this process could close channels even if another process is blocked on it, but maybe the SELECT solution would be more elegant (not sure though). EDIT2: or how about a SELECT procedure with just the in channels first (instead of the number), then a boolean (true..non blocking), and then the out channels?
- Rochus 3y agoYet another idea. SEND(VAR c: CHANNEL OF T; v: T) and RECEIVE(VAR v: T; VAR c: CHANNEL OF T), i.e. just a single send/receive, not built-in select, and data flows from right to left. SELECT( { VAR vN: Tn; VAR cN: CHANNEL OF Tn [;] } { VAR cM: CHANNEL OF Tm; vM: Tm [;] } ), i.e. distinguish receive or send by the order of value and channel, instead of an explicit number or a boolean separator. Still a boolean param could be added to allow non-blocking selects.
- 082349872349872 3y agoThis looks like it may wind up reinventing select(2), for which one should maybe consult poll(2) to see where that approach wound up. The underlying problem is that Oberon lacks an unordered select (WHILE ELSIF comes close but has the unfortunate —in this case, but not for many actor scenarios— repetition). Maybe something could be done with a CASE Poll(...) OF ... END? If life were a Ponyhof, I would ask for something along the lines of: PROCEDURE sendit(text: STRING): BOOLEAN; VAR fail: BOOLEAN; FailChan: CHANNEL OF BOOLEAN; BEGIN fail := FALSE; FailChan := After(TimeOut); SELECT SENDABLE(TxChan) THEN SEND(TxChan, text) | RECEIVABLE(FailChan) THEN RECEIVE(fail, FailChan) END RETURN fail END sendit; but that'd obviously be too large a change.
- Rochus 3y agoThanks. I'm neither happy yet with the SELECT() function. Concerning your proposal, I would rather not introduce a completely new control structure. But one could - like Wirth with the type CASE statement in Oberon-07 - reuse the WITH statement like: WITH SEND(TxChan, text) DO | RECEIVE(FailChan, fail) DO END where I would just have to change the Guard a bit like Guard = qualident ':' qualident | SEND ActualParameters | RECEIVE ActualParameters and even ELSE is available. What do you think?
- dfawcus 3y agoI had a read of the page after you updated it. I find the proposed SELECT function to be unwieldy, to the extent that one would really want some syntax added instead. Assuming I'm correctly inferring from the above, the WITH looks better. Presumably one could have multiple SEND and/or RECEIVE function clauses in the WITH statement, so acting as an equivalent to the Go Select? What would ELSE do? It it acting the same as the default clause in a Go select, i.e. allowing for a non-blocking call? The one other question with the above is how would one detect a closed channel when the RECEIVE clause hits?
- dfawcus 3y agoA simpler example would be wishing to have a send with a timeout. A simple implementation in go would be like: func sendit(text string) bool { select { case TxChan <- text: case <-time.After(10 * time.Millisecond): return false } return true }
- Rochus 3y agoOk, that's a good example, thanks; unless we would allow a timer process to close TxChan there seems to be no other way than a mixed send/receive select.