How to switch thread stack between threads?

Alexander Tormasov a.tormasov at innopolis.ru
Fri Jul 16 17:40:03 CEST 2021


>> 
>> probably I give a wrong picture of operations
> I did understand you the first time around. But I have to disappoint you.
> What you want is IMPOSSIBLE! At least in genode. Because you can't create such a context.

I do not plan to create it, I plan to update saved by taking some data from running thread. 
I am also considering re-mapping of a part of stack area. We do know that all aux (os-relates) structures from current and saved context has the same size (in running instance) and mapping offset from start of area, even could be aligned to page bound.
so, I can potentially save ucontext, take last stack pointer (e.g RSP register) and re-map OS-related areas to currently running thread… while technically it is similar to just copying it.
this is a kind of hack, still not sure that it will work reliably (while it could be definitely limited to combination of utcb and native_thread structure states for some platform, they could be ported on face-by-face) 

>> 1. I run arbitrary function with stack associated with first thread
> Every stack implies (is bound to) an os level thread.
>> 2. I copy current context using getcontext and store it somewhere
> You must store a pointer to an os level object that holds the os level thread (mutex is fine)
> with the context to be able to later resume in the correct context.

as I see, in this moment Stack object do contains 2 data structures - native_thread and utcb handled by native OS (mean updated) as a way to store native os thread data (as well as 3-d "Thread object" reference - this is genode object).
how they will be related to proposed mutex?

>> 3. I stop doing function from 1, by switching to another function/stack associated with thread 1
> That is possible.
>> 4. after some time I create a new os thread and run some code inside it
>> 5. then inside 2 thread I take old context from 2 above and perform setcontext from inside 2 thread to replace current function with state in the thread to the first one
> You can not use context from 2 in another thread. Alternatively you can reconstruct the call chain from the first thread in the second thread with Duffs Device (https://en.wikipedia.org/wiki/Duff's_device#See_also)
> And then you can construct a mirror context, which is the first context but in the second thread.

The problem that model of context is already implemented inside golang runtime (size of ~1m lines of code) using set/get/make context calls.
I do not understand how I can emulate them using Duff device co-routines without significant modification (mostly rewriting) of this not-mine code?
Even including go compiler: golang use stack variables, and do generate code which handle them (compiler).
Duff device approach require different model as I know, nothing should be stored in the stack...
Also see below

>> 
>> So, I don’t need mutexes and wait for something - I have a time gap between suspend of function in os 1 and it continuation on thread 2.
> The mutexes have another purpose. They regulate the os level threads when user level threads yield.

golang already does this.
as a part of language and runtime, it have a wrappers around sys calls and user-space preemtion points. 
It aware about existence of OS threads and plurality if goroutines, and remap goroutines in preemption points from one OS thread to another one.
so, user lever threads never call kernel directly to yield/etc, this is done on the language/runtime level. see (1)
in short, it periodically check possibility of preemption, and always do it during sys call (before and after), and handle blocked threads (AKA M structures) by itself having different queues for global/local instances, idle/blocked state/etc, and manipulate native OS threads (create/block/delete/etc) via pthread or similar libraries.
E.g., if you have a blocking sys call from inside goroutine G running on M OS thread, then, before actual call, it «park» M and appropriate G in such a way that it already utilise OS object to wait (e.g. futex on linux) and correctly return/resurrect blocked M after return from syscall, again via user-level scheduler and preemption point (it just activate M and find appropriate goroutine for it to run inside context, may be the same as make syscall and block it, may be another - via user-level scheduler).
This again assume mobility to goroutines with related stack between OS threads.

IN the last version of runtime golang developers even implement own simplified «setcontext» in asm without kernel sys calls (typically made for signals processing), - this is a core of all runtime.
So, if we want to have Golang running inside genode - we need to find a way to support their model of context switches by emulation of make/set/getcontext semantics.
And, it assume that any address could be used as a stack for thread (but genode allow only pre-defined and «chunked» stacks now).
This significantly limit the number of potential goroutines co-existing (in real heavy load programms it could be 10-th K of them…).

Thats why I still think that may be it worth to have a bit different way to obtain from genode thread reference to the Thread object by replacing Thread::myself() function (this is just an idea, not sure is it possible to implement it only for particular application).


1 https://medium.com/swlh/different-threading-models-why-i-feel-goroutine-is-better-though-with-some-limitations-b73863ba4dae



More information about the users mailing list