Changeset 9236060 for src/libcfa/concurrency/preemption.c
- Timestamp:
- Aug 14, 2017, 2:03:39 PM (7 years ago)
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/libcfa/concurrency/preemption.c
rfd344aa r9236060 1 // -*- Mode: CFA -*-2 1 // 3 2 // Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo … … 10 9 // Author : Thierry Delisle 11 10 // Created On : Mon Jun 5 14:20:42 2017 12 // Last Modified By : Thierry Delisle13 // Last Modified On : --14 // Update Count : 011 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jul 21 22:36:05 2017 13 // Update Count : 2 15 14 // 16 15 … … 34 33 #endif 35 34 35 //TODO move to defaults 36 36 #define __CFA_DEFAULT_PREEMPTION__ 10000 37 37 38 //TODO move to defaults 38 39 __attribute__((weak)) unsigned int default_preemption() { 39 40 return __CFA_DEFAULT_PREEMPTION__; 40 41 } 41 42 43 // Short hands for signal context information 42 44 #define __CFA_SIGCXT__ ucontext_t * 43 45 #define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt 44 46 47 // FwdDeclarations : timeout handlers 45 48 static void preempt( processor * this ); 46 49 static void timeout( thread_desc * this ); 47 50 51 // FwdDeclarations : Signal handlers 48 52 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ); 49 void sigHandler_alarm ( __CFA_SIGPARMS__ );50 53 void sigHandler_segv ( __CFA_SIGPARMS__ ); 51 54 void sigHandler_abort ( __CFA_SIGPARMS__ ); 52 55 56 // FwdDeclarations : sigaction wrapper 53 57 static 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 60 void * alarm_loop( __attribute__((unused)) void * args ); 61 62 // Machine specific register name 56 63 #ifdef __x86_64__ 57 64 #define CFA_REG_IP REG_RIP … … 60 67 #endif 61 68 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 } 62 77 63 78 //============================================================================================= … … 65 80 //============================================================================================= 66 81 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 67 90 void 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 76 99 if( node->kernel_alarm ) { 77 100 preempt( node->proc ); … … 81 104 } 82 105 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 88 111 } 89 112 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 102 122 void 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 105 123 alarm_node_t * alarm = this->preemption_alarm; 106 duration *= 1000;107 124 108 125 // Alarms need to be enabled … … 134 151 135 152 extern "C" { 153 // Disable interrupts by incrementing the counter 136 154 void disable_interrupts() { 137 155 __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 147 161 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 150 165 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 152 169 if( prev == 1 && proc->pending_preemption ) { 153 170 proc->pending_preemption = false; … … 155 172 } 156 173 174 // For debugging purposes : keep track of the last person to enable the interrupts 157 175 LIB_DEBUG_DO( proc->last_enable = caller; ) 158 176 } 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 161 187 static inline void signal_unblock( int sig ) { 162 188 sigset_t mask; … … 169 195 } 170 196 197 // sigprocmask wrapper : block a single signal 171 198 static inline void signal_block( int sig ) { 172 199 sigset_t mask; … … 179 206 } 180 207 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 193 209 static void preempt( processor * this ) { 194 210 pthread_kill( this->kernel_thread, SIGUSR1 ); 195 211 } 196 212 213 // reserved for future use 197 214 static void timeout( thread_desc * this ) { 198 215 //TODO : implement waking threads 199 216 } 200 217 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 201 228 //============================================================================================= 202 229 // Kernel Signal Startup/Shutdown logic 203 230 //============================================================================================= 204 231 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 208 234 void kernel_start_preemption() { 209 235 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 213 248 214 249 signal_block( SIGALRM ); … … 217 252 } 218 253 254 // Shutdown routine to deactivate preemption 255 // Called from kernel_shutdown 219 256 void kernel_stop_preemption() { 220 257 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n"); 221 258 259 // Block all signals since we are already shutting down 222 260 sigset_t mask; 223 261 sigfillset( &mask ); 224 262 sigprocmask( SIG_BLOCK, &mask, NULL ); 225 263 264 // Notify the alarm thread of the shutdown 226 265 sigval val = { 1 }; 227 266 pthread_sigqueue( alarm_thread, SIGALRM, val ); 267 268 // Wait for the preemption thread to finish 228 269 pthread_join( alarm_thread, NULL ); 270 271 // Preemption is now fully stopped 272 229 273 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n"); 230 274 } 231 275 276 // Raii ctor/dtor for the preemption_scope 277 // Used by thread to control when they want to receive preemption signals 232 278 void ?{}( preemption_scope & this, processor * proc ) { 233 (this.alarm){ proc };279 (this.alarm){ proc, zero_time, zero_time }; 234 280 this.proc = proc; 235 281 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) ); 237 284 } 238 285 … … 240 287 disable_interrupts(); 241 288 242 update_preemption( this.proc, 0);289 update_preemption( this.proc, zero_time ); 243 290 } 244 291 … … 247 294 //============================================================================================= 248 295 296 // Context switch signal handler 297 // Receives SIGUSR1 signal and causes the current thread to yield 249 298 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) { 250 299 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 263 315 void * alarm_loop( __attribute__((unused)) void * args ) { 316 // Block sigalrms to control when they arrive 264 317 sigset_t mask; 265 318 sigemptyset( &mask ); … … 270 323 } 271 324 325 // Main loop 272 326 while( true ) { 327 // Wait for a sigalrm 273 328 siginfo_t info; 274 329 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 ) 279 337 { 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 355 EXIT: 299 356 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n"); 300 357 return NULL; 301 358 } 302 359 360 // Sigaction wrapper : register an signal handler 303 361 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) { 304 362 struct sigaction act; … … 316 374 } 317 375 318 typedef void (*sa_handler_t)(int); 319 376 // Sigaction wrapper : restore default handler 320 377 static void __kernel_sigdefault( int sig ) { 321 378 struct sigaction act; 322 379 323 //act.sa_handler = SIG_DFL;380 act.sa_handler = SIG_DFL; 324 381 act.sa_flags = 0; 325 382 sigemptyset( &act.sa_mask ); … … 429 486 // raise( SIGABRT ); 430 487 // } 488 489 // Local Variables: // 490 // mode: c // 491 // tab-width: 4 // 492 // End: //
Note: See TracChangeset
for help on using the changeset viewer.