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

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