Ignore:
Timestamp:
Aug 14, 2017, 2:03:39 PM (7 years ago)
Author:
Rob Schluntz <rschlunt@…>
Branches:
ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, resolv-new, with_gc
Children:
74b007ba
Parents:
fd344aa (diff), 54cd58b (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' into references

File:
1 edited

Legend:

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

    rfd344aa r9236060  
    1 //                              -*- Mode: CFA -*-
    21//
    32// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
     
    109// Author           : Thierry Delisle
    1110// Created On       : Mon Jun 5 14:20:42 2017
    12 // Last Modified By : Thierry Delisle
    13 // Last Modified On : --
    14 // Update Count     : 0
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Fri Jul 21 22:36:05 2017
     13// Update Count     : 2
    1514//
    1615
     
    3433#endif
    3534
     35//TODO move to defaults
    3636#define __CFA_DEFAULT_PREEMPTION__ 10000
    3737
     38//TODO move to defaults
    3839__attribute__((weak)) unsigned int default_preemption() {
    3940        return __CFA_DEFAULT_PREEMPTION__;
    4041}
    4142
     43// Short hands for signal context information
    4244#define __CFA_SIGCXT__ ucontext_t *
    4345#define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt
    4446
     47// FwdDeclarations : timeout handlers
    4548static void preempt( processor   * this );
    4649static void timeout( thread_desc * this );
    4750
     51// FwdDeclarations : Signal handlers
    4852void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
    49 void sigHandler_alarm    ( __CFA_SIGPARMS__ );
    5053void sigHandler_segv     ( __CFA_SIGPARMS__ );
    5154void sigHandler_abort    ( __CFA_SIGPARMS__ );
    5255
     56// FwdDeclarations : sigaction wrapper
    5357static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );
    54 LIB_DEBUG_DO( bool validate( alarm_list_t * this ); )
    55 
     58
     59// FwdDeclarations : alarm thread main
     60void * alarm_loop( __attribute__((unused)) void * args );
     61
     62// Machine specific register name
    5663#ifdef __x86_64__
    5764#define CFA_REG_IP REG_RIP
     
    6067#endif
    6168
     69KERNEL_STORAGE(event_kernel_t, event_kernel);         // private storage for event kernel
     70event_kernel_t * event_kernel;                        // kernel public handle to even kernel
     71static pthread_t alarm_thread;                        // pthread handle to alarm thread
     72
     73void ?{}(event_kernel_t & this) {
     74        (this.alarms){};
     75        (this.lock){};
     76}
    6277
    6378//=============================================================================================
     
    6580//=============================================================================================
    6681
     82// Get next expired node
     83static inline alarm_node_t * get_expired( alarm_list_t * alarms, __cfa_time_t currtime ) {
     84        if( !alarms->head ) return NULL;                          // If no alarms return null
     85        if( alarms->head->alarm >= currtime ) return NULL;        // If alarms head not expired return null
     86        return pop(alarms);                                       // Otherwise just pop head
     87}
     88
     89// Tick one frame of the Discrete Event Simulation for alarms
    6790void tick_preemption() {
    68         // LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Ticking preemption\n" );
    69 
    70         alarm_list_t * alarms = &systemProcessor->alarms;
    71         __cfa_time_t currtime = __kernel_get_time();
    72         while( alarms->head && alarms->head->alarm < currtime ) {
    73                 alarm_node_t * node = pop(alarms);
    74                 // LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ticking %p\n", node );
    75 
     91        alarm_node_t * node = NULL;                     // Used in the while loop but cannot be declared in the while condition
     92        alarm_list_t * alarms = &event_kernel->alarms;  // Local copy for ease of reading
     93        __cfa_time_t currtime = __kernel_get_time();    // Check current time once so we everything "happens at once"
     94
     95        //Loop throught every thing expired
     96        while( node = get_expired( alarms, currtime ) ) {
     97
     98                // Check if this is a kernel
    7699                if( node->kernel_alarm ) {
    77100                        preempt( node->proc );
     
    81104                }
    82105
    83                 verify( validate( alarms ) );
    84 
    85                 if( node->period > 0 ) {
    86                         node->alarm = currtime + node->period;
    87                         insert( alarms, node );
     106                // Check if this is a periodic alarm
     107                __cfa_time_t period = node->period;
     108                if( period > 0 ) {
     109                        node->alarm = currtime + period;    // Alarm is periodic, add currtime to it (used cached current time)
     110                        insert( alarms, node );             // Reinsert the node for the next time it triggers
    88111                }
    89112                else {
    90                         node->set = false;
    91                 }
    92         }
    93 
    94         if( alarms->head ) {
    95                 __kernel_set_timer( alarms->head->alarm - currtime );
    96         }
    97 
    98         verify( validate( alarms ) );
    99         // LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ticking preemption done\n" );
    100 }
    101 
     113                        node->set = false;                  // Node is one-shot, just mark it as not pending
     114                }
     115        }
     116
     117        // If there are still alarms pending, reset the timer
     118        if( alarms->head ) { __kernel_set_timer( alarms->head->alarm - currtime ); }
     119}
     120
     121// Update the preemption of a processor and notify interested parties
    102122void update_preemption( processor * this, __cfa_time_t duration ) {
    103         LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Processor : %p updating preemption to %lu\n", this, duration );
    104 
    105123        alarm_node_t * alarm = this->preemption_alarm;
    106         duration *= 1000;
    107124
    108125        // Alarms need to be enabled
     
    134151
    135152extern "C" {
     153        // Disable interrupts by incrementing the counter
    136154        void disable_interrupts() {
    137155                __attribute__((unused)) unsigned short new_val = __atomic_add_fetch_2( &disable_preempt_count, 1, __ATOMIC_SEQ_CST );
    138                 verify( new_val < (unsigned short)65_000 );
    139                 verify( new_val != (unsigned short) 0 );
    140         }
    141 
    142         void enable_interrupts_noRF() {
    143                 __attribute__((unused)) unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
    144                 verify( prev != (unsigned short) 0 );
    145         }
    146 
     156                verify( new_val < 65_000u );              // If this triggers someone is disabling interrupts without enabling them
     157        }
     158
     159        // Enable interrupts by decrementing the counter
     160        // If counter reaches 0, execute any pending CtxSwitch
    147161        void enable_interrupts( DEBUG_CTX_PARAM ) {
    148                 processor * proc   = this_processor;
    149                 thread_desc * thrd = this_thread;
     162                processor * proc   = this_processor;      // Cache the processor now since interrupts can start happening after the atomic add
     163                thread_desc * thrd = this_thread;         // Cache the thread now since interrupts can start happening after the atomic add
     164
    150165                unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
    151                 verify( prev != (unsigned short) 0 );
     166                verify( prev != 0u );                     // If this triggers someone is enabled already enabled interruptsverify( prev != 0u );
     167
     168                // Check if we need to prempt the thread because an interrupt was missed
    152169                if( prev == 1 && proc->pending_preemption ) {
    153170                        proc->pending_preemption = false;
     
    155172                }
    156173
     174                // For debugging purposes : keep track of the last person to enable the interrupts
    157175                LIB_DEBUG_DO( proc->last_enable = caller; )
    158176        }
    159 }
    160 
     177
     178        // Disable interrupts by incrementint the counter
     179        // Don't execute any pending CtxSwitch even if counter reaches 0
     180        void enable_interrupts_noPoll() {
     181                __attribute__((unused)) unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
     182                verify( prev != 0u );                     // If this triggers someone is enabled already enabled interrupts
     183        }
     184}
     185
     186// sigprocmask wrapper : unblock a single signal
    161187static inline void signal_unblock( int sig ) {
    162188        sigset_t mask;
     
    169195}
    170196
     197// sigprocmask wrapper : block a single signal
    171198static inline void signal_block( int sig ) {
    172199        sigset_t mask;
     
    179206}
    180207
    181 static inline bool preemption_ready() {
    182         return disable_preempt_count == 0 && !preemption_in_progress;
    183 }
    184 
    185 static inline void defer_ctxSwitch() {
    186         this_processor->pending_preemption = true;
    187 }
    188 
    189 static inline void defer_alarm() {
    190         systemProcessor->pending_alarm = true;
    191 }
    192 
     208// kill wrapper : signal a processor
    193209static void preempt( processor * this ) {
    194210        pthread_kill( this->kernel_thread, SIGUSR1 );
    195211}
    196212
     213// reserved for future use
    197214static void timeout( thread_desc * this ) {
    198215        //TODO : implement waking threads
    199216}
    200217
     218
     219// Check if a CtxSwitch signal handler shoud defer
     220// If true  : preemption is safe
     221// If false : preemption is unsafe and marked as pending
     222static inline bool preemption_ready() {
     223        bool ready = disable_preempt_count == 0 && !preemption_in_progress; // Check if preemption is safe
     224        this_processor->pending_preemption = !ready;                        // Adjust the pending flag accordingly
     225        return ready;
     226}
     227
    201228//=============================================================================================
    202229// Kernel Signal Startup/Shutdown logic
    203230//=============================================================================================
    204231
    205 static pthread_t alarm_thread;
    206 void * alarm_loop( __attribute__((unused)) void * args );
    207 
     232// Startup routine to activate preemption
     233// Called from kernel_startup
    208234void kernel_start_preemption() {
    209235        LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");
    210         __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO );
    211         // __kernel_sigaction( SIGSEGV, sigHandler_segv     , SA_SIGINFO );
    212         // __kernel_sigaction( SIGBUS , sigHandler_segv     , SA_SIGINFO );
     236
     237        // Start with preemption disabled until ready
     238        disable_preempt_count = 1;
     239
     240        // Initialize the event kernel
     241        event_kernel = (event_kernel_t *)&storage_event_kernel;
     242        (*event_kernel){};
     243
     244        // Setup proper signal handlers
     245        __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO );         // CtxSwitch handler
     246        // __kernel_sigaction( SIGSEGV, sigHandler_segv     , SA_SIGINFO );      // Failure handler
     247        // __kernel_sigaction( SIGBUS , sigHandler_segv     , SA_SIGINFO );      // Failure handler
    213248
    214249        signal_block( SIGALRM );
     
    217252}
    218253
     254// Shutdown routine to deactivate preemption
     255// Called from kernel_shutdown
    219256void kernel_stop_preemption() {
    220257        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n");
    221258
     259        // Block all signals since we are already shutting down
    222260        sigset_t mask;
    223261        sigfillset( &mask );
    224262        sigprocmask( SIG_BLOCK, &mask, NULL );
    225263
     264        // Notify the alarm thread of the shutdown
    226265        sigval val = { 1 };
    227266        pthread_sigqueue( alarm_thread, SIGALRM, val );
     267
     268        // Wait for the preemption thread to finish
    228269        pthread_join( alarm_thread, NULL );
     270
     271        // Preemption is now fully stopped
     272
    229273        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");
    230274}
    231275
     276// Raii ctor/dtor for the preemption_scope
     277// Used by thread to control when they want to receive preemption signals
    232278void ?{}( preemption_scope & this, processor * proc ) {
    233         (this.alarm){ proc };
     279        (this.alarm){ proc, zero_time, zero_time };
    234280        this.proc = proc;
    235281        this.proc->preemption_alarm = &this.alarm;
    236         update_preemption( this.proc, this.proc->preemption );
     282
     283        update_preemption( this.proc, from_us(this.proc->cltr->preemption) );
    237284}
    238285
     
    240287        disable_interrupts();
    241288
    242         update_preemption( this.proc, 0 );
     289        update_preemption( this.proc, zero_time );
    243290}
    244291
     
    247294//=============================================================================================
    248295
     296// Context switch signal handler
     297// Receives SIGUSR1 signal and causes the current thread to yield
    249298void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
    250299        LIB_DEBUG_DO( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); )
    251         if( preemption_ready() ) {
    252                 preemption_in_progress = true;
    253                 signal_unblock( SIGUSR1 );
    254                 this_processor->pending_preemption = false;
    255                 preemption_in_progress = false;
    256                 BlockInternal( (thread_desc*)this_thread );
    257         }
    258         else {
    259                 defer_ctxSwitch();
    260         }
    261 }
    262 
     300
     301        // Check if it is safe to preempt here
     302        if( !preemption_ready() ) { return; }
     303
     304        preemption_in_progress = true;                      // Sync flag : prevent recursive calls to the signal handler
     305        signal_unblock( SIGUSR1 );                          // We are about to CtxSwitch out of the signal handler, let other handlers in
     306        preemption_in_progress = false;                     // Clear the in progress flag
     307
     308        // Preemption can occur here
     309
     310        BlockInternal( (thread_desc*)this_thread );         // Do the actual CtxSwitch
     311}
     312
     313// Main of the alarm thread
     314// Waits on SIGALRM and send SIGUSR1 to whom ever needs it
    263315void * alarm_loop( __attribute__((unused)) void * args ) {
     316        // Block sigalrms to control when they arrive
    264317        sigset_t mask;
    265318        sigemptyset( &mask );
     
    270323        }
    271324
     325        // Main loop
    272326        while( true ) {
     327                // Wait for a sigalrm
    273328                siginfo_t info;
    274329                int sig = sigwaitinfo( &mask, &info );
    275                 if( sig < 0 ) {
    276                         abortf( "internal error, sigwait" );
    277                 }
    278                 else if( sig == SIGALRM )
     330
     331                // If another signal arrived something went wrong
     332                assertf(sig == SIGALRM, "Kernel Internal Error, sigwait: Unexpected signal %d (%d : %d)\n", sig, info.si_code, info.si_value.sival_int);
     333
     334                LIB_DEBUG_PRINT_SAFE("Kernel : Caught alarm from %d with %d\n", info.si_code, info.si_value.sival_int );
     335                // Switch on the code (a.k.a. the sender) to
     336                switch( info.si_code )
    279337                {
    280                         LIB_DEBUG_PRINT_SAFE("Kernel : Caught signal %d (%d)\n", sig, info.si_value.sival_int );
    281                         if( info.si_value.sival_int == 0 )
    282                         {
    283                                 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n");
    284                                 lock( &systemProcessor->alarm_lock DEBUG_CTX2 );
    285                                 tick_preemption();
    286                                 unlock( &systemProcessor->alarm_lock );
    287                         }
    288                         else if( info.si_value.sival_int == 1 )
    289                         {
    290                                 break;
    291                         }
    292                 }
    293                 else
    294                 {
    295                         LIB_DEBUG_PRINT_SAFE("Kernel : Unexpected signal %d (%d)\n", sig, info.si_value.sival_int);
    296                 }
    297         }
    298 
     338                // Timers can apparently be marked as sent for the kernel
     339                // In either case, tick preemption
     340                case SI_TIMER:
     341                case SI_KERNEL:
     342                        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n");
     343                        lock( &event_kernel->lock DEBUG_CTX2 );
     344                        tick_preemption();
     345                        unlock( &event_kernel->lock );
     346                        break;
     347                // Signal was not sent by the kernel but by an other thread
     348                case SI_QUEUE:
     349                        // For now, other thread only signal the alarm thread to shut it down
     350                        // If this needs to change use info.si_value and handle the case here
     351                        goto EXIT;
     352                }
     353        }
     354
     355EXIT:
    299356        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n");
    300357        return NULL;
    301358}
    302359
     360// Sigaction wrapper : register an signal handler
    303361static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) {
    304362        struct sigaction act;
     
    316374}
    317375
    318 typedef void (*sa_handler_t)(int);
    319 
     376// Sigaction wrapper : restore default handler
    320377static void __kernel_sigdefault( int sig ) {
    321378        struct sigaction act;
    322379
    323         // act.sa_handler = SIG_DFL;
     380        act.sa_handler = SIG_DFL;
    324381        act.sa_flags = 0;
    325382        sigemptyset( &act.sa_mask );
     
    429486//      raise( SIGABRT );
    430487// }
     488
     489// Local Variables: //
     490// mode: c //
     491// tab-width: 4 //
     492// End: //
Note: See TracChangeset for help on using the changeset viewer.