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

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