xopxe / lumen

Lua Multitasking Environment.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Wrong behavior

jsetoain opened this issue · comments

Newly created tasks receive a scheduling step even when the scheduler hasn't been started. Even the example code shows a wrong behavior because of this:

local sched=require 'sched'

-- task emits signals
sched.run(function()
    for i = 1, 10 do
        sched.signal('an_event', i)
        sched.sleep(1)
    end
end)

Right here, the emitter task already signaled 'an_event' with value 1. As nobody is waiting for the signal, it gets lost.

-- task receives signals
sched.run(function()
    local waitd = {'an_event'}
    while true do
        local _, data = sched.wait(waitd)
        print(data)
    end
end)

Our receiver task waits for the signal and blocks, but it does so BEFORE we fire up the scheduler.

sched.go()

Now the emitter signals 'an_event' with value 2, which is consumed by the receiver task and everything goes as expected. I also find weird that the scheduler ends when the emitter task finishes, even though the receiver task is stuck in an infinite loop. Not sure that's the right behavior...

If you swap the order both tasks, first the one that waits then the one that emits, everything behaves as you expect. I will correct the example, as it is indeed more intuitive that way. Also, the scheduler can be seen as running from the start (the "sched.go()" call probably should've been called "sched.loop()")

Notice that the fact that you can miss events is independent from the fact that the sched.go() hasn't been called yet. If a task contains several wait() calls, or sleep(), it can miss an event (see wait buffering, pipes and streams).

The go() call ends when there is no task ready no run or waiting for a timeout. That means all tasks are blocked waiting for some events, and thus will never be waken (there is nobody to emit said events).

Yes, of course. I wasn't trying to say that the behavior was wrong because the signal got lost (I can see why this is happening), but because it got send (and lost) before I invoke sched.go(). This is not the behavior you find in Sierra's scheduler, for instance, and it's a bit odd (and potentially problematic) that scheduling begins before you enter the scheduling loop; I might want to make some preparations in between that change the behavior of tasks (modify global variables, wait for some event, sleep, setup some specific hardware...) and I would expect for things to work just fine. If scheduling is going to start as soon as I begin to add tasks to the scheduler, I think that's one of those things that should be clearly stated. IMHO, it's a bit counterintuitive.

I don't see much of a problem with stopping when tasks are stuck in endless waits, I was pointing it out just in case.

Knowing this is the intended behavior is enough for me, though. Thank you :-)

I see. Lumen plain signals are different from Sierra's (or so I gather): emitting a signal yields the control to waiting tasks immediately. This is for the purpose of lower latencies in responding to signals a the cost of one more yielding function. You can have Sierra-like "deferred" signals using sched.schedule_signal(). Then the signals will be processed by the scheduler itself outside the task.

Perhaps what you want is to use sched.new_task to setup the task but don't run it yet.

local func = function() end
--do something here
local task=sched.new_task(func)
--or here
sched.run(task) --now is runnig

(I just noticed new_task is misplaced in the docs as a field, will fix it)

Yes, I do that, actually, but as I said before it's a little bit confusing. If the documentation says:

go ()
Starts the scheduler. Will run until there is no more activity, i.e. there's no active task, and none of the waiting tasks has a timeout set.

The expected behavior (the behavior I would expect, anyway) is that task won't start running until I invoke sched.go() (like Sierra's sched.loop()). I suggest a change in the API documentation specifying this behavior, to prevent people from making wrong assumptions. Something like:

run (task, ...)
...whatever... the newly added task will start running immediately....

And, instead of starts the scheduler in go (), starts signal scheduling (EDIT: which is not exactly correct, since the first signal is delivered as soon as it's signaled) or something like that. Then people can decide if they are affected and which specific workaround they prefer.

Thank you for your time :-)

Ok, thanks for you input, I see you point. I'll review the documentation and try for a clearer wording.
Also, what do you think about renaming the go() function? I do believe "loop" is a more appropriate name, although it is even more confusing with Sierra's behavior. Probably Sierra should rename their method to "run" :-)

I guess "loop" is more specific to the actual method's semantics, and now that I understand better how yours work, indeed Sierra's "loop" is too unspecific :-P "start" or "run" would make more sense (I'd also replace "run" with "add", since that's what the run method actually does) :-)

Anyway, thanks again. I really appreciate your answers :-)

PS: Since all my concerns have been cleared out, I'm closing this issue.