1 /* Complete Context Control
2    Copyright (C) 1991-2022 Free Software Foundation, Inc.
3 
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License
6    as published by the Free Software Foundation; either version 2
7    of the License, or (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 #include <signal.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ucontext.h>
22 #include <sys/time.h>
23 
24 /* Set by the signal handler.  */
25 static volatile int expired;
26 
27 /* The contexts.  */
28 static ucontext_t uc[3];
29 
30 /* We do only a certain number of switches.  */
31 static int switches;
32 
33 
34 /* This is the function doing the work.  It is just a
35    skeleton, real code has to be filled in.  */
36 static void
f(int n)37 f (int n)
38 {
39   int m = 0;
40   while (1)
41     {
42       /* This is where the work would be done.  */
43       if (++m % 100 == 0)
44         {
45           putchar ('.');
46           fflush (stdout);
47         }
48 
49       /* Regularly the @var{expire} variable must be checked.  */
50       if (expired)
51         {
52           /* We do not want the program to run forever.  */
53           if (++switches == 20)
54             return;
55 
56           printf ("\nswitching from %d to %d\n", n, 3 - n);
57           expired = 0;
58           /* Switch to the other context, saving the current one.  */
59           swapcontext (&uc[n], &uc[3 - n]);
60         }
61     }
62 }
63 
64 /* This is the signal handler which simply set the variable.  */
65 void
handler(int signal)66 handler (int signal)
67 {
68   expired = 1;
69 }
70 
71 
72 int
main(void)73 main (void)
74 {
75   struct sigaction sa;
76   struct itimerval it;
77   char st1[8192];
78   char st2[8192];
79 
80   /* Initialize the data structures for the interval timer.  */
81   sa.sa_flags = SA_RESTART;
82   sigfillset (&sa.sa_mask);
83   sa.sa_handler = handler;
84   it.it_interval.tv_sec = 0;
85   it.it_interval.tv_usec = 1;
86   it.it_value = it.it_interval;
87 
88   /* Install the timer and get the context we can manipulate.  */
89   if (sigaction (SIGPROF, &sa, NULL) < 0
90       || setitimer (ITIMER_PROF, &it, NULL) < 0
91       || getcontext (&uc[1]) == -1
92       || getcontext (&uc[2]) == -1)
93     abort ();
94 
95   /* Create a context with a separate stack which causes the
96      function @code{f} to be call with the parameter @code{1}.
97      Note that the @code{uc_link} points to the main context
98      which will cause the program to terminate once the function
99      return.  */
100   uc[1].uc_link = &uc[0];
101   uc[1].uc_stack.ss_sp = st1;
102   uc[1].uc_stack.ss_size = sizeof st1;
103   makecontext (&uc[1], (void (*) (void)) f, 1, 1);
104 
105   /* Similarly, but @code{2} is passed as the parameter to @code{f}.  */
106   uc[2].uc_link = &uc[0];
107   uc[2].uc_stack.ss_sp = st2;
108   uc[2].uc_stack.ss_size = sizeof st2;
109   makecontext (&uc[2], (void (*) (void)) f, 1, 2);
110 
111   /* Start running.  */
112   swapcontext (&uc[0], &uc[1]);
113   putchar ('\n');
114 
115   return 0;
116 }
117