EMILUA(3em)

πŸ“¦ version 0.12.1, in nixos 26.05, nixpkgs unstable

πŸ’– Emilua β€” Lua execution engine πŸ’–

okay so bestie, you opened the EMILUA(3em) page and it is literally serving Emilua, the Lua execution engine. no cap, this is the whole vibe. πŸ¦‹


🌸 NAME 🌸

Emilua β€” the Lua execution engine. periodt. πŸ‘‘

✨ SYNOPSIS ✨

here's the group-chat screenshot, bestie: πŸŽ€

local condition_variable = require('condition_variable')

local function queue_consumer()
    scope(function()
        scope_cleanup_push(function() queue_mtx:unlock() end)
        queue_mtx:lock()
        while #queue == 0 do
            queue_cond:wait(queue_mtx)
        end
        for _, e in ipairs(queue) do
            consume_item(e)
        end
        queue = {}
    end)
end

and that's literally a condition variable. πŸ’…

πŸ’… FUNCTIONS πŸ’…

new() β†’ condition_variable

Constructor. it makes the condition variable. iconic. ✨

wait(self, m: mutex)

go read pthread_cond_wait() β€” that's the reference, bestie. 🧚

wait() is a cancellation point. Prior to the delivery of the cancellation request, the underlying mutex is re-acquired under the hood. no cap. 😭

notify_all(self)

go read pthread_cond_broadcast(). that's it. that's the tweet. 🐦

notify_one(self)

go read pthread_cond_signal(). slay. πŸ’…

πŸ”₯ NOTIFYING WITHOUT A LOCK πŸ”₯

okay bestie, this is the spicy part. If the condition variable, the notifier fiber and the waiting fiber all run in the same thread (and cooperative multitasking is used instead preemptive multitasking), then there is enough level of determinism to lift one restriction that exists in traditional condition variables:

Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread. β€” https://en.cppreference.com/w/cpp/thread/condition_variable

The reason why this restriction on the notifier fiber/thread exists is to avoid a race. Consider the following waiter fiber and the notifier fiber:

local function consumer()
    scope(function()
        scope_cleanup_push(function() m:unlock() end)
        m:lock()
        while not ready do
            c:wait(m)
        end

        -- ...
    end)
end

local function producer()
    ready = true
    c:notify_one()
end

Pay attention to the points when the waiter fiber checks if the event has been signalled by testing ready and the instant it blocks on c.wait(). If the notifier fiber mutates the shared variable and calls c.notify_one() between these two points, then the signalization is lost. c.notify_one() would be called by the time there would be no fiber blocked on c.wait(). That's why the notifier fiber needs to mutate the shared variable through a mutex. πŸ’€

In Emilua, this restriction doesn't apply (as long as there are no suspension points between the time the waiting fiber tests the condition and calls c.wait()) and the notifier fiber can mutate the shared variable without holding a lock on the mutex. In this case, the condition variable essentially becomes a non-suspending way (post semantics) to unpark a parked fiber. and yes β€” I've exploited this property in the past to avoid a few round-trips 😌. iykyk.


mwah β€” this iconic page is from Emilua 0.12.1-nixpkgs1, dated 1980-01-01 πŸ«ΆπŸ’–

πŸ“¦ where this page comes from