source: src/libcfa/concurrency/kernel.c @ d0a045c

aaron-thesisarm-ehcleanup-dtorsdeferred_resndemanglerjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprnew-envno_listpersistent-indexerresolv-newwith_gc
Last change on this file since d0a045c was d0a045c, checked in by Thierry Delisle <tdelisle@…>, 4 years ago

Faster (but maybe unsafe) interupt management

  • Property mode set to 100644
File size: 18.5 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// kernel.c --
8//
9// Author           : Thierry Delisle
10// Created On       : Tue Jan 17 12:27:26 2017
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Fri Dec  8 16:23:33 2017
13// Update Count     : 3
14//
15
16//C Includes
17#include <stddef.h>
18#define ftype `ftype`
19extern "C" {
20#include <stdio.h>
21#include <fenv.h>
22#include <sys/resource.h>
23#include <signal.h>
24#include <unistd.h>
25}
26#undef ftype
27
28//CFA Includes
29#include "kernel_private.h"
30#include "preemption.h"
31#include "startup.h"
32
33//Private includes
34#define __CFA_INVOKE_PRIVATE__
35#include "invoke.h"
36
37//Start and stop routine for the kernel, declared first to make sure they run first
38void kernel_startup(void)  __attribute__(( constructor( STARTUP_PRIORITY_KERNEL ) ));
39void kernel_shutdown(void) __attribute__(( destructor ( STARTUP_PRIORITY_KERNEL ) ));
40
41//-----------------------------------------------------------------------------
42// Kernel storage
43KERNEL_STORAGE(cluster,           mainCluster);
44KERNEL_STORAGE(processor,         mainProcessor);
45KERNEL_STORAGE(processorCtx_t,    mainProcessorCtx);
46KERNEL_STORAGE(thread_desc,       mainThread);
47KERNEL_STORAGE(machine_context_t, mainThreadCtx);
48
49cluster *     mainCluster;
50processor *   mainProcessor;
51thread_desc * mainThread;
52
53//-----------------------------------------------------------------------------
54// Global state
55
56thread_local coroutine_desc * volatile this_coroutine;
57thread_local thread_desc *    volatile this_thread;
58thread_local processor *      volatile this_processor;
59
60volatile thread_local bool preemption_in_progress = 0;
61volatile thread_local bool preemption_enabled = false;
62volatile thread_local unsigned short disable_preempt_count = 1;
63
64//-----------------------------------------------------------------------------
65// Main thread construction
66struct current_stack_info_t {
67        machine_context_t ctx;
68        unsigned int size;              // size of stack
69        void *base;                             // base of stack
70        void *storage;                  // pointer to stack
71        void *limit;                    // stack grows towards stack limit
72        void *context;                  // address of cfa_context_t
73        void *top;                              // address of top of storage
74};
75
76void ?{}( current_stack_info_t & this ) {
77        CtxGet( this.ctx );
78        this.base = this.ctx.FP;
79        this.storage = this.ctx.SP;
80
81        rlimit r;
82        getrlimit( RLIMIT_STACK, &r);
83        this.size = r.rlim_cur;
84
85        this.limit = (void *)(((intptr_t)this.base) - this.size);
86        this.context = &storage_mainThreadCtx;
87        this.top = this.base;
88}
89
90void ?{}( coStack_t & this, current_stack_info_t * info) with( this ) {
91        size      = info->size;
92        storage   = info->storage;
93        limit     = info->limit;
94        base      = info->base;
95        context   = info->context;
96        top       = info->top;
97        userStack = true;
98}
99
100void ?{}( coroutine_desc & this, current_stack_info_t * info) with( this ) {
101        stack{ info };
102        name = "Main Thread";
103        errno_ = 0;
104        state = Start;
105        starter = NULL;
106}
107
108void ?{}( thread_desc & this, current_stack_info_t * info) with( this ) {
109        self_cor{ info };
110}
111
112//-----------------------------------------------------------------------------
113// Processor coroutine
114
115// Construct the processor context of the main processor
116void ?{}(processorCtx_t & this, processor * proc) {
117        (this.__cor){ "Processor" };
118        this.__cor.starter = NULL;
119        this.proc = proc;
120        proc->runner = &this;
121}
122
123// Construct the processor context of non-main processors
124void ?{}(processorCtx_t & this, processor * proc, current_stack_info_t * info) {
125        (this.__cor){ info };
126        this.proc = proc;
127        proc->runner = &this;
128}
129
130void ?{}(processor & this) {
131        this{ mainCluster };
132}
133
134void ?{}(processor & this, cluster * cltr) {
135        this.cltr = cltr;
136        this.terminated{ 0 };
137        this.do_terminate = false;
138        this.preemption_alarm = NULL;
139        this.pending_preemption = false;
140
141        start( &this );
142}
143
144void ?{}(processor & this, cluster * cltr, processorCtx_t & runner) {
145        this.cltr = cltr;
146        this.terminated{ 0 };
147        this.do_terminate = false;
148        this.preemption_alarm = NULL;
149        this.pending_preemption = false;
150        this.kernel_thread = pthread_self();
151
152        this.runner = &runner;
153        __cfaabi_dbg_print_safe("Kernel : constructing main processor context %p\n", &runner);
154        runner{ &this };
155}
156
157void ^?{}(processor & this) with( this ){
158        if( ! do_terminate ) {
159                __cfaabi_dbg_print_safe("Kernel : core %p signaling termination\n", &this);
160                do_terminate = true;
161                P( terminated );
162                pthread_join( kernel_thread, NULL );
163        }
164}
165
166void ?{}(cluster & this) with( this ) {
167        ready_queue{};
168        ready_queue_lock{};
169
170        preemption = default_preemption();
171}
172
173void ^?{}(cluster & this) {
174
175}
176
177//=============================================================================================
178// Kernel Scheduling logic
179//=============================================================================================
180//Main of the processor contexts
181void main(processorCtx_t & runner) {
182        processor * this = runner.proc;
183
184        __cfaabi_dbg_print_safe("Kernel : core %p starting\n", this);
185
186        {
187                // Setup preemption data
188                preemption_scope scope = { this };
189
190                __cfaabi_dbg_print_safe("Kernel : core %p started\n", this);
191
192                thread_desc * readyThread = NULL;
193                for( unsigned int spin_count = 0; ! this->do_terminate; spin_count++ )
194                {
195                        readyThread = nextThread( this->cltr );
196
197                        if(readyThread)
198                        {
199                                verify( !preemption_enabled );
200
201                                runThread(this, readyThread);
202
203                                verify( !preemption_enabled );
204
205                                //Some actions need to be taken from the kernel
206                                finishRunning(this);
207
208                                spin_count = 0;
209                        }
210                        else
211                        {
212                                spin(this, &spin_count);
213                        }
214                }
215
216                __cfaabi_dbg_print_safe("Kernel : core %p stopping\n", this);
217        }
218
219        V( this->terminated );
220
221        __cfaabi_dbg_print_safe("Kernel : core %p terminated\n", this);
222}
223
224// runThread runs a thread by context switching
225// from the processor coroutine to the target thread
226void runThread(processor * this, thread_desc * dst) {
227        coroutine_desc * proc_cor = get_coroutine(*this->runner);
228        coroutine_desc * thrd_cor = get_coroutine(dst);
229
230        //Reset the terminating actions here
231        this->finish.action_code = No_Action;
232
233        //Update global state
234        this_thread = dst;
235
236        // Context Switch to the thread
237        ThreadCtxSwitch(proc_cor, thrd_cor);
238        // when ThreadCtxSwitch returns we are back in the processor coroutine
239}
240
241// Once a thread has finished running, some of
242// its final actions must be executed from the kernel
243void finishRunning(processor * this) with( this->finish ) {
244        if( action_code == Release ) {
245                verify( !preemption_enabled );
246                unlock( *lock );
247        }
248        else if( action_code == Schedule ) {
249                ScheduleThread( thrd );
250        }
251        else if( action_code == Release_Schedule ) {
252                verify( !preemption_enabled );
253                unlock( *lock );
254                ScheduleThread( thrd );
255        }
256        else if( action_code == Release_Multi ) {
257                verify( !preemption_enabled );
258                for(int i = 0; i < lock_count; i++) {
259                        unlock( *locks[i] );
260                }
261        }
262        else if( action_code == Release_Multi_Schedule ) {
263                for(int i = 0; i < lock_count; i++) {
264                        unlock( *locks[i] );
265                }
266                for(int i = 0; i < thrd_count; i++) {
267                        ScheduleThread( thrds[i] );
268                }
269        }
270        else {
271                assert(action_code == No_Action);
272        }
273}
274
275// Handles spinning logic
276// TODO : find some strategy to put cores to sleep after some time
277void spin(processor * this, unsigned int * spin_count) {
278        (*spin_count)++;
279}
280
281// Context invoker for processors
282// This is the entry point for processors (kernel threads)
283// It effectively constructs a coroutine by stealing the pthread stack
284void * CtxInvokeProcessor(void * arg) {
285        processor * proc = (processor *) arg;
286        this_processor = proc;
287        this_coroutine = NULL;
288        this_thread = NULL;
289        preemption_enabled = false;
290        disable_preempt_count = 1;
291        // SKULLDUGGERY: We want to create a context for the processor coroutine
292        // which is needed for the 2-step context switch. However, there is no reason
293        // to waste the perfectly valid stack create by pthread.
294        current_stack_info_t info;
295        machine_context_t ctx;
296        info.context = &ctx;
297        processorCtx_t proc_cor_storage = { proc, &info };
298
299        __cfaabi_dbg_print_safe("Coroutine : created stack %p\n", proc_cor_storage.__cor.stack.base);
300
301        //Set global state
302        this_coroutine = &proc->runner->__cor;
303        this_thread = NULL;
304
305        //We now have a proper context from which to schedule threads
306        __cfaabi_dbg_print_safe("Kernel : core %p created (%p, %p)\n", proc, proc->runner, &ctx);
307
308        // SKULLDUGGERY: Since the coroutine doesn't have its own stack, we can't
309        // resume it to start it like it normally would, it will just context switch
310        // back to here. Instead directly call the main since we already are on the
311        // appropriate stack.
312        proc_cor_storage.__cor.state = Active;
313        main( proc_cor_storage );
314        proc_cor_storage.__cor.state = Halted;
315
316        // Main routine of the core returned, the core is now fully terminated
317        __cfaabi_dbg_print_safe("Kernel : core %p main ended (%p)\n", proc, proc->runner);
318
319        return NULL;
320}
321
322void start(processor * this) {
323        __cfaabi_dbg_print_safe("Kernel : Starting core %p\n", this);
324
325        pthread_create( &this->kernel_thread, NULL, CtxInvokeProcessor, (void*)this );
326
327        __cfaabi_dbg_print_safe("Kernel : core %p started\n", this);
328}
329
330//-----------------------------------------------------------------------------
331// Scheduler routines
332void ScheduleThread( thread_desc * thrd ) {
333        // if( !thrd ) return;
334        verify( thrd );
335        verify( thrd->self_cor.state != Halted );
336
337        verify( !preemption_enabled );
338
339        verifyf( thrd->next == NULL, "Expected null got %p", thrd->next );
340
341        with( *this_processor->cltr ) {
342                lock  ( ready_queue_lock __cfaabi_dbg_ctx2 );
343                append( ready_queue, thrd );
344                unlock( ready_queue_lock );
345        }
346
347        verify( !preemption_enabled );
348}
349
350thread_desc * nextThread(cluster * this) with( *this ) {
351        verify( !preemption_enabled );
352        lock( ready_queue_lock __cfaabi_dbg_ctx2 );
353        thread_desc * head = pop_head( ready_queue );
354        unlock( ready_queue_lock );
355        verify( !preemption_enabled );
356        return head;
357}
358
359void BlockInternal() {
360        disable_interrupts();
361        verify( !preemption_enabled );
362        suspend();
363        verify( !preemption_enabled );
364        enable_interrupts( __cfaabi_dbg_ctx );
365}
366
367void BlockInternal( __spinlock_t * lock ) {
368        disable_interrupts();
369        this_processor->finish.action_code = Release;
370        this_processor->finish.lock        = lock;
371
372        verify( !preemption_enabled );
373        suspend();
374        verify( !preemption_enabled );
375
376        enable_interrupts( __cfaabi_dbg_ctx );
377}
378
379void BlockInternal( thread_desc * thrd ) {
380        disable_interrupts();
381        this_processor->finish.action_code = Schedule;
382        this_processor->finish.thrd        = thrd;
383
384        verify( !preemption_enabled );
385        suspend();
386        verify( !preemption_enabled );
387
388        enable_interrupts( __cfaabi_dbg_ctx );
389}
390
391void BlockInternal( __spinlock_t * lock, thread_desc * thrd ) {
392        assert(thrd);
393        disable_interrupts();
394        this_processor->finish.action_code = Release_Schedule;
395        this_processor->finish.lock        = lock;
396        this_processor->finish.thrd        = thrd;
397
398        verify( !preemption_enabled );
399        suspend();
400        verify( !preemption_enabled );
401
402        enable_interrupts( __cfaabi_dbg_ctx );
403}
404
405void BlockInternal(__spinlock_t * locks [], unsigned short count) {
406        disable_interrupts();
407        this_processor->finish.action_code = Release_Multi;
408        this_processor->finish.locks       = locks;
409        this_processor->finish.lock_count  = count;
410
411        verify( !preemption_enabled );
412        suspend();
413        verify( !preemption_enabled );
414
415        enable_interrupts( __cfaabi_dbg_ctx );
416}
417
418void BlockInternal(__spinlock_t * locks [], unsigned short lock_count, thread_desc * thrds [], unsigned short thrd_count) {
419        disable_interrupts();
420        this_processor->finish.action_code = Release_Multi_Schedule;
421        this_processor->finish.locks       = locks;
422        this_processor->finish.lock_count  = lock_count;
423        this_processor->finish.thrds       = thrds;
424        this_processor->finish.thrd_count  = thrd_count;
425
426        verify( !preemption_enabled );
427        suspend();
428        verify( !preemption_enabled );
429
430        enable_interrupts( __cfaabi_dbg_ctx );
431}
432
433void LeaveThread(__spinlock_t * lock, thread_desc * thrd) {
434        verify( !preemption_enabled );
435        this_processor->finish.action_code = thrd ? Release_Schedule : Release;
436        this_processor->finish.lock        = lock;
437        this_processor->finish.thrd        = thrd;
438
439        suspend();
440}
441
442//=============================================================================================
443// Kernel Setup logic
444//=============================================================================================
445//-----------------------------------------------------------------------------
446// Kernel boot procedures
447void kernel_startup(void) {
448        __cfaabi_dbg_print_safe("Kernel : Starting\n");
449
450        // Start by initializing the main thread
451        // SKULLDUGGERY: the mainThread steals the process main thread
452        // which will then be scheduled by the mainProcessor normally
453        mainThread = (thread_desc *)&storage_mainThread;
454        current_stack_info_t info;
455        (*mainThread){ &info };
456
457        __cfaabi_dbg_print_safe("Kernel : Main thread ready\n");
458
459        // Initialize the main cluster
460        mainCluster = (cluster *)&storage_mainCluster;
461        (*mainCluster){};
462
463        __cfaabi_dbg_print_safe("Kernel : main cluster ready\n");
464
465        // Initialize the main processor and the main processor ctx
466        // (the coroutine that contains the processing control flow)
467        mainProcessor = (processor *)&storage_mainProcessor;
468        (*mainProcessor){ mainCluster, *(processorCtx_t *)&storage_mainProcessorCtx };
469
470        //initialize the global state variables
471        this_processor = mainProcessor;
472        this_thread = mainThread;
473        this_coroutine = &mainThread->self_cor;
474
475        // Enable preemption
476        kernel_start_preemption();
477
478        // Add the main thread to the ready queue
479        // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread
480        ScheduleThread(mainThread);
481
482        // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX
483        // context. Hence, the main thread does not begin through CtxInvokeThread, like all other threads. The trick here is that
484        // mainThread is on the ready queue when this call is made.
485        resume( *mainProcessor->runner );
486
487
488
489        // THE SYSTEM IS NOW COMPLETELY RUNNING
490        __cfaabi_dbg_print_safe("Kernel : Started\n--------------------------------------------------\n\n");
491
492        enable_interrupts( __cfaabi_dbg_ctx );
493}
494
495void kernel_shutdown(void) {
496        __cfaabi_dbg_print_safe("\n--------------------------------------------------\nKernel : Shutting down\n");
497
498        disable_interrupts();
499
500        // SKULLDUGGERY: Notify the mainProcessor it needs to terminates.
501        // When its coroutine terminates, it return control to the mainThread
502        // which is currently here
503        mainProcessor->do_terminate = true;
504        suspend();
505
506        // THE SYSTEM IS NOW COMPLETELY STOPPED
507
508        // Disable preemption
509        kernel_stop_preemption();
510
511        // Destroy the main processor and its context in reverse order of construction
512        // These were manually constructed so we need manually destroy them
513        ^(*mainProcessor->runner){};
514        ^(mainProcessor){};
515
516        // Final step, destroy the main thread since it is no longer needed
517        // Since we provided a stack to this taxk it will not destroy anything
518        ^(mainThread){};
519
520        __cfaabi_dbg_print_safe("Kernel : Shutdown complete\n");
521}
522
523//=============================================================================================
524// Unexpected Terminating logic
525//=============================================================================================
526
527
528static __spinlock_t kernel_abort_lock;
529static __spinlock_t kernel_debug_lock;
530static bool kernel_abort_called = false;
531
532void * kernel_abort    (void) __attribute__ ((__nothrow__)) {
533        // abort cannot be recursively entered by the same or different processors because all signal handlers return when
534        // the globalAbort flag is true.
535        lock( kernel_abort_lock __cfaabi_dbg_ctx2 );
536
537        // first task to abort ?
538        if ( !kernel_abort_called ) {                   // not first task to abort ?
539                kernel_abort_called = true;
540                unlock( kernel_abort_lock );
541        }
542        else {
543                unlock( kernel_abort_lock );
544
545                sigset_t mask;
546                sigemptyset( &mask );
547                sigaddset( &mask, SIGALRM );                    // block SIGALRM signals
548                sigaddset( &mask, SIGUSR1 );                    // block SIGUSR1 signals
549                sigsuspend( &mask );                            // block the processor to prevent further damage during abort
550                _exit( EXIT_FAILURE );                          // if processor unblocks before it is killed, terminate it
551        }
552
553        return this_thread;
554}
555
556void kernel_abort_msg( void * kernel_data, char * abort_text, int abort_text_size ) {
557        thread_desc * thrd = kernel_data;
558
559        int len = snprintf( abort_text, abort_text_size, "Error occurred while executing task %.256s (%p)", thrd->self_cor.name, thrd );
560        __cfaabi_dbg_bits_write( abort_text, len );
561
562        if ( thrd != this_coroutine ) {
563                len = snprintf( abort_text, abort_text_size, " in coroutine %.256s (%p).\n", this_coroutine->name, this_coroutine );
564                __cfaabi_dbg_bits_write( abort_text, len );
565        }
566        else {
567                __cfaabi_dbg_bits_write( ".\n", 2 );
568        }
569}
570
571extern "C" {
572        void __cfaabi_dbg_bits_acquire() {
573                lock( kernel_debug_lock __cfaabi_dbg_ctx2 );
574        }
575
576        void __cfaabi_dbg_bits_release() {
577                unlock( kernel_debug_lock );
578        }
579}
580
581//=============================================================================================
582// Kernel Utilities
583//=============================================================================================
584//-----------------------------------------------------------------------------
585// Locks
586void  ?{}( semaphore & this, int count = 1 ) {
587        (this.lock){};
588        this.count = count;
589        (this.waiting){};
590}
591void ^?{}(semaphore & this) {}
592
593void P(semaphore & this) with( this ){
594        lock( lock __cfaabi_dbg_ctx2 );
595        count -= 1;
596        if ( count < 0 ) {
597                // queue current task
598                append( waiting, (thread_desc *)this_thread );
599
600                // atomically release spin lock and block
601                BlockInternal( &lock );
602        }
603        else {
604            unlock( lock );
605        }
606}
607
608void V(semaphore & this) with( this ) {
609        thread_desc * thrd = NULL;
610        lock( lock __cfaabi_dbg_ctx2 );
611        count += 1;
612        if ( count <= 0 ) {
613                // remove task at head of waiting list
614                thrd = pop_head( waiting );
615        }
616
617        unlock( lock );
618
619        // make new owner
620        WakeThread( thrd );
621}
622
623//-----------------------------------------------------------------------------
624// Debug
625__cfaabi_dbg_debug_do(
626        struct {
627                thread_desc * tail;
628        } __cfaabi_dbg_thread_list = { NULL };
629
630        void __cfaabi_dbg_thread_register( thread_desc * thrd ) {
631                if( !__cfaabi_dbg_thread_list.tail ) {
632                        __cfaabi_dbg_thread_list.tail = thrd;
633                        return;
634                }
635                __cfaabi_dbg_thread_list.tail->dbg_next = thrd;
636                thrd->dbg_prev = __cfaabi_dbg_thread_list.tail;
637                __cfaabi_dbg_thread_list.tail = thrd;
638        }
639
640        void __cfaabi_dbg_thread_unregister( thread_desc * thrd ) {
641                thread_desc * prev = thrd->dbg_prev;
642                thread_desc * next = thrd->dbg_next;
643
644                if( next ) { next->dbg_prev = prev; }
645                else       {
646                        assert( __cfaabi_dbg_thread_list.tail == thrd );
647                        __cfaabi_dbg_thread_list.tail = prev;
648                }
649
650                if( prev ) { prev->dbg_next = next; }
651
652                thrd->dbg_prev = NULL;
653                thrd->dbg_next = NULL;
654        }
655)
656// Local Variables: //
657// mode: c //
658// tab-width: 4 //
659// End: //
Note: See TracBrowser for help on using the repository browser.