seperate function for getting the stack pointer. moving towards a platform-specific...
[cake.git] / arch / all-unix / kernel / host_scheduler.c
blob1e5d56dd56daf7b5d52e6fe15a432002dbb4c7d0
1 #define DEBUG 1
3 #include <aros/system.h>
5 #define _GNU_SOURCE 1
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <ucontext.h>
11 #include <exec/lists.h>
12 #include <exec/execbase.h>
13 #include <hardware/intbits.h>
15 #include "etask.h"
16 #include "kernel_intern.h"
17 #include "host_debug.h"
19 /* We have to redefine these flags here because including exec_intern.h causes conflicts
20 between dos.h and WinAPI headers. This needs to be fixed - Pavel Fedin <sonic_amiga@rambler.ru */
21 #define SFB_SoftInt 5 /* There is a software interrupt */
22 #define SFF_SoftInt (1L<<5)
24 #define ARB_AttnSwitch 7 /* Delayed Switch() pending */
25 #define ARF_AttnSwitch (1L<<7)
26 #define ARB_AttnDispatch 15 /* Delayed Dispatch() pending */
27 #define ARF_AttnDispatch (1L<<15)
29 /* We also have to define needed exec functions here because proto/exec.h also conflicts with
30 WinAPI headers. */
31 #define Exception() AROS_LC0NR(void, Exception, struct ExecBase *, SysBase, 11, Exec)
32 #define Enqueue(arg1, arg2) AROS_LC2NR(void, Enqueue, \
33 AROS_LCA(struct List *,(arg1),A0), \
34 AROS_LCA(struct Node *,(arg2),A1), \
35 struct ExecBase *, SysBase, 45, Exec)
38 * Be careful with this, actually enabling this causes AROS to abort on first exception
39 * because of OutputDebugString() calls. Looks like WinAPI functions love to perform stack
40 * check and silently abort if they think something is wrong.
42 #define DINT(x) D(x)
43 #define DS(x) D(x)
44 #define DSLEEP(x) D(x)
46 #define RETURN_FROM_INTERRUPT() do { \
47 if (SysBase->IDNestCnt < 0) \
48 core_intr_enable(); \
49 return; \
50 } while (0)
53 * Task dispatcher. Basically it may be the same one no matter what scheduling algorithm is used
55 void core_Dispatch(void)
57 struct ExecBase *SysBase = *SysBasePtr;
58 struct Task *task;
60 D(bug("[kernel:scheduler] in core_Dispatch()\n"));
62 core_intr_disable();
64 /*
65 * Is the list of ready tasks empty? Well, increment the idle switch cound and stop the main thread.
67 if (IsListEmpty(&SysBase->TaskReady))
69 if (sleep_state != ss_RUNNING) {
70 SysBase->IdleCount++;
71 SysBase->AttnResched |= ARF_AttnSwitch;
73 DSLEEP(bug("[kernel:scheduler] TaskReady list empty. Sleeping for a while...\n"));
75 /* We are entering sleep mode */
76 sleep_state = ss_SLEEP_PENDING;
79 RETURN_FROM_INTERRUPT();
82 sleep_state = ss_RUNNING;
83 SysBase->DispCount++;
85 /* Get the first task from the TaskReady list, and populate it's settings through Sysbase */
86 task = (struct Task *)REMHEAD(&SysBase->TaskReady);
87 SysBase->ThisTask = task;
88 SysBase->Elapsed = SysBase->Quantum;
89 SysBase->SysFlags &= ~0x2000;
90 task->tc_State = TS_RUN;
91 SysBase->IDNestCnt = task->tc_IDNestCnt;
93 DS(bug("[kernel:scheduler] New task = %p (%s)\n", task, task->tc_Node.ln_Name));
95 /* Handle tasks's flags */
96 if (task->tc_Flags & TF_EXCEPT)
97 Exception();
99 if (task->tc_Flags & TF_LAUNCH)
101 task->tc_Launch(SysBase);
104 /* Leave interrupt and jump to the new task */
105 RETURN_FROM_INTERRUPT();
108 void core_Switch(void)
110 struct ExecBase *SysBase = *SysBasePtr;
111 struct Task *task;
113 D(bug("[kernel:scheduler] in core_Switch()\n"));
115 /* disable interrupts */
116 core_intr_disable();
118 task = SysBase->ThisTask;
120 DS(bug("[kernel:scheduler] switching out task 0x%08lx %d %s\n", task, task->tc_Node.ln_Pri, task->tc_Node.ln_Name));
122 /* store IDNestCnt into tasks's structure */
123 task->tc_IDNestCnt = SysBase->IDNestCnt;
124 task->tc_SPReg = (APTR) get_stack_pointer((void *) GetIntETask(task)->iet_Context);
126 /* And enable interrupts */
127 SysBase->IDNestCnt = -1;
128 core_intr_enable();
130 /* TF_SWITCH flag set? Call the switch routine */
131 if (task->tc_Flags & TF_SWITCH)
133 task->tc_Switch(SysBase);
136 core_Dispatch();
141 * Schedule the currently running task away. Put it into the TaskReady list
142 * in some smart way. This function is subject of change and it will be probably replaced
143 * by some plugin system in the future
145 void core_Schedule(void)
147 struct ExecBase *SysBase = *SysBasePtr;
148 struct Task *task;
150 D(bug("[kernel:scheduler] in core_Schedule()\n"));
152 /* disable interrupts */
153 core_intr_disable();
155 task = SysBase->ThisTask;
157 /* Clear the pending switch flag. */
158 SysBase->AttnResched &= ~ARF_AttnSwitch;
160 /* If task has pending exception, reschedule it so that the dispatcher may handle the exception */
161 if (!(task->tc_Flags & TF_EXCEPT))
163 /* Is the TaskReady empty? If yes, then the running task is the only one. Let it work */
164 if (IsListEmpty(&SysBase->TaskReady))
165 RETURN_FROM_INTERRUPT();
167 /* Does the TaskReady list contains tasks with priority equal or lower than current task?
168 * If so, then check further... */
169 if (((struct Task*)GetHead(&SysBase->TaskReady))->tc_Node.ln_Pri <= task->tc_Node.ln_Pri)
171 /* If the running task did not used it's whole quantum yet, let it work */
172 if (!(SysBase->SysFlags & 0x2000))
173 RETURN_FROM_INTERRUPT();
178 * If we got here, then the rescheduling is necessary.
179 * Put the task into the TaskReady list.
181 task->tc_State = TS_READY;
182 Enqueue(&SysBase->TaskReady, (struct Node *)task);
184 /* Select new task to run */
185 core_Switch();
190 * Leave the interrupt. This function receives the register frame used to leave the supervisor
191 * mode. It reschedules the task if it was asked for.
193 void core_ExitInterrupt(void)
195 struct ExecBase *SysBase = *SysBasePtr;
196 char TDNestCnt;
198 D(bug("[kernel:scheduler] in core_ExitInterrupt()\n"));
200 #if DEBUG
201 struct Task *task;
203 bug(" SysBase 0x%08x\n", SysBase);
205 bug(" current task: 0x%08x %d %s\n", SysBase->ThisTask, SysBase->ThisTask->tc_Node.ln_Pri, SysBase->ThisTask->tc_Node.ln_Name);
207 bug(" ready tasks:\n");
208 for (task = (struct Task *) SysBase->TaskReady.lh_Head; task->tc_Node.ln_Succ != NULL; task = (struct Task *) task->tc_Node.ln_Succ)
209 bug(" 0x%08x %d %s\n", task, task->tc_Node.ln_Pri, task->tc_Node.ln_Name);
211 bug(" waiting tasks:\n");
212 for (task = (struct Task *) SysBase->TaskWait.lh_Head; task->tc_Node.ln_Succ != NULL; task = (struct Task *) task->tc_Node.ln_Succ)
213 bug(" 0x%08x %d %s\n", task, task->tc_Node.ln_Pri, task->tc_Node.ln_Name);
214 #endif
216 if (SysBase)
218 /* Soft interrupt requested? It's high time to do it */
219 if (SysBase->SysFlags & SFF_SoftInt) {
220 DS(bug("[kernel:scheduler] Causing SoftInt\n"));
221 core_Cause(SysBase);
224 if (sleep_state != ss_RUNNING) {
225 core_Dispatch();
226 return;
229 /* If task switching is disabled, leave immediatelly */
230 TDNestCnt = SysBase->TDNestCnt; /* BYTE is unsigned in Windows so we can't use SysBase->TDNestCnt directly */
231 DS(bug("[kernel:scheduler] TDNestCnt is %d\n", TDNestCnt));
232 if (TDNestCnt < 0)
235 * Do not disturb task if it's not necessary.
236 * Reschedule only if switch pending flag is set. Exit otherwise.
238 if (SysBase->AttnResched & ARF_AttnSwitch)
240 DS(bug("[kernel:scheduler] Rescheduling\n"));
241 core_Schedule();
243 else
244 RETURN_FROM_INTERRUPT();
247 DS(else printf("[kernel:scheduler] SysBase is NULL\n");)
250 void core_Cause(struct ExecBase *SysBase)
252 struct IntVector *iv = &SysBase->IntVects[INTB_SOFTINT];
254 D(bug("[kernel:scheduler] in core_Cause()\n"));
256 /* If the SoftInt vector in SysBase is set, call it. It will do the rest for us */
257 if (iv->iv_Code)
259 iv->iv_Code(0, 0, 0, iv->iv_Code, SysBase);