man.bsd.lv manual page server

Manual Page Search Parameters

MAKECONTEXT_QUICK(3) Library Functions Manual MAKECONTEXT_QUICK(3)

makecontext_quick, swapcontext_quick, setcontext_quickquickly modify and exchange user thread contexts

library “libc”

#include <ucontext.h>

void
makecontext_quick(ucontext_t *ucp);

void
swapcontext_quick(ucontext_t *oucp, ucontext_t *nucp);

void
setcontext_quick(ucontext_t *ucp);

The quick context functions work similarly to the non-quick context functions but are designed for proper coroutine operation and synchronous switching. The signal mask is not adjusted in any manner by these routines, no system calls are made, and scratch registers are not required to be retained across calls.

Since no system calls need to be made and the FP state (being scratch across a procedure call) does not need to be saved or restored, these switching functions are at least 10 times faster than the non-quick versions. In addition, callers can setup quick contexts for cofunction chaining (when one cofunction return-chains to another), and for circular cofunction chaining loops, avoiding the need to save any register state at all in those configurations.

The () function initializes all fields of the passed-in context except ucp->uc_stack, ucp->uc_cofunc, and ucp->uc_arg. All other structural fields will be zerod. Note that ucp->uc_link will also be zerod for safety.

The caller must pre-initialize the uc_stack fields. ucp->uc_cofunc, and ucp->uc_arg should be initialized prior to making any context switches. This function will set the context up to call the cofunction as ucp->uc_cofunc(ucp, ucp->uc_arg). Note that this calling format is different from the non-quick context calls.

If the cofunction returns the wrapper will automatically reinitialize the context to reissue a cofunction call and then call the next cofunction via ucp->uc_link. If the link field is NULL, the wrapper issues an exit(0). If the linkages return to the ucontext, the cofunction call is reissued. The ucp->uc_cofunc, and ucp->uc_arg fields may be adjusted at any time to change the cofunction being called. Using the auto-linkage feature avoids saving register state on cofunction return and is the absolute quickest context switch possible, almost as fast as a normal procedure call would be.

The () function throws away the current context and switches to the target context. Again, the signal mask is not touched and scratch registers are not saved. If you desire to switch to a signal stack ucontext you must use the normal () function and not this one. This function is designed for synchronous switching only.

The () function saves the current register state and switches to the target context. This function returns when the old context is resumed. Again, the signal mask is not touched and scratch registers are not saved. If you desire to switch to a signal stack ucontext you must use the normal () function and not this one. It is acceptable to mix normal context functions with quick functions as long as you understand the ramifications.

There is no quick version for () on purpose.

These functions have no return value.

/*
 * quick context test program
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#define LOOPS	100000000L

static void test1(ucontext_t *ucp, void *arg);
static void test2(ucontext_t *ucp, void *arg);
static void test3(ucontext_t *ucp, void *arg);

int
main(int ac, char **av)
{
	ucontext_t ucp1;
	ucontext_t ucp2;
	ucontext_t ucp3;

	ucp1.uc_stack.ss_sp = malloc(32768);
	ucp1.uc_stack.ss_size = 32768;
	ucp1.uc_cofunc = test1;
	ucp1.uc_arg = (void *)(intptr_t)1;
	makecontext_quick(&ucp1);

	ucp2.uc_stack.ss_sp = malloc(32768);
	ucp2.uc_stack.ss_size = 32768;
	ucp2.uc_cofunc = test2;
	ucp2.uc_arg = (void *)(intptr_t)2;
	makecontext_quick(&ucp2);

	ucp3.uc_stack.ss_sp = malloc(32768);
	ucp3.uc_stack.ss_size = 32768;
	ucp3.uc_cofunc = test3;
	ucp3.uc_arg = (void *)(intptr_t)3;
	makecontext_quick(&ucp3);

	ucp1.uc_link = &ucp2;
	ucp2.uc_link = &ucp3;
	ucp3.uc_link = &ucp1;
	setcontext_quick(&ucp1);
}

long global_counter;

static void
test1(ucontext_t *ucp, void *arg)
{
	if ((intptr_t)ucp->uc_arg == 1) {
		printf("test1 entered for first time\n");
		ucp->uc_arg = (void *)(intptr_t)0;
	}
}

static void
test2(ucontext_t *ucp, void *arg)
{
	if ((intptr_t)ucp->uc_arg == 2) {
		printf("test2 entered for first time\n");
		ucp->uc_arg = (void *)(intptr_t)0;
	}
	++global_counter;
	if (global_counter > LOOPS)
		ucp->uc_link = NULL;	/* demonstrate documented exit(0) */
}

static void
test3(ucontext_t *ucp, void *arg)
{
	/* entered only once */
	assert((intptr_t)ucp->uc_arg == 3);
	printf("test3 entered for first time\n");
	printf("cycle through test1, test2, test3 %d times\n", LOOPS);
	ucp->uc_arg = (void *)(intptr_t)0;

	for (;;) {
		swapcontext_quick(ucp, ucp->uc_link);
	}
}

[]
There is not enough stack space in ucp to complete the operation.

getcontext(3), makecontext(3), setcontext(3), swapcontext(3), ucontext(3)

December 21, 2015 DragonFly-5.6.1