Separated Architecture independent thread controll into the root of the tree
[tpg/acess2.git] / Kernel / threads.c
1 /*
2  * Acess2
3  * threads.c
4  * - Common Thread Control
5  */
6 #include <common.h>
7 #include <threads.h>
8
9 // === CONSTANTS ===
10 #define DEFAULT_QUANTUM 10
11 #define DEFAULT_TICKETS 5
12 #define MAX_TICKETS             10
13
14 // === IMPORTS ===
15 extern void     ArchThreads_Init();
16 extern tThread  *Proc_GetCurThread();
17 extern int      Proc_Clone(Uint *Err, Uint Flags);
18
19 // === PROTOTYPES ===
20 void    Threads_Init();
21 void    Threads_SetName(char *NewName);
22 void    Threads_SetTickets(int Num);
23  int    Threads_WaitTID(int TID, int *status);
24 tThread *Threads_GetThread(Uint TID);
25 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
26 void    Threads_Exit(int TID, int Status);
27 void    Threads_Kill(tThread *Thread, int Status);
28 void    Threads_Yield();
29 void    Threads_Sleep();
30 void    Threads_Wake(tThread *Thread);
31  int    Threads_GetPID();
32  int    Threads_GetTID();
33  int    Threads_GetUID();
34  int    Threads_GetGID();
35 void    Threads_Dump();
36
37 // === GLOBALS ===
38 // -- Core Thread --
39 tThread gThreadZero = {
40         NULL, 0,        // Next, Lock
41         THREAD_STAT_ACTIVE,     // Status
42         0,      // Exit Status
43         0, 0,   // TID, TGID
44         0, 0,   // UID, GID
45         0,      // Parent Thread ID
46         "ThreadZero",   // Name
47         
48         0,      // Kernel Stack
49         {0},    // Saved State
50         {0},    // VM State
51         
52         0, {0}, {0},    // Signal State
53         
54         NULL, NULL,     // Messages, Last Message
55         DEFAULT_QUANTUM, DEFAULT_QUANTUM,       // Quantum, Remaining
56         DEFAULT_TICKETS,
57         {0}     // Default config to zero
58         };
59 // -- Processes --
60 // --- Locks ---
61 volatile int    giThreadListLock = 0;   ///\note NEVER use a heap function while locked
62 // --- Current State ---
63 volatile int    giNumActiveThreads = 0;
64 volatile int    giTotalTickets = 0;
65 volatile Uint   giNextTID = 1;
66 // --- Thread Lists ---
67 tThread *gActiveThreads = NULL;         // Currently Running Threads
68 tThread *gSleepingThreads = NULL;       // Sleeping Threads
69 tThread *gDeleteThreads = NULL;         // Threads to delete
70  int    giNumCPUs = 1;
71
72 // === CODE ===
73 /**
74  * \fn void Threads_Init()
75  * \brief Initialse the thread list
76  */
77 void Threads_Init()
78 {
79         ArchThreads_Init();
80         
81         // Create Initial Task
82         gActiveThreads = &gThreadZero;
83         giTotalTickets = gThreadZero.NumTickets;
84         giNumActiveThreads = 1;
85         
86         #if 1
87         // Create Idle Task
88         if(Proc_Clone(0, 0) == 0)
89         {
90                 tThread *cur = Proc_GetCurThread();
91                 cur->ThreadName = "Idle Thread";
92                 Threads_SetTickets(0);  // Never called randomly
93                 cur->Quantum = 1;       // 1 slice quantum
94                 for(;;) __asm__ __volatile__ ("hlt");   // Just yeilds
95         }
96         #endif
97 }
98
99 /**
100  * \fn void Threads_SetName(char *NewName)
101  * \brief Sets the current thread's name
102  */
103 void Threads_SetName(char *NewName)
104 {
105         tThread *cur = Proc_GetCurThread();
106         if( IsHeap(cur->ThreadName) )
107                 free( cur->ThreadName );
108         cur->ThreadName = malloc(strlen(NewName)+1);
109         strcpy(cur->ThreadName, NewName);
110 }
111
112 /**
113  * \fn void Threads_SetTickets(int Num)
114  * \brief Sets the 'priority' of a task
115  */
116 void Threads_SetTickets(int Num)
117 {
118         tThread *cur = Proc_GetCurThread();
119         if(Num < 0)     return;
120         if(Num > MAX_TICKETS)   Num = MAX_TICKETS;
121         
122         LOCK( &giThreadListLock );
123         giTotalTickets -= cur->NumTickets;
124         cur->NumTickets = Num;
125         giTotalTickets += Num;
126         //LOG("giTotalTickets = %i", giTotalTickets);
127         RELEASE( &giThreadListLock );
128 }
129
130 /**
131  * \fn void Threads_WaitTID(int TID, int *status)
132  * \brief Wait for a task to change state
133  */
134 int Threads_WaitTID(int TID, int *status)
135 {
136         // Any Child
137         if(TID == -1) {
138                 
139                 return -1;
140         }
141         
142         // Any peer/child thread
143         if(TID == 0) {
144                 
145                 return -1;
146         }
147         
148         // TGID = abs(TID)
149         if(TID < -1) {
150                 return -1;
151         }
152         
153         // Specific Thread
154         if(TID > 0) {
155                 tThread *t = Threads_GetThread(TID);
156                  int    initStatus = t->Status;
157                  int    ret;
158                 while(t->Status == initStatus)  Threads_Yield();
159                 ret = t->RetStatus;
160                 switch(t->Status)
161                 {
162                 case THREAD_STAT_ZOMBIE:
163                         t->Status = THREAD_STAT_DEAD;
164                         *status = 0;
165                         break;
166                 default:
167                         *status = -1;
168                         break;
169                 }
170                 return ret;
171         }
172         
173         return -1;
174 }
175
176 /**
177  * \fn tThread *Threads_GetThread(Uint TID)
178  * \brief Gets a thread given its TID
179  */
180 tThread *Threads_GetThread(Uint TID)
181 {
182         tThread *thread;
183         
184         // Search Active List
185         for(thread = gActiveThreads;
186                 thread;
187                 thread = thread->Next)
188         {
189                 if(thread->TID == TID)
190                         return thread;
191         }
192         
193         // Search Sleeping List
194         for(thread = gSleepingThreads;
195                 thread;
196                 thread = thread->Next)
197         {
198                 if(thread->TID == TID)
199                         return thread;
200         }
201         
202         return NULL;
203 }
204
205 /**
206  * \fn tThread *Threads_int_GetPrev(tThread *List, tThread *Thread)
207  * \brief Gets the previous entry in a thead linked list
208  */
209 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
210 {
211         tThread *ret;
212         // First Entry
213         if(*List == Thread) {
214                 return (tThread*)List;
215         } else {
216                 for(ret = *List;
217                         ret->Next && ret->Next != Thread;
218                         ret = ret->Next
219                         );
220                 // Error if the thread is not on the list
221                 if(!ret->Next || ret->Next != Thread) {
222                         return NULL;
223                 }
224         }
225         return ret;
226 }
227
228 /**
229  * \fn void Threads_Exit(int TID, int Status)
230  * \brief Exit the current process
231  */
232 void Threads_Exit(int TID, int Status)
233 {
234         Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
235 }
236
237 /**
238  * \fn void Threads_Kill(tThread *Thread, int Status)
239  * \brief Kill a thread
240  * \param TID   Thread ID (0 for current)
241  */
242 void Threads_Kill(tThread *Thread, int Status)
243 {
244         tThread *prev;
245         tMsg    *msg;
246         
247         // Kill all children
248         #if 0
249         {
250                 tThread *child;
251                 for(child = gActiveThreads;
252                         child;
253                         child = child->Next)
254                 {
255                         if(child->PTID == gCurrentThread->TID)
256                                 Threads_Kill(child, -1);
257                 }
258         }
259         #endif
260         
261         ///\note Double lock is needed due to overlap of locks
262         
263         // Lock thread (stop us recieving messages)
264         LOCK( &Thread->IsLocked );
265         
266         // Lock thread list
267         LOCK( &giThreadListLock );
268         
269         // Get previous thread on list
270         prev = Threads_int_GetPrev( &gActiveThreads, Thread );
271         if(!prev) {
272                 Warning("Proc_Exit - Current thread is not on the active queue");
273                 return;
274         }
275         
276         // Clear Message Queue
277         while( Thread->Messages )
278         {
279                 msg = Thread->Messages->Next;
280                 free( Thread->Messages );
281                 Thread->Messages = msg;
282         }
283         
284         Thread->Remaining = 0;  // Clear Remaining Quantum
285         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
286         prev->Next = Thread->Next;      // Remove from active
287         
288         giNumActiveThreads --;
289         giTotalTickets -= Thread->NumTickets;
290         
291         // Mark thread as a zombie
292         Thread->RetStatus = Status;
293         
294         // Don't Zombie if we are being killed as part of a tree
295         if(Status == -1)
296         {
297                 Thread->Status = THREAD_STAT_DEAD;
298                 // Add to delete queue
299                 if(gDeleteThreads) {
300                         Thread->Next = gDeleteThreads;
301                         gDeleteThreads = Thread;
302                 } else {
303                         Thread->Next = NULL;
304                         gDeleteThreads = Thread;
305                 }
306         } else {
307                 Thread->Status = THREAD_STAT_ZOMBIE;
308         }
309         
310         // Release spinlocks
311         RELEASE( &Thread->IsLocked );   // Released first so that it IS released
312         RELEASE( &giThreadListLock );
313         if(Status != -1)        HALT();
314 }
315
316 /**
317  * \fn void Threads_Yield()
318  * \brief Yield remainder of timeslice
319  */
320 void Threads_Yield()
321 {
322         Proc_GetCurThread()->Quantum = 0;
323         HALT();
324 }
325
326 /**
327  * \fn void Threads_Sleep()
328  * \brief Take the current process off the run queue
329  */
330 void Threads_Sleep()
331 {
332         tThread *cur = Proc_GetCurThread();
333         tThread *thread;
334         
335         //Log("Proc_Sleep: %i going to sleep", gCurrentThread->TID);
336         
337         // Acquire Spinlock
338         LOCK( &giThreadListLock );
339         
340         // Get thread before current thread
341         thread = Threads_int_GetPrev( &gActiveThreads, cur );
342         if(!thread) {
343                 Warning("Proc_Sleep - Current thread is not on the active queue");
344                 return;
345         }
346         
347         // Don't sleep if there is a message waiting
348         if( cur->Messages ) {
349                 RELEASE( &giThreadListLock );
350                 return;
351         }
352         
353         // Unset remaining timeslices (force a task switch on timer fire)
354         cur->Remaining = 0;
355         
356         // Remove from active list
357         thread->Next = cur->Next;
358         
359         // Add to Sleeping List (at the top)
360         cur->Next = gSleepingThreads;
361         gSleepingThreads = cur;
362         
363         // Reduce the active count & ticket count
364         giNumActiveThreads --;
365         giTotalTickets -= cur->NumTickets;
366         
367         // Mark thread as sleeping
368         cur->Status = THREAD_STAT_SLEEPING;
369         
370         // Release Spinlock
371         RELEASE( &giThreadListLock );
372         
373         HALT();
374 }
375
376
377 /**
378  * \fn void Threads_Wake( tThread *Thread )
379  * \brief Wakes a sleeping/waiting thread up
380  */
381 void Threads_Wake(tThread *Thread)
382 {
383         tThread *prev;
384         switch(Thread->Status)
385         {
386         case THREAD_STAT_ACTIVE:        break;
387         case THREAD_STAT_SLEEPING:
388                 LOCK( &giThreadListLock );
389                 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
390                 prev->Next = Thread->Next;      // Remove from sleeping queue
391                 Thread->Next = gActiveThreads;  // Add to active queue
392                 gActiveThreads = Thread;
393                 Thread->Status = THREAD_STAT_ACTIVE;
394                 RELEASE( &giThreadListLock );
395                 break;
396         case THREAD_STAT_WAITING:
397                 Warning("Thread_Wake - Waiting threads are not currently supported");
398                 break;
399         case THREAD_STAT_DEAD:
400                 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
401                 break;
402         default:
403                 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
404                 break;
405         }
406 }
407
408 // --- Process Structure Access Functions ---
409 int Threads_GetPID()
410 {
411         return Proc_GetCurThread()->TGID;
412 }
413 int Threads_GetTID()
414 {
415         return Proc_GetCurThread()->TID;
416 }
417 int Threads_GetUID()
418 {
419         return Proc_GetCurThread()->UID;
420 }
421 int Threads_GetGID()
422 {
423         return Proc_GetCurThread()->GID;
424 }
425
426 /**
427  * \fn void Threads_Dump()
428  * \brief Dums a list of currently running threads
429  */
430 void Threads_Dump()
431 {
432         tThread *thread;
433         
434         Log("Active Threads:");
435         for(thread=gActiveThreads;thread;thread=thread->Next)
436         {
437                 Log(" %i (%i) - %s", thread->TID, thread->TGID, thread->ThreadName);
438                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
439                 Log("  KStack 0x%x", thread->KernelStack);
440         }
441         Log("Sleeping Threads:");
442         for(thread=gSleepingThreads;thread;thread=thread->Next)
443         {
444                 Log(" %i (%i) - %s", thread->TID, thread->TGID, thread->ThreadName);
445                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
446                 Log("  KStack 0x%x", thread->KernelStack);
447         }
448 }

UCC git Repository :: git.ucc.asn.au