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

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

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

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