On Linux you can use makecontext to create a new stack and swapcontext to jump to it. That API was once defined by POSIX but deprecated after pthreads was added to the standard. It's still useable on Linux and several other OSs, with the caveat that you probably don't want to load or enable pthreads for your process. (I think you can mix the two in glibc today as glibc references thread-local data structures through a dedicated register, whereas a long time ago it was kept at the base of the current stack and so incompatible with alternate stacks. But other OSs might have problems and things might change in the future wrt glibc, too.)<p>Another solution which is actually (probably?) POSIX compatible is to use sigaltstack to create a new stack, save your current context with setjmp, invoke a signal, call setjmp to save your altstack, then longjmp back to your original position. Now you can jump back and forth at will, everything copacetic. Calling longjmp from a signal handler is perfectly legit and POSIX is careful to preserve that ability. But for obvious reasons you have to be very careful how you accomplish it.<p>Now, once you throw an interval timer into the mix things get tricky. Normally you would need to worry about the signal arriving while the C code is in async-unsafe library routines, but Erlang might be (I don't know for a fact, though) one of the few large projects that only ever uses signal-safe syscalls like mmap, read, write, etc. If it's only ever the user library executing libc code then there shouldn't be a problem.<p>One thing I really wish POSIX (or at least Linux) supported is per-thread signal handlers. With per-thread signal handlers you could bundle this magic into libraries in a clean manner in a multi-threaded process by preserving and restoring the existing signal mask, sigaltstack, and signal handler(s). Currently you can only preserve the first two; the third is process-global. I was working on a project recently where I caught SIGSEGV on a page fault, longjmp'd back, doubled the relevant memory buffer (or aborted if the address wasn't managed), and then restarted the computationally expensive operation. This allowed me to remove all the bounds checking code and resulted in a very significant speedup. But touching global state makes it very messy and not suitable for packaging into library form as these days it's best to assume a multi-threaded process environment.