Changeset 957453d for src/libcfa/concurrency/preemption.c
- Timestamp:
- Jul 20, 2017, 2:05:44 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:
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/libcfa/concurrency/preemption.c
rd49bfa8 r957453d 34 34 #endif 35 35 36 //TODO move to defaults 36 37 #define __CFA_DEFAULT_PREEMPTION__ 10000 37 38 39 //TODO move to defaults 38 40 __attribute__((weak)) unsigned int default_preemption() { 39 41 return __CFA_DEFAULT_PREEMPTION__; 40 42 } 41 43 44 // Short hands for signal context information 42 45 #define __CFA_SIGCXT__ ucontext_t * 43 46 #define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt 44 47 48 // FwdDeclarations : timeout handlers 45 49 static void preempt( processor * this ); 46 50 static void timeout( thread_desc * this ); 47 51 52 // FwdDeclarations : Signal handlers 48 53 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ); 49 void sigHandler_alarm ( __CFA_SIGPARMS__ );50 54 void sigHandler_segv ( __CFA_SIGPARMS__ ); 51 55 void sigHandler_abort ( __CFA_SIGPARMS__ ); 52 56 57 // FwdDeclarations : sigaction wrapper 53 58 static 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 61 void * alarm_loop( __attribute__((unused)) void * args ); 62 63 // Machine specific register name 56 64 #ifdef __x86_64__ 57 65 #define CFA_REG_IP REG_RIP … … 60 68 #endif 61 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 } 62 78 63 79 //============================================================================================= … … 65 81 //============================================================================================= 66 82 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 67 91 void 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 76 100 if( node->kernel_alarm ) { 77 101 preempt( node->proc ); … … 81 105 } 82 106 83 verify( validate( alarms ) ); 84 107 // Check if this is a periodic alarm 85 108 __cfa_time_t period = node->period; 86 109 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 90 112 } 91 113 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 104 123 void 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 107 124 alarm_node_t * alarm = this->preemption_alarm; 108 duration *= 1000;109 125 110 126 // Alarms need to be enabled … … 136 152 137 153 extern "C" { 154 // Disable interrupts by incrementing the counter 138 155 void disable_interrupts() { 139 156 __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 149 162 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 152 166 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 154 170 if( prev == 1 && proc->pending_preemption ) { 155 171 proc->pending_preemption = false; … … 157 173 } 158 174 175 // For debugging purposes : keep track of the last person to enable the interrupts 159 176 LIB_DEBUG_DO( proc->last_enable = caller; ) 160 177 } 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 163 188 static inline void signal_unblock( int sig ) { 164 189 sigset_t mask; … … 171 196 } 172 197 198 // sigprocmask wrapper : block a single signal 173 199 static inline void signal_block( int sig ) { 174 200 sigset_t mask; … … 181 207 } 182 208 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 195 210 static void preempt( processor * this ) { 196 211 pthread_kill( this->kernel_thread, SIGUSR1 ); 197 212 } 198 213 214 // reserved for future use 199 215 static void timeout( thread_desc * this ) { 200 216 //TODO : implement waking threads 201 217 } 202 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 203 229 //============================================================================================= 204 230 // Kernel Signal Startup/Shutdown logic 205 231 //============================================================================================= 206 232 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 210 235 void kernel_start_preemption() { 211 236 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 215 249 216 250 signal_block( SIGALRM ); … … 219 253 } 220 254 255 // Shutdown routine to deactivate preemption 256 // Called from kernel_shutdown 221 257 void kernel_stop_preemption() { 222 258 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n"); 223 259 260 // Block all signals since we are already shutting down 224 261 sigset_t mask; 225 262 sigfillset( &mask ); 226 263 sigprocmask( SIG_BLOCK, &mask, NULL ); 227 264 265 // Notify the alarm thread of the shutdown 228 266 sigval val = { 1 }; 229 267 pthread_sigqueue( alarm_thread, SIGALRM, val ); 268 269 // Wait for the preemption thread to finish 230 270 pthread_join( alarm_thread, NULL ); 271 272 // Preemption is now fully stopped 273 231 274 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n"); 232 275 } 233 276 277 // Raii ctor/dtor for the preemption_scope 278 // Used by thread to control when they want to receive preemption signals 234 279 void ?{}( preemption_scope * this, processor * proc ) { 235 (&this->alarm){ proc };280 (&this->alarm){ proc, zero_time, zero_time }; 236 281 this->proc = proc; 237 282 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) ); 239 285 } 240 286 … … 242 288 disable_interrupts(); 243 289 244 update_preemption( this->proc, 0);290 update_preemption( this->proc, zero_time ); 245 291 } 246 292 … … 249 295 //============================================================================================= 250 296 297 // Context switch signal handler 298 // Receives SIGUSR1 signal and causes the current thread to yield 251 299 void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) { 252 300 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 265 316 void * alarm_loop( __attribute__((unused)) void * args ) { 317 // Block sigalrms to control when they arrive 266 318 sigset_t mask; 267 319 sigemptyset( &mask ); … … 272 324 } 273 325 326 // Main loop 274 327 while( true ) { 328 // Wait for a sigalrm 275 329 siginfo_t info; 276 330 int sig = sigwaitinfo( &mask, &info ); 331 332 // If another signal arrived something went wrong 277 333 assertf(sig == SIGALRM, "Kernel Internal Error, sigwait: Unexpected signal %d (%d : %d)\n", sig, info.si_code, info.si_value.sival_int); 278 334 279 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 280 337 switch( info.si_code ) 281 338 { 339 // Timers can apparently be marked as sent for the kernel 340 // In either case, tick preemption 282 341 case SI_TIMER: 283 342 case SI_KERNEL: 284 343 LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n"); 285 lock( & systemProcessor->alarm_lock DEBUG_CTX2 );344 lock( &event_kernel->lock DEBUG_CTX2 ); 286 345 tick_preemption(); 287 unlock( & systemProcessor->alarm_lock );346 unlock( &event_kernel->lock ); 288 347 break; 348 // Signal was not sent by the kernel but by an other thread 289 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 290 352 goto EXIT; 291 353 } … … 297 359 } 298 360 361 // Sigaction wrapper : register an signal handler 299 362 static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) { 300 363 struct sigaction act; … … 312 375 } 313 376 314 typedef void (*sa_handler_t)(int); 315 377 // Sigaction wrapper : restore default handler 316 378 static void __kernel_sigdefault( int sig ) { 317 379 struct sigaction act; 318 380 319 //act.sa_handler = SIG_DFL;381 act.sa_handler = SIG_DFL; 320 382 act.sa_flags = 0; 321 383 sigemptyset( &act.sa_mask );
Note: See TracChangeset
for help on using the changeset viewer.