preempt: irq supervisor draft
[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 2008 Bernie Innocenti <bernie@codewiz.org>
30  * -->
31  *
32  * \brief Simple realtime multitasking scheduler.
33  *        Context switching is only done cooperatively.
34  *
35  * \version $Id: proc.c 1616 2008-08-10 19:41:26Z bernie $
36  * \author Bernie Innocenti <bernie@codewiz.org>
37  */
38
39 #include "proc_p.h"
40 #include "proc.h"
41
42 #include <cpu/frame.h> // CPU_IDLE
43 #include <drv/timer.h>
44
45
46 /*
47  * The time sharing scheduler forces a task switch when the current
48  * process has exhausted its quantum.
49  */
50 uint16_t Quantum;
51
52 Timer preempt_timer;
53
54 /**
55  * Disable preemptive task switching.
56  *
57  * The scheduler maintains a per-process nesting counter.  Task switching is
58  * effectively re-enabled only when the number of calls to proc_permit()
59  * matches the number of calls to proc_forbid().
60  *
61  * Calling functions that could sleep while task switching is disabled
62  * is dangerous, although supported.  Preemptive task switching is
63  * resumed while the process is sleeping and disabled again as soon as
64  * it wakes up again.
65  *
66  * \sa proc_permit()
67  */
68 void proc_forbid(void)
69 {
70         /* No need to protect against interrupts here. */
71         ++CurrentProcess->forbid_cnt;
72 }
73
74 /**
75  * Re-enable preemptive task switching.
76  *
77  * \sa proc_forbid()
78  */
79 void proc_permit(void)
80 {
81         /* No need to protect against interrupts here. */
82         --CurrentProcess->forbid_cnt;
83 }
84
85 static void (*irq_handlers[100])(void); // FIXME
86
87
88 void proc_preempt(void)
89 {
90         TRACE;
91
92         ATOMIC(LIST_ASSERT_VALID(&ProcReadyList));
93
94         TRACEMSG("hello1");
95         IRQ_DISABLE;
96         /* Poll on the ready queue for the first ready process */
97         while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList)))
98         {
99         //TRACEMSG("hello2");
100                 /*
101                  * Make sure we physically reenable interrupts here, no matter what
102                  * the current task status is. This is important because if we
103                  * are idle-spinning, we must allow interrupts, otherwise no
104                  * process will ever wake up.
105                  *
106                  * During idle-spinning, an interrupt can occur and it may
107                  * modify \p ProcReadyList. To ensure that compiler reload this
108                  * variable every while cycle we call CPU_MEMORY_BARRIER.
109                  * The memory barrier ensure that all variables used in this context
110                  * are reloaded.
111                  */
112                 IRQ_ENABLE;
113                 //FIXME: calls Qt stuff from sighandler! CPU_IDLE;
114                 MEMORY_BARRIER;
115                 IRQ_DISABLE;
116         //TRACEMSG("hello3");
117         }
118         IRQ_ENABLE;
119         TRACEMSG("hello4");
120 }
121
122 void proc_preempt_timer(UNUSED_ARG(void *, param))
123 {
124         IRQ_DISABLE;
125         if (CurrentProcess)
126         {
127                 TRACEMSG("preempting %p:%s", CurrentProcess, CurrentProcess->monitor.name);
128                 SCHED_ENQUEUE(CurrentProcess);
129                 IRQ_ENABLE;
130                 proc_preempt();
131         }
132         IRQ_ENABLE;
133
134         timer_setDelay(&preempt_timer, CONFIG_KERN_QUANTUM);
135         timer_add(&preempt_timer);
136 }
137
138 void proc_schedule(void)
139 {
140         TRACE;
141
142         // Will invoke proc_preempt() in interrupt context
143         kill(0, SIGUSR1);
144 }
145
146 void proc_yield(void)
147 {
148         ATOMIC(SCHED_ENQUEUE(CurrentProcess));
149
150         proc_schedule();
151 }
152
153 void proc_entry(void (*user_entry)(void))
154 {
155         user_entry();
156         proc_exit();
157 }
158
159 /* signal handler */
160 void irq_entry(int signum)
161 {
162         Process * const old_process = CurrentProcess;
163
164         irq_handlers[signum]();
165
166         if (!CurrentProcess)
167         {
168                 TRACEMSG("no runnable processes!");
169                 IRQ_ENABLE;
170                 pause();
171         }
172         else
173         {
174                 if (old_process != CurrentProcess)
175                 {
176                         TRACEMSG("switching from %p:%s to %p:%s",
177                                 old_process, old_process ? old_process->monitor.name : "-",
178                                 CurrentProcess, CurrentProcess->monitor.name);
179
180                         if (old_process)
181                                 swapcontext(&old_process->context, &CurrentProcess->context);
182                         else
183                                 setcontext(&CurrentProcess->context);
184
185                         /* not reached */
186                 }
187                 TRACEMSG("keeping %p:%s", CurrentProcess, CurrentProcess->monitor.name);
188         }
189 }
190
191 void irq_register(int irq, void (*callback)(void))
192 {
193         irq_handlers[irq] = callback;
194 }
195
196 void irq_init(void)
197 {
198         struct sigaction act;
199         act.sa_handler = irq_entry;
200         sigemptyset(&act.sa_mask);
201         //sigaddset(&act.sa_mask, irq);
202         act.sa_flags = SA_RESTART; /* | SA_SIGINFO; */
203
204         sigaction(SIGUSR1, &act, NULL);
205         #if !(ARCH & ARCH_QT)
206                 sigaction(SIGALRM, &act, NULL);
207         #endif
208 }
209
210 void preempt_init(void)
211 {
212         irq_init(); // FIXME: move before
213         irq_register(SIGUSR1, proc_preempt);
214
215         timer_setSoftInt(&preempt_timer, proc_preempt_timer, NULL);
216         timer_setDelay(&preempt_timer, CONFIG_KERN_QUANTUM);
217         timer_add(&preempt_timer);
218 }