- File:
-
- 1 edited
-
src/libcfa/concurrency/preemption.c (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/libcfa/concurrency/preemption.c
r6b0b624 rd6ff3ff 1 // -*- Mode: CFA -*- 1 2 // 2 3 // Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo … … 9 10 // Author : Thierry Delisle 10 11 // Created On : Mon Jun 5 14:20:42 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Fri Jul 21 22:36:05 201713 // Update Count : 212 // Last Modified By : Thierry Delisle 13 // Last Modified On : -- 14 // Update Count : 0 14 15 // 15 16 … … 33 34 #endif 34 35 35 //TODO move to defaults36 36 #define __CFA_DEFAULT_PREEMPTION__ 10000 37 37 38 //TODO move to defaults39 38 __attribute__((weak)) unsigned int default_preemption() { 40 39 return __CFA_DEFAULT_PREEMPTION__; 41 40 } 42 41 43 // Short hands for signal context information44 42 #define __CFA_SIGCXT__ ucontext_t * 45 43 #define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt 46 44 47 // FwdDeclarations : timeout handlers48 45 static void preempt( processor * this ); 49 46 static void timeout( thread_desc * this ); 50 47 51 // FwdDeclarations : Signal handlers52 48 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ); 49 void sigHandler_alarm ( __CFA_SIGPARMS__ ); 53 50 void sigHandler_segv ( __CFA_SIGPARMS__ ); 54 51 void sigHandler_abort ( __CFA_SIGPARMS__ ); 55 52 56 // FwdDeclarations : sigaction wrapper57 53 static 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 54 LIB_DEBUG_DO( bool validate( alarm_list_t * this ); ) 55 63 56 #ifdef __x86_64__ 64 57 #define CFA_REG_IP REG_RIP … … 67 60 #endif 68 61 69 KERNEL_STORAGE(event_kernel_t, event_kernel); // private storage for event kernel70 event_kernel_t * event_kernel; // kernel public handle to even kernel71 static pthread_t alarm_thread; // pthread handle to alarm thread72 73 void ?{}(event_kernel_t * this) {74 (&this->alarms){};75 (&this->lock){};76 }77 62 78 63 //============================================================================================= … … 80 65 //============================================================================================= 81 66 82 // Get next expired node83 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 null85 if( alarms->head->alarm >= currtime ) return NULL; // If alarms head not expired return null86 return pop(alarms); // Otherwise just pop head87 }88 89 // Tick one frame of the Discrete Event Simulation for alarms90 67 void tick_preemption() { 91 alarm_node_t * node = NULL; // Used in the while loop but cannot be declared in the while condition92 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 expired96 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 99 76 if( node->kernel_alarm ) { 100 77 preempt( node->proc ); … … 104 81 } 105 82 106 // Check if this is a periodic alarm107 __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 triggers83 verify( validate( alarms ) ); 84 85 if( node->period > 0 ) { 86 node->alarm = currtime + node->period; 87 insert( alarms, node ); 111 88 } 112 89 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 122 102 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 123 105 alarm_node_t * alarm = this->preemption_alarm; 106 duration *= 1000; 124 107 125 108 // Alarms need to be enabled … … 151 134 152 135 extern "C" { 153 // Disable interrupts by incrementing the counter154 136 void disable_interrupts() { 155 137 __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 161 147 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; 165 150 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 ); 169 152 if( prev == 1 && proc->pending_preemption ) { 170 153 proc->pending_preemption = false; … … 172 155 } 173 156 174 // For debugging purposes : keep track of the last person to enable the interrupts175 157 LIB_DEBUG_DO( proc->last_enable = caller; ) 176 158 } 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 187 161 static inline void signal_unblock( int sig ) { 188 162 sigset_t mask; … … 195 169 } 196 170 197 // sigprocmask wrapper : block a single signal198 171 static inline void signal_block( int sig ) { 199 172 sigset_t mask; … … 206 179 } 207 180 208 // kill wrapper : signal a processor 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 209 193 static void preempt( processor * this ) { 210 194 pthread_kill( this->kernel_thread, SIGUSR1 ); 211 195 } 212 196 213 // reserved for future use214 197 static void timeout( thread_desc * this ) { 215 198 //TODO : implement waking threads 216 199 } 217 200 218 219 // Check if a CtxSwitch signal handler shoud defer220 // If true : preemption is safe221 // If false : preemption is unsafe and marked as pending222 static inline bool preemption_ready() {223 bool ready = disable_preempt_count == 0 && !preemption_in_progress; // Check if preemption is safe224 this_processor->pending_preemption = !ready; // Adjust the pending flag accordingly225 return ready;226 }227 228 201 //============================================================================================= 229 202 // Kernel Signal Startup/Shutdown logic 230 203 //============================================================================================= 231 204 232 // Startup routine to activate preemption 233 // Called from kernel_startup 205 static pthread_t alarm_thread; 206 void * alarm_loop( __attribute__((unused)) void * args ); 207 234 208 void kernel_start_preemption() { 235 209 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 ); 248 213 249 214 signal_block( SIGALRM ); … … 252 217 } 253 218 254 // Shutdown routine to deactivate preemption255 // Called from kernel_shutdown256 219 void kernel_stop_preemption() { 257 220 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n"); 258 221 259 // Block all signals since we are already shutting down260 222 sigset_t mask; 261 223 sigfillset( &mask ); 262 224 sigprocmask( SIG_BLOCK, &mask, NULL ); 263 225 264 // Notify the alarm thread of the shutdown265 226 sigval val = { 1 }; 266 227 pthread_sigqueue( alarm_thread, SIGALRM, val ); 267 268 // Wait for the preemption thread to finish269 228 pthread_join( alarm_thread, NULL ); 270 271 // Preemption is now fully stopped272 273 229 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n"); 274 230 } 275 231 276 // Raii ctor/dtor for the preemption_scope277 // Used by thread to control when they want to receive preemption signals278 232 void ?{}( preemption_scope * this, processor * proc ) { 279 (&this->alarm){ proc , zero_time, zero_time};233 (&this->alarm){ proc }; 280 234 this->proc = proc; 281 235 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 ); 284 237 } 285 238 … … 287 240 disable_interrupts(); 288 241 289 update_preemption( this->proc, zero_time);242 update_preemption( this->proc, 0 ); 290 243 } 291 244 … … 294 247 //============================================================================================= 295 248 296 // Context switch signal handler297 // Receives SIGUSR1 signal and causes the current thread to yield298 249 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) { 299 250 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 315 263 void * alarm_loop( __attribute__((unused)) void * args ) { 316 // Block sigalrms to control when they arrive317 264 sigset_t mask; 318 265 sigemptyset( &mask ); … … 323 270 } 324 271 325 // Main loop326 272 while( true ) { 327 // Wait for a sigalrm328 273 siginfo_t info; 329 274 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 ) 337 279 { 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 356 299 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n"); 357 300 return NULL; 358 301 } 359 302 360 // Sigaction wrapper : register an signal handler361 303 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) { 362 304 struct sigaction act; … … 374 316 } 375 317 376 // Sigaction wrapper : restore default handler 318 typedef void (*sa_handler_t)(int); 319 377 320 static void __kernel_sigdefault( int sig ) { 378 321 struct sigaction act; 379 322 380 act.sa_handler = SIG_DFL;323 // act.sa_handler = SIG_DFL; 381 324 act.sa_flags = 0; 382 325 sigemptyset( &act.sa_mask ); … … 486 429 // raise( SIGABRT ); 487 430 // } 488 489 // Local Variables: //490 // mode: c //491 // tab-width: 4 //492 // End: //
Note:
See TracChangeset
for help on using the changeset viewer.