Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/libcfa/concurrency/preemption.c

    rc5ac6d5 r969b3fe  
    1515//
    1616
     17#include "libhdr.h"
    1718#include "preemption.h"
    1819
    1920extern "C" {
     21#include <errno.h>
     22#include <execinfo.h>
     23#define __USE_GNU
    2024#include <signal.h>
    21 }
    22 
    23 #define __CFA_DEFAULT_PREEMPTION__ 10
    24 
     25#undef __USE_GNU
     26#include <stdio.h>
     27#include <string.h>
     28#include <unistd.h>
     29}
     30
     31
     32#ifdef __USE_STREAM__
     33#include "fstream"
     34#endif
     35
     36//TODO move to defaults
     37#define __CFA_DEFAULT_PREEMPTION__ 10000
     38
     39//TODO move to defaults
    2540__attribute__((weak)) unsigned int default_preemption() {
    2641        return __CFA_DEFAULT_PREEMPTION__;
    2742}
    2843
     44// Short hands for signal context information
     45#define __CFA_SIGCXT__ ucontext_t *
     46#define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt
     47
     48// FwdDeclarations : timeout handlers
    2949static void preempt( processor   * this );
    3050static void timeout( thread_desc * this );
    3151
     52// FwdDeclarations : Signal handlers
     53void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
     54void sigHandler_segv     ( __CFA_SIGPARMS__ );
     55void sigHandler_abort    ( __CFA_SIGPARMS__ );
     56
     57// FwdDeclarations : sigaction wrapper
     58static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );
     59
     60// FwdDeclarations : alarm thread main
     61void * alarm_loop( __attribute__((unused)) void * args );
     62
     63// Machine specific register name
     64#ifdef __x86_64__
     65#define CFA_REG_IP REG_RIP
     66#else
     67#define CFA_REG_IP REG_EIP
     68#endif
     69
     70KERNEL_STORAGE(event_kernel_t, event_kernel);         // private storage for event kernel
     71event_kernel_t * event_kernel;                        // kernel public handle to even kernel
     72static pthread_t alarm_thread;                        // pthread handle to alarm thread
     73
     74void ?{}(event_kernel_t * this) {
     75        (&this->alarms){};
     76        (&this->lock){};
     77}
     78
    3279//=============================================================================================
    3380// Kernel Preemption logic
    3481//=============================================================================================
    3582
    36 void kernel_start_preemption() {
    37 
    38 }
    39 
     83// Get next expired node
     84static inline alarm_node_t * get_expired( alarm_list_t * alarms, __cfa_time_t currtime ) {
     85        if( !alarms->head ) return NULL;                          // If no alarms return null
     86        if( alarms->head->alarm >= currtime ) return NULL;        // If alarms head not expired return null
     87        return pop(alarms);                                       // Otherwise just pop head
     88}
     89
     90// Tick one frame of the Discrete Event Simulation for alarms
    4091void tick_preemption() {
    41         alarm_list_t * alarms = &systemProcessor->alarms;
    42         __cfa_time_t currtime = __kernel_get_time();
    43         while( alarms->head && alarms->head->alarm < currtime ) {
    44                 alarm_node_t * node = pop(alarms);
     92        alarm_node_t * node = NULL;                     // Used in the while loop but cannot be declared in the while condition
     93        alarm_list_t * alarms = &event_kernel->alarms;  // Local copy for ease of reading
     94        __cfa_time_t currtime = __kernel_get_time();    // Check current time once so we everything "happens at once"
     95
     96        //Loop throught every thing expired
     97        while( node = get_expired( alarms, currtime ) ) {
     98
     99                // Check if this is a kernel
    45100                if( node->kernel_alarm ) {
    46101                        preempt( node->proc );
     
    50105                }
    51106
    52                 if( node->period > 0 ) {
    53                         node->alarm += node->period;
    54                         insert( alarms, node );
     107                // Check if this is a periodic alarm
     108                __cfa_time_t period = node->period;
     109                if( period > 0 ) {
     110                        node->alarm = currtime + period;    // Alarm is periodic, add currtime to it (used cached current time)
     111                        insert( alarms, node );             // Reinsert the node for the next time it triggers
    55112                }
    56113                else {
    57                         node->set = false;
    58                 }
    59         }
    60 
    61         if( alarms->head ) {
    62                 __kernel_set_timer( alarms->head->alarm - currtime );
    63         }
    64 }
    65 
     114                        node->set = false;                  // Node is one-shot, just mark it as not pending
     115                }
     116        }
     117
     118        // If there are still alarms pending, reset the timer
     119        if( alarms->head ) { __kernel_set_timer( alarms->head->alarm - currtime ); }
     120}
     121
     122// Update the preemption of a processor and notify interested parties
    66123void update_preemption( processor * this, __cfa_time_t duration ) {
    67         //     assert( THREAD_GETMEM( disableInt ) && THREAD_GETMEM( disableIntCnt ) == 1 );
    68124        alarm_node_t * alarm = this->preemption_alarm;
    69125
     
    89145}
    90146
     147//=============================================================================================
     148// Kernel Signal Tools
     149//=============================================================================================
     150
     151LIB_DEBUG_DO( static thread_local void * last_interrupt = 0; )
     152
     153extern "C" {
     154        // Disable interrupts by incrementing the counter
     155        void disable_interrupts() {
     156                __attribute__((unused)) unsigned short new_val = __atomic_add_fetch_2( &disable_preempt_count, 1, __ATOMIC_SEQ_CST );
     157                verify( new_val < 65_000u );              // If this triggers someone is disabling interrupts without enabling them
     158        }
     159
     160        // Enable interrupts by decrementing the counter
     161        // If counter reaches 0, execute any pending CtxSwitch
     162        void enable_interrupts( DEBUG_CTX_PARAM ) {
     163                processor * proc   = this_processor;      // Cache the processor now since interrupts can start happening after the atomic add
     164                thread_desc * thrd = this_thread;         // Cache the thread now since interrupts can start happening after the atomic add
     165
     166                unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
     167                verify( prev != 0u );                     // If this triggers someone is enabled already enabled interruptsverify( prev != 0u );
     168
     169                // Check if we need to prempt the thread because an interrupt was missed
     170                if( prev == 1 && proc->pending_preemption ) {
     171                        proc->pending_preemption = false;
     172                        BlockInternal( thrd );
     173                }
     174
     175                // For debugging purposes : keep track of the last person to enable the interrupts
     176                LIB_DEBUG_DO( proc->last_enable = caller; )
     177        }
     178
     179        // Disable interrupts by incrementint the counter
     180        // Don't execute any pending CtxSwitch even if counter reaches 0
     181        void enable_interrupts_noPoll() {
     182                __attribute__((unused)) unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
     183                verify( prev != 0u );                     // If this triggers someone is enabled already enabled interrupts
     184        }
     185}
     186
     187// sigprocmask wrapper : unblock a single signal
     188static inline void signal_unblock( int sig ) {
     189        sigset_t mask;
     190        sigemptyset( &mask );
     191        sigaddset( &mask, sig );
     192
     193        if ( pthread_sigmask( SIG_UNBLOCK, &mask, NULL ) == -1 ) {
     194            abortf( "internal error, pthread_sigmask" );
     195        }
     196}
     197
     198// sigprocmask wrapper : block a single signal
     199static inline void signal_block( int sig ) {
     200        sigset_t mask;
     201        sigemptyset( &mask );
     202        sigaddset( &mask, sig );
     203
     204        if ( pthread_sigmask( SIG_BLOCK, &mask, NULL ) == -1 ) {
     205            abortf( "internal error, pthread_sigmask" );
     206        }
     207}
     208
     209// kill wrapper : signal a processor
     210static void preempt( processor * this ) {
     211        pthread_kill( this->kernel_thread, SIGUSR1 );
     212}
     213
     214// reserved for future use
     215static void timeout( thread_desc * this ) {
     216        //TODO : implement waking threads
     217}
     218
     219
     220// Check if a CtxSwitch signal handler shoud defer
     221// If true  : preemption is safe
     222// If false : preemption is unsafe and marked as pending
     223static inline bool preemption_ready() {
     224        bool ready = disable_preempt_count == 0 && !preemption_in_progress; // Check if preemption is safe
     225        this_processor->pending_preemption = !ready;                        // Adjust the pending flag accordingly
     226        return ready;
     227}
     228
     229//=============================================================================================
     230// Kernel Signal Startup/Shutdown logic
     231//=============================================================================================
     232
     233// Startup routine to activate preemption
     234// Called from kernel_startup
     235void kernel_start_preemption() {
     236        LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");
     237
     238        // Start with preemption disabled until ready
     239        disable_preempt_count = 1;
     240
     241        // Initialize the event kernel
     242        event_kernel = (event_kernel_t *)&storage_event_kernel;
     243        event_kernel{};
     244
     245        // Setup proper signal handlers
     246        __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO );         // CtxSwitch handler
     247        // __kernel_sigaction( SIGSEGV, sigHandler_segv     , SA_SIGINFO );      // Failure handler
     248        // __kernel_sigaction( SIGBUS , sigHandler_segv     , SA_SIGINFO );      // Failure handler
     249
     250        signal_block( SIGALRM );
     251
     252        pthread_create( &alarm_thread, NULL, alarm_loop, NULL );
     253}
     254
     255// Shutdown routine to deactivate preemption
     256// Called from kernel_shutdown
     257void kernel_stop_preemption() {
     258        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n");
     259
     260        // Block all signals since we are already shutting down
     261        sigset_t mask;
     262        sigfillset( &mask );
     263        sigprocmask( SIG_BLOCK, &mask, NULL );
     264
     265        // Notify the alarm thread of the shutdown
     266        sigval val = { 1 };
     267        pthread_sigqueue( alarm_thread, SIGALRM, val );
     268
     269        // Wait for the preemption thread to finish
     270        pthread_join( alarm_thread, NULL );
     271
     272        // Preemption is now fully stopped
     273
     274        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");
     275}
     276
     277// Raii ctor/dtor for the preemption_scope
     278// Used by thread to control when they want to receive preemption signals
    91279void ?{}( preemption_scope * this, processor * proc ) {
    92         (&this->alarm){ proc };
     280        (&this->alarm){ proc, zero_time, zero_time };
    93281        this->proc = proc;
    94282        this->proc->preemption_alarm = &this->alarm;
    95         update_preemption( this->proc, this->proc->preemption );
     283
     284        update_preemption( this->proc, from_us(this->proc->cltr->preemption) );
    96285}
    97286
    98287void ^?{}( preemption_scope * this ) {
    99         update_preemption( this->proc, 0 );
    100 }
    101 
    102 //=============================================================================================
    103 // Kernel Signal logic
    104 //=============================================================================================
    105 
    106 static inline bool preemption_ready() {
    107         return this_processor->disable_preempt_count == 0;
    108 }
    109 
    110 static inline void defer_ctxSwitch() {
    111         this_processor->pending_preemption = true;
    112 }
    113 
    114 static inline void defer_alarm() {
    115         systemProcessor->pending_alarm = true;
    116 }
    117 
    118 void sigHandler_ctxSwitch( __attribute__((unused)) int sig ) {
    119         if( preemption_ready() ) {
    120                 ScheduleInternal( this_processor->current_thread );
    121         }
    122         else {
    123                 defer_ctxSwitch();
    124         }
    125 }
    126 
    127 void sigHandler_alarm( __attribute__((unused)) int sig ) {
    128         if( try_lock( &systemProcessor->alarm_lock ) ) {
    129                 tick_preemption();
    130                 unlock( &systemProcessor->alarm_lock );
    131         }
    132         else {
    133                 defer_alarm();
    134         }
    135 }
    136 
    137 static void preempt( processor * this ) {
    138         pthread_kill( this->kernel_thread, SIGUSR1 );
    139 }
    140 
    141 static void timeout( thread_desc * this ) {
    142         //TODO : implement waking threads
    143 }
     288        disable_interrupts();
     289
     290        update_preemption( this->proc, zero_time );
     291}
     292
     293//=============================================================================================
     294// Kernel Signal Handlers
     295//=============================================================================================
     296
     297// Context switch signal handler
     298// Receives SIGUSR1 signal and causes the current thread to yield
     299void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
     300        LIB_DEBUG_DO( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); )
     301
     302        // Check if it is safe to preempt here
     303        if( !preemption_ready() ) { return; }
     304
     305        preemption_in_progress = true;                      // Sync flag : prevent recursive calls to the signal handler
     306        signal_unblock( SIGUSR1 );                          // We are about to CtxSwitch out of the signal handler, let other handlers in
     307        preemption_in_progress = false;                     // Clear the in progress flag
     308
     309        // Preemption can occur here
     310
     311        BlockInternal( (thread_desc*)this_thread );         // Do the actual CtxSwitch
     312}
     313
     314// Main of the alarm thread
     315// Waits on SIGALRM and send SIGUSR1 to whom ever needs it
     316void * alarm_loop( __attribute__((unused)) void * args ) {
     317        // Block sigalrms to control when they arrive
     318        sigset_t mask;
     319        sigemptyset( &mask );
     320        sigaddset( &mask, SIGALRM );
     321
     322        if ( pthread_sigmask( SIG_BLOCK, &mask, NULL ) == -1 ) {
     323            abortf( "internal error, pthread_sigmask" );
     324        }
     325
     326        // Main loop
     327        while( true ) {
     328                // Wait for a sigalrm
     329                siginfo_t info;
     330                int sig = sigwaitinfo( &mask, &info );
     331
     332                // If another signal arrived something went wrong
     333                assertf(sig == SIGALRM, "Kernel Internal Error, sigwait: Unexpected signal %d (%d : %d)\n", sig, info.si_code, info.si_value.sival_int);
     334
     335                LIB_DEBUG_PRINT_SAFE("Kernel : Caught alarm from %d with %d\n", info.si_code, info.si_value.sival_int );
     336                // Switch on the code (a.k.a. the sender) to
     337                switch( info.si_code )
     338                {
     339                // Timers can apparently be marked as sent for the kernel
     340                // In either case, tick preemption
     341                case SI_TIMER:
     342                case SI_KERNEL:
     343                        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n");
     344                        lock( &event_kernel->lock DEBUG_CTX2 );
     345                        tick_preemption();
     346                        unlock( &event_kernel->lock );
     347                        break;
     348                // Signal was not sent by the kernel but by an other thread
     349                case SI_QUEUE:
     350                        // For now, other thread only signal the alarm thread to shut it down
     351                        // If this needs to change use info.si_value and handle the case here
     352                        goto EXIT;
     353                }
     354        }
     355
     356EXIT:
     357        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n");
     358        return NULL;
     359}
     360
     361// Sigaction wrapper : register an signal handler
     362static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) {
     363        struct sigaction act;
     364
     365        act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
     366        act.sa_flags = flags;
     367
     368        if ( sigaction( sig, &act, NULL ) == -1 ) {
     369                LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO,
     370                        " __kernel_sigaction( sig:%d, handler:%p, flags:%d ), problem installing signal handler, error(%d) %s.\n",
     371                        sig, handler, flags, errno, strerror( errno )
     372                );
     373                _exit( EXIT_FAILURE );
     374        }
     375}
     376
     377// Sigaction wrapper : restore default handler
     378static void __kernel_sigdefault( int sig ) {
     379        struct sigaction act;
     380
     381        act.sa_handler = SIG_DFL;
     382        act.sa_flags = 0;
     383        sigemptyset( &act.sa_mask );
     384
     385        if ( sigaction( sig, &act, NULL ) == -1 ) {
     386                LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO,
     387                        " __kernel_sigdefault( sig:%d ), problem reseting signal handler, error(%d) %s.\n",
     388                        sig, errno, strerror( errno )
     389                );
     390                _exit( EXIT_FAILURE );
     391        }
     392}
     393
     394//=============================================================================================
     395// Terminating Signals logic
     396//=============================================================================================
     397
     398LIB_DEBUG_DO(
     399        static void __kernel_backtrace( int start ) {
     400                // skip first N stack frames
     401
     402                enum { Frames = 50 };
     403                void * array[Frames];
     404                int size = backtrace( array, Frames );
     405                char ** messages = backtrace_symbols( array, size );
     406
     407                // find executable name
     408                *index( messages[0], '(' ) = '\0';
     409                #ifdef __USE_STREAM__
     410                serr | "Stack back trace for:" | messages[0] | endl;
     411                #else
     412                fprintf( stderr, "Stack back trace for: %s\n", messages[0]);
     413                #endif
     414
     415                // skip last 2 stack frames after main
     416                for ( int i = start; i < size && messages != NULL; i += 1 ) {
     417                        char * name = NULL;
     418                        char * offset_begin = NULL;
     419                        char * offset_end = NULL;
     420
     421                        for ( char *p = messages[i]; *p; ++p ) {
     422                                // find parantheses and +offset
     423                                if ( *p == '(' ) {
     424                                        name = p;
     425                                }
     426                                else if ( *p == '+' ) {
     427                                        offset_begin = p;
     428                                }
     429                                else if ( *p == ')' ) {
     430                                        offset_end = p;
     431                                        break;
     432                                }
     433                        }
     434
     435                        // if line contains symbol print it
     436                        int frameNo = i - start;
     437                        if ( name && offset_begin && offset_end && name < offset_begin ) {
     438                                // delimit strings
     439                                *name++ = '\0';
     440                                *offset_begin++ = '\0';
     441                                *offset_end++ = '\0';
     442
     443                                #ifdef __USE_STREAM__
     444                                serr    | "("  | frameNo | ")" | messages[i] | ":"
     445                                        | name | "+" | offset_begin | offset_end | endl;
     446                                #else
     447                                fprintf( stderr, "(%i) %s : %s + %s %s\n", frameNo, messages[i], name, offset_begin, offset_end);
     448                                #endif
     449                        }
     450                        // otherwise, print the whole line
     451                        else {
     452                                #ifdef __USE_STREAM__
     453                                serr | "(" | frameNo | ")" | messages[i] | endl;
     454                                #else
     455                                fprintf( stderr, "(%i) %s\n", frameNo, messages[i] );
     456                                #endif
     457                        }
     458                }
     459
     460                free( messages );
     461        }
     462)
     463
     464// void sigHandler_segv( __CFA_SIGPARMS__ ) {
     465//      LIB_DEBUG_DO(
     466//              #ifdef __USE_STREAM__
     467//              serr    | "*CFA runtime error* program cfa-cpp terminated with"
     468//                      | (sig == SIGSEGV ? "segment fault." : "bus error.")
     469//                      | endl;
     470//              #else
     471//              fprintf( stderr, "*CFA runtime error* program cfa-cpp terminated with %s\n", sig == SIGSEGV ? "segment fault." : "bus error." );
     472//              #endif
     473
     474//              // skip first 2 stack frames
     475//              __kernel_backtrace( 1 );
     476//      )
     477//      exit( EXIT_FAILURE );
     478// }
     479
     480// void sigHandler_abort( __CFA_SIGPARMS__ ) {
     481//      // skip first 6 stack frames
     482//      LIB_DEBUG_DO( __kernel_backtrace( 6 ); )
     483
     484//      // reset default signal handler
     485//      __kernel_sigdefault( SIGABRT );
     486
     487//      raise( SIGABRT );
     488// }
Note: See TracChangeset for help on using the changeset viewer.