Ignore:
Timestamp:
Jul 20, 2017, 2:05:44 PM (7 years ago)
Author:
Andrew Beach <ajbeach@…>
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:
c72f9fd
Parents:
d49bfa8 (diff), 8b28a52 (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' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

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

    rd49bfa8 r957453d  
    3434#endif
    3535
     36//TODO move to defaults
    3637#define __CFA_DEFAULT_PREEMPTION__ 10000
    3738
     39//TODO move to defaults
    3840__attribute__((weak)) unsigned int default_preemption() {
    3941        return __CFA_DEFAULT_PREEMPTION__;
    4042}
    4143
     44// Short hands for signal context information
    4245#define __CFA_SIGCXT__ ucontext_t *
    4346#define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt
    4447
     48// FwdDeclarations : timeout handlers
    4549static void preempt( processor   * this );
    4650static void timeout( thread_desc * this );
    4751
     52// FwdDeclarations : Signal handlers
    4853void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
    49 void sigHandler_alarm    ( __CFA_SIGPARMS__ );
    5054void sigHandler_segv     ( __CFA_SIGPARMS__ );
    5155void sigHandler_abort    ( __CFA_SIGPARMS__ );
    5256
     57// FwdDeclarations : sigaction wrapper
    5358static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );
    54 LIB_DEBUG_DO( bool validate( alarm_list_t * this ); )
    55 
     59
     60// FwdDeclarations : alarm thread main
     61void * alarm_loop( __attribute__((unused)) void * args );
     62
     63// Machine specific register name
    5664#ifdef __x86_64__
    5765#define CFA_REG_IP REG_RIP
     
    6068#endif
    6169
     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}
    6278
    6379//=============================================================================================
     
    6581//=============================================================================================
    6682
     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
    6791void tick_preemption() {
    68         alarm_list_t * alarms = &systemProcessor->alarms;
    69         __cfa_time_t currtime = __kernel_get_time();
    70 
    71         // LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Ticking preemption @ %llu\n", currtime );
    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 
     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
    76100                if( node->kernel_alarm ) {
    77101                        preempt( node->proc );
     
    81105                }
    82106
    83                 verify( validate( alarms ) );
    84 
     107                // Check if this is a periodic alarm
    85108                __cfa_time_t period = node->period;
    86109                if( period > 0 ) {
    87                         node->alarm = currtime + period;
    88                         // LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Reinsert %p @ %llu (%llu + %llu)\n", node, node->alarm, currtime, period );
    89                         insert( alarms, node );
     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
    90112                }
    91113                else {
    92                         node->set = false;
    93                 }
    94         }
    95 
    96         if( alarms->head ) {
    97                 __kernel_set_timer( alarms->head->alarm - currtime );
    98         }
    99 
    100         verify( validate( alarms ) );
    101         // LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ticking preemption done\n" );
    102 }
    103 
     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
    104123void update_preemption( processor * this, __cfa_time_t duration ) {
    105         LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Processor : %p updating preemption to %llu\n", this, duration );
    106 
    107124        alarm_node_t * alarm = this->preemption_alarm;
    108         duration *= 1000;
    109125
    110126        // Alarms need to be enabled
     
    136152
    137153extern "C" {
     154        // Disable interrupts by incrementing the counter
    138155        void disable_interrupts() {
    139156                __attribute__((unused)) unsigned short new_val = __atomic_add_fetch_2( &disable_preempt_count, 1, __ATOMIC_SEQ_CST );
    140                 verify( new_val < (unsigned short)65_000 );
    141                 verify( new_val != (unsigned short) 0 );
    142         }
    143 
    144         void enable_interrupts_noRF() {
    145                 __attribute__((unused)) unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
    146                 verify( prev != (unsigned short) 0 );
    147         }
    148 
     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
    149162        void enable_interrupts( DEBUG_CTX_PARAM ) {
    150                 processor * proc   = this_processor;
    151                 thread_desc * thrd = this_thread;
     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
    152166                unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
    153                 verify( prev != (unsigned short) 0 );
     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
    154170                if( prev == 1 && proc->pending_preemption ) {
    155171                        proc->pending_preemption = false;
     
    157173                }
    158174
     175                // For debugging purposes : keep track of the last person to enable the interrupts
    159176                LIB_DEBUG_DO( proc->last_enable = caller; )
    160177        }
    161 }
    162 
     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
    163188static inline void signal_unblock( int sig ) {
    164189        sigset_t mask;
     
    171196}
    172197
     198// sigprocmask wrapper : block a single signal
    173199static inline void signal_block( int sig ) {
    174200        sigset_t mask;
     
    181207}
    182208
    183 static inline bool preemption_ready() {
    184         return disable_preempt_count == 0 && !preemption_in_progress;
    185 }
    186 
    187 static inline void defer_ctxSwitch() {
    188         this_processor->pending_preemption = true;
    189 }
    190 
    191 static inline void defer_alarm() {
    192         systemProcessor->pending_alarm = true;
    193 }
    194 
     209// kill wrapper : signal a processor
    195210static void preempt( processor * this ) {
    196211        pthread_kill( this->kernel_thread, SIGUSR1 );
    197212}
    198213
     214// reserved for future use
    199215static void timeout( thread_desc * this ) {
    200216        //TODO : implement waking threads
    201217}
    202218
     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
    203229//=============================================================================================
    204230// Kernel Signal Startup/Shutdown logic
    205231//=============================================================================================
    206232
    207 static pthread_t alarm_thread;
    208 void * alarm_loop( __attribute__((unused)) void * args );
    209 
     233// Startup routine to activate preemption
     234// Called from kernel_startup
    210235void kernel_start_preemption() {
    211236        LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");
    212         __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO );
    213         // __kernel_sigaction( SIGSEGV, sigHandler_segv     , SA_SIGINFO );
    214         // __kernel_sigaction( SIGBUS , sigHandler_segv     , SA_SIGINFO );
     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
    215249
    216250        signal_block( SIGALRM );
     
    219253}
    220254
     255// Shutdown routine to deactivate preemption
     256// Called from kernel_shutdown
    221257void kernel_stop_preemption() {
    222258        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n");
    223259
     260        // Block all signals since we are already shutting down
    224261        sigset_t mask;
    225262        sigfillset( &mask );
    226263        sigprocmask( SIG_BLOCK, &mask, NULL );
    227264
     265        // Notify the alarm thread of the shutdown
    228266        sigval val = { 1 };
    229267        pthread_sigqueue( alarm_thread, SIGALRM, val );
     268
     269        // Wait for the preemption thread to finish
    230270        pthread_join( alarm_thread, NULL );
     271
     272        // Preemption is now fully stopped
     273
    231274        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");
    232275}
    233276
     277// Raii ctor/dtor for the preemption_scope
     278// Used by thread to control when they want to receive preemption signals
    234279void ?{}( preemption_scope * this, processor * proc ) {
    235         (&this->alarm){ proc };
     280        (&this->alarm){ proc, zero_time, zero_time };
    236281        this->proc = proc;
    237282        this->proc->preemption_alarm = &this->alarm;
    238         update_preemption( this->proc, this->proc->preemption );
     283
     284        update_preemption( this->proc, from_us(this->proc->cltr->preemption) );
    239285}
    240286
     
    242288        disable_interrupts();
    243289
    244         update_preemption( this->proc, 0 );
     290        update_preemption( this->proc, zero_time );
    245291}
    246292
     
    249295//=============================================================================================
    250296
     297// Context switch signal handler
     298// Receives SIGUSR1 signal and causes the current thread to yield
    251299void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
    252300        LIB_DEBUG_DO( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); )
    253         if( preemption_ready() ) {
    254                 preemption_in_progress = true;
    255                 signal_unblock( SIGUSR1 );
    256                 this_processor->pending_preemption = false;
    257                 preemption_in_progress = false;
    258                 BlockInternal( (thread_desc*)this_thread );
    259         }
    260         else {
    261                 defer_ctxSwitch();
    262         }
    263 }
    264 
     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
    265316void * alarm_loop( __attribute__((unused)) void * args ) {
     317        // Block sigalrms to control when they arrive
    266318        sigset_t mask;
    267319        sigemptyset( &mask );
     
    272324        }
    273325
     326        // Main loop
    274327        while( true ) {
     328                // Wait for a sigalrm
    275329                siginfo_t info;
    276330                int sig = sigwaitinfo( &mask, &info );
     331
     332                // If another signal arrived something went wrong
    277333                assertf(sig == SIGALRM, "Kernel Internal Error, sigwait: Unexpected signal %d (%d : %d)\n", sig, info.si_code, info.si_value.sival_int);
    278334
    279335                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
    280337                switch( info.si_code )
    281338                {
     339                // Timers can apparently be marked as sent for the kernel
     340                // In either case, tick preemption
    282341                case SI_TIMER:
    283342                case SI_KERNEL:
    284343                        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n");
    285                         lock( &systemProcessor->alarm_lock DEBUG_CTX2 );
     344                        lock( &event_kernel->lock DEBUG_CTX2 );
    286345                        tick_preemption();
    287                         unlock( &systemProcessor->alarm_lock );
     346                        unlock( &event_kernel->lock );
    288347                        break;
     348                // Signal was not sent by the kernel but by an other thread
    289349                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
    290352                        goto EXIT;
    291353                }
     
    297359}
    298360
     361// Sigaction wrapper : register an signal handler
    299362static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) {
    300363        struct sigaction act;
     
    312375}
    313376
    314 typedef void (*sa_handler_t)(int);
    315 
     377// Sigaction wrapper : restore default handler
    316378static void __kernel_sigdefault( int sig ) {
    317379        struct sigaction act;
    318380
    319         // act.sa_handler = SIG_DFL;
     381        act.sa_handler = SIG_DFL;
    320382        act.sa_flags = 0;
    321383        sigemptyset( &act.sa_mask );
Note: See TracChangeset for help on using the changeset viewer.