makecontext(3)
๐ makecontext(3) โ manipulate user context ๐
okay so bestie, we're in the Library Functions Manual today and the star of the show is literally makecontext, with swapcontext riding shotgun. it's giving system-level thread-of-control chaos and honestly? obsessed. ๐ฆ
โจ NAME โจ
makecontext, swapcontext โ manipulate user context
๐ LIBRARY ๐
Standard C library (libc, -lc) ๐
๐ธ SYNOPSIS ๐ธ
#include <ucontext.h>
void makecontext(ucontext_t *ucp, typeof(void (int arg0, ...)) *func,
int argc, ...);
int swapcontext(ucontext_t *restrict oucp,
const ucontext_t *restrict ucp);
๐ซถ DESCRIPTION ๐ซถ
So in a System V-like environment, you get this type called ucontext_t โ it lives in <ucontext.h> and it's fully described over in getcontext(3), go read that bestie โ plus four functions: getcontext(3), setcontext(3), makecontext(), and swapcontext(). Together they let you do user-level context switching between multiple threads of control inside one process. no kernel threads required, we're doing this ourselves ๐
makecontext() modifies the context that ucp points to (and you gotta get that ucp from a call to getcontext(3) first, that's the whole vibe). Before you invoke makecontext(), the caller must:
- allocate a new stack for this context and assign its address to
ucp->uc_stack, and - define a successor context and assign its address to
ucp->uc_link.
Then, when this context gets activated later (via setcontext(3) or swapcontext()), the function func is called and it's passed that whole series of integer (int) arguments that come after argc. You, the caller, have to say how many of those arguments there are in argc. โจ
And when func returns? The successor context gets activated. If that successor context pointer is NULL, the thread exits. that's it, that's the end, no cap ๐ญ
swapcontext() saves the current context into the struct that oucp points to, and then activates the context that ucp points to. swap-and-go, iconic behaviour honestly ๐
๐ฏ RETURN VALUE ๐ฏ
When it works, swapcontext() does not return. (But bestie, we may absolutely return later โ if oucp gets activated, it looks like swapcontext() returned 0. surprise! ๐)
On error, swapcontext() returns -1 and sets errno to indicate the error.
๐ ERRORS ๐
ENOMEMโ Insufficient stack space left. the stack said no more, we're out ๐
๐ฅ ATTRIBUTES ๐ฅ
For an explanation of the terms used in this section, see attributes(7). (yes it's a whole glossary, go look ๐ง)
makecontext()โ Thread safety:MT-Safe race:ucpswapcontext()โ Thread safety:MT-Safe race:oucp race:ucp
note the race: bits bestie โ those are the parameters where race conditions are expected/possible. not me casually pointing that out ๐ซฃ
๐ STANDARDS ๐
None. ๐ literally none, it's giving outlaw behaviour.
๐ HISTORY ๐
- glibc 2.1
- SUSv2
- POSIX.1-2001
- Removed in POSIX.1-2008, citing portability issues, and recommending that applications be rewritten to use POSIX threads instead.
so yeah, they dropped it and told everybody to go use pthreads instead. rip bozo, but respectfully ๐ญ
๐ NOTES ๐
The interpretation of ucp->uc_stack is exactly the same as in sigaltstack(2) โ namely, this struct contains the start and length of a memory area to be used as the stack, regardless of the direction of growth of the stack. So your program does not have to worry about that direction at all. one less thing, slay โจ
Now here's the spicy one. On architectures where int and pointer types are the same size (e.g., x86-32, where both types are 32 bits), you may be able to get away with passing pointers as arguments to makecontext() following argc.
However, doing this:
- is not guaranteed to be portable,
- is undefined according to the standards, and
- won't work on architectures where pointers are larger than
ints.
Nevertheless, starting with glibc 2.8, glibc makes some changes to makecontext(), to permit this on some 64-bit architectures (e.g., x86-64). so like... it might work. don't count on it though fr fr ๐
๐ EXAMPLES ๐
The example program below demonstrates the use of getcontext(3), makecontext(), and swapcontext(). Running the program produces the following output:
$ ./a.out
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting
look at that bounce between func2 and func1, it's giving ping pong but make it context switching ๐๐ญ
๐ Program source ๐
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
static ucontext_t uctx_main, uctx_func1, uctx_func2;
static void
func1(void)
{
printf("%s: started\n", __func__);
printf("%s: swapcontext(&uctx_func1, &uctx_func2)\n", __func__);
if (swapcontext(&uctx_func1, &uctx_func2) == -1)
err(EXIT_FAILURE, "swapcontext");
printf("%s: returning\n", __func__);
}
static void
func2(void)
{
printf("%s: started\n", __func__);
printf("%s: swapcontext(&uctx_func2, &uctx_func1)\n", __func__);
if (swapcontext(&uctx_func2, &uctx_func1) == -1)
err(EXIT_FAILURE, "swapcontext");
printf("%s: returning\n", __func__);
}
int
main(int argc, char *argv[])
{
char func1_stack[16384];
char func2_stack[16384];
if (getcontext(&uctx_func1) == -1)
err(EXIT_FAILURE, "getcontext");
uctx_func1.uc_stack.ss_sp = func1_stack;
uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
uctx_func1.uc_link = &uctx_main;
makecontext(&uctx_func1, func1, 0);
if (getcontext(&uctx_func2) == -1)
err(EXIT_FAILURE, "getcontext");
uctx_func2.uc_stack.ss_sp = func2_stack;
uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
/* Successor context is f1(), unless argc > 1 */
uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
makecontext(&uctx_func2, func2, 0);
printf("%s: swapcontext(&uctx_main, &uctx_func2)\n", __func__);
if (swapcontext(&uctx_main, &uctx_func2) == -1)
err(EXIT_FAILURE, "swapcontext");
printf("%s: exiting\n", __func__);
exit(EXIT_SUCCESS);
}
thosechar func1_stack[16384];andchar func2_stack[16384];are the stacks bestie โ you HAVE to allocate them yourself beforemakecontext(), that's literally the rule we talked about above ๐
๐ SEE ALSO ๐
sigaction(2), sigaltstack(2), sigprocmask(2), getcontext(3), sigsetjmp(3)
and that's the whole page bestie, you ate it, no crumbs left ๐ซถ
Linux man-pages 6.20 โ dated 2026-02-08 ๐ mwah, go write some contexts, you got this ๐โจ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 2001, Andries E. Brouwer <aeb@cwi.nl>
- Copyright 2006, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms