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

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