Many changes, bugfixes to user vsnprintf and to escape code handling
[tpg/acess2.git] / Kernel / threads.c
1 /*
2  * Acess2
3  * threads.c
4  * - Common Thread Control
5  */
6 #include <acess.h>
7 #include <threads.h>
8 #include <errno.h>
9
10 // === CONSTANTS ===
11 #define DEFAULT_QUANTUM 10
12 #define DEFAULT_TICKETS 5
13 #define MAX_TICKETS             10
14 const enum eConfigTypes cCONFIG_TYPES[] = {
15         CFGT_HEAPSTR,   // CFG_VFS_CWD
16         CFGT_INT,       // CFG_VFS_MAXFILES
17         CFGT_NULL
18 };
19
20 // === IMPORTS ===
21 extern void     ArchThreads_Init();
22 extern void     Proc_Start();
23 extern tThread  *Proc_GetCurThread();
24 extern int      Proc_Clone(Uint *Err, Uint Flags);
25 extern void     Proc_CallFaultHandler(tThread *Thread);
26
27 // === PROTOTYPES ===
28 void    Threads_Init();
29  int    Threads_SetName(char *NewName);
30 char    *Threads_GetName(int ID);
31 void    Threads_SetTickets(int Num);
32 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
33  int    Threads_WaitTID(int TID, int *status);
34 tThread *Threads_GetThread(Uint TID);
35 void    Threads_AddToDelete(tThread *Thread);
36 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
37 void    Threads_Exit(int TID, int Status);
38 void    Threads_Kill(tThread *Thread, int Status);
39 void    Threads_Yield();
40 void    Threads_Sleep();
41 void    Threads_Wake(tThread *Thread);
42 void    Threads_AddActive(tThread *Thread);
43  int    Threads_GetPID();
44  int    Threads_GetTID();
45 tUID    Threads_GetUID();
46  int    Threads_SetUID(Uint *Errno, tUID ID);
47 tGID    Threads_GetGID();
48  int    Threads_SetGID(Uint *Errno, tUID ID);
49 void    Threads_Dump();
50
51 // === GLOBALS ===
52 // -- Core Thread --
53 tThread gThreadZero = {
54         NULL, 0,        // Next, Lock
55         THREAD_STAT_ACTIVE,     // Status
56         0,      // Exit Status
57         0, 0,   // TID, TGID
58         0, 0,   // UID, GID
59         0,      // Parent Thread ID
60         "ThreadZero",   // Name
61         
62         0,      // Kernel Stack
63         {0},    // Saved State
64         {0},    // VM State
65         
66         0, 0,   // Current Fault, Fault Handler
67         
68         NULL, NULL,     // Messages, Last Message
69         DEFAULT_QUANTUM, DEFAULT_QUANTUM,       // Quantum, Remaining
70         DEFAULT_TICKETS,
71         {0}     // Default config to zero
72         };
73 // -- Processes --
74 // --- Locks ---
75 volatile int    giThreadListLock = 0;   ///\note NEVER use a heap function while locked
76 // --- Current State ---
77 volatile int    giNumActiveThreads = 0;
78 volatile int    giTotalTickets = 0;
79 volatile Uint   giNextTID = 1;
80 // --- Thread Lists ---
81 tThread *gActiveThreads = NULL;         // Currently Running Threads
82 tThread *gSleepingThreads = NULL;       // Sleeping Threads
83 tThread *gDeleteThreads = NULL;         // Threads to delete
84  int    giNumCPUs = 1;
85
86 // === CODE ===
87 /**
88  * \fn void Threads_Init()
89  * \brief Initialse the thread list
90  */
91 void Threads_Init()
92 {
93         ArchThreads_Init();
94         
95         // Create Initial Task
96         gActiveThreads = &gThreadZero;
97         giTotalTickets = gThreadZero.NumTickets;
98         giNumActiveThreads = 1;
99         
100         #if 1
101         // Create Idle Task
102         if(Proc_Clone(0, 0) == 0)
103         {
104                 tThread *cur = Proc_GetCurThread();
105                 cur->ThreadName = "Idle Thread";
106                 Threads_SetTickets(0);  // Never called randomly
107                 cur->Quantum = 1;       // 1 slice quantum
108                 HALT();
109                 for(;;) {
110                         HALT(); // Just yeilds
111                 }
112         }
113         #endif
114         
115         Proc_Start();
116 }
117
118 /**
119  * \fn void Threads_SetName(char *NewName)
120  * \brief Sets the current thread's name
121  */
122 int Threads_SetName(char *NewName)
123 {
124         tThread *cur = Proc_GetCurThread();
125         if( IsHeap(cur->ThreadName) )
126                 free( cur->ThreadName );
127         cur->ThreadName = malloc(strlen(NewName)+1);
128         strcpy(cur->ThreadName, NewName);
129         return 0;
130 }
131
132 /**
133  * \fn char *Threads_GetName(int ID)
134  * \brief Gets a thread's name
135  */
136 char *Threads_GetName(int ID)
137 {
138         if(ID == -1) {
139                 return Proc_GetCurThread()->ThreadName;
140         }
141         return NULL;
142 }
143
144 /**
145  * \fn void Threads_SetTickets(int Num)
146  * \brief Sets the 'priority' of a task
147  */
148 void Threads_SetTickets(int Num)
149 {
150         tThread *cur = Proc_GetCurThread();
151         if(Num < 0)     return;
152         if(Num > MAX_TICKETS)   Num = MAX_TICKETS;
153         
154         LOCK( &giThreadListLock );
155         giTotalTickets -= cur->NumTickets;
156         cur->NumTickets = Num;
157         giTotalTickets += Num;
158         //LOG("giTotalTickets = %i", giTotalTickets);
159         RELEASE( &giThreadListLock );
160 }
161
162 /**
163  * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
164  */
165 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
166 {
167         tThread *cur, *new;
168          int    i;
169         cur = Proc_GetCurThread();
170         
171         new = malloc(sizeof(tThread));
172         if(new == NULL) {
173                 *Err = -ENOMEM;
174                 return NULL;
175         }
176         
177         new->Next = NULL;
178         new->IsLocked = 0;
179         new->Status = THREAD_STAT_ACTIVE;
180         new->RetStatus = 0;
181         
182         // Get Thread ID
183         new->TID = giNextTID++;
184         new->PTID = cur->TID;
185         
186         // Clone Name
187         new->ThreadName = malloc(strlen(cur->ThreadName)+1);
188         strcpy(new->ThreadName, cur->ThreadName);
189         
190         // Set Thread Group ID (PID)
191         if(Flags & CLONE_VM)
192                 new->TGID = new->TID;
193         else
194                 new->TGID = cur->TGID;
195         
196         // Messages are not inherited
197         new->Messages = NULL;
198         new->LastMessage = NULL;
199         
200         // Set State
201         new->Remaining = new->Quantum = cur->Quantum;
202         new->NumTickets = cur->NumTickets;
203         
204         // Set Signal Handlers
205         new->CurFaultNum = 0;
206         new->FaultHandler = cur->FaultHandler;
207         
208         for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
209         {
210                 switch(cCONFIG_TYPES[i])
211                 {
212                 default:
213                         new->Config[i] = cur->Config[i];
214                         break;
215                 case CFGT_HEAPSTR:
216                         if(cur->Config[i])
217                                 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
218                         else
219                                 new->Config[i] = 0;
220                         break;
221                 }
222         }
223         
224         return new;
225 }
226
227 /**
228  * \fn Uint *Threads_GetCfgPtr(int Id)
229  */
230 Uint *Threads_GetCfgPtr(int Id)
231 {
232         if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
233                 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
234                 return NULL;
235         }
236         
237         return &Proc_GetCurThread()->Config[Id];
238 }
239
240 /**
241  * \fn void Threads_WaitTID(int TID, int *status)
242  * \brief Wait for a task to change state
243  */
244 int Threads_WaitTID(int TID, int *status)
245 {       
246         // Any Child
247         if(TID == -1) {
248                 
249                 return -1;
250         }
251         
252         // Any peer/child thread
253         if(TID == 0) {
254                 
255                 return -1;
256         }
257         
258         // TGID = abs(TID)
259         if(TID < -1) {
260                 return -1;
261         }
262         
263         // Specific Thread
264         if(TID > 0) {
265                 tThread *t = Threads_GetThread(TID);
266                  int    initStatus = t->Status;
267                  int    ret;
268                 
269                 if(initStatus != THREAD_STAT_ZOMBIE) {
270                         while(t->Status == initStatus) {
271                                 Threads_Yield();
272                         }
273                 }
274                 
275                 ret = t->RetStatus;
276                 switch(t->Status)
277                 {
278                 case THREAD_STAT_ZOMBIE:
279                         t->Status = THREAD_STAT_DEAD;
280                         if(status)      *status = 0;
281                         Threads_AddToDelete( t );
282                         break;
283                 default:
284                         if(status)      *status = -1;
285                         break;
286                 }
287                 return ret;
288         }
289         
290         return -1;
291 }
292
293 /**
294  * \fn tThread *Threads_GetThread(Uint TID)
295  * \brief Gets a thread given its TID
296  */
297 tThread *Threads_GetThread(Uint TID)
298 {
299         tThread *thread;
300         
301         // Search Active List
302         for(thread = gActiveThreads;
303                 thread;
304                 thread = thread->Next)
305         {
306                 if(thread->TID == TID)
307                         return thread;
308         }
309         
310         // Search Sleeping List
311         for(thread = gSleepingThreads;
312                 thread;
313                 thread = thread->Next)
314         {
315                 if(thread->TID == TID)
316                         return thread;
317         }
318         
319         return NULL;
320 }
321
322 /**
323  * \fn void Threads_AddToDelete(tThread *Thread)
324  * \brief Adds a thread to the delete queue
325  */
326 void Threads_AddToDelete(tThread *Thread)
327 {
328         // Add to delete queue
329         if(gDeleteThreads) {
330                 Thread->Next = gDeleteThreads;
331                 gDeleteThreads = Thread;
332         } else {
333                 Thread->Next = NULL;
334                 gDeleteThreads = Thread;
335         }
336 }
337
338 /**
339  * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
340  * \brief Gets the previous entry in a thead linked list
341  */
342 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
343 {
344         tThread *ret;
345         // First Entry
346         if(*List == Thread) {
347                 return (tThread*)List;
348         } else {
349                 for(ret = *List;
350                         ret->Next && ret->Next != Thread;
351                         ret = ret->Next
352                         );
353                 // Error if the thread is not on the list
354                 if(!ret->Next || ret->Next != Thread) {
355                         return NULL;
356                 }
357         }
358         return ret;
359 }
360
361 /**
362  * \fn void Threads_Exit(int TID, int Status)
363  * \brief Exit the current process
364  */
365 void Threads_Exit(int TID, int Status)
366 {
367         if( TID == 0 )
368                 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
369         else
370                 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
371         for(;;) HALT(); // Just in case
372 }
373
374 /**
375  * \fn void Threads_Kill(tThread *Thread, int Status)
376  * \brief Kill a thread
377  * \param Thread        Thread to kill
378  * \param Status        Status code to return to the parent
379  */
380 void Threads_Kill(tThread *Thread, int Status)
381 {
382         tThread *prev;
383         tMsg    *msg;
384         
385         // Kill all children
386         #if 0
387         {
388                 tThread *child;
389                 for(child = gActiveThreads;
390                         child;
391                         child = child->Next)
392                 {
393                         if(child->PTID == Thread->TID)
394                                 Threads_Kill(child, -1);
395                 }
396         }
397         #endif
398         
399         ///\note Double lock is needed due to overlap of locks
400         
401         // Lock thread (stop us recieving messages)
402         LOCK( &Thread->IsLocked );
403         
404         // Lock thread list
405         LOCK( &giThreadListLock );
406         
407         // Get previous thread on list
408         prev = Threads_int_GetPrev( &gActiveThreads, Thread );
409         if(!prev) {
410                 Warning("Proc_Exit - Current thread is not on the active queue");
411                 return;
412         }
413         
414         // Clear Message Queue
415         while( Thread->Messages )
416         {
417                 msg = Thread->Messages->Next;
418                 free( Thread->Messages );
419                 Thread->Messages = msg;
420         }
421         
422         Thread->Remaining = 0;  // Clear Remaining Quantum
423         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
424         prev->Next = Thread->Next;      // Remove from active
425         
426         giNumActiveThreads --;
427         giTotalTickets -= Thread->NumTickets;
428         
429         // Mark thread as a zombie
430         Thread->RetStatus = Status;
431         
432         // Don't Zombie if we are being killed as part of a tree
433         if(Status == -1)
434         {
435                 Thread->Status = THREAD_STAT_DEAD;
436                 Threads_AddToDelete( Thread );
437         } else {
438                 Thread->Status = THREAD_STAT_ZOMBIE;
439         }
440         
441         // Release spinlocks
442         RELEASE( &Thread->IsLocked );   // Released first so that it IS released
443         RELEASE( &giThreadListLock );
444         
445         //Log("Thread %i went *hurk*", Thread->TID);
446         
447         if(Status != -1)        HALT();
448 }
449
450 /**
451  * \fn void Threads_Yield()
452  * \brief Yield remainder of timeslice
453  */
454 void Threads_Yield()
455 {
456         Proc_GetCurThread()->Remaining = 0;
457         HALT();
458 }
459
460 /**
461  * \fn void Threads_Sleep()
462  * \brief Take the current process off the run queue
463  */
464 void Threads_Sleep()
465 {
466         tThread *cur = Proc_GetCurThread();
467         tThread *thread;
468         
469         //Log_Log("Threads", "%i going to sleep", cur->TID);
470         
471         // Acquire Spinlock
472         LOCK( &giThreadListLock );
473         
474         // Get thread before current thread
475         thread = Threads_int_GetPrev( &gActiveThreads, cur );
476         if(!thread) {
477                 Warning("Threads_Sleep - Current thread is not on the active queue");
478                 Threads_Dump();
479                 return;
480         }
481         
482         // Don't sleep if there is a message waiting
483         if( cur->Messages ) {
484                 RELEASE( &giThreadListLock );
485                 return;
486         }
487         
488         // Unset remaining timeslices (force a task switch on timer fire)
489         cur->Remaining = 0;
490         
491         // Remove from active list
492         thread->Next = cur->Next;
493         
494         // Add to Sleeping List (at the top)
495         cur->Next = gSleepingThreads;
496         gSleepingThreads = cur;
497         
498         // Reduce the active count & ticket count
499         giNumActiveThreads --;
500         giTotalTickets -= cur->NumTickets;
501         
502         // Mark thread as sleeping
503         cur->Status = THREAD_STAT_SLEEPING;
504         
505         // Release Spinlock
506         RELEASE( &giThreadListLock );
507         
508         while(cur->Status != THREAD_STAT_ACTIVE)        HALT();
509 }
510
511
512 /**
513  * \fn void Threads_Wake( tThread *Thread )
514  * \brief Wakes a sleeping/waiting thread up
515  */
516 void Threads_Wake(tThread *Thread)
517 {
518         tThread *prev;
519         switch(Thread->Status)
520         {
521         case THREAD_STAT_ACTIVE:        break;
522         case THREAD_STAT_SLEEPING:
523                 //Log_Log("Threads", "Waking %i (%p) from sleeping", Thread->TID, Thread);
524                 LOCK( &giThreadListLock );
525                 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
526                 prev->Next = Thread->Next;      // Remove from sleeping queue
527                 Thread->Next = gActiveThreads;  // Add to active queue
528                 gActiveThreads = Thread;
529                 giNumActiveThreads ++;
530                 giTotalTickets += Thread->NumTickets;
531                 Thread->Status = THREAD_STAT_ACTIVE;
532                 RELEASE( &giThreadListLock );
533                 break;
534         case THREAD_STAT_WAITING:
535                 Warning("Thread_Wake - Waiting threads are not currently supported");
536                 break;
537         case THREAD_STAT_DEAD:
538                 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
539                 break;
540         default:
541                 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
542                 break;
543         }
544 }
545
546 void Threads_WakeTID(tTID Thread)
547 {
548         Threads_Wake( Threads_GetThread(Thread) );
549 }
550
551 /**
552  * \fn void Threads_AddActive(tThread *Thread)
553  * \brief Adds a thread to the active queue
554  */
555 void Threads_AddActive(tThread *Thread)
556 {
557         LOCK( &giThreadListLock );
558         Thread->Next = gActiveThreads;
559         gActiveThreads = Thread;
560         giNumActiveThreads ++;
561         giTotalTickets += Thread->NumTickets;
562         //Log("Threads_AddActive: giNumActiveThreads = %i, giTotalTickets = %i",
563         //      giNumActiveThreads, giTotalTickets);
564         RELEASE( &giThreadListLock );
565 }
566
567 /**
568  * \fn void Threads_SetSignalHandler(Uint Handler)
569  * \brief Sets the signal handler for a signal
570  */
571 void Threads_SetFaultHandler(Uint Handler)
572 {       
573         Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
574         Proc_GetCurThread()->FaultHandler = Handler;
575 }
576
577 /**
578  * \fn void Threads_Fault(int Num)
579  * \brief Calls a fault handler
580  */
581 void Threads_Fault(int Num)
582 {
583         tThread *thread = Proc_GetCurThread();
584         
585         Log_Log("Threads", "Threads_Fault: thread = %p", thread);
586         
587         if(!thread)     return ;
588         
589         Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
590         
591         switch(thread->FaultHandler)
592         {
593         case 0: // Panic?
594                 Threads_Kill(thread, -1);
595                 HALT();
596                 return ;
597         case 1: // Dump Core?
598                 Threads_Kill(thread, -1);
599                 HALT();
600                 return ;
601         }
602         
603         // Double Fault? Oh, F**k
604         if(thread->CurFaultNum != 0) {
605                 Threads_Kill(thread, -1);       // For now, just kill
606                 HALT();
607         }
608         
609         Proc_CallFaultHandler(thread);
610 }
611
612 // --- Process Structure Access Functions ---
613 tPID Threads_GetPID()
614 {
615         return Proc_GetCurThread()->TGID;
616 }
617 tTID Threads_GetTID()
618 {
619         return Proc_GetCurThread()->TID;
620 }
621 tUID Threads_GetUID()
622 {
623         return Proc_GetCurThread()->UID;
624 }
625 tGID Threads_GetGID()
626 {
627         return Proc_GetCurThread()->GID;
628 }
629
630 int Threads_SetUID(Uint *Errno, tUID ID)
631 {
632         tThread *t = Proc_GetCurThread();
633         if( t->UID != 0 ) {
634                 *Errno = -EACCES;
635                 return -1;
636         }
637         Log("Threads_SetUID - Setting User ID to %i", ID);
638         t->UID = ID;
639         return 0;
640 }
641
642 int Threads_SetGID(Uint *Errno, tGID ID)
643 {
644         tThread *t = Proc_GetCurThread();
645         if( t->UID != 0 ) {
646                 *Errno = -EACCES;
647                 return -1;
648         }
649         Log("Threads_SetGID - Setting Group ID to %i", ID);
650         t->GID = ID;
651         return 0;
652 }
653
654 /**
655  * \fn void Threads_Dump()
656  * \brief Dums a list of currently running threads
657  */
658 void Threads_Dump()
659 {
660         tThread *thread;
661         tThread *cur = Proc_GetCurThread();
662         
663         Log("Active Threads:");
664         for(thread=gActiveThreads;thread;thread=thread->Next)
665         {
666                 Log("%c%i (%i) - %s",
667                         (thread==cur?'*':' '),
668                         thread->TID, thread->TGID, thread->ThreadName);
669                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
670                 Log("  KStack 0x%x", thread->KernelStack);
671         }
672         Log("Sleeping Threads:");
673         for(thread=gSleepingThreads;thread;thread=thread->Next)
674         {
675                 Log("%c%i (%i) - %s",
676                         (thread==cur?'*':' '),
677                         thread->TID, thread->TGID, thread->ThreadName);
678                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
679                 Log("  KStack 0x%x", thread->KernelStack);
680         }
681 }
682
683 /**
684  * \fn tThread *Threads_GetNextToRun(int CPU)
685  * \brief Gets the next thread to run
686  */
687 tThread *Threads_GetNextToRun(int CPU)
688 {
689         tThread *thread;
690          int    ticket;
691          int    number;
692         
693         if(giNumActiveThreads == 0) {
694                 //Log_Debug("Threads", "CPU%i has no threads to run", CPU);
695                 return NULL;
696         }
697         
698         // Special case: 1 thread
699         if(giNumActiveThreads == 1) {
700                 //Log_Debug("Threads", "CPU%i has only one thread %i %s",
701                 //      CPU, gActiveThreads->TID, gActiveThreads->ThreadName);
702                 return gActiveThreads;
703         }
704         
705         //Log(" Threads_GetNextToRun: giNumActiveThreads=%i,giTotalTickets=%i",
706         //      giNumActiveThreads, giTotalTickets);
707         // Get the ticket number
708         ticket = number = rand() % giTotalTickets;
709         
710         //Log(" Threads_GetNextToRun: ticket = %i", ticket);
711         
712         // Find the next thread
713         for(thread=gActiveThreads;thread;thread=thread->Next)
714         {
715                 if(thread->NumTickets > number) break;
716                 number -= thread->NumTickets;
717         }
718         
719         // Error Check
720         if(thread == NULL)
721         {
722                 number = 0;
723                 for(thread=gActiveThreads;thread;thread=thread->Next)
724                         number += thread->NumTickets;
725                 Panic("Bookeeping Failed - giTotalTicketCount (%i) != true count (%i)",
726                         giTotalTickets, number);
727         }
728         
729         //Log_Debug("Threads", "Switching CPU%i to %p (%s)",
730         //      CPU, thread, thread->ThreadName);
731         
732         return thread;
733 }
734
735 /**
736  * \fn void Threads_SegFault(tVAddr Addr)
737  * \brief Called when a Segment Fault occurs
738  */
739 void Threads_SegFault(tVAddr Addr)
740 {
741         Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
742         Threads_Fault( 1 );
743         //Threads_Exit( 0, -1 );
744 }
745
746 // === EXPORTS ===
747 EXPORT(Threads_GetUID);

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