Ignore:
File:
1 edited

Legend:

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

    r6b0b624 rd6ff3ff  
     1//                              -*- Mode: CFA -*-
    12//
    23// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
     
    910// Author           : Thierry Delisle
    1011// Created On       : Mon Jun 5 14:20:42 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jul 21 22:36:05 2017
    13 // Update Count     : 2
     12// Last Modified By : Thierry Delisle
     13// Last Modified On : --
     14// Update Count     : 0
    1415//
    1516
     
    3334#endif
    3435
    35 //TODO move to defaults
    3636#define __CFA_DEFAULT_PREEMPTION__ 10000
    3737
    38 //TODO move to defaults
    3938__attribute__((weak)) unsigned int default_preemption() {
    4039        return __CFA_DEFAULT_PREEMPTION__;
    4140}
    4241
    43 // Short hands for signal context information
    4442#define __CFA_SIGCXT__ ucontext_t *
    4543#define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt
    4644
    47 // FwdDeclarations : timeout handlers
    4845static void preempt( processor   * this );
    4946static void timeout( thread_desc * this );
    5047
    51 // FwdDeclarations : Signal handlers
    5248void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
     49void sigHandler_alarm    ( __CFA_SIGPARMS__ );
    5350void sigHandler_segv     ( __CFA_SIGPARMS__ );
    5451void sigHandler_abort    ( __CFA_SIGPARMS__ );
    5552
    56 // FwdDeclarations : sigaction wrapper
    5753static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );
    58 
    59 // FwdDeclarations : alarm thread main
    60 void * alarm_loop( __attribute__((unused)) void * args );
    61 
    62 // Machine specific register name
     54LIB_DEBUG_DO( bool validate( alarm_list_t * this ); )
     55
    6356#ifdef __x86_64__
    6457#define CFA_REG_IP REG_RIP
     
    6760#endif
    6861
    69 KERNEL_STORAGE(event_kernel_t, event_kernel);         // private storage for event kernel
    70 event_kernel_t * event_kernel;                        // kernel public handle to even kernel
    71 static pthread_t alarm_thread;                        // pthread handle to alarm thread
    72 
    73 void ?{}(event_kernel_t * this) {
    74         (&this->alarms){};
    75         (&this->lock){};
    76 }
    7762
    7863//=============================================================================================
     
    8065//=============================================================================================
    8166
    82 // Get next expired node
    83 static 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
    9067void tick_preemption() {
    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
     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
    9976                if( node->kernel_alarm ) {
    10077                        preempt( node->proc );
     
    10481                }
    10582
    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
     83                verify( validate( alarms ) );
     84
     85                if( node->period > 0 ) {
     86                        node->alarm = currtime + node->period;
     87                        insert( alarms, node );
    11188                }
    11289                else {
    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
     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
    122102void 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
    123105        alarm_node_t * alarm = this->preemption_alarm;
     106        duration *= 1000;
    124107
    125108        // Alarms need to be enabled
     
    151134
    152135extern "C" {
    153         // Disable interrupts by incrementing the counter
    154136        void disable_interrupts() {
    155137                __attribute__((unused)) unsigned short new_val = __atomic_add_fetch_2( &disable_preempt_count, 1, __ATOMIC_SEQ_CST );
    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
     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
    161147        void enable_interrupts( DEBUG_CTX_PARAM ) {
    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 
     148                processor * proc   = this_processor;
     149                thread_desc * thrd = this_thread;
    165150                unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
    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
     151                verify( prev != (unsigned short) 0 );
    169152                if( prev == 1 && proc->pending_preemption ) {
    170153                        proc->pending_preemption = false;
     
    172155                }
    173156
    174                 // For debugging purposes : keep track of the last person to enable the interrupts
    175157                LIB_DEBUG_DO( proc->last_enable = caller; )
    176158        }
    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
     159}
     160
    187161static inline void signal_unblock( int sig ) {
    188162        sigset_t mask;
     
    195169}
    196170
    197 // sigprocmask wrapper : block a single signal
    198171static inline void signal_block( int sig ) {
    199172        sigset_t mask;
     
    206179}
    207180
    208 // kill wrapper : signal a processor
     181static inline bool preemption_ready() {
     182        return disable_preempt_count == 0 && !preemption_in_progress;
     183}
     184
     185static inline void defer_ctxSwitch() {
     186        this_processor->pending_preemption = true;
     187}
     188
     189static inline void defer_alarm() {
     190        systemProcessor->pending_alarm = true;
     191}
     192
    209193static void preempt( processor * this ) {
    210194        pthread_kill( this->kernel_thread, SIGUSR1 );
    211195}
    212196
    213 // reserved for future use
    214197static void timeout( thread_desc * this ) {
    215198        //TODO : implement waking threads
    216199}
    217200
    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
    222 static 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 
    228201//=============================================================================================
    229202// Kernel Signal Startup/Shutdown logic
    230203//=============================================================================================
    231204
    232 // Startup routine to activate preemption
    233 // Called from kernel_startup
     205static pthread_t alarm_thread;
     206void * alarm_loop( __attribute__((unused)) void * args );
     207
    234208void kernel_start_preemption() {
    235209        LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");
    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
     210        __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO );
     211        // __kernel_sigaction( SIGSEGV, sigHandler_segv     , SA_SIGINFO );
     212        // __kernel_sigaction( SIGBUS , sigHandler_segv     , SA_SIGINFO );
    248213
    249214        signal_block( SIGALRM );
     
    252217}
    253218
    254 // Shutdown routine to deactivate preemption
    255 // Called from kernel_shutdown
    256219void kernel_stop_preemption() {
    257220        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n");
    258221
    259         // Block all signals since we are already shutting down
    260222        sigset_t mask;
    261223        sigfillset( &mask );
    262224        sigprocmask( SIG_BLOCK, &mask, NULL );
    263225
    264         // Notify the alarm thread of the shutdown
    265226        sigval val = { 1 };
    266227        pthread_sigqueue( alarm_thread, SIGALRM, val );
    267 
    268         // Wait for the preemption thread to finish
    269228        pthread_join( alarm_thread, NULL );
    270 
    271         // Preemption is now fully stopped
    272 
    273229        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");
    274230}
    275231
    276 // Raii ctor/dtor for the preemption_scope
    277 // Used by thread to control when they want to receive preemption signals
    278232void ?{}( preemption_scope * this, processor * proc ) {
    279         (&this->alarm){ proc, zero_time, zero_time };
     233        (&this->alarm){ proc };
    280234        this->proc = proc;
    281235        this->proc->preemption_alarm = &this->alarm;
    282 
    283         update_preemption( this->proc, from_us(this->proc->cltr->preemption) );
     236        update_preemption( this->proc, this->proc->preemption );
    284237}
    285238
     
    287240        disable_interrupts();
    288241
    289         update_preemption( this->proc, zero_time );
     242        update_preemption( this->proc, 0 );
    290243}
    291244
     
    294247//=============================================================================================
    295248
    296 // Context switch signal handler
    297 // Receives SIGUSR1 signal and causes the current thread to yield
    298249void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
    299250        LIB_DEBUG_DO( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); )
    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
     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
    315263void * alarm_loop( __attribute__((unused)) void * args ) {
    316         // Block sigalrms to control when they arrive
    317264        sigset_t mask;
    318265        sigemptyset( &mask );
     
    323270        }
    324271
    325         // Main loop
    326272        while( true ) {
    327                 // Wait for a sigalrm
    328273                siginfo_t info;
    329274                int sig = sigwaitinfo( &mask, &info );
    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 )
     275                if( sig < 0 ) {
     276                        abortf( "internal error, sigwait" );
     277                }
     278                else if( sig == SIGALRM )
    337279                {
    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 
    355 EXIT:
     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
    356299        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n");
    357300        return NULL;
    358301}
    359302
    360 // Sigaction wrapper : register an signal handler
    361303static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) {
    362304        struct sigaction act;
     
    374316}
    375317
    376 // Sigaction wrapper : restore default handler
     318typedef void (*sa_handler_t)(int);
     319
    377320static void __kernel_sigdefault( int sig ) {
    378321        struct sigaction act;
    379322
    380         act.sa_handler = SIG_DFL;
     323        // act.sa_handler = SIG_DFL;
    381324        act.sa_flags = 0;
    382325        sigemptyset( &act.sa_mask );
     
    486429//      raise( SIGABRT );
    487430// }
    488 
    489 // Local Variables: //
    490 // mode: c //
    491 // tab-width: 4 //
    492 // End: //
Note: See TracChangeset for help on using the changeset viewer.