Documented GVF_SAVE_VAR alongside other flags, and removed a query/doubt
[AROS.git] / arch / all-unix / kernel / coretest.c
blob73c4cf4a9462db17042ef1d5ef10dd64564fad9c
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <exec/lists.h>
7 #include <exec/tasks.h>
8 #include <exec/execbase.h>
9 #include <unistd.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <kernel_cpu.h>
15 #include <sys/time.h>
17 /* Prototypes */
18 void Dispatch (void);
19 void Enqueue (struct List * list, struct Node * node);
20 void AddTail (struct List * list, struct Node * node);
21 void Switch (void);
23 /* This is used to count the number of interrupts. */
24 ULONG cnt;
26 /* This flag means that a task switch is pending */
27 #define SB_SAR 15
28 #define SF_SAR 0x8000
30 /* Dummy SysBase */
31 struct ExecBase _SysBase;
32 struct ExecBase* SysBase = &_SysBase;
34 /* Dummy KernelBase */
35 struct PlatformData
37 sigset_t sig_int_mask;
40 struct PlatformData Kernel_PlatformData;
42 #define PD(x) Kernel_PlatformData
44 /* Some tasks */
45 struct Task Task1, Task2, Task3, Task4, TaskMain;
47 /* List functions */
48 void AddTail (struct List * list, struct Node * node)
50 ADDTAIL(list,node);
51 } /* AddTail */
53 void AddHead (struct List * list, struct Node * node)
55 ADDHEAD (list, node);
56 } /* AddHead */
58 void Remove (struct Node * node)
60 REMOVE(node);
61 } /* Remove */
63 /* Enter a node into a sorted list */
64 void Enqueue (struct List * list, struct Node * node)
66 struct Node * next;
68 ForeachNode (list, next)
70 /* Look for the first node with a lower priority */
71 if (node->ln_Pri > next->ln_Pri)
72 break;
75 /* Insert "node" before "next" */
76 node->ln_Pred = next->ln_Pred;
77 next->ln_Pred->ln_Succ = node;
78 next->ln_Pred = node;
79 node->ln_Succ = next;
80 } /* Enqueue */
82 #define STR(x) (x && x->ln_Name ? (char *)x->ln_Name : "NULL")
84 /* Print a list with nodes with names. */
85 void PrintList (struct List * list)
87 struct Node * node;
88 int t = 0;
90 printf ("list %p { head=%s, tail=%s } = ", list,
91 STR(list->lh_Head), STR(list->lh_TailPred));
93 for (node=GetHead(list); node; node=GetSucc(node))
95 printf ("%s (%p { succ=%s pred=%s pri=%d}), ", node->ln_Name, node,
96 STR(GetSucc(node)), STR(GetPred(node)),
97 node->ln_Pri
99 if (++t > 10)
100 break;
103 printf ("\n");
106 #undef STR
108 /* Macro to get a pointer to the current running task */
109 #define THISTASK (SysBase->ThisTask)
112 Disable and enable signals. Don't use these in the signal handler
113 because then some signal might break the signal handler and then
114 stack will overflow.
116 #define DISABLE sigprocmask (SIG_BLOCK, &Kernel_PlatformData.sig_int_mask, NULL)
117 #define ENABLE sigprocmask (SIG_UNBLOCK, &Kernel_PlatformData.sig_int_mask, NULL)
119 /* Enable/Disable interrupts */
120 void Disable (void)
123 Disable signals only if they are not already. The initial value of
124 IDNestCnt is -1.
126 if (SysBase->IDNestCnt++ < 0)
128 DISABLE;
130 } /* Disable */
132 void Enable (void)
135 Enable signals only if the number of calls of Enable() matches the
136 calls of Disable().
138 if (--SysBase->IDNestCnt < 0)
140 ENABLE;
142 } /* Enable */
144 /* Allow/forbid task switches */
145 void Forbid (void)
148 Task switches are only allowed if TDNestCnt is < 0. The initial
149 value of TDNestCnt is -1.
151 ++ SysBase->TDNestCnt;
152 } /* Forbid */
154 void Permit (void)
156 /* Count calls and check if interrupts are allowed. */
157 if (--SysBase->TDNestCnt < 0
158 && SysBase->IDNestCnt < 0
162 Task switches are allowed again. If a switch is pending
163 right now, do it.
165 if (SysBase->SysFlags & SF_SAR)
167 /* Clear flag */
168 SysBase->SysFlags &= ~SF_SAR;
170 /* Do task switch */
171 Switch ();
174 } /* Permit */
176 /* Main routine: Insert a task into the list of tasks to run. */
177 void Reschedule (struct Task * task)
180 The code in here defines how "good" the task switching is.
181 There are seveal things which should be taken into account:
183 1. No task should block the CPU forever even if it is an
184 endless loop.
186 2. Tasks with a higher priority should get the CPU more often.
188 3. Tasks with a low priority should get the CPU every now and then.
190 Enqueue() fulfills 2 but not 1 and 3. AddTail() fulfills 1 and 3.
192 A better way would be to "deteriorate" a task, ie. decrease the
193 priority and use Enqueue() but at this time, I can't do this
194 because I have no good way to extend the task structure (I
195 need a variable to store the original prio).
197 AddTail (&SysBase->TaskReady, (struct Node *)task);
198 } /* Reschedule */
200 /* Switch to a new task if the current task is not running and no
201 exception has been raised. */
202 ULONG Wait (ULONG sigmask);
203 void Switch (void)
205 struct Task * task = THISTASK;
207 /* Check that the task is not running and no exception is pending */
208 if (task->tc_State != TS_RUN && !(task->tc_Flags & TF_EXCEPT) )
210 /* Allow signals. */
211 ENABLE;
214 Make sure there is a signal. This is somewhat tricky: The
215 current task (which is excuting this funcion) will loose the
216 CPU (ie. some code of another task will be executed). Then
217 at some time in the future, the current task (ie. the one
218 that has executed the kill()) will get the CPU back and
219 continue with the code after the kill().
221 kill (getpid(), SIGALRM);
223 } /* Switch */
226 This waits for a "signal". That's not a Unix signal but a flag set
227 by some other task (eg. if you send a command to a device, the
228 device will call you back when it has processes the command.
229 When this happens such a "signal" will be set). The task will be
230 suspended until any of the bits given to Wait() are set in the
231 tasks' signal mask. Again, this signal mask has nothing to do
232 with the Unix signal mask.
234 It's a dummy right now. All it does is switch to another task.
236 ULONG Wait (ULONG sigmask)
238 struct Task * task = THISTASK;
241 Task is no longer running. If we didn't do this, Switch() would do
242 nothing.
244 task->tc_State = TS_READY;
246 /* Let another task run. */
247 Switch ();
249 /* When I get the CPU back, this code is executed */
250 return 0;
253 /* Simple main for a task: Print a message and wait for a signal. */
254 void Main1 (void)
256 struct Task * task = THISTASK;
257 STRPTR name = task->tc_Node.ln_Name;
259 while (1)
261 printf ("Main1: %s\n", name);
263 Wait (1);
267 /* Another method of waiting (but an inferior one). */
268 void busy_wait (void)
270 int t;
272 for (t=cnt; t==cnt; );
275 /* Same as Main1 but wait by polling */
276 void Main2 (void)
278 struct Task * task = THISTASK;
279 STRPTR name = task->tc_Node.ln_Name;
281 while (1)
283 printf ("Main2: %s\n", name);
286 Kids, don't do this at home. I'm a professional.
288 This is to make sure even endless loops don't harm the
289 system. Even if this task has a higher priority than any
290 other task in the system, the other tasks will get the
291 CPU every now and then.
293 busy_wait();
297 #define DEBUG_STACK 0
298 #define STACKOFFSET 0
301 The signal handler. It will store the current tasks context and
302 switch to another task if this is allowed right now.
304 static void sighandler (int sig, regs_t *sc)
306 cnt ++;
308 /* Are task switches allowed ? */
309 if (SysBase->TDNestCnt < 0)
311 struct AROSCPUContext *ctx = THISTASK->tc_UnionETask.tc_ETask->et_RegFrame;
313 #if DEBUG_STACK
314 PRINT_SC(sc);
315 #endif
317 /* Save all registers and the stack pointer */
318 SAVEREGS(ctx, sc);
319 THISTASK->tc_SPReg = (APTR)SP(sc);
321 /* Find a new task to run */
322 Dispatch ();
323 /* Restore signal mask */
324 if (SysBase->IDNestCnt < 0)
325 SC_ENABLE(sc);
326 else
327 SC_DISABLE(sc);
329 ctx = THISTASK->tc_UnionETask.tc_ETask->et_RegFrame;
330 RESTOREREGS(ctx, sc);
331 SP(sc) = (IPTR)THISTASK->tc_SPReg;
333 #if DEBUG_STACK
334 PRINT_SC(sc);
335 #endif
338 else
340 /* Set flag: switch tasks as soon as switches are allowed again */
341 SysBase->SysFlags |= SF_SAR;
343 } /* sighandler */
345 /* Let the sigcore do it's magic */
346 GLOBAL_SIGNAL_INIT(sighandler)
348 /* Find another task which is allowed to run and modify SysBase accordingly */
349 void Dispatch (void)
351 struct Task * this = THISTASK;
352 struct Task * task;
354 /* Check the stack */
355 if (this->tc_SPReg <= this->tc_SPLower
356 || this->tc_SPReg >= this->tc_SPUpper
359 printf ("illegal stack (SP %p, lower %p, upper %p)\n", this->tc_SPReg, this->tc_SPLower, this->tc_SPUpper);
362 /* Try to find a task which is ready to run */
363 if ((task = (struct Task *)GetHead (&SysBase->TaskReady)))
365 printf ("Dispatch: Old = %s (Stack = %lx), new = %s\n",
366 this->tc_Node.ln_Name,
367 (IPTR)this->tc_SPUpper - (IPTR)this->tc_SPReg,
368 task->tc_Node.ln_Name);
370 /* Remove new task from the list */
371 Remove ((struct Node *)task);
373 /* Sort the old task into the list of tasks which want to run */
374 Reschedule (this);
376 /* Save disable counters */
377 this->tc_TDNestCnt = SysBase->TDNestCnt;
378 this->tc_IDNestCnt = SysBase->IDNestCnt;
380 /* Set new counters */
381 SysBase->TDNestCnt = task->tc_TDNestCnt;
382 SysBase->IDNestCnt = task->tc_IDNestCnt;
384 /* Switch task */
385 THISTASK = task;
387 /* Set new states of the tasks */
388 this->tc_State = TS_READY;
389 task->tc_State = TS_RUN;
391 printf("leaving dispatch!\n");
392 } /* Dispatch */
395 Initialize the system: Install an interrupt handler and make sure
396 it is called at 50Hz
398 void InitCore(void)
400 struct sigaction sa;
401 struct itimerval interval;
403 /* Install a handler for the ALARM signal */
404 sa.sa_handler = (SIGHANDLER_T)sighandler_gate;
405 sa.sa_flags = SA_RESTART;
406 #ifdef __linux__
407 sa.sa_restorer = NULL;
408 #endif /* __linux__ */
409 sigfillset (&sa.sa_mask);
411 sigaction (SIGALRM, &sa, NULL);
413 /* Set 50Hz intervall for ALARM signal */
414 interval.it_interval.tv_sec = interval.it_value.tv_sec = 1;
415 interval.it_interval.tv_usec = interval.it_value.tv_usec = 1000000/50;
417 setitimer (ITIMER_REAL, &interval, NULL);
418 } /* InitCore */
420 #define STACK_SIZE 4096
422 /* Create a new task */
423 void AddTask (struct Task * task, STRPTR name, BYTE pri, APTR pc)
425 IPTR *sp;
426 struct AROSCPUContext *ctx;
428 /* Init task structure */
429 memset (task, 0, sizeof (struct Task));
431 /* Init fields with real values */
432 task->tc_Node.ln_Pri = pri;
433 task->tc_Node.ln_Name = name;
434 task->tc_State = TS_READY;
436 /* Allow task switches and signals */
437 task->tc_TDNestCnt = -1;
438 task->tc_IDNestCnt = -1;
440 /* Allocate a stack */
441 sp = malloc (STACK_SIZE * sizeof(IPTR));
444 Copy bounds of stack in task structure. Note that the stack
445 grows to small addresses (ie. storing something on the stack
446 decreases the stack pointer).
448 task->tc_SPLower = sp;
449 sp += STACK_SIZE;
450 task->tc_SPUpper = sp;
453 Let the sigcore do it's magic. Create a frame from which an
454 initial task context can be restored from.
457 task->tc_UnionETask.tc_ETask = malloc(sizeof(struct IntETask));
458 task->tc_Flags |= TF_ETASK;
459 ctx = malloc(sizeof(struct AROSCPUContext));
460 task->tc_UnionETask.tc_ETask->et_RegFrame = ctx;
462 PREPARE_INITIAL_CONTEXT(ctx);
463 PREPARE_INITIAL_FRAME(ctx, sp, (IPTR)pc);
465 /* Save new stack pointer */
466 task->tc_SPReg = sp;
468 /* Add task to queue by priority */
469 Enqueue (&SysBase->TaskReady, (struct Node *)task);
470 } /* AddTask */
473 Main routine: Create four tasks (three with the Mains above and one
474 for main(). Wait for some task switches then terminate cleanly.
476 int main (int argc, char ** argv)
478 /* Init SysBase */
479 NEWLIST (&SysBase->TaskReady);
480 NEWLIST (&SysBase->TaskWait);
482 sigfillset(&Kernel_PlatformData.sig_int_mask);
484 /* Signals and task switches are not allowed right now */
485 SysBase->IDNestCnt = 0;
486 SysBase->TDNestCnt = 0;
488 /* Add three tasks */
489 AddTask (&Task1, "Task 1", 0, Main1);
490 AddTask (&Task2, "Task 2", 5, Main2);
491 AddTask (&Task3, "Task 3", 0, Main2);
492 PrintList (&SysBase->TaskReady);
495 Add main task. Make sure the stack check is ok. This task is *not*
496 added to the list. It is stored in THISTASK and will be added to
497 the list at the next call to Dispatch().
499 Also a trick with the stack: This is the stack of the Unix process.
500 We don't know where it lies in memory nor how big it is (it can
501 grow), so we set the maximum possible limits.
503 TaskMain.tc_Node.ln_Pri = 0;
504 TaskMain.tc_Node.ln_Name = "Main";
505 TaskMain.tc_State = TS_READY;
506 TaskMain.tc_SPLower = 0;
507 TaskMain.tc_SPUpper = (APTR)-1;
509 TaskMain.tc_UnionETask.tc_ETask = malloc(sizeof(struct IntETask));
510 TaskMain.tc_Flags |= TF_ETASK;
511 TaskMain.tc_UnionETask.tc_ETask->et_RegFrame = malloc(sizeof(struct AROSCPUContext));
513 /* The currently running task is me, myself and I */
514 THISTASK = &TaskMain;
516 /* Start interrupts and allow them. */
517 InitCore ();
518 Enable ();
519 Permit ();
521 /* Wait for 10000 signals */
522 while (cnt < 20)
524 printf ("%6d\n", cnt);
526 /* Wait for a "signal" from another task. */
527 Wait (1);
530 /* Make sure we don't get disturbed in the cleanup */
531 Disable ();
533 /* Show how many signals have been processed */
534 printf ("Exit after %d signals\n", cnt);
536 return 0;
537 } /* main */