On Mon, Dec 6, 2021 at 3:47 AM Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote: > > On Mon, Nov 29, 2021 at 09:34:49AM -0800, Peter Oskolkov wrote: > > On Mon, Nov 29, 2021 at 8:41 AM Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote: > > > > Also, timeout on sys_umcg_wait() gets you the exact same situation (or > > > worse, multiple running workers). > > > > It should not. Timed out workers should be added to the runnable list > > and not become running unless a server chooses so. So sys_umcg_wait() > > with a timeout should behave similarly to a normal sleep, in that the > > server is woken upon the worker blocking, and upon the worker wakeup > > the worker is added to the woken workers list and waits for a server > > to run it. The only difference is that in a sleep the worker becomes > > BLOCKED, while in sys_umcg_wait() the worker is RUNNABLE the whole > > time. > > > > Why then have sys_umcg_wait() with a timeout at all, instead of > > calling nanosleep()? Because the worker in sys_umcg_wait() can be > > context-switched into by another worker, or made running by a server; > > if the worker is in nanosleep(), it just sleeps. > > I've been trying to figure out the semantics of that timeout thing, and > I can't seem to make sense of it. > > Consider two workers: > > S0 running A S1 running B > > therefore: > > S0::state == RUNNABLE S1::state == RUNNABLE > A::server_tid == S0.tid B::server_tid = S1.tid > A::state == RUNNING B::state == RUNNING > > Doing: > > self->state = RUNNABLE; self->state = RUNNABLE; > sys_umcg_wait(0); sys_umcg_wait(10); > umcg_enqueue_runnable() umcg_enqueue_runnable() sys_umcg_wait() should not enqueue the worker as runnable; workers are enqueued to indicate wakeup events. > umcg_wake() umcg_wake() > umcg_wait() umcg_wait() > hrtimer_start() > > In both cases we get the exact same outcome: > > A::state == RUNNABLE B::state == RUNNABLE > S0::state == RUNNING S1::state == RUNNING > S0::runnable_ptr == &A S1::runnable_ptr = &B So without sys_umcg_wait enqueueing into the queue, the state now is A::state == RUNNABLE B::state == RUNNABLE S0::state == RUNNING S1::state == RUNNING S0::runnable_ptr == NULL S1::runnable_ptr = NULL > > > Which is, AFAICT, the exact state you wanted to achieve, except B now > has an active timer, but what do you want it to do when that goes? When the timer goes off, _then_ B is enqueued into the queue, so the state becomes A::state == RUNNABLE B::state == RUNNABLE S0::state == RUNNING S1::state == RUNNING S0::runnable_ptr == NULL S1::runnable_ptr = &B So worker timeouts in sys_umcg_wait are treated as wakeup events, with the difference that when the worker is eventually scheduled by a server, sys_umcg_wait returns with ETIMEDOUT. > > I'm tempted to say workers cannot have timeout, and servers can use it > to wake themselves.