Avoid warning for unused arg when compiled without some CONFIG_KERN_xx options
[bertos.git] / kern / proc.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See devlib/README for information.
7  * -->
8  *
9  * \brief Simple realtime multitasking scheduler.
10  *        Context switching is only done cooperatively.
11  *
12  * \version $Id$
13  *
14  * \author Bernardo Innocenti <bernie@develer.com>
15  * \author Stefano Fedrigo <aleph@develer.com>
16  */
17
18 /*
19  * $Log$
20  * Revision 1.4  2004/07/13 19:21:28  aleph
21  * Avoid warning for unused arg when compiled without some CONFIG_KERN_xx options
22  *
23  * Revision 1.3  2004/06/06 18:37:57  bernie
24  * Rename event macros to look like regular functions.
25  *
26  * Revision 1.2  2004/06/03 11:27:09  bernie
27  * Add dual-license information.
28  *
29  * Revision 1.1  2004/05/23 17:27:00  bernie
30  * Import kern/ subdirectory.
31  *
32  */
33
34 #include "cpu.h"
35 #include "proc_p.h"
36 #include "proc.h"
37 #include "event.h"
38 #include "hw.h"
39 #include <drv/kdebug.h>
40
41 #include <string.h> /* memset() */
42
43 /* CPU dependent context switching routines */
44 extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp);
45
46 /*
47  * The scheduer tracks ready and waiting processes
48  * by enqueuing them in these lists. A pointer to the currently
49  * running process is stored in the CurrentProcess pointer.
50  *
51  * NOTE: these variables are protected by DI/EI locking
52  */
53 REGISTER Process *CurrentProcess;
54 REGISTER List     ProcReadyList;
55
56 #if CONFIG_KERN_PREEMPTIVE
57 /*
58  * The time sharing scheduler forces a task switch when
59  * the current process has consumed its quantum.
60  */
61 uint16_t Quantum;
62 #endif
63
64
65 /* In Win32 we must emulate stack on the real process stack */
66 #if (ARCH & ARCH_EMUL)
67 extern List StackFreeList;
68 #endif
69
70 /* The main process (the one that executes main()) */
71 struct Process MainProcess;
72
73 static void proc_init_struct(Process* proc)
74 {
75         /* Avoid warning for unused argument */
76         (void)proc;
77
78 #if CONFIG_KERN_TIMER
79         event_initSignal(&proc->proc_timer.expire, proc, SIG_SINGLE);
80 #endif
81
82 #if CONFIG_KERN_SIGNALS
83         proc->sig_recv = 0;
84 #endif
85
86 #if CONFIG_KERN_HEAP
87         proc->flags = 0;
88 #endif
89 }
90
91 void proc_init(void)
92 {
93         INITLIST(&ProcReadyList);
94
95         /* We "promote" the current context into a real process. The only thing we have
96            to do is create a PCB and make it current. We don't need to setup the stack
97            pointer because it will be written the first time we switch to another process. */
98         proc_init_struct(&MainProcess);
99         CurrentProcess = &MainProcess;
100 }
101
102
103 /*!
104  * Create a new process, starting at the provided entry point.
105  *
106  * \return Process structure of new created process
107  *         if successful, NULL otherwise.
108  */
109 Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base)
110 {
111         Process *proc;
112         size_t i;
113         size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t);
114 #if CONFIG_KERN_HEAP
115         bool free_stack = false;
116 #endif
117
118 #if (ARCH & ARCH_EMUL)
119         /* Ignore stack provided by caller
120         * and use the large enough default instead
121         */
122         stack_base = (cpustack_t *)StackFreeList.head;
123         REMOVE((Node *)stack_base);
124         stacksize = DEF_STACKSIZE;
125 #elif CONFIG_KERN_HEAP
126         /* Did the caller provide a stack for us? */
127         if (!stack_base)
128         {
129                 /* Did the caller specify the desired stack size? */
130                 if (!stacksize)
131                         stacksize = CONFIG_KERN_DEFSTACKSIZE + sizeof(Process);
132
133                 /* Allocate stack dinamically */
134                 if (!(stack_base = heap_alloc(stacksize)))
135                         return NULL;
136
137                 free_stack = true;
138         }
139 #else
140         /* Stack must have been provided by the user */
141         ASSERT(stack_base);
142         ASSERT(stacksize);
143 #endif
144
145 #ifdef _DEBUG
146         /* Fill-in the stack with a special marker to help debugging */
147         memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t));
148 #endif /* _DEBUG */
149
150         /* Initialize the process control block */
151         if (CPU_STACK_GROWS_UPWARD)
152         {
153                 proc = (Process*)stack_base;
154                 proc->stack = stack_base + proc_size_words;
155                 if (CPU_SP_ON_EMPTY_SLOT)
156                         proc->stack++;
157         }
158         else
159         {
160                 proc = (Process*)(stack_base + stacksize / sizeof(cpustack_t) - proc_size_words);
161                 proc->stack = (cpustack_t*)proc;
162                 if (CPU_SP_ON_EMPTY_SLOT)
163                         proc->stack--;
164         }
165
166         proc_init_struct(proc);
167
168 #if CONFIG_KERN_HEAP
169         proc->stack_base = stack_base;
170         proc->stack_size = stack_size;
171         if (free_stack)
172                 proc->flags |= PF_FREESTACK;
173 #endif
174
175         /* Initialize process stack frame */
176         CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit);
177         CPU_PUSH_CALL_CONTEXT(proc->stack, entry);
178
179         /* Push a clean set of CPU registers for asm_switch_context() */
180         for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
181                 CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
182
183         /* Add to ready list */
184         DISABLE_INTS;
185         SCHED_ENQUEUE(proc);
186         ENABLE_INTS;
187
188         return proc;
189 }
190
191
192 /*!
193  * System scheduler: pass CPU control to the next process in
194  * the ready queue.
195  *
196  * Saving and restoring the context on the stack is done
197  * by a CPU-dependent support routine which must usually be
198  * written in assembly.
199  */
200 void proc_schedule(void)
201 {
202         /* This function must not have any "auto" variables, otherwise
203          * the compiler might put them on the stack of the process
204          * being switched out.
205          */
206         static Process *old_process;
207
208         /* Remember old process to save its context later */
209         old_process = CurrentProcess;
210         CurrentProcess = NULL;
211
212         /* Poll on the ready queue for the first ready process
213          */
214         for(;;) /* forever */
215         {
216                 /* Do CPU specific idle processing (ARGH, should be moved to the end of the loop!) */
217                 SCHEDULER_IDLE;
218
219                 DISABLE_INTS;
220                 if (!ISLISTEMPTY(&ProcReadyList))
221                 {
222                         /* Get process from ready list */
223                         CurrentProcess = (Process *)ProcReadyList.head;
224                         REMOVE((Node *)CurrentProcess);
225                         ENABLE_INTS;
226                         break;
227                 }
228                 ENABLE_INTS;
229         }
230
231         /* Optimization: don't switch contexts when the active
232          * process has not changed.
233          */
234         if (CurrentProcess != old_process)
235         {
236                 static cpustack_t* dummy;
237
238 #if CONFIG_KERN_PREEMPTIVE
239                 /* Reset quantum for this process */
240                 Quantum = CONFIG_KERN_QUANTUM;
241 #endif
242
243                 /* Save context of old process and switch to new process. If there is no
244                  * old process, we save the old stack pointer into a dummy variable that
245                  * we ignore. In fact, this happens only when the old process has just
246                  * exited.
247                  * TODO: Instead of physically clearing the process at exit time, a zombie
248                  * list should be created.
249                  */
250                 asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
251         }
252
253         /* This RET resumes the execution on the new process */
254 }
255
256
257 /*!
258  * Terminate the current process
259  */
260 void proc_exit(void)
261 {
262 #if CONFIG_KERN_HEAP
263         /* The following code is BROKEN.
264          * We are freeing our own stack before entering proc_schedule()
265          * BAJO: A correct fix would be to rearrange the scheduler with
266          *  an additional parameter which frees the old stack/process
267          *  after a context switch.
268          */
269         if (CurrentProcess->flags & PF_FREESTACK)
270                 heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
271         heap_free(CurrentProcess);
272 #endif
273
274 #if (ARCH & ARCH_EMUL)
275 #error This is wrong
276         /* Reinsert process stack in free list */
277         ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack
278                 - (DEF_STACKSIZE / sizeof(cpustack_t))));
279
280         /* NOTE: At this point the first two words of what used
281          * to be our stack contain a list node. From now on, we
282          * rely on the compiler not reading/writing the stack.
283          */
284 #endif /* ARCH_EMUL */
285
286         CurrentProcess = NULL;
287         proc_schedule();
288         /* not reached */
289 }
290
291
292 /*!
293  * Co-operative context switch
294  */
295 void proc_switch(void)
296 {
297         DISABLE_INTS;
298         SCHED_ENQUEUE(CurrentProcess);
299         ENABLE_INTS;
300         proc_schedule();
301 }
302
303
304 /*!
305  * Get the pointer to the current process
306  */
307 struct Process *proc_current(void)
308 {
309         return CurrentProcess;
310 }
311
312
313 #if 0 /* Simple testcase for the scheduler */
314
315 /*!
316  * Proc scheduling test subthread 1
317  */
318 static void NORETURN proc_test_thread1(void)
319 {
320         for (;;)
321         {
322                 kputs(">task 1\n");
323                 timer_delay(50);
324                 proc_switch();
325         }
326 }
327
328 /*!
329  * Proc scheduling test subthread 2
330  */
331 static void NORETURN proc_test_thread2(void)
332 {
333         for (;;)
334         {
335                 kputs(">task 2\n");
336                 timer_delay(75);
337                 proc_switch();
338         }
339 }
340
341 static cpustack_t proc_test_stack1[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
342 static cpustack_t proc_test_stack2[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
343
344 /*!
345  * Proc scheduling test
346  */
347 void NORETURN proc_test(void)
348 {
349         proc_new(proc_test_thread1, sizeof(proc_test_stack1), proc_test_stack1);
350         proc_new(proc_test_thread2, sizeof(proc_test_stack2), proc_test_stack2);
351         kputs("Created tasks\n");
352
353         kputs("stack1:\n");
354         kdump(proc_test_stack1+sizeof(proc_test_stack1)-64, 64);
355         kputs("stack2:\n");
356         kdump(proc_test_stack2+sizeof(proc_test_stack1)-64, 64);
357
358         for (;;)
359         {
360                 kputs(">main task\n");
361                 timer_delay(93);
362                 proc_switch();
363         }
364
365         ASSERT(false);
366 }
367 #endif