9af6c0d1b402a973cc144532c15c2f1ae5d15d2f
[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 <kern/irq.h>
43 #include <cpu/frame.h> // CPU_IDLE
44 #include <drv/timer.h>
45 #include <cfg/module.h>
46
47
48 /*
49  * The time sharing scheduler forces a task switch when the current
50  * process has exhausted its quantum.
51  */
52 uint16_t Quantum;
53
54 Timer preempt_timer;
55
56 /**
57  * Disable preemptive task switching.
58  *
59  * The scheduler maintains a per-process nesting counter.  Task switching is
60  * effectively re-enabled only when the number of calls to proc_permit()
61  * matches the number of calls to proc_forbid().
62  *
63  * Calling functions that could sleep while task switching is disabled
64  * is dangerous, although supported.  Preemptive task switching is
65  * resumed while the process is sleeping and disabled again as soon as
66  * it wakes up again.
67  *
68  * \sa proc_permit()
69  */
70 void proc_forbid(void)
71 {
72         /* No need to protect against interrupts here. */
73         ++CurrentProcess->forbid_cnt;
74 }
75
76 /**
77  * Re-enable preemptive task switching.
78  *
79  * \sa proc_forbid()
80  */
81 void proc_permit(void)
82 {
83         /* No need to protect against interrupts here. */
84         --CurrentProcess->forbid_cnt;
85 }
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 void preempt_init(void)
160 {
161         MOD_CHECK(irq);
162         MOD_CHECK(timer);
163
164         irq_register(SIGUSR1, proc_preempt);
165
166         timer_setSoftint(&preempt_timer, proc_preempt_timer, NULL);
167         timer_setDelay(&preempt_timer, CONFIG_KERN_QUANTUM);
168         timer_add(&preempt_timer);
169 }