6 ms·
Structured concurrency and Lua
- belmarca 4y agoDoes HN have a solid book recommendation on the topic of concurrency?
- ufo 4y agoThis is taking advantage of coroutines and to-be-closed variables. 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++. 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".
- JonChesterfield 4y agoOne of the things you can do in a coroutine is spawn a new process, with stdin etc wired up, and yield while waiting for the new process to do things. That puts a really convenient API on top of concurrent batch operations.
- belm0 4y agoThis doesn't work well in practice, because part of structured concurrency (at least as Python Trio introduced it) is to have a sane story about cancellation and timeouts. There isn't a safe way to cancel an OS thread or process short of some prearranged handshaking-- the thread or process being wrapped certainly needs to cooperate. Coroutines give you an easy way to manage cancellation. Essentially, any yield back to the scheduler is an opportunity to inject a cancel into the task. The code run within the task doesn't need to do anything special (beyond not starving the scheduler).
- JonChesterfield 4y agoThat's an interesting observation. The process-in-coroutine I use doesn't do cancellation. I think I'd implement it by having lua send a signal to the child process which seems sufficient.
- vanderZwan 4y ago> 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'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 asynchronous 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. Here's Céu, a language from 2011 with scoped synchronous concurrency: input int KEY; par/or do every 1s do _printf("Hello World!\n"); end with await KEY; end (Prints the “Hello World!” message every second, terminating on a key press.) And hey, since we're talking about Lua, maybe we can acknowledge LuaGravity, which has existed since 2009? Don't get me wrong though, I'm glad structured concurrency is catching on and that new work is being done in this area. [0] https://en.wikipedia.org/wiki/Synchronous_programming_language https://en.wikipedia.org/wiki/Synchronous_programming_langua... [1] http://ceu-lang.org/ http://ceu-lang.org/ [2] https://fsantanna.github.io/luagravity/ https://fsantanna.github.io/luagravity/
- belm0 4y agoI'm open, as always, to refining my understanding of what structured concurrency is, what various forms it takes on, and how it came about. I don't think it's so obvious that the article has misstated, however. I was not-quite-intentionally using a broader notion of structured concurrency, including not only the initial concept (tie task hierarchy to code structure, propagate errors up the call + task stack), but also critical pieces that are built on top of that, notably cancel/timeout scopes [1]. I don't think these pieces had been assembled to form something powerful enough to make concurrency manageable, until about the time of Nathaniel J. Smith's articles and Python Trio. I'm revising the text slightly, and adding a footnote: > It achieves this in ways that seem subtle to us-- clearly so, since its utility didn't reach critical mass until around 2018† > † While it's argued that the core concept of structured concurrency has existed in some libraries and languages prior to the recent boom, these lacked significant parts of the story for making concurrency manageable. One is powerful, ergonomic control of timeouts and cancellation-- itself dependent on structured concurrency. Another is striving for a program that follows structured concurrency as a whole, including direct and transient dependencies. Nathaniel J. Smith was first to piece this all together, with a set of articles, and corresponding Python Trio concurrency library. Though I suspect this will welcome more controversy ;) [1] https://vorpus.org/blog/timeouts-and-cancellation-for-humans/ https://vorpus.org/blog/timeouts-and-cancellation-for-humans...
- _ZeD_ 4y agoThe equivalent for python is trio https://trio.readthedocs.io/en/stable/ https://trio.readthedocs.io/en/stable/
- andrewmcwatters 4y agoI find concurrency (and threading) concepts in Lua fun and all, but these topics seem to ignore practical issues for academic, historical, or theoretical ones. 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. It's particularly frustrating when you want to do something advanced, like pull in an FFI for Lua, but also implement GIL behavior. 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. 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. 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. I realize it's technically a different topic, but it's basically the same area of concern. Except, well, I find there to be fewer practical use cases for cooperative multitasking versus preemptive.
- belm0 4y agoActually, there are advantages to embracing single-OS-thread programming. It's an incredibly efficient use of CPU resources, and you generally don't need explicit locking. Resulting programs are much easier to understand, maintain, and diagnose. (I work daily on a complex, soft real-time, production app written by a team of developers, and the code has no locks.)
- luaduran 4y ago
- auraham 4y agoI 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.