preempt: initialize process context structure in proc_new()
[bertos.git] / bertos / kern / preempt.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2001, 2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999, 2000, 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
31  * -->
32  *
33  * \brief Simple realtime multitasking scheduler.
34  *        Context switching is only done cooperatively.
35  *
36  * \version $Id: proc.c 1616 2008-08-10 19:41:26Z bernie $
37  * \author Bernie Innocenti <bernie@codewiz.org>
38  * \author Stefano Fedrigo <aleph@develer.com>
39  */
40
41 #include "proc_p.h"
42 #include "proc.h"
43
44 #include <cpu/frame.h> // CPU_IDLE
45
46 #include <unistd.h> // XXX alarm()
47
48
49 /*
50  * The time sharing scheduler forces a task switch when the current
51  * process has exhausted its quantum.
52  */
53 uint16_t Quantum;
54
55 /**
56  * Disable preemptive task switching.
57  *
58  * The scheduler maintains a per-process nesting counter.  Task switching is
59  * effectively re-enabled only when the number of calls to proc_permit()
60  * matches the number of calls to proc_forbid().
61  *
62  * Calling functions that could sleep while task switching is disabled
63  * is dangerous, although supported.  Preemptive task switching is
64  * resumed while the process is sleeping and disabled again as soon as
65  * it wakes up again.
66  *
67  * \sa proc_permit()
68  */
69 void proc_forbid(void)
70 {
71         /* No need to protect against interrupts here. */
72         ++CurrentProcess->forbid_cnt;
73 }
74
75 /**
76  * Re-enable preemptive task switching.
77  *
78  * \sa proc_forbid()
79  */
80 void proc_permit(void)
81 {
82         /* No need to protect against interrupts here. */
83         --CurrentProcess->forbid_cnt;
84 }
85
86 static void (*irq_handlers[100])(void); // FIXME
87
88
89 void proc_preempt(void)
90 {
91         TRACE;
92
93         ATOMIC(LIST_ASSERT_VALID(&ProcReadyList));
94
95         IRQ_DISABLE;
96         /* Poll on the ready queue for the first ready process */
97         while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList)))
98         {
99                 /*
100                  * Make sure we physically reenable interrupts here, no matter what
101                  * the current task status is. This is important because if we
102                  * are idle-spinning, we must allow interrupts, otherwise no
103                  * process will ever wake up.
104                  *
105                  * During idle-spinning, an interrupt can occur and it may
106                  * modify \p ProcReadyList. To ensure that compiler reload this
107                  * variable every while cycle we call CPU_MEMORY_BARRIER.
108                  * The memory barrier ensure that all variables used in this context
109                  * are reloaded.
110                  */
111                 IRQ_ENABLE;
112                 CPU_IDLE;
113                 MEMORY_BARRIER;
114                 IRQ_DISABLE;
115         }
116         IRQ_ENABLE;
117 }
118
119 void proc_preempt_timer(void)
120 {
121         // TODO: check Quantum
122
123         alarm(1);
124         ATOMIC(SCHED_ENQUEUE(CurrentProcess));
125         proc_schedule();
126 }
127
128 void proc_schedule(void)
129 {
130         kill(0, SIGUSR1);
131 }
132
133 void proc_yield(void)
134 {
135         ATOMIC(SCHED_ENQUEUE(CurrentProcess));
136
137         proc_schedule();
138 }
139
140 /* signal handler */
141 void irq_entry(int signum)
142 {
143         Process *old_process;
144
145         TRACEMSG("storing %p:%s", CurrentProcess, CurrentProcess->monitor.name);
146         CurrentProcess->leaving = false;
147         getcontext(&CurrentProcess->context);
148         /* We get here in two ways: directly, and after setcontext() below */
149
150         if (CurrentProcess->leaving)
151         {
152                 TRACEMSG("leaving to %p:%s", CurrentProcess, CurrentProcess->monitor.name);
153                 return;
154         }
155
156         old_process = CurrentProcess;
157
158         irq_handlers[signum]();
159
160         if (old_process != CurrentProcess)
161         {
162                 TRACEMSG("launching %p:%s", CurrentProcess, CurrentProcess->monitor.name);
163                 CurrentProcess->leaving = true;
164                 setcontext(&CurrentProcess->context);
165                 /* not reached */
166         }
167
168         TRACEMSG("keeping %p:%s", CurrentProcess, CurrentProcess->monitor.name);
169 }
170
171 void preempt_init(void)
172 {
173         struct sigaction act;
174         act.sa_handler = irq_entry;
175         sigemptyset(&act.sa_mask);
176         sigaddset(&act.sa_mask, SIGUSR1);
177         sigaddset(&act.sa_mask, SIGALRM);
178         act.sa_flags = SA_RESTART; /* | SA_SIGINFO; */
179
180         irq_handlers[SIGUSR1] = proc_preempt;
181         irq_handlers[SIGALRM] = proc_preempt_timer;
182         sigaction(SIGUSR1, &act, NULL);
183         sigaction(SIGALRM, &act, NULL);
184
185         alarm(1);  // FIXME
186 }