preempt: much closer
[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         TRACEMSG("hello1");
96         IRQ_DISABLE;
97         /* Poll on the ready queue for the first ready process */
98         while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList)))
99         {
100         TRACEMSG("hello2");
101                 /*
102                  * Make sure we physically reenable interrupts here, no matter what
103                  * the current task status is. This is important because if we
104                  * are idle-spinning, we must allow interrupts, otherwise no
105                  * process will ever wake up.
106                  *
107                  * During idle-spinning, an interrupt can occur and it may
108                  * modify \p ProcReadyList. To ensure that compiler reload this
109                  * variable every while cycle we call CPU_MEMORY_BARRIER.
110                  * The memory barrier ensure that all variables used in this context
111                  * are reloaded.
112                  */
113                 IRQ_ENABLE;
114                 //FIXME: calls Qt stuff from sighandler! CPU_IDLE;
115                 MEMORY_BARRIER;
116                 IRQ_DISABLE;
117         TRACEMSG("hello3");
118         }
119         IRQ_ENABLE;
120         TRACEMSG("hello4");
121 }
122
123 void proc_preempt_timer(void)
124 {
125         // TODO: check Quantum
126
127         alarm(1);
128
129         if (CurrentProcess)
130         {
131                 TRACEMSG("preempting %p:%s", CurrentProcess, CurrentProcess->monitor.name);
132                 ATOMIC(SCHED_ENQUEUE(CurrentProcess));
133                 proc_schedule();
134         }
135 }
136
137 void proc_schedule(void)
138 {
139         TRACE;
140
141         // Will invoke proc_preempt() in interrupt context
142         kill(0, SIGUSR1);
143 }
144
145 void proc_yield(void)
146 {
147         ATOMIC(SCHED_ENQUEUE(CurrentProcess));
148
149         proc_schedule();
150 }
151
152 void proc_entry(void (*user_entry)(void))
153 {
154         user_entry();
155         proc_exit();
156 }
157
158 /* signal handler */
159 void irq_entry(int signum)
160 {
161         Process *old_process;
162
163 //      TRACEMSG("storing %p:%s", CurrentProcess, CurrentProcess->monitor.name);
164 //      CurrentProcess->leaving = false;
165 //      getcontext(&CurrentProcess->context);
166         /* We get here in two ways: directly, and after setcontext() below */
167
168 //      if (CurrentProcess->leaving)
169 //      {
170 //              TRACEMSG("leaving to %p:%s", CurrentProcess, CurrentProcess->monitor.name);
171 //              return;
172 //      }
173
174         old_process = CurrentProcess;
175
176         irq_handlers[signum]();
177
178         if (old_process != CurrentProcess)
179         {
180                 TRACEMSG("switching from %p:%s to %p:%s",
181                         old_process, old_process->monitor.name,
182                         CurrentProcess, CurrentProcess->monitor.name);
183                 swapcontext(&old_process->context, &CurrentProcess->context);
184 //              TRACEMSG("launching %p:%s", CurrentProcess, CurrentProcess->monitor.name);
185 //              CurrentProcess->leaving = true;
186 //              setcontext(&CurrentProcess->context);
187                 /* not reached */
188         }
189
190         TRACEMSG("keeping %p:%s", CurrentProcess, CurrentProcess->monitor.name);
191 }
192
193 void preempt_init(void)
194 {
195         struct sigaction act;
196         act.sa_handler = irq_entry;
197         sigemptyset(&act.sa_mask);
198         sigaddset(&act.sa_mask, SIGUSR1);
199         sigaddset(&act.sa_mask, SIGALRM);
200         act.sa_flags = SA_RESTART; /* | SA_SIGINFO; */
201
202         irq_handlers[SIGUSR1] = proc_preempt;
203         irq_handlers[SIGALRM] = proc_preempt_timer;
204         sigaction(SIGUSR1, &act, NULL);
205         sigaction(SIGALRM, &act, NULL);
206
207         alarm(1);  // FIXME
208 }