Ignore:
File:
1 edited

Legend:

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

    r969b3fe rc5ac6d5  
    1515//
    1616
    17 #include "libhdr.h"
    1817#include "preemption.h"
    1918
    2019extern "C" {
    21 #include <errno.h>
    22 #include <execinfo.h>
    23 #define __USE_GNU
    2420#include <signal.h>
    25 #undef __USE_GNU
    26 #include <stdio.h>
    27 #include <string.h>
    28 #include <unistd.h>
    2921}
    3022
     23#define __CFA_DEFAULT_PREEMPTION__ 10
    3124
    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
    4025__attribute__((weak)) unsigned int default_preemption() {
    4126        return __CFA_DEFAULT_PREEMPTION__;
    4227}
    4328
    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
    4929static void preempt( processor   * this );
    5030static void timeout( thread_desc * this );
    51 
    52 // FwdDeclarations : Signal handlers
    53 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
    54 void sigHandler_segv     ( __CFA_SIGPARMS__ );
    55 void sigHandler_abort    ( __CFA_SIGPARMS__ );
    56 
    57 // FwdDeclarations : sigaction wrapper
    58 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );
    59 
    60 // FwdDeclarations : alarm thread main
    61 void * 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 
    70 KERNEL_STORAGE(event_kernel_t, event_kernel);         // private storage for event kernel
    71 event_kernel_t * event_kernel;                        // kernel public handle to even kernel
    72 static pthread_t alarm_thread;                        // pthread handle to alarm thread
    73 
    74 void ?{}(event_kernel_t * this) {
    75         (&this->alarms){};
    76         (&this->lock){};
    77 }
    7831
    7932//=============================================================================================
     
    8134//=============================================================================================
    8235
    83 // Get next expired node
    84 static 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
     36void kernel_start_preemption() {
     37
    8838}
    8939
    90 // Tick one frame of the Discrete Event Simulation for alarms
    9140void tick_preemption() {
    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
     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);
    10045                if( node->kernel_alarm ) {
    10146                        preempt( node->proc );
     
    10550                }
    10651
    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
     52                if( node->period > 0 ) {
     53                        node->alarm += node->period;
     54                        insert( alarms, node );
    11255                }
    11356                else {
    114                         node->set = false;                  // Node is one-shot, just mark it as not pending
     57                        node->set = false;
    11558                }
    11659        }
    11760
    118         // If there are still alarms pending, reset the timer
    119         if( alarms->head ) { __kernel_set_timer( alarms->head->alarm - currtime ); }
     61        if( alarms->head ) {
     62                __kernel_set_timer( alarms->head->alarm - currtime );
     63        }
    12064}
    12165
    122 // Update the preemption of a processor and notify interested parties
    12366void update_preemption( processor * this, __cfa_time_t duration ) {
     67        //     assert( THREAD_GETMEM( disableInt ) && THREAD_GETMEM( disableIntCnt ) == 1 );
    12468        alarm_node_t * alarm = this->preemption_alarm;
    12569
     
    14589}
    14690
     91void ?{}( preemption_scope * this, processor * proc ) {
     92        (&this->alarm){ proc };
     93        this->proc = proc;
     94        this->proc->preemption_alarm = &this->alarm;
     95        update_preemption( this->proc, this->proc->preemption );
     96}
     97
     98void ^?{}( preemption_scope * this ) {
     99        update_preemption( this->proc, 0 );
     100}
     101
    147102//=============================================================================================
    148 // Kernel Signal Tools
     103// Kernel Signal logic
    149104//=============================================================================================
    150105
    151 LIB_DEBUG_DO( static thread_local void * last_interrupt = 0; )
     106static inline bool preemption_ready() {
     107        return this_processor->disable_preempt_count == 0;
     108}
    152109
    153 extern "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
     110static inline void defer_ctxSwitch() {
     111        this_processor->pending_preemption = true;
     112}
     113
     114static inline void defer_alarm() {
     115        systemProcessor->pending_alarm = true;
     116}
     117
     118void sigHandler_ctxSwitch( __attribute__((unused)) int sig ) {
     119        if( preemption_ready() ) {
     120                ScheduleInternal( this_processor->current_thread );
    158121        }
    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
     122        else {
     123                defer_ctxSwitch();
    184124        }
    185125}
    186126
    187 // sigprocmask wrapper : unblock a single signal
    188 static 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" );
     127void 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();
    195134        }
    196135}
    197136
    198 // sigprocmask wrapper : block a single signal
    199 static 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
    210137static void preempt( processor * this ) {
    211138        pthread_kill( this->kernel_thread, SIGUSR1 );
    212139}
    213140
    214 // reserved for future use
    215141static void timeout( thread_desc * this ) {
    216142        //TODO : implement waking threads
    217143}
    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
    223 static 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
    235 void 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
    257 void 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
    279 void ?{}( preemption_scope * this, processor * proc ) {
    280         (&this->alarm){ proc, zero_time, zero_time };
    281         this->proc = proc;
    282         this->proc->preemption_alarm = &this->alarm;
    283 
    284         update_preemption( this->proc, from_us(this->proc->cltr->preemption) );
    285 }
    286 
    287 void ^?{}( preemption_scope * this ) {
    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
    299 void 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
    316 void * 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 
    356 EXIT:
    357         LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n");
    358         return NULL;
    359 }
    360 
    361 // Sigaction wrapper : register an signal handler
    362 static 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
    378 static 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 
    398 LIB_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.