> <i>It achieves this in ways that seem subtle to us-- clearly so, since it wasn't really discovered and applied until the late 2010's</i><p>I'm getting a little annoyed by the structured concurrency crowd ignoring the synchronous concurrency paradigm as a historical precedent. It has existed since the 1980s and as far as I can tell there is a huge overlap in concepts. The only major difference I'm seeing is the addition of scoped <i>asynchronous</i> concurrency on top of synchronous concurrency (meaning single-threaded concurrency). That's significant for sure, but also not so much that it's cool to ignore the existing previous work.<p>Here's Céu, a language from 2011 with scoped synchronous concurrency:<p><pre><code> input int KEY;
par/or do
every 1s do
_printf("Hello World!\n");
end
with
await KEY;
end
</code></pre>
(Prints the “Hello World!” message every second, terminating on a key press.)<p>And hey, since we're talking about Lua, maybe we can acknowledge LuaGravity, which has existed since 2009?<p>Don't get me wrong though, I'm glad structured concurrency is catching on and that new work is being done in this area.<p>[0] <a href="https://en.wikipedia.org/wiki/Synchronous_programming_language" rel="nofollow">https://en.wikipedia.org/wiki/Synchronous_programming_langua...</a><p>[1] <a href="http://ceu-lang.org/" rel="nofollow">http://ceu-lang.org/</a><p>[2] <a href="https://fsantanna.github.io/luagravity/" rel="nofollow">https://fsantanna.github.io/luagravity/</a>
This is taking advantage of coroutines and to-be-closed variables.<p>To-be-closed variables were recently introduced in Lua 5.4. Lua runs a destructor method when these variables go out of scope, like the RAII pattern in c++.<p>Coroutines have been in Lua for quite a while now. They allow cooperative concurrency (as opposed to preemptive concurrency a-la threads). It works similar to async generators as found in Python, C#, etc, except that Lua coroutines are stackful. You can yield inside an inner function and it'll pause the whole call stack. In the article, the yield is inside the async_sleep function. When you call a function that yields, you don't need to explicitly mark that with an "await".
I find concurrency (and threading) concepts in Lua fun and all, but these topics seem to ignore practical issues for academic, historical, or theoretical ones.<p>I use Lua a lot, but the global interpreter lock or lua_lock/lua_unlock implementation detail is one I don't see people talk too much about.<p>It's particularly frustrating when you want to do something advanced, like pull in an FFI for Lua, but also implement GIL behavior.<p>I haven't ever seen someone implement this correctly. I think it should be easy in theory, but I haven't given it a go in a while.<p>The current state of things is, you can use LuaJIT and accept no thread safety, or use PUC-Rio Lua with a custom lua_open/lua_lock implementation and use the FFI library separately. I haven't actually seen anyone do the latter. So in the field, it's all an untested concept, I think.<p>Maybe someone out there is doing this today and not talking about it. But there's no open source solution. Lua Lanes doesn't address this concern, and stylistic adaptions of cooperative multitasking don't either.<p>I realize it's technically a different topic, but it's basically the same area of concern.<p>Except, well, I find there to be fewer practical use cases for cooperative multitasking versus preemptive.
The equivalent for python is trio<p><a href="https://trio.readthedocs.io/en/stable/" rel="nofollow">https://trio.readthedocs.io/en/stable/</a>
I skimmed the code example and although I understand that it spawn two tasks, the syntax is not so clear for me. I rather prefer the actor-based model of concurrency of Elixir/Erlang instead of async functions and Go channels. I somewhat biased, though.