Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / Win32
Tip/Trick

Unix ucontext_t Operations on Windows Platforms

Rate me:
Please Sign up or sign in to vote.
3.93/5 (5 votes)
7 Mar 2007LGPL33 min read 122.1K   2.5K   10   25
A tip on the implementation of Unix ucontext_t operations on Microsoft Windows.

Introduction

In this work, we implement the Unix ucontext_t operations on Windows platforms based on the Win32 API GetThreadContext and SetThreadContext functions. It is useful for Unix programmers that need to migrate their user-level threading code directly on Windows instead of using a Unix-to-NT porting environment ([1,2]).

Background

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. For more information on the usage of these functions, you can look at [3]. On Windows platforms, however, the Microsoft C Runtime Library provides only the first set of functions [4].

Using the Code

The following program (testcontext.c - included in the demo project) is a typical example that makes use of the ucontex_t operations. It runs successfully on both Unix and Windows platforms. On Windows, the programmer has only to include the two files (ucontext.[c,h]) in his application code. In the demo project, these two files are stored in the unix2nt directory, in order to make straightforward the compilation and execution of the test program on Unix platforms.

C++
/* testcontext.c : demo of ucontex_t operations on Win32/64 platforms */
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

ucontext_t auc,buc,mainuc;

void a(DUMMYARGS int p)
{
    int i;

    for (i = 0; i < 10; i++)
    {
        printf("a%d ", p);
        swapcontext(&auc, &buc);        /* switch to thread B */
    }

    printf("\nswitching to main\n");
    swapcontext(&auc, &mainuc);         /* switch to main thread */
}

void b(DUMMYARGS int p)
{
    int i;

    for (i = 0; i < 10; i++)
    {
        printf("b%d ", p);
        swapcontext(&buc, &auc);        /* switch to thread A */
    }
}

int main(void)
{
    int aparam = 1, bparam = 2;

    printf("start\n");                  /* main thread starts */

    /* Set up context for thread A (Unix code, see manpages) */
    getcontext(&auc);
    auc.uc_stack.ss_size = 16 * 1024;

    if ((auc.uc_stack.ss_sp = malloc(auc.uc_stack.ss_size)) == NULL)
        perror("malloc"), exit(1);

    auc.uc_stack.ss_flags = 0;
    makecontext(&auc, a, 1, aparam);

    /* Set up context for thread B */
    getcontext(&buc);
    buc.uc_stack.ss_size = 16 * 1024;

    if ((buc.uc_stack.ss_sp = malloc(buc.uc_stack.ss_size)) == NULL)
        perror("malloc"), exit(1);

    buc.uc_stack.ss_flags = 0;
    makecontext(&buc, b, 1, bparam);

    /* Switch to A */
    getcontext(&mainuc);           /* Save the context of main thread */
    swapcontext(&mainuc, &auc);    /* Switch to thread A */

    printf("\ndone\n");  /* Execution control returned to main thread */
    return 0;
}

If you compile and run the above program, you get the following output:

C:\>testcontext.exe
start
a1 b2 a1 b2 a1 b2 a1 b2 a1 b2 a1 b2 a1 b2 a1 b2 a1 b2 a1 b2      
switching to main

done
C:\>   

Implementation

We implemented the above functionality based on the CONTEXT data structure and the GetThreadContext and SetThreadContext functions [4]. In ucontext.h, we defined the appropriate data structures and function prototypes. The implementation of getcontext and setcontext is straightforward, using the corresponding GetThreadContext and SetThreadContext functions. In swapcontext, the current thread saves its context (getcontext) and then restores a new context (setcontext). The trickiest point is in the implementation of makecontext, where we reserve stack space for the function arguments; then we set appropriately the instruction (Eip) and the stack pointer (Esp) fields, and finally, we copy the function arguments in the reserved space. Although the latest specification of makecontext [3] restricts the arguments of the function to be of type int, according to our implementation, the maximum allowed size of an argument is 64 bits, enabling thus the passing of variables of type double. For more implementation details, see the ucontext.c and ucontext.h files.

Win64 Support

For Win32 compilation, the code works exactly as before. To support Win64 (x64) compilation, the following additions were introduced:

  1. The instruction and stack pointers are appropriately set using the Rip and Rsp fields of the CONTEXT data structure.
  2. The DUMMYARGS macro has been added in ucontext.h. This macro must be used at the beginning of the argument list of the function specified at makecontext. For Win32, the macro is empty and can be omitted, while for Win64 it defines 4 long integer variables. These hidden and unused variables are passed to the function through registers, according to the __fastcall x64 calling convention [5], allowing for the actual user parameters to be passed through the stack, similarly to the Win32 case.
  3. A macro in ucontext.h replaces malloc(x) with _aligned_malloc(x,64).

History

  • May 22, 2003 - Initial release
  • May 25, 2004 - Revised source code
  • Mar 6, 2007 - Distributed under the LGPL license
  • Mar 15, 2014 - Updated for x86_64 support

References

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior)
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questiontry to run code on visual 13 getting errors using the demo and another project. Pin
Member 1274222215-Sep-16 8:51
Member 1274222215-Sep-16 8:51 
Questiondoesn work in 64bit cygwin. 32bit is ok. Pin
Member 1123401814-Nov-14 3:22
Member 1123401814-Nov-14 3:22 
QuestionAccess Violation and Stack Overflow Pin
Member 455692213-Nov-14 6:10
Member 455692213-Nov-14 6:10 
Questionx86_64 Pin
L_R_N9-Apr-12 11:16
L_R_N9-Apr-12 11:16 
AnswerRe: x86_64 Pin
xdoukas1-Aug-12 12:38
xdoukas1-Aug-12 12:38 
GeneralRe: x86_64 Pin
Baruch Burstein20-Nov-12 0:07
Baruch Burstein20-Nov-12 0:07 
GeneralWindows 7 Pin
MarshallBanana20-Aug-10 15:49
MarshallBanana20-Aug-10 15:49 
GeneralRe: Windows 7 Pin
xdoukas8-Oct-10 7:56
xdoukas8-Oct-10 7:56 
QuestionHow can I compile it with MinGW ? Pin
dmichel7626-Oct-09 4:28
dmichel7626-Oct-09 4:28 
AnswerRe: How can I compile it with MinGW ? Pin
xdoukas28-Oct-09 9:55
xdoukas28-Oct-09 9:55 
GeneralSuspend Problem - any advice Pin
snr196510-Sep-09 7:23
snr196510-Sep-09 7:23 
GeneralRe: Suspend Problem - any advice Pin
xdoukas28-Oct-09 9:02
xdoukas28-Oct-09 9:02 
GeneralUnpredictable behaviour due to race condition. Pin
WolfgangSt7-May-09 5:36
WolfgangSt7-May-09 5:36 
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.
GeneralRe: Unpredictable behaviour due to race condition. Pin
xdoukas28-Oct-09 8:56
xdoukas28-Oct-09 8:56 
Answercrash if function a() is allowed to return. basically uc_link needed to be implemented, which i've done. Pin
subatomicglue12-Jan-09 19:07
subatomicglue12-Jan-09 19:07 
GeneralRe: crash if function a() is allowed to return. basically uc_link needed to be implemented, which i've done. Pin
xdoukas28-Oct-09 8:48
xdoukas28-Oct-09 8:48 
GeneralTwo Questions Pin
Member 116599210-Jun-04 15:17
Member 116599210-Jun-04 15:17 
GeneralRe: Two Questions Pin
xdoukas11-Jun-04 2:33
xdoukas11-Jun-04 2:33 
GeneralRe: Two Questions Pin
Member 116599211-Jun-04 5:56
Member 116599211-Jun-04 5:56 
GeneralRe: Two Questions Pin
xdoukas11-Jun-04 7:17
xdoukas11-Jun-04 7:17 
GeneralRe: Two Questions Pin
xdoukas13-Jun-04 5:20
xdoukas13-Jun-04 5:20 
QuestionReinventing the wheel? Pin
Michael Dunn30-May-04 20:09
sitebuilderMichael Dunn30-May-04 20:09 
AnswerRe: Reinventing the wheel? Pin
xdoukas31-May-04 23:17
xdoukas31-May-04 23:17 
QuestionThreads? Pin
Jörgen Sigvardsson29-May-03 14:37
Jörgen Sigvardsson29-May-03 14:37 
AnswerRe: Threads? Pin
xdoukas30-May-03 3:14
xdoukas30-May-03 3:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.