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

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