Commit | Line | Data |
---|---|---|
3278bd05 TM |
1 | #include <ucontext.h> |
2 | #include <sys/types.h> | |
3 | #include <sys/time.h> | |
4 | #include <signal.h> | |
5 | #include <stdio.h> | |
6 | #include <unistd.h> | |
7 | #include <stdlib.h> | |
8 | #include <poll.h> | |
9 | ||
10 | /* ucontext sample program | |
11 | ||
12 | Downloaded from https://gist.github.com/DanGe42/7148946 | |
13 | A non-trivial example of how to use user contexts for trivial scheduling. | |
14 | ||
15 | by Jon Kaplan and Robert Spier (October 24th, 1999) | |
16 | Updated for 2000 and poll, Robert Spier | |
17 | sigprocmask gaff fixed by Ben Slusky | |
18 | ported to Linux by Eric Cronin | |
19 | $Id: context_demo.c 37 2006-10-12 22:16:59Z ecronin $ | |
20 | ||
21 | Demonstrates swapping between multiple processor contexts (in a | |
22 | _stable_ way). n-1 contexts do nothing. 1 context accepts input | |
23 | and outputs it. | |
24 | */ | |
25 | ||
26 | ||
27 | #define NUMCONTEXTS 10 /* how many contexts to make */ | |
28 | #define STACKSIZE 4096 /* stack size */ | |
29 | #define INTERVAL 100 /* timer interval in nanoseconds */ | |
30 | ||
31 | sigset_t set; /* process wide signal mask */ | |
32 | ucontext_t signal_context; /* the interrupt context */ | |
33 | void *signal_stack; /* global interrupt stack */ | |
34 | ||
35 | ucontext_t contexts[NUMCONTEXTS]; /* store our context info */ | |
36 | int curcontext = 0; /* whats the current context? */ | |
37 | ucontext_t *cur_context; /* a pointer to the current_context */ | |
38 | ||
39 | /* The scheduling algorithm; selects the next context to run, then starts it. */ | |
40 | void | |
41 | scheduler() | |
42 | { | |
43 | printf("scheduling out thread %d\n", curcontext); | |
44 | ||
45 | curcontext = (curcontext + 1) % NUMCONTEXTS; /* round robin */ | |
46 | cur_context = &contexts[curcontext]; | |
47 | ||
48 | printf("scheduling in thread %d\n", curcontext); | |
49 | ||
50 | setcontext(cur_context); /* go */ | |
51 | } | |
52 | ||
53 | /* | |
54 | Timer interrupt handler. | |
55 | Creates a new context to run the scheduler in, masks signals, then swaps | |
56 | contexts saving the previously executing thread and jumping to the | |
57 | scheduler. | |
58 | */ | |
59 | void | |
60 | timer_interrupt(int j, siginfo_t *si, void *old_context) | |
61 | { | |
62 | /* Create new scheduler context */ | |
63 | getcontext(&signal_context); | |
64 | signal_context.uc_stack.ss_sp = signal_stack; | |
65 | signal_context.uc_stack.ss_size = STACKSIZE; | |
66 | signal_context.uc_stack.ss_flags = 0; | |
67 | sigemptyset(&signal_context.uc_sigmask); | |
68 | makecontext(&signal_context, scheduler, 1); | |
69 | ||
70 | /* save running thread, jump to scheduler */ | |
71 | swapcontext(cur_context,&signal_context); | |
72 | } | |
73 | ||
74 | /* Set up SIGALRM signal handler */ | |
75 | void | |
76 | setup_signals(void) | |
77 | { | |
78 | struct sigaction act; | |
79 | ||
80 | act.sa_sigaction = timer_interrupt; | |
81 | sigemptyset(&act.sa_mask); | |
82 | act.sa_flags = SA_RESTART | SA_SIGINFO; | |
83 | ||
84 | sigemptyset(&set); | |
85 | sigaddset(&set, SIGALRM); | |
86 | ||
87 | if(sigaction(SIGALRM, &act, NULL) != 0) { | |
88 | perror("Signal handler"); | |
89 | } | |
90 | } | |
91 | ||
92 | ||
93 | /* Thread bodies */ | |
94 | void | |
95 | thread1() | |
96 | { | |
97 | while(1) { | |
98 | poll(NULL,0,100); | |
99 | }; /* do nothing nicely */ | |
100 | } | |
101 | ||
102 | void | |
103 | thread2() | |
104 | { | |
105 | char buf[1024]; | |
106 | /* get a string.. print a string.. ad infinitum */ | |
107 | while(1) { | |
108 | fgets(buf, 1024, stdin); | |
109 | printf("[[[[[[%s]]]]]]\n",buf); | |
110 | } | |
111 | } | |
112 | ||
113 | /* helper function to create a context. | |
114 | initialize the context from the current context, setup the new | |
115 | stack, signal mask, and tell it which function to call. | |
116 | */ | |
117 | void | |
118 | mkcontext(ucontext_t *uc, void *function) | |
119 | { | |
120 | void * stack; | |
121 | ||
122 | getcontext(uc); | |
123 | ||
124 | stack = malloc(STACKSIZE); | |
125 | if (stack == NULL) { | |
126 | perror("malloc"); | |
127 | exit(1); | |
128 | } | |
129 | /* we need to initialize the ucontext structure, give it a stack, | |
130 | flags, and a sigmask */ | |
131 | uc->uc_stack.ss_sp = stack; | |
132 | uc->uc_stack.ss_size = STACKSIZE; | |
133 | uc->uc_stack.ss_flags = 0; | |
134 | if (sigemptyset(&uc->uc_sigmask) < 0){ | |
135 | perror("sigemptyset"); | |
136 | exit(1); | |
137 | } | |
138 | ||
139 | /* setup the function we're going to, and n-1 arguments. */ | |
140 | makecontext(uc, function, 1); | |
141 | ||
142 | printf("context is %p\n", uc); | |
143 | } | |
144 | ||
145 | ||
146 | int | |
147 | main() | |
148 | { | |
149 | int i; | |
150 | struct itimerval it; | |
151 | ||
152 | fprintf(stderr,"Process Id: %d\n", (int)getpid()); | |
153 | ||
154 | /* allocate the global signal/interrupt stack */ | |
155 | signal_stack = malloc(STACKSIZE); | |
156 | if (signal_stack == NULL) { | |
157 | perror("malloc"); | |
158 | exit(1); | |
159 | } | |
160 | ||
161 | /* make all our contexts */ | |
162 | mkcontext(&contexts[0], thread2); | |
163 | for(i=1; i < NUMCONTEXTS; i++) | |
164 | mkcontext(&contexts[i], thread1); | |
165 | ||
166 | ||
167 | /* initialize the signal handlers */ | |
168 | setup_signals(); | |
169 | ||
170 | /* setup our timer */ | |
171 | it.it_interval.tv_sec = 0; | |
172 | it.it_interval.tv_usec = INTERVAL * 1000; | |
173 | it.it_value = it.it_interval; | |
174 | if (setitimer(ITIMER_REAL, &it, NULL) ) perror("setitiimer"); | |
175 | ||
176 | /* force a swap to the first context */ | |
177 | cur_context = &contexts[0]; | |
178 | setcontext(&contexts[0]); | |
179 | ||
180 | return 0; /* make gcc happy */ | |
181 | } |