Hi all, it seems like the synchronous programming model is very valuable and useful. So I was wondering why we don't see it more often in other languages that are now mainstream? E.g. language constructs like par/or and par/and seem like are a great, easy way to make parallel compositions and do synchronization, etc... Why don't we see such constructs in newer languages like Rust or Go that also have some focus on safe concurrency?
Some languages have co-routines which are somewhat "compatible" with the synchronous model.
However, to support syntactic parallel compositions, the language really has to embrace and compromise with the execution model, which would affect the whole design of the language.
In particular, the "par/or" is tricky since it must abort another unaware line of execution immediately.
I like this paper discussing immediate/orthogonal abortion: https://link.springer.com/chapter/10.1007%2F3-540-57529-4_44
So, I believe it's too risky and the tradeoffs are not yet fully understood.
These languages tend to have half solutions with "async/await" and similar alternatives.
I also think people in the academia still see the synchronous model as somewhat disingenuous, regradless of all research done.
Another reason is the growth of functional languages which affected research in synchronous languages towards functional reactive programming.
to me I don't see much distinction between a synchronous model and an asynchronous one that has methods to impose synchronization when you need it. The synchronous model is arguably simpler and easier to get right, if that's the "very valuable" part you mean
I believe he's not talking about synchronous/asynchronous call, but actually about their execution semantics (synchronous scheduling) after they start/while they execute (with deterministic preemption, etc).
Asynchronous dispatching is easy in Céu as you suggested. Although, we don't have the
await a or b syntax, we do have the
spawn to get a reference and then could do
par/or do await a; with await b; end.
Synchronous scheduling is what allows the
par/or to be safe and brings all the nice features of Céu. That's also the compromise thing I mentioned and the reason why I think others do not embrace it.
var a = spawn Trail1; var b = spawn Trail2; await a or b;Let spawn now be synchronous. Have
aget priority because it is listed in the code first, like
await...orsemantics so that the completion of
band vice versa. Do those changes effectively reproduce the existing
Let spawn now be synchronous.
Synchronous in which sense?
var a = spawn Trail1; var b = spawn Trail2; await a or b;
would be equivalent to
var a = spawn Trail1; var b = spawn Trail2; par/or do await a; with await b; end
which is equivalent to
par/or do await Trail1; with await Trail2; end
The first syntax existed in the past. The other two are valid now.
par/and(the rejoining behavior) come first before, or "enclose" if you will, the creating of the trails? Does it help with scheduling or concurrency analysis, for instance? I admit the fake spawn/await example I put together feels more familiar to me because it's closer to how lots of languages do it, so I'm wondering what motivated par/or/and etc design choice, whether it be technical or your own personal preference
par/or do await 1s; with await Trail2(); end
par/or do await 1s; with await Trail1(); await 500ms; await Trail2(); end
await 1s or Trail2(), for example. However, I do think your second example would be more verbose if it had to be done with a
var a = spawn do await Trail1(); <etc> end await 1s or a;
I was wondering if abortion of asynchronous code is possible in Céu (e.g. await async/thread) or is it only possible for synchronous code?
Yes, it is possible and supported.
Asynchronous threads exist in Céu for two reasons:
atomicblock for mutual exclusion (e.g., to communicate the result of a computation or blocking input).
par/orwants to abort a nested thread, these are the possibilities:1. The thread is inside an
atomicblock. This is actually not a possibility since the synchronous side could not be executing (mutual exclusion).2. The thread is computing a value.3. The thread is blocked.
par/orwants to abort a thread it does two things:- It calls an ABORT wrapper that, if supported by the underlying API (e.g. pthread_cancel), signals the thread that it should terminate. If not supported, the thread will just waste CPU cycles.
abortedflag in the global structure. In the generated code, when a thread tries to enter an
atomicblock, it first checks this flag to possibly terminate directly.
printfthe user will see its message on the screen.
fcloseat its end, so it should resort to pthread's clean up mechanisms.