Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / arch / i386-darwin / exec / coretest.c
blob9405a390386341f278ae5c3682265b6b8904cb29
1 /*
2 Copyright © 1995-2001, 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 <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sigcore.h>
14 #define timeval sys_timeval
15 #include <sys/time.h>
16 #undef timeval
18 #include <proto/arossupport.h>
20 /* Prototypes */
21 void Dispatch (void);
22 void Enqueue (struct List * list, struct Node * node);
23 void AddTail (struct List * list, struct Node * node);
24 void Switch (void);
26 /* This is used to count the number of interrupts. */
27 ULONG cnt;
29 /* Let the sigcore do it's magic */
30 GLOBAL_SIGNAL_INIT
32 /* This flag means that a task switch is pending */
33 #define SB_SAR 15
34 #define SF_SAR 0x8000
36 /* Dummy SysBase */
37 struct ExecBase _SysBase, * SysBase = &_SysBase;
39 /* Some tasks */
40 struct Task Task1, Task2, Task3, Task4, TaskMain;
42 /* List functions */
43 void AddTail (struct List * list, struct Node * node)
45 ADDTAIL(list,node);
46 } /* AddTail */
48 void AddHead (struct List * list, struct Node * node)
50 ADDHEAD (list, node);
51 } /* AddHead */
53 void Remove (struct Node * node)
55 REMOVE(node);
56 } /* Remove */
58 /* Enter a node into a sorted list */
59 void Enqueue (struct List * list, struct Node * node)
61 struct Node * next;
63 ForeachNode (list, next)
65 /* Look for the first node with a lower priority */
66 if (node->ln_Pri > next->ln_Pri)
67 break;
70 /* Insert "node" before "next" */
71 node->ln_Pred = next->ln_Pred;
72 next->ln_Pred->ln_Succ = node;
73 next->ln_Pred = node;
74 node->ln_Succ = next;
75 } /* Enqueue */
77 #define STR(x) (x && x->ln_Name ? (char *)x->ln_Name : "NULL")
79 /* Print a list with nodes with names. */
80 void PrintList (struct List * list)
82 struct Node * node;
83 int t = 0;
85 printf ("list %p { head=%s, tail=%s } = ", list,
86 STR(list->lh_Head), STR(list->lh_TailPred));
88 for (node=GetHead(list); node; node=GetSucc(node))
90 printf ("%s (%p { succ=%s pred=%s pri=%d}), ", node->ln_Name, node,
91 STR(GetSucc(node)), STR(GetPred(node)),
92 node->ln_Pri
94 if (++t > 10)
95 break;
98 printf ("\n");
101 #undef STR
103 /* Macro to get a pointer to the current running task */
104 #define THISTASK (SysBase->ThisTask)
107 Disable and enable signals. Don't use these in the signal handler
108 because then some signal might break the signal handler and then
109 stack will overflow.
111 #define DISABLE ({sigset_t set; sigfillset(&set); sigprocmask (SIG_BLOCK, &set, NULL);})
112 #define ENABLE ({sigset_t set; sigfillset(&set); sigprocmask (SIG_UNBLOCK, &set, NULL);})
114 /* Enable/Disable interrupts */
115 void Disable (void)
118 Disable signals only if they are not already. The initial value of
119 IDNestCnt is -1.
121 if (SysBase->IDNestCnt++ < 0)
123 DISABLE;
125 } /* Disable */
127 void Enable (void)
130 Enable signals only if the number of calls of Enable() matches the
131 calls of Disable().
133 if (--SysBase->IDNestCnt < 0)
135 ENABLE;
137 } /* Enable */
139 /* Allow/forbid task switches */
140 void Forbid (void)
143 Task switches are only allowed if TDNestCnt is < 0. The initial
144 value of TDNestCnt is -1.
146 ++ SysBase->TDNestCnt;
147 } /* Forbid */
149 void Permit (void)
151 /* Count calls and check if interrupts are allowed. */
152 if (--SysBase->TDNestCnt < 0
153 && SysBase->IDNestCnt < 0
157 Task switches are allowed again. If a switch is pending
158 right now, do it.
160 if (SysBase->SysFlags & SF_SAR)
162 /* Clear flag */
163 SysBase->SysFlags &= ~SF_SAR;
165 /* Do task switch */
166 Switch ();
169 } /* Permit */
171 /* Main routine: Insert a task into the list of tasks to run. */
172 void Reschedule (struct Task * task)
175 The code in here defines how "good" the task switching is.
176 There are seveal things which should be taken into account:
178 1. No task should block the CPU forever even if it is an
179 endless loop.
181 2. Tasks with a higher priority should get the CPU more often.
183 3. Tasks with a low priority should get the CPU every now and then.
185 Enqueue() fulfills 2 but not 1 and 3. AddTail() fulfills 1 and 3.
187 A better way would be to "deteriorate" a task, ie. decrease the
188 priority and use Enqueue() but at this time, I can't do this
189 because I have no good way to extend the task structure (I
190 need a variable to store the original prio).
192 AddTail (&SysBase->TaskReady, (struct Node *)task);
193 } /* Reschedule */
195 /* Switch to a new task if the current task is not running and no
196 exception has been raised. */
197 ULONG Wait (ULONG sigmask);
198 void Switch (void)
200 struct Task * task = THISTASK;
202 /* Check that the task is not running and no exception is pending */
203 if (task->tc_State != TS_RUN && !(task->tc_Flags & TF_EXCEPT) )
205 /* Allow signals. */
206 ENABLE;
209 Make sure there is a signal. This is somewhat tricky: The
210 current task (which is excuting this funcion) will loose the
211 CPU (ie. some code of another task will be executed). Then
212 at some time in the future, the current task (ie. the one
213 that has executed the kill()) will get the CPU back and
214 continue with the code after the kill().
216 kill (getpid(), SIGALRM);
218 } /* Switch */
221 This waits for a "signal". That's not a Unix signal but a flag set
222 by some other task (eg. if you send a command to a device, the
223 device will call you back when it has processes the command.
224 When this happens such a "signal" will be set). The task will be
225 suspended until any of the bits given to Wait() are set in the
226 tasks' signal mask. Again, this signal mask has nothing to do
227 with the Unix signal mask.
229 It's a dummy right now. All it does is switch to another task.
231 ULONG Wait (ULONG sigmask)
233 struct Task * task = THISTASK;
236 Task is no longer running. If we didn't do this, Switch() would do
237 nothing.
239 task->tc_State = TS_READY;
241 /* Let another task run. */
242 Switch ();
244 /* When I get the CPU back, this code is executed */
245 return 0;
248 /* Simple main for a task: Print a message and wait for a signal. */
249 void Main1 (void)
251 struct Task * task = THISTASK;
252 STRPTR name = task->tc_Node.ln_Name;
254 while (1)
256 printf ("Main1: %s\n", name);
258 Wait (1);
262 /* Another method of waiting (but an inferior one). */
263 void busy_wait (void)
265 int t;
267 for (t=cnt; t==cnt; );
270 /* Same as Main1 but wait by polling */
271 void Main2 (void)
273 struct Task * task = THISTASK;
274 STRPTR name = task->tc_Node.ln_Name;
276 while (1)
278 printf ("Main2: %s\n", name);
281 Kids, don't do this at home. I'm a professional.
283 This is to make sure even endless loops don't harm the
284 system. Even if this task has a higher priority than any
285 other task in the system, the other tasks will get the
286 CPU every now and then.
288 busy_wait();
292 #define DEBUG_STACK 0
293 #define STACKOFFSET 0
296 The signal handler. It will store the current tasks context and
297 switch to another task if this is allowed right now.
299 static void sighandler (int sig, sigcontext_t * sc)
301 cnt ++;
303 /* Are task switches allowed ? */
304 if (SysBase->TDNestCnt < 0)
306 /* Save all registers and the stack pointer */
308 SAVEREGS(THISTASK,sc);
310 #if DEBUG_STACK
311 PRINT_SC(sc);
312 PRINT_STACK(sp);
313 #endif
315 /* Find a new task to run */
316 Dispatch ();
317 /* Restore signal mask */
318 if (SysBase->IDNestCnt < 0)
319 SC_ENABLE(sc);
320 else
321 SC_DISABLE(sc);
323 #if DEBUG_STACK
324 PRINT_STACK(sp);
325 printf ("\n");
326 #endif
328 RESTOREREGS(THISTASK,sc);
331 else
333 /* Set flag: switch tasks as soon as switches are allowed again */
334 SysBase->SysFlags |= SF_SAR;
336 } /* sighandler */
338 /* Find another task which is allowed to run and modify SysBase accordingly */
339 void Dispatch (void)
341 struct Task * this = THISTASK;
342 struct Task * task;
344 /* Check the stack */
345 if (this->tc_SPReg <= this->tc_SPLower
346 || this->tc_SPReg >= this->tc_SPUpper
349 printf ("illegal stack\n");
352 /* Try to find a task which is ready to run */
353 if ((task = GetHead (&SysBase->TaskReady)))
355 #if 1
356 printf ("Dispatch: Old = %s (Stack = %x), new = %s\n",
357 this->tc_Node.ln_Name,
358 (IPTR)this->tc_SPUpper - (IPTR)this->tc_SPReg,
359 task->tc_Node.ln_Name);
360 #endif
362 /* Remove new task from the list */
363 Remove ((struct Node *)task);
365 /* Sort the old task into the list of tasks which want to run */
366 Reschedule (this);
368 /* Save disable counters */
369 this->tc_TDNestCnt = SysBase->TDNestCnt;
370 this->tc_IDNestCnt = SysBase->IDNestCnt;
372 /* Set new counters */
373 SysBase->TDNestCnt = task->tc_TDNestCnt;
374 SysBase->IDNestCnt = task->tc_IDNestCnt;
376 /* Switch task */
377 THISTASK = task;
379 /* Set new states of the tasks */
380 this->tc_State = TS_READY;
381 task->tc_State = TS_RUN;
383 printf("leaving dispatch!\n");
384 } /* Dispatch */
387 Initialize the system: Install an interrupt handler and make sure
388 it is called at 50Hz
390 void InitCore(void)
392 struct sigaction sa;
393 struct itimerval interval;
395 /* Install a handler for the ALARM signal */
396 sa.sa_handler = (SIGHANDLER_T)SIGHANDLER;
397 sa.sa_flags = SA_RESTART;
398 #ifdef __linux__
399 sa.sa_restorer = NULL;
400 #endif /* __linux__ */
401 sigfillset (&sa.sa_mask);
403 sigaction (SIGALRM, &sa, NULL);
405 /* Set 50Hz intervall for ALARM signal */
406 interval.it_interval.tv_sec = interval.it_value.tv_sec = 1;
407 interval.it_interval.tv_usec = interval.it_value.tv_usec = 1000000/50;
409 setitimer (ITIMER_REAL, &interval, NULL);
410 } /* InitCore */
412 #define STACK_SIZE 4096
414 /* Create a new task */
415 void AddTask (struct Task * task, STRPTR name, BYTE pri, APTR pc)
417 SP_TYPE * sp;
419 /* Init task structure */
420 memset (task, 0, sizeof (struct Task));
422 /* Init fields with real values */
423 task->tc_Node.ln_Pri = pri;
424 task->tc_Node.ln_Name = name;
425 task->tc_State = TS_READY;
427 /* Allow task switches and signals */
428 task->tc_TDNestCnt = -1;
429 task->tc_IDNestCnt = -1;
431 /* Allocate a stack */
432 sp = malloc (STACK_SIZE * sizeof (SP_TYPE));
435 Copy bounds of stack in task structure. Note that the stack
436 grows to small addresses (ie. storing something on the stack
437 decreases the stack pointer).
439 task->tc_SPLower = sp;
440 sp += STACK_SIZE;
441 task->tc_SPUpper = sp;
444 Let the sigcore do it's magic. Create a frame from which an
445 initial task context can be restored from.
448 GetIntETask(task) = malloc(sizeof(struct IntETask));
449 task->tc_Flags |= TF_ETASK;
450 GetCpuContext(task) = malloc(SIZEOF_ALL_REGISTERS);
452 PREPARE_INITIAL_FRAME(sp,pc);
453 PREPARE_INITIAL_CONTEXT(task,pc);
455 /* Save new stack pointer */
456 task->tc_SPReg = sp;
458 /* Add task to queue by priority */
459 Enqueue (&SysBase->TaskReady, (struct Node *)task);
460 } /* AddTask */
463 Main routine: Create four tasks (three with the Mains above and one
464 for main(). Wait for some task switches then terminate cleanly.
466 int main (int argc, char ** argv)
468 /* Init SysBase */
469 NEWLIST (&SysBase->TaskReady);
470 NEWLIST (&SysBase->TaskWait);
472 /* Signals and task switches are not allowed right now */
473 SysBase->IDNestCnt = 0;
474 SysBase->TDNestCnt = 0;
476 /* Add three tasks */
477 AddTask (&Task1, "Task 1", 0, Main1);
478 AddTask (&Task2, "Task 2", 5, Main2);
479 AddTask (&Task3, "Task 3", 0, Main2);
480 PrintList (&SysBase->TaskReady);
483 Add main task. Make sure the stack check is ok. This task is *not*
484 added to the list. It is stored in THISTASK and will be added to
485 the list at the next call to Dispatch().
487 Also a trick with the stack: This is the stack of the Unix process.
488 We don't know where it lies in memory nor how big it is (it can
489 grow), so we use "reasonable" defaults. The upper bounds is the
490 first argument (or the last local variable but we don't have any
491 right now). If the stack ever passes by this, we begin to trash
492 data on our stack. The lower bounds is 0 (well, we could restrict
493 the stack to, say, STACK_SIZE from SPUpper, but Unix is responsible
494 for this stack, so I don't really care).
496 TaskMain.tc_Node.ln_Pri = 0;
497 TaskMain.tc_Node.ln_Name = "Main";
498 TaskMain.tc_State = TS_READY;
499 TaskMain.tc_SPLower = 0;
500 TaskMain.tc_SPUpper = &argc;
502 TaskMain.tc_UnionETask.tc_ETask = malloc(sizeof(struct IntETask));
503 TaskMain.tc_Flags |= TF_ETASK;
504 ((struct IntETask *)TaskMain.tc_UnionETask.tc_ETask)->iet_Context = malloc(SIZEOF_ALL_REGISTERS);
506 /* The currently running task is me, myself and I */
507 THISTASK = &TaskMain;
509 /* Start interrupts and allow them. */
510 InitCore ();
511 Enable ();
512 Permit ();
514 /* Wait for 10000 signals */
515 while (cnt < 20)
517 printf ("%6ld\n", cnt);
519 /* Wait for a "signal" from another task. */
520 Wait (1);
523 /* Make sure we don't get disturbed in the cleanup */
524 Disable ();
526 /* Show how many signals have been processed */
527 printf ("Exit after %ld signals\n", cnt);
529 return 0;
530 } /* main */