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

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