Created
October 12, 2017 20:10
-
-
Save elliott-beach/c52300a30db2c6dc744a5c9e5e107b22 to your computer and use it in GitHub Desktop.
Comments on setjmp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <setjmp.h> | |
#include <signal.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/time.h> | |
#define SECOND 1000000 | |
#define STACK_SIZE 4096 | |
char stack1[STACK_SIZE]; | |
char stack2[STACK_SIZE]; | |
// This is the Thread Control Block. We can use the same structure for our htreading, | |
// but with 1000 instead of 2. | |
sigjmp_buf jbuf[2]; | |
void switchThreads(); | |
void f() | |
{ | |
int i=0; | |
while(1) { | |
++i; | |
printf("in f (%d)\n",i); | |
if (i % 3 == 0) { | |
printf("f: switching\n"); | |
switchThreads(); | |
} | |
usleep(SECOND); | |
} | |
} | |
void g() | |
{ | |
int i=0; | |
while(1){ | |
++i; | |
printf("in g (%d)\n",i); | |
if (i % 5 == 0) { | |
printf("g: switching\n"); | |
switchThreads(); | |
} | |
usleep(SECOND); | |
} | |
} | |
#ifdef __x86_64__ | |
/* code for 64 bit Intel arch */ | |
typedef unsigned long address_t; | |
#define JB_SP 6 | |
#define JB_PC 7 | |
/* A translation is required when using an address of a variable. | |
Use this as a black box in your code. */ | |
address_t translate_address(address_t addr) | |
{ | |
address_t ret; | |
asm volatile("xor %%fs:0x30,%0\n" | |
"rol $0x11,%0\n" | |
: "=g" (ret) | |
: "0" (addr)); | |
return ret; | |
} | |
#else | |
/* code for 32 bit Intel arch */ | |
typedef unsigned int address_t; | |
#define JB_SP 4 | |
#define JB_PC 5 | |
/* A translation is required when using an address of a variable. | |
Use this as a black box in your code. */ | |
address_t translate_address(address_t addr) | |
{ | |
address_t ret; | |
asm volatile("xor %%gs:0x18,%0\n" | |
"rol $0x9,%0\n" | |
: "=g" (ret) | |
: "0" (addr)); | |
return ret; | |
} | |
#endif | |
void setup() | |
{ | |
address_t sp, pc; | |
// Make thread 1's stack point to memory on the data section. | |
sp = (address_t)stack1 + STACK_SIZE - sizeof(int); | |
// Make thread 1's program counter point to f. | |
pc = (address_t)f; | |
// Copy sp,pc into the jmpbuf struct. | |
sigsetjmp(jbuf[0],1); | |
(jbuf[0]->__jmpbuf)[JB_SP] = translate_address(sp); | |
(jbuf[0]->__jmpbuf)[JB_PC] = translate_address(pc); | |
sigemptyset(&jbuf[0]->__saved_mask); | |
// Make thread 2's stack point to memory on the data section. | |
sp = (address_t)stack2 + STACK_SIZE - sizeof(int); | |
// Make thread 2's program counter point to g. | |
pc = (address_t)g; | |
// Copy sp,pc into the jmpbuf struct. | |
sigsetjmp(jbuf[1],1); | |
(jbuf[1]->__jmpbuf)[JB_SP] = translate_address(sp); | |
(jbuf[1]->__jmpbuf)[JB_PC] = translate_address(pc); | |
sigemptyset(&jbuf[1]->__saved_mask); | |
} | |
void switchThreads() | |
{ | |
static int currentThread = 0; | |
// thread_yield() and premption will return to this point. | |
int ret_val = sigsetjmp(jbuf[currentThread],1); | |
printf("SWITCH: ret_val=%d\n", ret_val); | |
if (ret_val == 1) { | |
return; | |
} | |
// TODO: we will handle scheduling threads here. | |
currentThread = 1 - currentThread; | |
siglongjmp(jbuf[currentThread],1); | |
} | |
int main() | |
{ | |
setup(); | |
siglongjmp(jbuf[0],1); | |
return 0; | |
} | |
I am confused on macOS, I have sigjmp_buf as unsigned int array, how to set value of pc and sp to it?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wasn't really sure what the fields of the sigjmp struct are. I think that these are roughly what the fields of the struct sigjmp look like as well the jmpbuf buffer.
struct sigjmp_buf {
/* NOTE: The machine-dependent definitions of
__sigsetjmp' assume that a
jmp_buf' begins with a__jmp_buf' and that
__mask_was_saved' follows it. Do not move these membersor add others before it. /
__jmp_buf __jmpbuf; / Calling environment. /
int __mask_was_saved; / Saved the signal mask? /
__sigset_t __saved_mask; / Saved signal mask. */
};
On second thought, I think it's heavily architecture dependent, but it's at least something like this. I guess the demo makes it clear what the implementation looks like.
jmp_buf:
offset size description
0 16 call-saved registers (r2-r17)
16 2 frame pointer (r29:r28)
18 2 stack pointer (SPH:SPL)
20 1 status register (SREG)
21 3 return address (PC) (2 bytes used for <=128K flash)
24 = total size