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

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