 |
|
 |
This code is obviously pure-x86. You should improve it with at least x86_64 compatibility (ARM compatibility would be nice, but hardly necessary at the moment).
Namely:
1) CONTEXT on x86_64 doesn't have Eip or Esp, it has Rip and Rsp instead
2) Various constants (like "8" or "4") might have to be adjusted. It would be better to define them in one place, and their definition might change depending on the architecture
3) Use correct types for pointer<->integer conversion. Namely - to store pointers in integer form use uintptr_t (from stdint.h) instead of unsigned long.
The ifdef you'd have to use will look like this:
#if defined(_X86_)
...
#elif defined(_AMD64_)
...
#else
# error unknown architecture
#endif
At least that is what MinGW-W64 defines.
|
|
|
|
 |
|
 |
I've been playing around with some code based on this trick, but I'm getting reports that it doesn't work at all on Windows 7. I don't have it so I can't test it, but have you tried this on Windows 7?
|
|
|
|
 |
|
 |
Hi,
I have just tested it on my Win7 laptop using Cygwin (gcc)
without encountering any problem.
It must work with Visual Studio too.
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
Is there a way to compile this into a .lib with MinGW/MSYS ? that would really helpful !
|
|
|
|
 |
|
 |
Hi,
I have just tested the code with MinGW/MSYS (MINGW32_NT-6.0)
and it works fine!
a) Download and extract the demo zip file into a MSYS directory,
b) type at the MSYS command prompt:
gcc -Iunix2nt -o testcontext testcontext.c unix2nt/ucontext.c
c) Run the executable
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
hi there - like the code - adapted it to work in a series of classes.
i have developed a series of micro-threads which each contain a CONTEXT and then using a timer, periodically swap a specific threads context for a given micro-threads. i can in theory have any number of micro-threads per windows thread. using the timer, i can implement my own scheduler which works at a configurable frequency (i.e. i want my graphics to work at 30Hz, AI to run at 10Hz, etc), plus making each windows thread assignable to a specific processer / core, i have a sort of multi-core scheduling solution (N number of processors = N number of windows-threads). i can implement any kind of scheduling procedure, it works fine.
i worked about that this solution adds about 1% to 1.6% to a solution implementing this approach - so thanks for the idea and the code. i've tested it on a multicore machine - no problems at all. it works very well and no assembler at all!! pure C++.
my problem is when i suspend a micro-thread, i have to suspend that windows thread - no problem, i can see the suspend counter for the windows thread increase + the scheduler suspend for the swap - so i can take that into account when resuming the windows-thread with the next mirco-thread - but the other micro-thread (that not suspended) is suspended and never runs again. i can not work out why?? in debug mode seem work fine, but in release mode - problems. the reason for suspending a micro-thread is when a micro-thread finishes early, i have a sort of fix - a tight loop, but feel the suspend is better.
have you every heard of this?? any ideas?? again thanks for the idea
|
|
|
|
 |
|
 |
Hi,
I have the feeling that your problem is a general one and does not depend on the implementation of ucontext_t, right?
Posting your question to a newsgroup will really help you get answer.
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
SetThreadContext is not allowed to be called to set the
context of a running thread as this can lead to unpredictable behaviour
depending on your OS version or CPU type.
Refer the documentation at:
http://msdn.microsoft.com/en-us/library/ms680632(VS.85).aspx
You can create a temporary thread to solve this race condition
like this:
class thread_context_loader
{
private:
static CONTEXT ctx;
static HANDLE thread;
static volatile long sync;
static unsigned long WINAPI start(void *)
{
while (InterlockedCompareExchange( &sync, 1, 1 ) != 1)
SwitchToThread();
SuspendThread( thread );
SetThreadContext( thread, &ctx );
ResumeThread( thread );
CloseHandle( thread );
return 0;
}
public:
static void set(CONTEXT &context)
{
ctx = context;
DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &thread,
0, FALSE, DUPLICATE_SAME_ACCESS );
InterlockedExchange( &sync, 0 );
CreateThread( 0, 0, start, 0, 0, 0 );
InterlockedExchange( &sync, 1 );
for ( ;; ) {}
}
};
Or when expecting many setcontext calls delegate a precreated thread for the switching.
|
|
|
|
 |
|
 |
Thank you for your comment. This is an important detail for those who may need it.
I haven't tested the code thoroughly to see some unpredicted behavior. Practically there may be no problem because the running thread calls SetThreadContext to change its own context and not that of another thread.
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
Looks as if "uc_link" is not supported by this implementation (uc_link specifies where to return to when the thread is finished...)...
I've implemented the functionality in my microfiber example:
http://www.subatomicglue.com/secret/coro/readme.html[^]
basically inside of makecontext I add a couple things to the stack: I add a return address (function pointer) after the args and the uc_link pointer before the args. The return address (function pointer) is to a helper function that cleans up the args (moves the stack pointer 8 for each arg) and then reads the uc_link from the stack, and calls setcontext with it. The trick is to generate many of the mentioned helper functions - 1 per number of arguments expected. I supported up to 10 args (consequently requiring 11 generated return function helpers) as I thought 10 args would be plenty... but it's easy to extend to more if needed.
|
|
|
|
 |
|
 |
After a long time absence, I check this page again.
I am happy to see that this code can be useful.
I would like to thank you for sharing your improvements.
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
1. According to Microsoft, GetThreadContext and SetThreadContext are undefined for running threads. See for example http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/setthreadcontext.asp
Doesn't this make this approach questionable?
2. The second question is a little bit more subtle: If I look at swapcontext, I get the impression that swapcontext should effect exactly one context switch.
Swapcontext calls getcontext, which calls GetThreadContext. Then the result of GetThreadContext is used (indirectly) to call SetThreadContext to effect the context switch. Now the result of GetThreadContext is returned in the EAX register [I think]. However, when we switch back from thread b to thread a we get the old value of EAX *before* the call, not the function result [since the function result did not exist yet]. It seems to me that this value is undefined. Apparently, the value of the EAX register just *happened* to have the right value [with your compiler].
This can be fixed by explictly setting the EAX register in CONTEXT to 0.
|
|
|
|
 |
|
 |
Hi,
1. Although Get/SetThreadContext are undefined for running threads, it seems that they work if the calling thread applies these routines to itself.
2. You are right! In fact, I had that in mind but since it was working (with VC++), I stopped there... So, I will follow your suggestion. Thanks!
By the way, have you managed to reproduce that undefined behaviour using a different compiler/program?
In any case, I will try to perform some more experiments.
Thanks again!
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
> 1. Although Get/SetThreadContext are undefined for running threads, it seems that they work if the calling thread applies these routines to itself.
I think that relying on undefined behaviour is fine for hacking, but if you put such code into real programs, you should be aware that any new version of the OS might break your program.
|
|
|
|
 |
|
 |
The truth is that I am not so sure if this is undefined behaviour or not. If thread A tries to change the context of running thread B then I understand that this makes no sense. But I am not sure if this behaviour is undefined or problematic if A == B.
Moreover, in this case (A == B), SetThreadContext just changes some registers and performs a local goto, i.e. it is very similar to setjmp/longmp and SwitchToFiber.
Thus, instead of hacking it might be just an option not mentioned in the documentation of SetThreadContext.
In any case, I believe that Windows is a very mature and stable OS and in my humble opinion it is very unlikely that a future version will break this program.
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
Eventually, having performed some experiments, I "managed" to remember exactly how my code works
Well, although it seems so simple, swapcontext is very tricky: in fact, when a preempted context is reloaded, it restarts from setcontext (!) instead of going back to getcontext.
Probably, this has to do with the way SetThreadContext is implemented internally in the operating system.
This explains why the program works correctly, without having to set the EAX register. I have already tested it with VC++, gcc (Mingw32) and icl (Intel).
Panagiotis
--
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
|
 |
|
 |
No, my intension was not to reinvent the wheel but to provide something very close to this Unix data structure (ucontext_t).
By the way, does "reinvention" refer to the API or the internal implementation?
Panagiotis
---
Panagiotis E. Hadjidoukas
|
|
|
|
 |
|
 |
You wrote
Most modern Unix environments provide to the programmer two options that allow user-level context switching between multiple threads of control within a process: either with the type jmpbuf defined in <setjmp.h> and the setjmp/longjmp pair of functions, or with the type ucontext_t defined in <ucontext.h> and the four functions getcontext, setcontext, makecontext and swapcontext.
No, set/getjmp nor ucontext_t is used to switch between threads. They are basically inter-function gotos and will not affect anything but the executing threads program counter and stack. In fact, ucontext_t is inherently unsafe with threads, because in most UNIX implementations (if not all), the context structure contains information about the stack.
And besides, you can't just "switch thread context" - a thread is where it's at, and nowhere else; the program counter!
--
I'm the figure head on a ship of fools
|
|
|
|
 |
|
 |
Probably you have missed something... We discuss about user-level threads -
with their own stack. If you search the Internet, you will find many implementations of user-level thread libraries. For examples, look at
GNU Portable Threads (www.gnu.org/software/pth/), which are based on set/longjmp or ucontext_t. Moreover, ucontext_t is the best choice, if you want to implement preemptive user-level threads on Unix.
Panagiotis E. Hadjidoukas
|
|
|
|
 |