Changeset 3febb2d
- Timestamp:
- Nov 5, 2020, 7:25:33 PM (3 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 16ba4a6, 3959595
- Parents:
- 7d651a6 (diff), f4e35326 (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. - Location:
- libcfa/src/concurrency
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/alarm.cfa
r7d651a6 r3febb2d 45 45 //============================================================================================= 46 46 47 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period 47 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period) with( this ) { 48 48 this.thrd = thrd; 49 49 this.alarm = alarm; 50 50 this.period = period; 51 51 set = false; 52 kernel_alarm = false;52 type = User; 53 53 } 54 54 … … 58 58 this.period = period; 59 59 set = false; 60 kernel_alarm = true; 60 type = Kernel; 61 } 62 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ) with( this ) { 63 this.thrd = thrd; 64 this.alarm = alarm; 65 this.period = period; 66 this.callback = callback; 67 set = false; 68 type = Callback; 61 69 } 62 70 -
libcfa/src/concurrency/alarm.hfa
r7d651a6 r3febb2d 39 39 //============================================================================================= 40 40 41 enum alarm_type{ Kernel = 0, User = 1, Callback = 2 }; 42 43 struct alarm_node_t; 44 45 typedef void (*Alarm_Callback)(alarm_node_t & ); 46 41 47 struct alarm_node_t { 42 48 Time alarm; // time when alarm goes off … … 50 56 }; 51 57 58 Alarm_Callback callback; 59 52 60 bool set :1; // whether or not the alarm has be registered 53 bool kernel_alarm :1; // true if this is not a user defined alarm61 enum alarm_type type; // true if this is not a user defined alarm 54 62 }; 55 63 DLISTED_MGD_IMPL_OUT(alarm_node_t) … … 57 65 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period ); 58 66 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ); 67 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ); 59 68 void ^?{}( alarm_node_t & this ); 60 69 -
libcfa/src/concurrency/locks.cfa
r7d651a6 r3febb2d 15 15 this.t = t; 16 16 this.lock = 0p; 17 this.listed = false; 17 18 } 18 19 … … 21 22 this.info = info; 22 23 this.lock = 0p; 24 this.listed = false; 23 25 } 24 26 … … 77 79 if ( owner == kernelTLS.this_thread && !multi_acquisition) { 78 80 fprintf(stderr, "A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock."); // Possibly throw instead 79 81 exit(EXIT_FAILURE); 80 82 } else if ( owner != 0p && owner != kernelTLS.this_thread ) { 81 83 append( blocked_threads, kernelTLS.this_thread ); 82 84 wait_count++; 83 85 unlock( lock ); 84 park( __cfaabi_dbg_ctx);86 park( ); 85 87 } else if ( owner == kernelTLS.this_thread && multi_acquisition ) { 86 88 recursion_count++; … … 111 113 lock( lock __cfaabi_dbg_ctx2 ); 112 114 if ( owner == 0p ){ // no owner implies lock isn't held 113 fprintf( stderr, "There was an attempt to release a lock that isn't held" ); 115 fprintf( stderr, "There was an attempt to release a lock that isn't held" ); 114 116 return; 115 117 } else if ( strict_owner && owner != kernelTLS.this_thread ) { 116 fprintf( stderr, "A thread other than the owner attempted to release an owner lock" ); 118 fprintf( stderr, "A thread other than the owner attempted to release an owner lock" ); 117 119 return; 118 120 } … … 123 125 recursion_count = ( thrd && multi_acquisition ? 1 : 0 ); 124 126 wait_count--; 125 unpark( thrd __cfaabi_dbg_ctx2);127 unpark( thrd ); 126 128 } 127 129 unlock( lock ); … … 150 152 owner = t; 151 153 if ( multi_acquisition ) recursion_count = 1; 152 unpark( t __cfaabi_dbg_ctx2 ); 154 #if !defined( __CFA_NO_STATISTICS__ ) 155 kernelTLS.this_stats = t->curr_cluster->stats; 156 #endif 157 unpark( t ); 153 158 unlock( lock ); 154 159 } … … 158 163 lock( lock __cfaabi_dbg_ctx2 ); 159 164 if ( owner == 0p ){ // no owner implies lock isn't held 160 fprintf( stderr, "A lock that is not held was passed to a synchronization lock" ); 165 fprintf( stderr, "A lock that is not held was passed to a synchronization lock" ); 161 166 } else if ( strict_owner && owner != kernelTLS.this_thread ) { 162 fprintf( stderr, "A thread other than the owner of a lock passed it to a synchronization lock" ); 167 fprintf( stderr, "A thread other than the owner of a lock passed it to a synchronization lock" ); 163 168 } else { 164 169 $thread * thrd = pop_head( blocked_threads ); … … 166 171 recursion_count = ( thrd && multi_acquisition ? 1 : 0 ); 167 172 wait_count--; 168 unpark( thrd __cfaabi_dbg_ctx2);173 unpark( thrd ); 169 174 } 170 175 unlock( lock ); … … 175 180 /////////////////////////////////////////////////////////////////// 176 181 177 // In an ideal world this may not be necessary 178 // Is it possible for nominal inheritance to inherit traits?? 179 // If that occurs we would avoid all this extra code 182 // This is temporary until an inheritance bug is fixed 180 183 181 184 void lock( mutex_lock & this ){ … … 228 231 229 232 /////////////////////////////////////////////////////////////////// 230 //// Synchronization Locks233 //// condition variable 231 234 /////////////////////////////////////////////////////////////////// 232 235 233 236 forall(dtype L | is_blocking_lock(L)) { 234 void ?{}( synchronization_lock(L) & this, bool reacquire_after_signal ){ 237 238 void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) { 239 // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin. 240 lock( cond->lock __cfaabi_dbg_ctx2 ); 241 if ( (*i)->listed ) { // is thread on queue 242 info_thread(L) * copy = *i; 243 remove( cond->blocked_threads, i ); //remove this thread O(1) 244 cond->wait_count--; 245 if( !copy->lock ) { 246 unlock( cond->lock ); 247 #if !defined( __CFA_NO_STATISTICS__ ) 248 kernelTLS.this_stats = copy->t->curr_cluster->stats; 249 #endif 250 unpark( copy->t ); 251 } else { 252 add_(*copy->lock, copy->t); // call lock's add_ 253 } 254 } 255 unlock( cond->lock ); 256 } 257 258 void alarm_node_wrap_cast( alarm_node_t & a ) { 259 timeout_handler( (alarm_node_wrap(L) &)a ); 260 } 261 262 void ?{}( condition_variable(L) & this ){ 235 263 this.lock{}; 236 264 this.blocked_threads{}; 237 265 this.count = 0; 238 this.reacquire_after_signal = reacquire_after_signal;239 }240 241 void ^?{}( synchronization_lock(L) & this ){242 // default243 }244 245 void ?{}( condition_variable(L) & this ){246 ((synchronization_lock(L) &)this){ true };247 266 } 248 267 … … 251 270 } 252 271 253 void ?{}( thread_queue(L) & this ){254 ((synchronization_lock(L) &)this){ false};255 } 256 257 void ^?{}( thread_queue(L) & this ){272 void ?{}( alarm_node_wrap(L) & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ) { 273 this.alarm_node{ thrd, alarm, period, callback }; 274 } 275 276 void ^?{}( alarm_node_wrap(L) & this ) { 258 277 // default 259 278 } 260 279 261 bool notify_one( synchronization_lock(L) & this ) with( this ) {280 bool notify_one( condition_variable(L) & this ) with( this ) { 262 281 lock( lock __cfaabi_dbg_ctx2 ); 263 282 bool ret = !!blocked_threads; 264 283 info_thread(L) * popped = pop_head( blocked_threads ); 284 popped->listed = false; 265 285 if(popped != 0p) { 266 if( reacquire_after_signal ){ 286 count--; 287 if (popped->lock) { 267 288 add_(*popped->lock, popped->t); 268 289 } else { 269 unpark( 270 popped->t __cfaabi_dbg_ctx2 271 ); 290 unpark(popped->t); 272 291 } 273 292 } … … 276 295 } 277 296 278 bool notify_all( synchronization_lock(L) & this ) with(this) {297 bool notify_all( condition_variable(L) & this ) with(this) { 279 298 lock( lock __cfaabi_dbg_ctx2 ); 280 299 bool ret = blocked_threads ? true : false; 281 300 while( blocked_threads ) { 282 301 info_thread(L) * popped = pop_head( blocked_threads ); 302 popped->listed = false; 283 303 if(popped != 0p){ 284 if( reacquire_after_signal ){ 304 count--; 305 if (popped->lock) { 285 306 add_(*popped->lock, popped->t); 286 307 } else { 287 unpark( 288 popped->t __cfaabi_dbg_ctx2 289 ); 308 unpark(popped->t); 290 309 } 291 310 } … … 295 314 } 296 315 297 uintptr_t front( synchronization_lock(L) & this ) with(this) { 298 return (*peek(blocked_threads)).info; 299 } 300 301 bool empty( synchronization_lock(L) & this ) with(this) { 316 uintptr_t front( condition_variable(L) & this ) with(this) { 317 if(!blocked_threads) return NULL; 318 return peek(blocked_threads)->info; 319 } 320 321 bool empty( condition_variable(L) & this ) with(this) { 302 322 return blocked_threads ? false : true; 303 323 } 304 324 305 int counter( synchronization_lock(L) & this ) with(this) {325 int counter( condition_variable(L) & this ) with(this) { 306 326 return count; 307 327 } 308 328 309 void queue_info_thread( synchronization_lock(L) & this, info_thread(L) & i ) with(this) { 310 lock( lock __cfaabi_dbg_ctx2 ); 311 append( blocked_threads, &i ); 312 count++; 313 unlock( lock ); 314 park( __cfaabi_dbg_ctx ); 315 } 316 317 318 void wait( synchronization_lock(L) & this ) with(this) { 319 info_thread( L ) i = { kernelTLS.this_thread }; 320 queue_info_thread( this, i ); 321 } 322 323 void wait( synchronization_lock(L) & this, uintptr_t info ) with(this) { 324 info_thread( L ) i = { kernelTLS.this_thread, info }; 325 queue_info_thread( this, i ); 326 } 327 // I still need to implement the time delay wait routines 328 bool wait( synchronization_lock(L) & this, Duration duration ) with(this) { 329 timeval tv = { time(0) }; 330 Time t = { tv }; 331 return wait( this, t + duration ); 332 } 333 334 bool wait( synchronization_lock(L) & this, uintptr_t info, Duration duration ) with(this) { 335 // TODO: ADD INFO 336 return wait( this, duration ); 337 } 338 339 bool wait( synchronization_lock(L) & this, Time time ) with(this) { 340 return false; //default 341 } 342 343 bool wait( synchronization_lock(L) & this, uintptr_t info, Time time ) with(this) { 344 // TODO: ADD INFO 345 return wait( this, time ); 346 } 347 348 void queue_info_thread_unlock( synchronization_lock(L) & this, L & l, info_thread(L) & i ) with(this) { 329 // helper for wait()'s' without a timeout 330 void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) { 349 331 lock( lock __cfaabi_dbg_ctx2 ); 350 332 append( this.blocked_threads, &i ); 351 333 count++; 352 i.lock = &l; 353 size_t recursion_count = get_recursion_count(l); 354 remove_( l ); 355 unlock( lock ); 356 park( __cfaabi_dbg_ctx ); // blocks here 357 358 set_recursion_count(l, recursion_count); // resets recursion count here after waking 359 } 360 361 void wait( synchronization_lock(L) & this, L & l ) with(this) { 334 i.listed = true; 335 size_t recursion_count; 336 if (i.lock) { 337 recursion_count = get_recursion_count(*i.lock); 338 remove_( *i.lock ); 339 } 340 341 unlock( lock ); 342 park( ); // blocks here 343 344 if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking 345 } 346 347 // helper for wait()'s' with a timeout 348 void queue_info_thread_timeout( condition_variable(L) & this, info_thread(L) & info, Time t ) with(this) { 349 lock( lock __cfaabi_dbg_ctx2 ); 350 351 info_thread(L) * queue_ptr = &info; 352 353 alarm_node_wrap(L) node_wrap = { info.t, t, 0`s, alarm_node_wrap_cast }; 354 node_wrap.cond = &this; 355 node_wrap.i = &queue_ptr; 356 357 register_self( &node_wrap.alarm_node ); 358 359 append( blocked_threads, queue_ptr ); 360 info.listed = true; 361 count++; 362 363 size_t recursion_count; 364 if (info.lock) { 365 recursion_count = get_recursion_count(*info.lock); 366 remove_( *info.lock ); 367 } 368 369 unlock( lock ); 370 park(); 371 372 if (info.lock) set_recursion_count(*info.lock, recursion_count); 373 } 374 375 void wait( condition_variable(L) & this ) with(this) { 376 info_thread( L ) i = { kernelTLS.this_thread }; 377 queue_info_thread( this, i ); 378 } 379 380 void wait( condition_variable(L) & this, uintptr_t info ) with(this) { 381 info_thread( L ) i = { kernelTLS.this_thread, info }; 382 queue_info_thread( this, i ); 383 } 384 385 void wait( condition_variable(L) & this, Duration duration ) with(this) { 386 info_thread( L ) i = { kernelTLS.this_thread }; 387 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 388 } 389 390 void wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) { 391 info_thread( L ) i = { kernelTLS.this_thread, info }; 392 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 393 } 394 395 void wait( condition_variable(L) & this, Time time ) with(this) { 396 info_thread( L ) i = { kernelTLS.this_thread }; 397 queue_info_thread_timeout(this, i, time); 398 } 399 400 void wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) { 401 info_thread( L ) i = { kernelTLS.this_thread, info }; 402 queue_info_thread_timeout(this, i, time); 403 } 404 405 void wait( condition_variable(L) & this, L & l ) with(this) { 362 406 info_thread(L) i = { kernelTLS.this_thread }; 363 queue_info_thread_unlock( this, l, i ); 364 } 365 366 void wait( synchronization_lock(L) & this, L & l, uintptr_t info ) with(this) { 407 i.lock = &l; 408 queue_info_thread( this, i ); 409 } 410 411 void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) { 367 412 info_thread(L) i = { kernelTLS.this_thread, info }; 368 queue_info_thread_unlock( this, l, i ); 369 } 370 371 bool wait( synchronization_lock(L) & this, L & l, Duration duration ) with(this) { 372 timeval tv = { time(0) }; 373 Time t = { tv }; 374 return wait( this, l, t + duration ); 375 } 376 377 bool wait( synchronization_lock(L) & this, L & l, uintptr_t info, Duration duration ) with(this) { 378 // TODO: ADD INFO 379 return wait( this, l, duration ); 380 } 381 382 bool wait( synchronization_lock(L) & this, L & l, Time time ) with(this) { 383 return false; //default 384 } 385 386 bool wait( synchronization_lock(L) & this, L & l, uintptr_t info, Time time ) with(this) { 387 // TODO: ADD INFO 388 return wait( this, l, time ); 389 } 390 } 391 392 /////////////////////////////////////////////////////////////////// 393 //// condition lock alternative approach 394 /////////////////////////////////////////////////////////////////// 395 396 // the solution below is less efficient but does not require the lock to have a specific add/remove routine 397 398 /////////////////////////////////////////////////////////////////// 399 //// is_simple_lock 400 /////////////////////////////////////////////////////////////////// 401 402 forall(dtype L | is_simple_lock(L)) { 403 void ?{}( condition_lock(L) & this ){ 404 // default 405 } 406 407 void ^?{}( condition_lock(L) & this ){ 408 // default 409 } 410 411 bool notify_one( condition_lock(L) & this ) with(this) { 412 return notify_one( c_var ); 413 } 414 415 bool notify_all( condition_lock(L) & this ) with(this) { 416 return notify_all( c_var ); 417 } 418 419 void wait( condition_lock(L) & this, L & l ) with(this) { 420 lock( m_lock ); 421 size_t recursion = get_recursion_count( l ); 422 unlock( l ); 423 wait( c_var, m_lock ); 424 lock( l ); 425 set_recursion_count( l , recursion ); 426 unlock( m_lock ); 427 } 428 } 413 i.lock = &l; 414 queue_info_thread( this, i ); 415 } 416 417 void wait( condition_variable(L) & this, L & l, Duration duration ) with(this) { 418 info_thread(L) i = { kernelTLS.this_thread }; 419 i.lock = &l; 420 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 421 } 422 423 void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) { 424 info_thread(L) i = { kernelTLS.this_thread, info }; 425 i.lock = &l; 426 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 427 } 428 429 void wait( condition_variable(L) & this, L & l, Time time ) with(this) { 430 info_thread(L) i = { kernelTLS.this_thread }; 431 i.lock = &l; 432 queue_info_thread_timeout(this, i, time ); 433 } 434 435 void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) { 436 info_thread(L) i = { kernelTLS.this_thread, info }; 437 i.lock = &l; 438 queue_info_thread_timeout(this, i, time ); 439 } 440 } -
libcfa/src/concurrency/locks.hfa
r7d651a6 r3febb2d 1 #pragma once 2 1 3 #include <stdbool.h> 2 4 … … 10 12 #include "time.hfa" 11 13 #include <sys/time.h> 14 #include "alarm.hfa" 12 15 13 16 /////////////////////////////////////////////////////////////////// … … 32 35 info_thread(L) * next; 33 36 L * lock; 37 bool listed; // true if info_thread is on queue, false otherwise; 34 38 }; 35 39 … … 119 123 /////////////////////////////////////////////////////////////////// 120 124 forall(dtype L | is_blocking_lock(L)) { 121 struct synchronization_lock{125 struct condition_variable { 122 126 // Spin lock used for mutual exclusion 123 127 __spinlock_t lock; … … 128 132 // Count of current blocked threads 129 133 int count; 130 131 // If true threads will reacquire the lock they block on upon waking132 bool reacquire_after_signal;133 134 }; 134 135 struct condition_variable {136 inline synchronization_lock(L);137 };138 139 struct thread_queue {140 inline synchronization_lock(L);141 };142 143 144 void ?{}( synchronization_lock(L) & this, bool multi_acquisition, bool strict_owner );145 void ^?{}( synchronization_lock(L) & this );146 135 147 136 void ?{}( condition_variable(L) & this ); 148 137 void ^?{}( condition_variable(L) & this ); 149 138 150 void ?{}( thread_queue(L) & this );151 void ^?{}( thread_queue(L) & this );139 struct alarm_node_wrap { 140 alarm_node_t alarm_node; 152 141 153 bool notify_one( synchronization_lock(L) & this ); 154 bool notify_all( synchronization_lock(L) & this ); 142 condition_variable(L) * cond; 155 143 156 uintptr_t front( synchronization_lock(L) & this ); 157 158 bool empty( synchronization_lock(L) & this ); 159 int counter( synchronization_lock(L) & this ); 160 161 // wait functions that are not passed a mutex lock 162 void wait( synchronization_lock(L) & this ); 163 void wait( synchronization_lock(L) & this, uintptr_t info ); 164 bool wait( synchronization_lock(L) & this, Duration duration ); 165 bool wait( synchronization_lock(L) & this, uintptr_t info, Duration duration ); 166 bool wait( synchronization_lock(L) & this, Time time ); 167 bool wait( synchronization_lock(L) & this, uintptr_t info, Time time ); 168 169 // wait functions that are passed a lock 170 bool notify_one( synchronization_lock(L) & this, L & l ); 171 bool notify_all( synchronization_lock(L) & this, L & l ); 172 173 void wait( synchronization_lock(L) & this, L & l ); 174 void wait( synchronization_lock(L) & this, L & l, uintptr_t info ); 175 bool wait( synchronization_lock(L) & this, L & l, Duration duration ); 176 bool wait( synchronization_lock(L) & this, L & l, uintptr_t info, Duration duration ); 177 bool wait( synchronization_lock(L) & this, L & l, Time time ); 178 bool wait( synchronization_lock(L) & this, L & l, uintptr_t info, Time time ); 179 } 180 181 /////////////////////////////////////////////////////////////////// 182 //// condition lock alternative approach 183 /////////////////////////////////////////////////////////////////// 184 185 186 /////////////////////////////////////////////////////////////////// 187 //// is_simple_lock 188 /////////////////////////////////////////////////////////////////// 189 190 trait is_simple_lock(dtype L | sized(L)) { 191 void lock( L & ); // For synchronization locks to use when acquiring 192 void unlock( L & ); // For synchronization locks to use when releasing 193 size_t get_recursion_count( L & ); // to get recursion count for cond lock to reset after waking 194 void set_recursion_count( L &, size_t recursion ); // to set recursion count after getting signalled; 195 }; 196 197 forall(dtype L | is_simple_lock(L)) { 198 struct condition_lock { 199 // Spin lock used for mutual exclusion 200 mutex_lock m_lock; 201 202 condition_variable( mutex_lock ) c_var; 144 info_thread(L) ** i; 203 145 }; 204 146 205 void ?{}( condition_lock(L) & this);206 void ^?{}( condition_lock(L) & this );147 void ?{}( alarm_node_wrap(L) & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ); 148 void ^?{}( alarm_node_wrap(L) & this ); 207 149 208 bool notify_one( condition_lock(L) & this ); 209 bool notify_all( condition_lock(L) & this ); 210 void wait( condition_lock(L) & this, L & l ); 150 void alarm_node_callback( alarm_node_wrap(L) & this ); 151 152 void alarm_node_wrap_cast( alarm_node_t & a ); 153 154 bool notify_one( condition_variable(L) & this ); 155 bool notify_all( condition_variable(L) & this ); 156 157 uintptr_t front( condition_variable(L) & this ); 158 159 bool empty( condition_variable(L) & this ); 160 int counter( condition_variable(L) & this ); 161 162 // TODO: look into changing timout routines to return bool showing if signalled or woken by kernel 163 void wait( condition_variable(L) & this ); 164 void wait( condition_variable(L) & this, uintptr_t info ); 165 void wait( condition_variable(L) & this, Duration duration ); 166 void wait( condition_variable(L) & this, uintptr_t info, Duration duration ); 167 void wait( condition_variable(L) & this, Time time ); 168 void wait( condition_variable(L) & this, uintptr_t info, Time time ); 169 170 void wait( condition_variable(L) & this, L & l ); 171 void wait( condition_variable(L) & this, L & l, uintptr_t info ); 172 void wait( condition_variable(L) & this, L & l, Duration duration ); 173 void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ); 174 void wait( condition_variable(L) & this, L & l, Time time ); 175 void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ); 211 176 } -
libcfa/src/concurrency/preemption.cfa
r7d651a6 r3febb2d 105 105 106 106 // Check if this is a kernel 107 if( node-> kernel_alarm) {107 if( node->type == Kernel ) { 108 108 preempt( node->proc ); 109 109 } 110 else if( node->type == User ) { 111 timeout( node->thrd ); 112 } 110 113 else { 111 timeout( node->thrd);114 node->callback(*node); 112 115 } 113 116
Note: See TracChangeset
for help on using the changeset viewer.