- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/libcfa/concurrency/preemption.c
r969b3fe rc5ac6d5 15 15 // 16 16 17 #include "libhdr.h"18 17 #include "preemption.h" 19 18 20 19 extern "C" { 21 #include <errno.h>22 #include <execinfo.h>23 #define __USE_GNU24 20 #include <signal.h> 25 #undef __USE_GNU26 #include <stdio.h>27 #include <string.h>28 #include <unistd.h>29 21 } 30 22 23 #define __CFA_DEFAULT_PREEMPTION__ 10 31 24 32 #ifdef __USE_STREAM__33 #include "fstream"34 #endif35 36 //TODO move to defaults37 #define __CFA_DEFAULT_PREEMPTION__ 1000038 39 //TODO move to defaults40 25 __attribute__((weak)) unsigned int default_preemption() { 41 26 return __CFA_DEFAULT_PREEMPTION__; 42 27 } 43 28 44 // Short hands for signal context information45 #define __CFA_SIGCXT__ ucontext_t *46 #define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt47 48 // FwdDeclarations : timeout handlers49 29 static void preempt( processor * this ); 50 30 static void timeout( thread_desc * this ); 51 52 // FwdDeclarations : Signal handlers53 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );54 void sigHandler_segv ( __CFA_SIGPARMS__ );55 void sigHandler_abort ( __CFA_SIGPARMS__ );56 57 // FwdDeclarations : sigaction wrapper58 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );59 60 // FwdDeclarations : alarm thread main61 void * alarm_loop( __attribute__((unused)) void * args );62 63 // Machine specific register name64 #ifdef __x86_64__65 #define CFA_REG_IP REG_RIP66 #else67 #define CFA_REG_IP REG_EIP68 #endif69 70 KERNEL_STORAGE(event_kernel_t, event_kernel); // private storage for event kernel71 event_kernel_t * event_kernel; // kernel public handle to even kernel72 static pthread_t alarm_thread; // pthread handle to alarm thread73 74 void ?{}(event_kernel_t * this) {75 (&this->alarms){};76 (&this->lock){};77 }78 31 79 32 //============================================================================================= … … 81 34 //============================================================================================= 82 35 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 36 void kernel_start_preemption() { 37 88 38 } 89 39 90 // Tick one frame of the Discrete Event Simulation for alarms91 40 void tick_preemption() { 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 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); 100 45 if( node->kernel_alarm ) { 101 46 preempt( node->proc ); … … 105 50 } 106 51 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 52 if( node->period > 0 ) { 53 node->alarm += node->period; 54 insert( alarms, node ); 112 55 } 113 56 else { 114 node->set = false; // Node is one-shot, just mark it as not pending57 node->set = false; 115 58 } 116 59 } 117 60 118 // If there are still alarms pending, reset the timer 119 if( alarms->head ) { __kernel_set_timer( alarms->head->alarm - currtime ); } 61 if( alarms->head ) { 62 __kernel_set_timer( alarms->head->alarm - currtime ); 63 } 120 64 } 121 65 122 // Update the preemption of a processor and notify interested parties123 66 void update_preemption( processor * this, __cfa_time_t duration ) { 67 // assert( THREAD_GETMEM( disableInt ) && THREAD_GETMEM( disableIntCnt ) == 1 ); 124 68 alarm_node_t * alarm = this->preemption_alarm; 125 69 … … 145 89 } 146 90 91 void ?{}( preemption_scope * this, processor * proc ) { 92 (&this->alarm){ proc }; 93 this->proc = proc; 94 this->proc->preemption_alarm = &this->alarm; 95 update_preemption( this->proc, this->proc->preemption ); 96 } 97 98 void ^?{}( preemption_scope * this ) { 99 update_preemption( this->proc, 0 ); 100 } 101 147 102 //============================================================================================= 148 // Kernel Signal Tools103 // Kernel Signal logic 149 104 //============================================================================================= 150 105 151 LIB_DEBUG_DO( static thread_local void * last_interrupt = 0; ) 106 static inline bool preemption_ready() { 107 return this_processor->disable_preempt_count == 0; 108 } 152 109 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 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 ); 158 121 } 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 122 else { 123 defer_ctxSwitch(); 184 124 } 185 125 } 186 126 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" ); 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(); 195 134 } 196 135 } 197 136 198 // sigprocmask wrapper : block a single signal199 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 processor210 137 static void preempt( processor * this ) { 211 138 pthread_kill( this->kernel_thread, SIGUSR1 ); 212 139 } 213 140 214 // reserved for future use215 141 static void timeout( thread_desc * this ) { 216 142 //TODO : implement waking threads 217 143 } 218 219 220 // Check if a CtxSwitch signal handler shoud defer221 // If true : preemption is safe222 // If false : preemption is unsafe and marked as pending223 static inline bool preemption_ready() {224 bool ready = disable_preempt_count == 0 && !preemption_in_progress; // Check if preemption is safe225 this_processor->pending_preemption = !ready; // Adjust the pending flag accordingly226 return ready;227 }228 229 //=============================================================================================230 // Kernel Signal Startup/Shutdown logic231 //=============================================================================================232 233 // Startup routine to activate preemption234 // Called from kernel_startup235 void kernel_start_preemption() {236 LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");237 238 // Start with preemption disabled until ready239 disable_preempt_count = 1;240 241 // Initialize the event kernel242 event_kernel = (event_kernel_t *)&storage_event_kernel;243 event_kernel{};244 245 // Setup proper signal handlers246 __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO ); // CtxSwitch handler247 // __kernel_sigaction( SIGSEGV, sigHandler_segv , SA_SIGINFO ); // Failure handler248 // __kernel_sigaction( SIGBUS , sigHandler_segv , SA_SIGINFO ); // Failure handler249 250 signal_block( SIGALRM );251 252 pthread_create( &alarm_thread, NULL, alarm_loop, NULL );253 }254 255 // Shutdown routine to deactivate preemption256 // Called from kernel_shutdown257 void kernel_stop_preemption() {258 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n");259 260 // Block all signals since we are already shutting down261 sigset_t mask;262 sigfillset( &mask );263 sigprocmask( SIG_BLOCK, &mask, NULL );264 265 // Notify the alarm thread of the shutdown266 sigval val = { 1 };267 pthread_sigqueue( alarm_thread, SIGALRM, val );268 269 // Wait for the preemption thread to finish270 pthread_join( alarm_thread, NULL );271 272 // Preemption is now fully stopped273 274 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");275 }276 277 // Raii ctor/dtor for the preemption_scope278 // Used by thread to control when they want to receive preemption signals279 void ?{}( preemption_scope * this, processor * proc ) {280 (&this->alarm){ proc, zero_time, zero_time };281 this->proc = proc;282 this->proc->preemption_alarm = &this->alarm;283 284 update_preemption( this->proc, from_us(this->proc->cltr->preemption) );285 }286 287 void ^?{}( preemption_scope * this ) {288 disable_interrupts();289 290 update_preemption( this->proc, zero_time );291 }292 293 //=============================================================================================294 // Kernel Signal Handlers295 //=============================================================================================296 297 // Context switch signal handler298 // Receives SIGUSR1 signal and causes the current thread to yield299 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 here303 if( !preemption_ready() ) { return; }304 305 preemption_in_progress = true; // Sync flag : prevent recursive calls to the signal handler306 signal_unblock( SIGUSR1 ); // We are about to CtxSwitch out of the signal handler, let other handlers in307 preemption_in_progress = false; // Clear the in progress flag308 309 // Preemption can occur here310 311 BlockInternal( (thread_desc*)this_thread ); // Do the actual CtxSwitch312 }313 314 // Main of the alarm thread315 // Waits on SIGALRM and send SIGUSR1 to whom ever needs it316 void * alarm_loop( __attribute__((unused)) void * args ) {317 // Block sigalrms to control when they arrive318 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 loop327 while( true ) {328 // Wait for a sigalrm329 siginfo_t info;330 int sig = sigwaitinfo( &mask, &info );331 332 // If another signal arrived something went wrong333 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) to337 switch( info.si_code )338 {339 // Timers can apparently be marked as sent for the kernel340 // In either case, tick preemption341 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 thread349 case SI_QUEUE:350 // For now, other thread only signal the alarm thread to shut it down351 // If this needs to change use info.si_value and handle the case here352 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 handler362 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 handler378 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 logic396 //=============================================================================================397 398 LIB_DEBUG_DO(399 static void __kernel_backtrace( int start ) {400 // skip first N stack frames401 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 name408 *index( messages[0], '(' ) = '\0';409 #ifdef __USE_STREAM__410 serr | "Stack back trace for:" | messages[0] | endl;411 #else412 fprintf( stderr, "Stack back trace for: %s\n", messages[0]);413 #endif414 415 // skip last 2 stack frames after main416 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 +offset423 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 it436 int frameNo = i - start;437 if ( name && offset_begin && offset_end && name < offset_begin ) {438 // delimit strings439 *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 #else447 fprintf( stderr, "(%i) %s : %s + %s %s\n", frameNo, messages[i], name, offset_begin, offset_end);448 #endif449 }450 // otherwise, print the whole line451 else {452 #ifdef __USE_STREAM__453 serr | "(" | frameNo | ")" | messages[i] | endl;454 #else455 fprintf( stderr, "(%i) %s\n", frameNo, messages[i] );456 #endif457 }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 // #else471 // fprintf( stderr, "*CFA runtime error* program cfa-cpp terminated with %s\n", sig == SIGSEGV ? "segment fault." : "bus error." );472 // #endif473 474 // // skip first 2 stack frames475 // __kernel_backtrace( 1 );476 // )477 // exit( EXIT_FAILURE );478 // }479 480 // void sigHandler_abort( __CFA_SIGPARMS__ ) {481 // // skip first 6 stack frames482 // LIB_DEBUG_DO( __kernel_backtrace( 6 ); )483 484 // // reset default signal handler485 // __kernel_sigdefault( SIGABRT );486 487 // raise( SIGABRT );488 // }
Note:
See TracChangeset
for help on using the changeset viewer.