- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/libcfa/concurrency/preemption.c
rc5ac6d5 r969b3fe 15 15 // 16 16 17 #include "libhdr.h" 17 18 #include "preemption.h" 18 19 19 20 extern "C" { 21 #include <errno.h> 22 #include <execinfo.h> 23 #define __USE_GNU 20 24 #include <signal.h> 21 } 22 23 #define __CFA_DEFAULT_PREEMPTION__ 10 24 25 #undef __USE_GNU 26 #include <stdio.h> 27 #include <string.h> 28 #include <unistd.h> 29 } 30 31 32 #ifdef __USE_STREAM__ 33 #include "fstream" 34 #endif 35 36 //TODO move to defaults 37 #define __CFA_DEFAULT_PREEMPTION__ 10000 38 39 //TODO move to defaults 25 40 __attribute__((weak)) unsigned int default_preemption() { 26 41 return __CFA_DEFAULT_PREEMPTION__; 27 42 } 28 43 44 // Short hands for signal context information 45 #define __CFA_SIGCXT__ ucontext_t * 46 #define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt 47 48 // FwdDeclarations : timeout handlers 29 49 static void preempt( processor * this ); 30 50 static void timeout( thread_desc * this ); 31 51 52 // FwdDeclarations : Signal handlers 53 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ); 54 void sigHandler_segv ( __CFA_SIGPARMS__ ); 55 void sigHandler_abort ( __CFA_SIGPARMS__ ); 56 57 // FwdDeclarations : sigaction wrapper 58 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ); 59 60 // FwdDeclarations : alarm thread main 61 void * alarm_loop( __attribute__((unused)) void * args ); 62 63 // Machine specific register name 64 #ifdef __x86_64__ 65 #define CFA_REG_IP REG_RIP 66 #else 67 #define CFA_REG_IP REG_EIP 68 #endif 69 70 KERNEL_STORAGE(event_kernel_t, event_kernel); // private storage for event kernel 71 event_kernel_t * event_kernel; // kernel public handle to even kernel 72 static pthread_t alarm_thread; // pthread handle to alarm thread 73 74 void ?{}(event_kernel_t * this) { 75 (&this->alarms){}; 76 (&this->lock){}; 77 } 78 32 79 //============================================================================================= 33 80 // Kernel Preemption logic 34 81 //============================================================================================= 35 82 36 void kernel_start_preemption() { 37 38 } 39 83 // Get next expired node 84 static 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 40 91 void tick_preemption() { 41 alarm_list_t * alarms = &systemProcessor->alarms; 42 __cfa_time_t currtime = __kernel_get_time(); 43 while( alarms->head && alarms->head->alarm < currtime ) { 44 alarm_node_t * node = pop(alarms); 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 45 100 if( node->kernel_alarm ) { 46 101 preempt( node->proc ); … … 50 105 } 51 106 52 if( node->period > 0 ) { 53 node->alarm += node->period; 54 insert( alarms, node ); 107 // Check if this is a periodic alarm 108 __cfa_time_t period = node->period; 109 if( period > 0 ) { 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 55 112 } 56 113 else { 57 node->set = false; 58 } 59 } 60 61 if( alarms->head ) {62 __kernel_set_timer( alarms->head->alarm - currtime );63 64 } 65 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 66 123 void update_preemption( processor * this, __cfa_time_t duration ) { 67 // assert( THREAD_GETMEM( disableInt ) && THREAD_GETMEM( disableIntCnt ) == 1 );68 124 alarm_node_t * alarm = this->preemption_alarm; 69 125 … … 89 145 } 90 146 147 //============================================================================================= 148 // Kernel Signal Tools 149 //============================================================================================= 150 151 LIB_DEBUG_DO( static thread_local void * last_interrupt = 0; ) 152 153 extern "C" { 154 // Disable interrupts by incrementing the counter 155 void disable_interrupts() { 156 __attribute__((unused)) unsigned short new_val = __atomic_add_fetch_2( &disable_preempt_count, 1, __ATOMIC_SEQ_CST ); 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 162 void enable_interrupts( DEBUG_CTX_PARAM ) { 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 166 unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST ); 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 170 if( prev == 1 && proc->pending_preemption ) { 171 proc->pending_preemption = false; 172 BlockInternal( thrd ); 173 } 174 175 // For debugging purposes : keep track of the last person to enable the interrupts 176 LIB_DEBUG_DO( proc->last_enable = caller; ) 177 } 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 188 static inline void signal_unblock( int sig ) { 189 sigset_t mask; 190 sigemptyset( &mask ); 191 sigaddset( &mask, sig ); 192 193 if ( pthread_sigmask( SIG_UNBLOCK, &mask, NULL ) == -1 ) { 194 abortf( "internal error, pthread_sigmask" ); 195 } 196 } 197 198 // sigprocmask wrapper : block a single signal 199 static inline void signal_block( int sig ) { 200 sigset_t mask; 201 sigemptyset( &mask ); 202 sigaddset( &mask, sig ); 203 204 if ( pthread_sigmask( SIG_BLOCK, &mask, NULL ) == -1 ) { 205 abortf( "internal error, pthread_sigmask" ); 206 } 207 } 208 209 // kill wrapper : signal a processor 210 static void preempt( processor * this ) { 211 pthread_kill( this->kernel_thread, SIGUSR1 ); 212 } 213 214 // reserved for future use 215 static void timeout( thread_desc * this ) { 216 //TODO : implement waking threads 217 } 218 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 223 static 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 229 //============================================================================================= 230 // Kernel Signal Startup/Shutdown logic 231 //============================================================================================= 232 233 // Startup routine to activate preemption 234 // Called from kernel_startup 235 void kernel_start_preemption() { 236 LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n"); 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 249 250 signal_block( SIGALRM ); 251 252 pthread_create( &alarm_thread, NULL, alarm_loop, NULL ); 253 } 254 255 // Shutdown routine to deactivate preemption 256 // Called from kernel_shutdown 257 void kernel_stop_preemption() { 258 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n"); 259 260 // Block all signals since we are already shutting down 261 sigset_t mask; 262 sigfillset( &mask ); 263 sigprocmask( SIG_BLOCK, &mask, NULL ); 264 265 // Notify the alarm thread of the shutdown 266 sigval val = { 1 }; 267 pthread_sigqueue( alarm_thread, SIGALRM, val ); 268 269 // Wait for the preemption thread to finish 270 pthread_join( alarm_thread, NULL ); 271 272 // Preemption is now fully stopped 273 274 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n"); 275 } 276 277 // Raii ctor/dtor for the preemption_scope 278 // Used by thread to control when they want to receive preemption signals 91 279 void ?{}( preemption_scope * this, processor * proc ) { 92 (&this->alarm){ proc };280 (&this->alarm){ proc, zero_time, zero_time }; 93 281 this->proc = proc; 94 282 this->proc->preemption_alarm = &this->alarm; 95 update_preemption( this->proc, this->proc->preemption ); 283 284 update_preemption( this->proc, from_us(this->proc->cltr->preemption) ); 96 285 } 97 286 98 287 void ^?{}( preemption_scope * this ) { 99 update_preemption( this->proc, 0 ); 100 } 101 102 //============================================================================================= 103 // Kernel Signal logic 104 //============================================================================================= 105 106 static inline bool preemption_ready() { 107 return this_processor->disable_preempt_count == 0; 108 } 109 110 static inline void defer_ctxSwitch() { 111 this_processor->pending_preemption = true; 112 } 113 114 static inline void defer_alarm() { 115 systemProcessor->pending_alarm = true; 116 } 117 118 void sigHandler_ctxSwitch( __attribute__((unused)) int sig ) { 119 if( preemption_ready() ) { 120 ScheduleInternal( this_processor->current_thread ); 121 } 122 else { 123 defer_ctxSwitch(); 124 } 125 } 126 127 void sigHandler_alarm( __attribute__((unused)) int sig ) { 128 if( try_lock( &systemProcessor->alarm_lock ) ) { 129 tick_preemption(); 130 unlock( &systemProcessor->alarm_lock ); 131 } 132 else { 133 defer_alarm(); 134 } 135 } 136 137 static void preempt( processor * this ) { 138 pthread_kill( this->kernel_thread, SIGUSR1 ); 139 } 140 141 static void timeout( thread_desc * this ) { 142 //TODO : implement waking threads 143 } 288 disable_interrupts(); 289 290 update_preemption( this->proc, zero_time ); 291 } 292 293 //============================================================================================= 294 // Kernel Signal Handlers 295 //============================================================================================= 296 297 // Context switch signal handler 298 // Receives SIGUSR1 signal and causes the current thread to yield 299 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) { 300 LIB_DEBUG_DO( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); ) 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 316 void * alarm_loop( __attribute__((unused)) void * args ) { 317 // Block sigalrms to control when they arrive 318 sigset_t mask; 319 sigemptyset( &mask ); 320 sigaddset( &mask, SIGALRM ); 321 322 if ( pthread_sigmask( SIG_BLOCK, &mask, NULL ) == -1 ) { 323 abortf( "internal error, pthread_sigmask" ); 324 } 325 326 // Main loop 327 while( true ) { 328 // Wait for a sigalrm 329 siginfo_t info; 330 int sig = sigwaitinfo( &mask, &info ); 331 332 // If another signal arrived something went wrong 333 assertf(sig == SIGALRM, "Kernel Internal Error, sigwait: Unexpected signal %d (%d : %d)\n", sig, info.si_code, info.si_value.sival_int); 334 335 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 337 switch( info.si_code ) 338 { 339 // Timers can apparently be marked as sent for the kernel 340 // In either case, tick preemption 341 case SI_TIMER: 342 case SI_KERNEL: 343 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n"); 344 lock( &event_kernel->lock DEBUG_CTX2 ); 345 tick_preemption(); 346 unlock( &event_kernel->lock ); 347 break; 348 // Signal was not sent by the kernel but by an other thread 349 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 352 goto EXIT; 353 } 354 } 355 356 EXIT: 357 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n"); 358 return NULL; 359 } 360 361 // Sigaction wrapper : register an signal handler 362 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) { 363 struct sigaction act; 364 365 act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler; 366 act.sa_flags = flags; 367 368 if ( sigaction( sig, &act, NULL ) == -1 ) { 369 LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, 370 " __kernel_sigaction( sig:%d, handler:%p, flags:%d ), problem installing signal handler, error(%d) %s.\n", 371 sig, handler, flags, errno, strerror( errno ) 372 ); 373 _exit( EXIT_FAILURE ); 374 } 375 } 376 377 // Sigaction wrapper : restore default handler 378 static void __kernel_sigdefault( int sig ) { 379 struct sigaction act; 380 381 act.sa_handler = SIG_DFL; 382 act.sa_flags = 0; 383 sigemptyset( &act.sa_mask ); 384 385 if ( sigaction( sig, &act, NULL ) == -1 ) { 386 LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, 387 " __kernel_sigdefault( sig:%d ), problem reseting signal handler, error(%d) %s.\n", 388 sig, errno, strerror( errno ) 389 ); 390 _exit( EXIT_FAILURE ); 391 } 392 } 393 394 //============================================================================================= 395 // Terminating Signals logic 396 //============================================================================================= 397 398 LIB_DEBUG_DO( 399 static void __kernel_backtrace( int start ) { 400 // skip first N stack frames 401 402 enum { Frames = 50 }; 403 void * array[Frames]; 404 int size = backtrace( array, Frames ); 405 char ** messages = backtrace_symbols( array, size ); 406 407 // find executable name 408 *index( messages[0], '(' ) = '\0'; 409 #ifdef __USE_STREAM__ 410 serr | "Stack back trace for:" | messages[0] | endl; 411 #else 412 fprintf( stderr, "Stack back trace for: %s\n", messages[0]); 413 #endif 414 415 // skip last 2 stack frames after main 416 for ( int i = start; i < size && messages != NULL; i += 1 ) { 417 char * name = NULL; 418 char * offset_begin = NULL; 419 char * offset_end = NULL; 420 421 for ( char *p = messages[i]; *p; ++p ) { 422 // find parantheses and +offset 423 if ( *p == '(' ) { 424 name = p; 425 } 426 else if ( *p == '+' ) { 427 offset_begin = p; 428 } 429 else if ( *p == ')' ) { 430 offset_end = p; 431 break; 432 } 433 } 434 435 // if line contains symbol print it 436 int frameNo = i - start; 437 if ( name && offset_begin && offset_end && name < offset_begin ) { 438 // delimit strings 439 *name++ = '\0'; 440 *offset_begin++ = '\0'; 441 *offset_end++ = '\0'; 442 443 #ifdef __USE_STREAM__ 444 serr | "(" | frameNo | ")" | messages[i] | ":" 445 | name | "+" | offset_begin | offset_end | endl; 446 #else 447 fprintf( stderr, "(%i) %s : %s + %s %s\n", frameNo, messages[i], name, offset_begin, offset_end); 448 #endif 449 } 450 // otherwise, print the whole line 451 else { 452 #ifdef __USE_STREAM__ 453 serr | "(" | frameNo | ")" | messages[i] | endl; 454 #else 455 fprintf( stderr, "(%i) %s\n", frameNo, messages[i] ); 456 #endif 457 } 458 } 459 460 free( messages ); 461 } 462 ) 463 464 // void sigHandler_segv( __CFA_SIGPARMS__ ) { 465 // LIB_DEBUG_DO( 466 // #ifdef __USE_STREAM__ 467 // serr | "*CFA runtime error* program cfa-cpp terminated with" 468 // | (sig == SIGSEGV ? "segment fault." : "bus error.") 469 // | endl; 470 // #else 471 // fprintf( stderr, "*CFA runtime error* program cfa-cpp terminated with %s\n", sig == SIGSEGV ? "segment fault." : "bus error." ); 472 // #endif 473 474 // // skip first 2 stack frames 475 // __kernel_backtrace( 1 ); 476 // ) 477 // exit( EXIT_FAILURE ); 478 // } 479 480 // void sigHandler_abort( __CFA_SIGPARMS__ ) { 481 // // skip first 6 stack frames 482 // LIB_DEBUG_DO( __kernel_backtrace( 6 ); ) 483 484 // // reset default signal handler 485 // __kernel_sigdefault( SIGABRT ); 486 487 // raise( SIGABRT ); 488 // }
Note:
See TracChangeset
for help on using the changeset viewer.