- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/kernel.cfa
r29cb302 rada0246d 118 118 // Kernel Scheduling logic 119 119 static $thread * __next_thread(cluster * this); 120 static bool __has_next_thread(cluster * this);121 120 static void __run_thread(processor * this, $thread * dst); 121 static $thread * __halt(processor * this); 122 static bool __wake_one(cluster * cltr, bool was_empty); 122 123 static bool __wake_proc(processor *); 123 static bool __wake_one(struct __processor_id_t * id, cluster * cltr);124 static void __halt(processor * this);125 124 126 125 //----------------------------------------------------------------------------- 127 126 // Kernel storage 128 KERNEL_STORAGE(cluster, mainCluster); 129 KERNEL_STORAGE(processor, mainProcessor); 130 KERNEL_STORAGE($thread, mainThread); 131 KERNEL_STORAGE(__stack_t, mainThreadCtx); 132 KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock); 133 #if !defined(__CFA_NO_STATISTICS__) 134 KERNEL_STORAGE(__stats_t, mainProcStats); 135 #endif 136 137 cluster * mainCluster; 138 processor * mainProcessor; 139 $thread * mainThread; 140 __scheduler_RWLock_t * __scheduler_lock; 127 KERNEL_STORAGE(cluster, mainCluster); 128 KERNEL_STORAGE(processor, mainProcessor); 129 KERNEL_STORAGE($thread, mainThread); 130 KERNEL_STORAGE(__stack_t, mainThreadCtx); 131 132 cluster * mainCluster; 133 processor * mainProcessor; 134 $thread * mainThread; 141 135 142 136 extern "C" { … … 150 144 thread_local struct KernelThreadData kernelTLS __attribute__ ((tls_model ( "initial-exec" ))) = { 151 145 NULL, // cannot use 0p 152 NULL,153 146 NULL, 154 147 { 1, false, false }, … … 197 190 198 191 void ?{}( $thread & this, current_stack_info_t * info) with( this ) { 199 ticket = 1;200 192 state = Start; 201 193 self_cor{ info }; … … 205 197 self_mon.recursion = 1; 206 198 self_mon_p = &self_mon; 207 link.next = 0p; 208 link.prev = 0p; 199 next = 0p; 209 200 210 201 node.next = 0p; … … 229 220 static void * __invoke_processor(void * arg); 230 221 231 void ?{}(processor & this, const char name[], cluster & _cltr) with( this ) {222 void ?{}(processor & this, const char name[], cluster & cltr) with( this ) { 232 223 this.name = name; 233 this.cltr = &_cltr; 234 id = -1u; 224 this.cltr = &cltr; 235 225 terminated{ 0 }; 236 226 destroyer = 0p; … … 245 235 246 236 this.stack = __create_pthread( &this.kernel_thread, __invoke_processor, (void *)&this ); 247 __atomic_fetch_add( &cltr->nprocessors, 1u, __ATOMIC_SEQ_CST );248 237 249 238 __cfadbg_print_safe(runtime_core, "Kernel : core %p created\n", &this); … … 265 254 266 255 free( this.stack ); 267 268 __atomic_fetch_sub( &cltr->nprocessors, 1u, __ATOMIC_SEQ_CST );269 256 } 270 257 … … 272 259 this.name = name; 273 260 this.preemption_rate = preemption_rate; 274 this.nprocessors = 0;275 261 ready_queue{}; 262 ready_queue_lock{}; 276 263 277 264 #if !defined(__CFA_NO_STATISTICS__) 278 265 print_stats = false; 279 stats = alloc();280 __init_stats( stats );281 266 #endif 282 267 268 procs{ __get }; 269 idles{ __get }; 283 270 threads{ __get }; 284 271 … … 290 277 void ^?{}(cluster & this) { 291 278 __kernel_io_shutdown( this, &this == mainCluster ); 292 293 #if !defined(__CFA_NO_STATISTICS__)294 if(this.print_stats) {295 __print_stats( this.stats );296 }297 free( this.stats );298 #endif299 279 300 280 unregister(this); … … 315 295 __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this); 316 296 317 // register the processor unless it's the main thread which is handled in the boot sequence 318 if(this != mainProcessor) { 319 this->id = doregister((__processor_id_t*)this); 320 // Lock the RWlock so no-one pushes/pops while we are changing the queue 321 uint_fast32_t last_size = ready_mutate_lock(); 322 323 // Adjust the ready queue size 324 ready_queue_grow( this->cltr ); 325 326 // Unlock the RWlock 327 ready_mutate_unlock( last_size ); 328 } 297 doregister(this->cltr, this); 329 298 330 299 { … … 339 308 readyThread = __next_thread( this->cltr ); 340 309 310 // If no ready thread 311 if( readyThread == 0p ) { 312 // Block until a thread is ready 313 readyThread = __halt(this); 314 } 315 341 316 // Check if we actually found a thread 342 317 if( readyThread ) { 343 318 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 344 319 /* paranoid */ verifyf( readyThread->state == Ready || readyThread->preempted != __NO_PREEMPTION, "state : %d, preempted %d\n", readyThread->state, readyThread->preempted); 345 /* paranoid */ verifyf( readyThread->link.next == 0p, "Expected null got %p", readyThread->link.next ); 346 __builtin_prefetch( readyThread->context.SP ); 320 /* paranoid */ verifyf( readyThread->next == 0p, "Expected null got %p", readyThread->next ); 347 321 348 322 // We found a thread run it … … 351 325 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 352 326 } 353 else {354 // Block until a thread is ready355 __halt(this);356 }357 327 } 358 328 … … 360 330 } 361 331 332 unregister(this->cltr, this); 333 362 334 V( this->terminated ); 363 335 364 // unregister the processor unless it's the main thread which is handled in the boot sequence365 if(this != mainProcessor) {366 // Lock the RWlock so no-one pushes/pops while we are changing the queue367 uint_fast32_t last_size = ready_mutate_lock();368 369 // Adjust the ready queue size370 ready_queue_shrink( this->cltr );371 372 // Make sure we aren't on the idle queue373 #if !defined(__CFA_NO_STATISTICS__)374 bool removed =375 #endif376 unsafe_remove( this->cltr->idles, this );377 378 #if !defined(__CFA_NO_STATISTICS__)379 if(removed) __tls_stats()->ready.sleep.exits++;380 #endif381 382 // Unlock the RWlock383 ready_mutate_unlock( last_size );384 385 // Finally we don't need the read_lock any more386 unregister((__processor_id_t*)this);387 }388 else {389 // HACK : the coroutine context switch expects this_thread to be set390 // and it make sense for it to be set in all other cases except here391 // fake it392 kernelTLS.this_thread = mainThread;393 }394 395 336 __cfadbg_print_safe(runtime_core, "Kernel : core %p terminated\n", this); 337 338 // HACK : the coroutine context switch expects this_thread to be set 339 // and it make sense for it to be set in all other cases except here 340 // fake it 341 if( this == mainProcessor ) kernelTLS.this_thread = mainThread; 396 342 } 397 343 … … 414 360 // Actually run the thread 415 361 RUNNING: while(true) { 416 thrd_dst->preempted = __NO_PREEMPTION; 417 thrd_dst->state = Active; 362 if(unlikely(thrd_dst->preempted)) { 363 thrd_dst->preempted = __NO_PREEMPTION; 364 verify(thrd_dst->state == Active || thrd_dst->state == Rerun); 365 } else { 366 verify(thrd_dst->state == Blocked || thrd_dst->state == Ready); // Ready means scheduled normally, blocked means rerun 367 thrd_dst->state = Active; 368 } 418 369 419 370 __cfaabi_dbg_debug_do( … … 447 398 if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) { 448 399 // The thread was preempted, reschedule it and reset the flag 449 __schedule_thread( (__processor_id_t*)this,thrd_dst );400 __schedule_thread( thrd_dst ); 450 401 break RUNNING; 451 402 } 452 403 453 if(unlikely(thrd_dst->state == Halted)) {454 // The thread has halted, it should never be scheduled/run again455 // We may need to wake someone up here since456 unpark( this->destroyer __cfaabi_dbg_ctx2 );457 this->destroyer = 0p;458 break RUNNING;459 }460 461 /* paranoid */ verify( thrd_dst->state == Active );462 thrd_dst->state = Blocked;463 464 404 // set state of processor coroutine to active and the thread to inactive 465 int old_ticket = __atomic_fetch_sub(&thrd_dst->ticket, 1, __ATOMIC_SEQ_CST); 466 __cfaabi_dbg_debug_do( thrd_dst->park_result = old_ticket; ) 467 switch(old_ticket) { 468 case 1: 405 static_assert(sizeof(thrd_dst->state) == sizeof(int)); 406 enum coroutine_state old_state = __atomic_exchange_n(&thrd_dst->state, Blocked, __ATOMIC_SEQ_CST); 407 __cfaabi_dbg_debug_do( thrd_dst->park_result = old_state; ) 408 switch(old_state) { 409 case Halted: 410 // The thread has halted, it should never be scheduled/run again, leave it back to Halted and move on 411 thrd_dst->state = Halted; 412 413 // We may need to wake someone up here since 414 unpark( this->destroyer __cfaabi_dbg_ctx2 ); 415 this->destroyer = 0p; 416 break RUNNING; 417 case Active: 469 418 // This is case 1, the regular case, nothing more is needed 470 419 break RUNNING; 471 case 2:420 case Rerun: 472 421 // This is case 2, the racy case, someone tried to run this thread before it finished blocking 473 422 // In this case, just run it again. … … 475 424 default: 476 425 // This makes no sense, something is wrong abort 477 abort( );426 abort("Finished running a thread that was Blocked/Start/Primed %d\n", old_state); 478 427 } 479 428 } … … 489 438 $coroutine * proc_cor = get_coroutine(kernelTLS.this_processor->runner); 490 439 $thread * thrd_src = kernelTLS.this_thread; 491 492 #if !defined(__CFA_NO_STATISTICS__)493 struct processor * last_proc = kernelTLS.this_processor;494 #endif495 440 496 441 // Run the thread on this processor … … 508 453 } 509 454 510 #if !defined(__CFA_NO_STATISTICS__)511 if(last_proc != kernelTLS.this_processor) {512 __tls_stats()->ready.threads.migration++;513 }514 #endif515 516 455 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 517 456 /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) < ((uintptr_t)__get_stack(thrd_src->curr_cor)->base ), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too small.\n", thrd_src ); … … 524 463 // It effectively constructs a coroutine by stealing the pthread stack 525 464 static void * __invoke_processor(void * arg) { 526 #if !defined( __CFA_NO_STATISTICS__ )527 __stats_t local_stats;528 __init_stats( &local_stats );529 kernelTLS.this_stats = &local_stats;530 #endif531 532 465 processor * proc = (processor *) arg; 533 466 kernelTLS.this_processor = proc; … … 561 494 __cfadbg_print_safe(runtime_core, "Kernel : core %p main ended (%p)\n", proc, &proc->runner); 562 495 563 #if !defined(__CFA_NO_STATISTICS__)564 __tally_stats(proc->cltr->stats, &local_stats);565 #endif566 567 496 return 0p; 568 497 } … … 662 591 // Scheduler routines 663 592 // KERNEL ONLY 664 void __schedule_thread( struct __processor_id_t * id, $thread * thrd ) { 665 /* paranoid */ verify( thrd ); 666 /* paranoid */ verify( thrd->state != Halted ); 593 void __schedule_thread( $thread * thrd ) with( *thrd->curr_cluster ) { 667 594 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 668 595 /* paranoid */ #if defined( __CFA_WITH_VERIFY__ ) 669 /* paranoid */ 670 671 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active,672 596 /* paranoid */ if( thrd->state == Blocked || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION, 597 "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted ); 598 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active || thrd->state == Rerun, 599 "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted ); 673 600 /* paranoid */ #endif 674 /* paranoid */ verifyf( thrd-> link.next == 0p, "Expected null got %p", thrd->link.next );601 /* paranoid */ verifyf( thrd->next == 0p, "Expected null got %p", thrd->next ); 675 602 676 603 if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready; 677 604 678 ready_schedule_lock ( id ); 679 push( thrd->curr_cluster, thrd ); 680 681 #if !defined(__CFA_NO_STATISTICS__) 682 bool woke = 683 #endif 684 __wake_one(id, thrd->curr_cluster); 685 686 #if !defined(__CFA_NO_STATISTICS__) 687 if(woke) __tls_stats()->ready.sleep.wakes++; 688 #endif 689 ready_schedule_unlock( id ); 605 lock ( ready_queue_lock __cfaabi_dbg_ctx2 ); 606 bool was_empty = !(ready_queue != 0); 607 append( ready_queue, thrd ); 608 unlock( ready_queue_lock ); 609 610 __wake_one(thrd->curr_cluster, was_empty); 690 611 691 612 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); … … 696 617 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 697 618 698 ready_schedule_lock ( (__processor_id_t*)kernelTLS.this_processor);699 $thread * head = pop( this);700 ready_schedule_unlock( (__processor_id_t*)kernelTLS.this_processor);619 lock( ready_queue_lock __cfaabi_dbg_ctx2 ); 620 $thread * head = pop_head( ready_queue ); 621 unlock( ready_queue_lock ); 701 622 702 623 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); … … 704 625 } 705 626 706 // KERNEL ONLY707 static bool __has_next_thread(cluster * this) with( *this ) {708 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );709 710 ready_schedule_lock ( (__processor_id_t*)kernelTLS.this_processor );711 bool not_empty = query( this );712 ready_schedule_unlock( (__processor_id_t*)kernelTLS.this_processor );713 714 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );715 return not_empty;716 }717 718 627 // KERNEL ONLY unpark with out disabling interrupts 719 void __unpark( struct __processor_id_t * id, $thread * thrd __cfaabi_dbg_ctx_param2 ) { 628 void __unpark( $thread * thrd __cfaabi_dbg_ctx_param2 ) { 629 static_assert(sizeof(thrd->state) == sizeof(int)); 630 720 631 // record activity 721 632 __cfaabi_dbg_debug_do( char * old_caller = thrd->unpark_caller; ) 722 633 __cfaabi_dbg_record_thrd( *thrd, false, caller ); 723 634 724 int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST);725 __cfaabi_dbg_debug_do( thrd->unpark_result = old_ ticket; thrd->unpark_state = thrd->state; )726 switch(old_ ticket) {727 case 1:635 enum coroutine_state old_state = __atomic_exchange_n(&thrd->state, Rerun, __ATOMIC_SEQ_CST); 636 __cfaabi_dbg_debug_do( thrd->unpark_result = old_state; ) 637 switch(old_state) { 638 case Active: 728 639 // Wake won the race, the thread will reschedule/rerun itself 729 640 break; 730 case 0:641 case Blocked: 731 642 /* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION ); 732 /* paranoid */ verify( thrd->state == Blocked );733 643 734 644 // Wake lost the race, 735 __schedule_thread( id, thrd ); 645 thrd->state = Blocked; 646 __schedule_thread( thrd ); 736 647 break; 648 case Rerun: 649 abort("More than one thread attempted to schedule thread %p\n", thrd); 650 break; 651 case Halted: 652 case Start: 653 case Primed: 737 654 default: 738 655 // This makes no sense, something is wrong abort … … 745 662 746 663 disable_interrupts(); 747 __unpark( (__processor_id_t*)kernelTLS.this_processor,thrd __cfaabi_dbg_ctx_fwd2 );664 __unpark( thrd __cfaabi_dbg_ctx_fwd2 ); 748 665 enable_interrupts( __cfaabi_dbg_ctx ); 749 666 } … … 780 697 781 698 $thread * thrd = kernelTLS.this_thread; 782 /* paranoid */ verify(thrd->state == Active );699 /* paranoid */ verify(thrd->state == Active || thrd->state == Rerun); 783 700 784 701 // SKULLDUGGERY: It is possible that we are preempting this thread just before … … 787 704 // If that is the case, abandon the preemption. 788 705 bool preempted = false; 789 if(thrd-> link.next == 0p) {706 if(thrd->next == 0p) { 790 707 preempted = true; 791 708 thrd->preempted = reason; … … 813 730 __cfa_dbg_global_clusters.list{ __get }; 814 731 __cfa_dbg_global_clusters.lock{}; 815 816 // Initialize the global scheduler lock817 __scheduler_lock = (__scheduler_RWLock_t*)&storage___scheduler_lock;818 (*__scheduler_lock){};819 732 820 733 // Initialize the main cluster … … 851 764 pending_preemption = false; 852 765 kernel_thread = pthread_self(); 853 id = -1u;854 766 855 767 runner{ &this }; 856 768 __cfadbg_print_safe(runtime_core, "Kernel : constructed main processor context %p\n", &runner); 857 858 __atomic_fetch_add( &cltr->nprocessors, 1u, __ATOMIC_SEQ_CST );859 769 } 860 770 … … 864 774 (*mainProcessor){}; 865 775 866 mainProcessor->id = doregister( (__processor_id_t*)mainProcessor);867 868 776 //initialize the global state variables 869 777 kernelTLS.this_processor = mainProcessor; 870 778 kernelTLS.this_thread = mainThread; 871 779 872 #if !defined( __CFA_NO_STATISTICS__ )873 kernelTLS.this_stats = (__stats_t *)& storage_mainProcStats;874 __init_stats( kernelTLS.this_stats );875 #endif876 877 780 // Enable preemption 878 781 kernel_start_preemption(); … … 880 783 // Add the main thread to the ready queue 881 784 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread 882 __schedule_thread( (__processor_id_t *)mainProcessor,mainThread);785 __schedule_thread(mainThread); 883 786 884 787 // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX … … 924 827 kernel_stop_preemption(); 925 828 926 unregister((__processor_id_t*)mainProcessor);927 928 829 // Destroy the main processor and its context in reverse order of construction 929 830 // These were manually constructed so we need manually destroy them 930 831 void ^?{}(processor & this) with( this ){ 931 832 /* paranoid */ verify( this.do_terminate == true ); 932 __atomic_fetch_sub( &cltr->nprocessors, 1u, __ATOMIC_SEQ_CST );933 __cfaabi_dbg_print_safe("Kernel : destroyed main processor context %p\n", &runner);934 833 } 935 834 … … 937 836 938 837 // Final step, destroy the main thread since it is no longer needed 939 940 838 // Since we provided a stack to this taxk it will not destroy anything 941 839 /* paranoid */ verify(mainThread->self_cor.stack.storage == (__stack_t*)(((uintptr_t)&storage_mainThreadCtx)| 0x1)); … … 944 842 ^(*mainCluster){}; 945 843 946 ^(*__scheduler_lock){};947 948 844 ^(__cfa_dbg_global_clusters.list){}; 949 845 ^(__cfa_dbg_global_clusters.lock){}; … … 955 851 // Kernel Idle Sleep 956 852 //============================================================================================= 853 static $thread * __halt(processor * this) with( *this ) { 854 if( do_terminate ) return 0p; 855 856 // First, lock the cluster idle 857 lock( cltr->idle_lock __cfaabi_dbg_ctx2 ); 858 859 // Check if we can find a thread 860 if( $thread * found = __next_thread( cltr ) ) { 861 unlock( cltr->idle_lock ); 862 return found; 863 } 864 865 // Move this processor from the active list to the idle list 866 move_to_front(cltr->procs, cltr->idles, *this); 867 868 // Unlock the idle lock so we don't go to sleep with a lock 869 unlock (cltr->idle_lock); 870 871 // We are ready to sleep 872 __cfadbg_print_safe(runtime_core, "Kernel : Processor %p ready to sleep\n", this); 873 wait( idle ); 874 875 // We have woken up 876 __cfadbg_print_safe(runtime_core, "Kernel : Processor %p woke up and ready to run\n", this); 877 878 // Get ourself off the idle list 879 with( *cltr ) { 880 lock (idle_lock __cfaabi_dbg_ctx2); 881 move_to_front(idles, procs, *this); 882 unlock(idle_lock); 883 } 884 885 // Don't check the ready queue again, we may not be in a position to run a thread 886 return 0p; 887 } 888 957 889 // Wake a thread from the front if there are any 958 static bool __wake_one(struct __processor_id_t * id, cluster * this) { 959 /* paranoid */ verify( ready_schedule_islocked( id ) ); 960 961 // Check if there is a sleeping processor 962 processor * p = pop(this->idles); 963 964 // If no one is sleeping, we are done 965 if( 0p == p ) return false; 966 967 // We found a processor, wake it up 968 post( p->idle ); 969 890 static bool __wake_one(cluster * this, __attribute__((unused)) bool force) { 891 // if we don't want to force check if we know it's false 892 // if( !this->idles.head && !force ) return false; 893 894 // First, lock the cluster idle 895 lock( this->idle_lock __cfaabi_dbg_ctx2 ); 896 897 // Check if there is someone to wake up 898 if( !this->idles.head ) { 899 // Nope unlock and return false 900 unlock( this->idle_lock ); 901 return false; 902 } 903 904 // Wake them up 905 __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this->idles.head); 906 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 907 post( this->idles.head->idle ); 908 909 // Unlock and return true 910 unlock( this->idle_lock ); 970 911 return true; 971 912 } … … 981 922 982 923 return ret; 983 }984 985 static void __halt(processor * this) with( *this ) {986 if( do_terminate ) return;987 988 #if !defined(__CFA_NO_STATISTICS__)989 __tls_stats()->ready.sleep.halts++;990 #endif991 // Push self to queue992 push(cltr->idles, *this);993 994 // Makre sure we don't miss a thread995 if( __has_next_thread(cltr) ) {996 // A thread was posted, make sure a processor is woken up997 struct __processor_id_t *id = (struct __processor_id_t *) this;998 ready_schedule_lock ( id );999 __wake_one( id, cltr );1000 ready_schedule_unlock( id );1001 #if !defined(__CFA_NO_STATISTICS__)1002 __tls_stats()->ready.sleep.cancels++;1003 #endif1004 }1005 1006 wait( idle );1007 924 } 1008 925 … … 1161 1078 cltr->nthreads -= 1; 1162 1079 unlock(cltr->thread_list_lock); 1080 } 1081 1082 void doregister( cluster * cltr, processor * proc ) { 1083 lock (cltr->idle_lock __cfaabi_dbg_ctx2); 1084 cltr->nprocessors += 1; 1085 push_front(cltr->procs, *proc); 1086 unlock (cltr->idle_lock); 1087 } 1088 1089 void unregister( cluster * cltr, processor * proc ) { 1090 lock (cltr->idle_lock __cfaabi_dbg_ctx2); 1091 remove(cltr->procs, *proc ); 1092 cltr->nprocessors -= 1; 1093 unlock(cltr->idle_lock); 1163 1094 } 1164 1095
Note:
See TracChangeset
for help on using the changeset viewer.