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

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