Hi Daniel,
On 03/25/2011 06:12 PM, Daniel Waddington wrote:
the registers and changing the ip/sp. I think the main issue is that in our first attempt we did not copy the "additional context members" and "UTCB" to the new stack frame. The thread switching works until you try to access the utcb.
I suspect that the culprit here is the 'Thread_base::myself()' function, which is used by the callee to determine it's 'Thread_base' object. This function uses the stack pointer as key to find the callee's 'Thread_base::Context'. The stack pointer is expected to reside within the so-called thread-context area. Each thread owns a slot within this virtual address range (currently a slot is 1MB - details can be found in 'base/include/base/thread.h'). Within each slot, there resides the thread's stack and the its 'Thread_base::Context' object.
Now, if you switch the stack pointer to an address allocated somewhere outside the thread-context area, the 'Thread_base::myself()' function will assume that the callee is the main thread (the main thread has no slot in the thread-context area, its stack is located within the BSS segment). Consequently, functionality that relies on the thread context (such as locking) is going to fail.
To overcome this problem, I see three possible solutions:
First, the 'Thread_base' class could be extended by a hook for considering additional stack ranges when 'Thread_base::myself()' is called. This hook would by able to tell 'Thread_base::myself()' about the physical thread context belonging to stack ranges allocated by a user-level threading library.
Alternatively, you might try placing the stack of the user-level thread within the same context-area slot as used by the physical thread so that 'Thread_base::myself()' will always return the correct physical 'Thread_base' object. To see how to directly interact with the thread-context area, please have a look at 'base/src/base/thread/thread.cc', in particular '_alloc_context()'.
As a third approach, you might consider adding support for user-level threads in a way that each user-level thread uses a real 'Thread_base' context but not a physical thread. Each thread context of a user-level thread would have a reference to the physical 'Thread_base' object used for its execution. This way, when 'Thread_base::myself()' is called, the 'myself()' function would first look whether the callee is a user-level thread. If so, it would return the referenced physical 'Thread_base' object. I think, this approach would be the most elegant one, mostly because it would allow the detection of stack overflows of user-level threads as each user-level thread would be subject to the overflow protection provided by the thread-context mechanism.
Best regards Norman