2 Copyright © 1995-2005, The AROS Development Team. All rights reserved.
8 #include <exec/execbase.h>
9 #include <exec/memory.h>
10 #include <utility/tagitem.h>
11 #include <aros/libcall.h>
12 #include <aros/config.h>
13 #include <proto/exec.h>
15 #include "exec_util.h"
17 #include "exec_debug.h"
18 #ifndef DEBUG_NewAddTask
19 # define DEBUG_NewAddTask 0
25 #include <aros/debug.h>
27 /*****************************************************************************
31 AROS_LH4(APTR
, NewAddTask
,
34 AROS_LHA(struct Task
*, task
, A1
),
35 AROS_LHA(APTR
, initialPC
, A2
),
36 AROS_LHA(APTR
, finalPC
, A3
),
37 AROS_LHA(struct TagItem
*, tagList
, A4
),
40 struct ExecBase
*, SysBase
, 152, Exec
)
43 Add a new task to the system. If the new task has the highest
44 priority of all and task switches are allowed it will be started
47 You must initialise certain fields, and allocate a stack before
48 calling this function. The fields that must be initialised are:
49 tc_SPLower, tc_SPUpper, tc_SPReg, and the tc_Node structure.
51 If any other fields are initialised to zero, then they will be
52 filled in with the system defaults.
54 The value of tc_SPReg will be used as the location for the stack
55 pointer. You can place any arguments you wish to pass to the Task
56 onto the stack before calling AddTask(). However note that you may
57 need to consider the stack direction on your processor.
59 Memory can be added to the tc_MemEntry list and will be freed when
60 the task dies. The new task's registers are set to 0.
63 task - Pointer to task structure.
64 initialPC - Entry point for the new task.
65 finalPC - Routine that is called if the initialPC() function
66 returns. A NULL pointer installs the default finalizer.
69 The address of the new task or NULL if the operation failed.
82 ******************************************************************************/
86 D(bug("Call NewAddTask (%08lx (\"%s\"), %08lx, %08lx)\n"
88 , task
->tc_Node
.ln_Name
92 ASSERT_VALID_PTR(task
);
94 /* Set node type to NT_TASK if not set to something else. */
95 if(task
->tc_Node
.ln_Type
== 0)
96 task
->tc_Node
.ln_Type
= NT_TASK
;
98 /* Sigh - you should provide a name for your task. */
99 if(task
->tc_Node
.ln_Name
== NULL
)
100 task
->tc_Node
.ln_Name
= "unknown task";
102 /* This is moved into SysBase at the tasks's startup */
103 task
->tc_IDNestCnt
=-1;
104 task
->tc_TDNestCnt
=-1;
106 task
->tc_State
= TS_ADDED
;
109 task
->tc_SigWait
= 0;
110 task
->tc_SigRecvd
= 0;
111 task
->tc_SigExcept
= 0;
113 /* Signals default to all system signals allocated. */
114 if(task
->tc_SigAlloc
== 0)
115 task
->tc_SigAlloc
= SysBase
->TaskSigAlloc
;
118 * tc_SigWait, tc_SigRecvd, tc_SigExcept are default 0
119 * tc_ExceptCode, tc_ExceptData default to NULL.
122 /* Currently only used for segmentation violation */
123 if(task
->tc_TrapCode
== NULL
)
124 task
->tc_TrapCode
= SysBase
->TaskTrapCode
;
126 if (task
->tc_ExceptCode
== NULL
)
127 task
->tc_ExceptCode
=SysBase
->TaskExceptCode
;
130 If you can't to store the registers on the signal stack, you
133 task
->tc_Flags
|= TF_ETASK
;
135 /* Allocate the ETask structure if requested */
136 if (task
->tc_Flags
& TF_ETASK
)
141 * We don't add this to the task memory, it isn't free'd by
142 * RemTask(), rather by somebody else calling ChildFree().
143 * Alternatively, an orphaned task will free its own ETask.
145 task
->tc_UnionETask
.tc_ETask
= AllocVec
147 sizeof (struct IntETask
),
151 if (!task
->tc_UnionETask
.tc_ETask
)
154 et
= (struct ETask
*)task
->tc_UnionETask
.tc_ETask
;
155 NEWLIST(&et
->et_Children
);
156 /* Add the newly created task to the parent's children
158 et
->et_Parent
= FindTask(NULL
);
160 ADDHEAD(&GetETask(et
->et_Parent
)->et_Children
, et
);
163 /* Initialise the message list */
164 NEWLIST(&et
->et_TaskMsgPort
.mp_MsgList
);
165 et
->et_TaskMsgPort
.mp_Flags
= PA_SIGNAL
;
166 et
->et_TaskMsgPort
.mp_Node
.ln_Type
= NT_MSGPORT
;
167 et
->et_TaskMsgPort
.mp_SigTask
= task
;
168 et
->et_TaskMsgPort
.mp_SigBit
= SIGB_CHILD
;
170 /* Initialise the trap fields */
171 et
->et_TrapAlloc
= SysBase
->TaskTrapAlloc
;
176 while(et
->et_UniqueID
== 0)
179 * Add some fuzz on wrapping. Its likely that the early numbers
180 * where taken by somebody else.
182 if(++SysBase
->ex_TaskID
== 0)
183 SysBase
->exTaskID
= 1024;
186 if(FindTaskByID(SysBase
->ex_TaskID
, SysBase
) == NULL
)
187 et
->et_UniqueID
= SysBase
->ex_TaskID
;
195 task
->tc_UnionETask
.tc_ETrap
.tc_ETrapAlloc
= SysBase
->TaskTrapAlloc
;
196 task
->tc_UnionETask
.tc_ETrap
.tc_ETrapAble
= 0;
199 /* Get new stackpointer. Note, the doc says this MUST be initialised. */
200 if (task
->tc_SPReg
==NULL
)
201 #if AROS_STACK_GROWS_DOWNWARDS
202 task
->tc_SPReg
= (UBYTE
*)(task
->tc_SPUpper
) - SP_OFFSET
;
204 task
->tc_SPReg
= (UBYTE
*)(task
->tc_SPLower
) - SP_OFFSET
;
209 UBYTE
*startfill
, *endfill
;
211 #if AROS_STACK_GROWS_DOWNWARDS
212 startfill
= (UBYTE
*)task
->tc_SPLower
;
213 endfill
= ((UBYTE
*)task
->tc_SPReg
) - 16;
215 startfill
= ((UBYTE
*)task
->tc_SPReg
) + 16;
216 endfill
= ((UBYTE
*)task
->tc_SPUpper
) - 1; /* FIXME: -1 correct ?? */
219 while(startfill
<= endfill
)
227 /* Default finalizer? */
229 finalPC
= SysBase
->TaskExitCode
;
231 /* Init new context. */
232 if (!PrepareContext (task
, initialPC
, finalPC
, tagList
))
234 FreeVec(task
->tc_UnionETask
.tc_ETask
);
238 /* Set the task flags for switch and launch. */
240 task
->tc_Flags
|=TF_SWITCH
;
243 task
->tc_Flags
|=TF_LAUNCH
;
245 /* tc_MemEntry _must_ already be set. */
248 Protect the task lists. This must be done with Disable() because
249 of Signal() which is usable from interrupts and may change those
254 /* Add the new task to the ready list. */
255 task
->tc_State
=TS_READY
;
256 Enqueue(&SysBase
->TaskReady
,&task
->tc_Node
);
259 Determine if a task switch is necessary. (If the new task has a
260 higher priority than the current one and the current one
261 is still active.) If the current task isn't of type TS_RUN it
264 if(task
->tc_Node
.ln_Pri
>SysBase
->ThisTask
->tc_Node
.ln_Pri
&&
265 SysBase
->ThisTask
->tc_State
==TS_RUN
)
267 /* Are taskswitches allowed? (Don't count own Disable() here) */
268 if(SysBase
->TDNestCnt
>=0||SysBase
->IDNestCnt
>0) {
269 /* No. Store it for later. */
270 SysBase
->AttnResched
|=0x80;
272 /* Switches are allowed. Move the current task away. */
273 SysBase
->ThisTask
->tc_State
=TS_READY
;
274 Enqueue(&SysBase
->TaskReady
,&SysBase
->ThisTask
->tc_Node
);
276 /* And force a rescedule. */
283 ReturnPtr ("NewAddTask", struct Task
*, task
);
287 /* Default finaliser. */
288 void Exec_TaskFinaliser(void)
290 /* Get rid of current task. */
291 RemTask(SysBase
->ThisTask
);