1 /** 2 * The thread module provides support for thread creation and management. 3 * 4 * If AtomicSuspendCount is used for speed reasons all signals are sent together. 5 * When debugging gdb funnels all signals through one single handler, and if 6 * the signals arrive quickly enough they will be coalesced in a single signal, 7 * (discarding the second) thus it is possible to loose signals, which blocks 8 * the program. Thus when debugging it is better to use the slower SuspendOneAtTime 9 * version. 10 * 11 * Copyright: Copyright (C) 2005-2006 Sean Kelly, Fawzi. All rights reserved. 12 * License: BSD style: $(LICENSE) 13 * Authors: Sean Kelly, Fawzi Mohamed 14 */ 15 module tango.core.Thread; 16 17 public import core.thread; 18 import Time = tango.core.Time; 19 20 extern(C) 21 { 22 void thread_yield() 23 { 24 Thread.yield(); 25 } 26 27 void thread_sleep(double period) 28 { 29 Thread.sleep(Time.seconds(period)); 30 } 31 } 32 33 /+ 34 35 import tango.core.sync.Atomic; 36 debug(Thread) 37 import tango.stdc.stdio : printf; 38 39 40 // this should be true for most architectures 41 version = StackGrowsDown; 42 version(OSX){ 43 version=AtomicSuspendCount; 44 } 45 version(linux){ 46 version=AtomicSuspendCount; 47 } 48 49 public 50 { 51 // import tango.core.TimeSpan; 52 } 53 private 54 { 55 import tango.core.Exception; 56 57 extern (C) void _d_monitorenter(Object); 58 extern (C) void _d_monitorexit(Object); 59 60 // 61 // exposed by compiler runtime 62 // 63 extern (C) void* rt_stackBottom(); 64 extern (C) void* rt_stackTop(); 65 66 67 void* getStackBottom() 68 { 69 return rt_stackBottom(); 70 } 71 72 73 void* getStackTop() 74 { 75 version( D_InlineAsm_X86 ) 76 { 77 asm 78 { 79 naked; 80 mov EAX, ESP; 81 ret; 82 } 83 } 84 else 85 { 86 return rt_stackTop(); 87 } 88 } 89 90 version(D_InlineAsm_X86){ 91 uint getEBX(){ 92 uint retVal; 93 asm{ 94 mov retVal,EBX; 95 } 96 return retVal; 97 } 98 } 99 } 100 101 102 //////////////////////////////////////////////////////////////////////////////// 103 // Thread Entry Point and Signal Handlers 104 //////////////////////////////////////////////////////////////////////////////// 105 106 107 version( Win32 ) 108 { 109 private 110 { 111 import tango.stdc.stdint : uintptr_t; // for _beginthreadex decl below 112 import tango.sys.win32.UserGdi; 113 114 enum DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFF; 115 116 // 117 // avoid multiple imports via tango.sys.windows.process 118 // 119 extern (Windows) alias uint function(void*) btex_fptr; 120 extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*); 121 122 123 // 124 // entry point for Windows threads 125 // 126 extern (Windows) uint thread_entryPoint( void* arg ) 127 { 128 Thread obj = cast(Thread) arg; 129 assert( obj ); 130 scope( exit ) Thread.remove( obj ); 131 132 assert( obj.m_curr is &obj.m_main ); 133 obj.m_main.bstack = getStackBottom(); 134 obj.m_main.tstack = obj.m_main.bstack; 135 Thread.add( &obj.m_main ); 136 Thread.setThis( obj ); 137 138 // NOTE: No GC allocations may occur until the stack pointers have 139 // been set and Thread.getThis returns a valid reference to 140 // this thread object (this latter condition is not strictly 141 // necessary on Win32 but it should be followed for the sake 142 // of consistency). 143 144 // TODO: Consider putting an auto exception object here (using 145 // alloca) forOutOfMemoryError plus something to track 146 // whether an exception is in-flight? 147 148 try 149 { 150 obj.run(); 151 } 152 catch( Object o ) 153 { 154 obj.m_unhandled = o; 155 } 156 return 0; 157 } 158 159 160 // 161 // copy of the same-named function in phobos.std.thread--it uses the 162 // Windows naming convention to be consistent with GetCurrentThreadId 163 // 164 HANDLE GetCurrentThreadHandle() 165 { 166 enum uint DUPLICATE_SAME_ACCESS = 0x00000002; 167 168 HANDLE curr = GetCurrentThread(), 169 proc = GetCurrentProcess(), 170 hndl; 171 172 DuplicateHandle( proc, curr, proc, &hndl, 0, TRUE, DUPLICATE_SAME_ACCESS ); 173 return hndl; 174 } 175 } 176 } 177 else version( Posix ) 178 { 179 private 180 { 181 import tango.stdc.posix.semaphore; 182 import tango.stdc.posix.pthread; 183 import tango.stdc.posix.signal; 184 import tango.stdc.posix.time; 185 import tango.stdc.errno; 186 187 extern (C) int getErrno(); 188 189 version( GNU ) 190 { 191 import gcc.builtins; 192 } 193 194 195 // 196 // entry point for POSIX threads 197 // 198 extern (C) void* thread_entryPoint( void* arg ) 199 { 200 Thread obj = cast(Thread) arg; 201 assert( obj ); 202 scope( exit ) 203 { 204 // NOTE: isRunning should be set to false after the thread is 205 // removed or a double-removal could occur between this 206 // function and thread_suspendAll. 207 Thread.remove( obj ); 208 obj.m_isRunning = false; 209 } 210 211 static extern (C) void thread_cleanupHandler( void* arg ) 212 { 213 Thread obj = cast(Thread) arg; 214 assert( obj ); 215 216 // NOTE: If the thread terminated abnormally, just set it as 217 // not running and let thread_suspendAll remove it from 218 // the thread list. This is safer and is consistent 219 // with the Windows thread code. 220 obj.m_isRunning = false; 221 } 222 223 // NOTE: Using void to skip the initialization here relies on 224 // knowledge of how pthread_cleanup is implemented. It may 225 // not be appropriate for all platforms. However, it does 226 // avoid the need to link the pthread module. If any 227 // implementation actually requires default initialization 228 // then pthread_cleanup should be restructured to maintain 229 // the current lack of a link dependency. 230 version( linux ) 231 { 232 pthread_cleanup cleanup = void; 233 cleanup.push( &thread_cleanupHandler, cast(void*) obj ); 234 } 235 else version(OSX) 236 { 237 pthread_cleanup cleanup = void; 238 cleanup.push( &thread_cleanupHandler, cast(void*) obj ); 239 } 240 else version( solaris ) 241 { 242 pthread_cleanup cleanup = void; 243 cleanup.push( &thread_cleanupHandler, cast(void*) obj ); 244 } 245 else 246 { 247 pthread_cleanup_push( &thread_cleanupHandler, cast(void*) obj ); 248 } 249 250 // NOTE: For some reason this does not always work for threads. 251 //obj.m_main.bstack = getStackBottom(); 252 version( D_InlineAsm_X86 ) 253 { 254 static void* getBasePtr() 255 { 256 asm 257 { 258 naked; 259 mov EAX, EBP; 260 ret; 261 } 262 } 263 264 obj.m_main.bstack = getBasePtr(); 265 } 266 else version( StackGrowsDown ) 267 obj.m_main.bstack = &obj + 1; 268 else 269 obj.m_main.bstack = &obj; 270 obj.m_main.tstack = obj.m_main.bstack; 271 assert( obj.m_curr == &obj.m_main ); 272 Thread.add( &obj.m_main ); 273 Thread.setThis( obj ); 274 275 // NOTE: No GC allocations may occur until the stack pointers have 276 // been set and Thread.getThis returns a valid reference to 277 // this thread object (this latter condition is not strictly 278 // necessary on Win32 but it should be followed for the sake 279 // of consistency). 280 281 // TODO: Consider putting an auto exception object here (using 282 // alloca) forOutOfMemoryError plus something to track 283 // whether an exception is in-flight? 284 285 try 286 { 287 obj.run(); 288 } 289 catch( Throwable o ) 290 { 291 obj.m_unhandled = o; 292 } 293 return null; 294 } 295 296 297 // 298 // used to track the number of suspended threads 299 // 300 version(AtomicSuspendCount){ 301 int suspendCount; 302 } else { 303 sem_t suspendCount; 304 } 305 306 307 extern (C) void thread_suspendHandler( int sig ) 308 in 309 { 310 assert( sig == SIGUSR1 ); 311 } 312 body 313 { 314 version( LDC) 315 { 316 version(X86) 317 { 318 uint eax,ecx,edx,ebx,ebp,esi,edi; 319 asm 320 { 321 mov eax[EBP], EAX ; 322 mov ecx[EBP], ECX ; 323 mov edx[EBP], EDX ; 324 mov ebx[EBP], EBX ; 325 mov ebp[EBP], EBP ; 326 mov esi[EBP], ESI ; 327 mov edi[EBP], EDI ; 328 } 329 } 330 else version (X86_64) 331 { 332 ulong rax,rbx,rcx,rdx,rbp,rsi,rdi,rsp,r8,r9,r10,r11,r12,r13,r14,r15; 333 asm 334 { 335 movq rax[RBP], RAX ; 336 movq rbx[RBP], RBX ; 337 movq rcx[RBP], RCX ; 338 movq rdx[RBP], RDX ; 339 movq rbp[RBP], RBP ; 340 movq rsi[RBP], RSI ; 341 movq rdi[RBP], RDI ; 342 movq rsp[RBP], RSP ; 343 movq r8 [RBP], R8 ; 344 movq r9 [RBP], R9 ; 345 movq r10[RBP], R10 ; 346 movq r11[RBP], R11 ; 347 movq r12[RBP], R12 ; 348 movq r13[RBP], R13 ; 349 movq r14[RBP], R14 ; 350 movq r15[RBP], R15 ; 351 } 352 } 353 else 354 { 355 static assert( false, "Architecture not supported." ); 356 } 357 } 358 else version( D_InlineAsm_X86 ) 359 { 360 asm 361 { 362 pushad; 363 } 364 } 365 else version( GNU ) 366 { 367 __builtin_unwind_init(); 368 } 369 else version ( D_InlineAsm_X86_64 ) 370 { 371 asm 372 { 373 // Not sure what goes here, pushad is invalid in 64 bit code 374 push RAX ; 375 push RBX ; 376 push RCX ; 377 push RDX ; 378 push RSI ; 379 push RDI ; 380 push RBP ; 381 push R8 ; 382 push R9 ; 383 push R10 ; 384 push R11 ; 385 push R12 ; 386 push R13 ; 387 push R14 ; 388 push R15 ; 389 push RAX ; // 16 byte align the stack 390 } 391 } 392 else 393 { 394 static assert( false, "Architecture not supported." ); 395 } 396 397 // NOTE: Since registers are being pushed and popped from the stack, 398 // any other stack data used by this function should be gone 399 // before the stack cleanup code is called below. 400 { 401 Thread obj = Thread.getThis(); 402 403 // NOTE: The thread reference returned by getThis is set within 404 // the thread startup code, so it is possible that this 405 // handler may be called before the reference is set. In 406 // this case it is safe to simply suspend and not worry 407 // about the stack pointers as the thread will not have 408 // any references to GC-managed data. 409 if( obj && !obj.m_lock ) 410 { 411 obj.m_curr.tstack = getStackTop(); 412 } 413 414 sigset_t sigres = void; 415 int status; 416 417 status = sigfillset( &sigres ); 418 assert( status == 0 ); 419 420 status = sigdelset( &sigres, SIGUSR2 ); 421 assert( status == 0 ); 422 423 version (AtomicSuspendCount){ 424 auto oldV=flagAdd(suspendCount,1); 425 } else { 426 status = sem_post( &suspendCount ); 427 assert( status == 0 ); 428 } 429 430 // here one could do some work (like scan the current stack in this thread...) 431 432 sigsuspend( &sigres ); 433 434 if( obj && !obj.m_lock ) 435 { 436 obj.m_curr.tstack = obj.m_curr.bstack; 437 } 438 } 439 440 version( LDC) 441 { 442 // nothing to pop 443 } 444 else version( D_InlineAsm_X86 ) 445 { 446 asm 447 { 448 popad; 449 } 450 } 451 else version( GNU ) 452 { 453 // registers will be popped automatically 454 } 455 else version ( D_InlineAsm_X86_64 ) 456 { 457 asm 458 { 459 // Not sure what goes here, popad is invalid in 64 bit code 460 pop RAX ; // 16 byte align the stack 461 pop R15 ; 462 pop R14 ; 463 pop R13 ; 464 pop R12 ; 465 pop R11 ; 466 pop R10 ; 467 pop R9 ; 468 pop R8 ; 469 pop RBP ; 470 pop RDI ; 471 pop RSI ; 472 pop RDX ; 473 pop RCX ; 474 pop RBX ; 475 pop RAX ; 476 } 477 } 478 else 479 { 480 static assert( false, "Architecture not supported." ); 481 } 482 } 483 484 485 extern (C) void thread_resumeHandler( int sig ) 486 in 487 { 488 assert( sig == SIGUSR2 ); 489 } 490 body 491 { 492 int status; 493 version (AtomicSuspendCount){ 494 auto oldV=flagAdd(suspendCount,-1); 495 } else { 496 status = sem_post( &suspendCount ); 497 } 498 assert( status == 0 ); 499 } 500 } 501 502 alias void function(int) sHandler; 503 sHandler _thread_abortHandler=null; 504 505 extern (C) void thread_abortHandler( int sig ){ 506 if (_thread_abortHandler!is null){ 507 _thread_abortHandler(sig); 508 } else { 509 exit(-1); 510 } 511 } 512 513 extern (C) void setthread_abortHandler(sHandler f){ 514 _thread_abortHandler=f; 515 } 516 517 } 518 else 519 { 520 // NOTE: This is the only place threading versions are checked. If a new 521 // version is added, the module code will need to be searched for 522 // places where version-specific code may be required. This can be 523 // easily accomlished by searching for 'Windows' or 'Posix'. 524 static assert( false, "Unknown threading implementation." ); 525 } 526 527 528 //////////////////////////////////////////////////////////////////////////////// 529 // Thread 530 //////////////////////////////////////////////////////////////////////////////// 531 532 533 /** 534 * This class encapsulates all threading functionality for the D 535 * programming language. As thread manipulation is a required facility 536 * for garbage collection, all user threads should derive from this 537 * class, and instances of this class should never be explicitly deleted. 538 * A new thread may be created using either derivation or composition, as 539 * in the following example. 540 * 541 * Example: 542 * ----------------------------------------------------------------------------- 543 * class DerivedThread : Thread 544 * { 545 * this() 546 * { 547 * super( &run ); 548 * } 549 * 550 * private : 551 * void run() 552 * { 553 * printf( "Derived thread running.\n" ); 554 * } 555 * } 556 * 557 * void threadFunc() 558 * { 559 * printf( "Composed thread running.\n" ); 560 * } 561 * 562 * // create instances of each type 563 * Thread derived = new DerivedThread(); 564 * Thread composed = new Thread( &threadFunc ); 565 * 566 * // start both threads 567 * derived.start(); 568 * composed.start(); 569 * ----------------------------------------------------------------------------- 570 */ 571 class Thread 572 { 573 //////////////////////////////////////////////////////////////////////////// 574 // Initialization 575 //////////////////////////////////////////////////////////////////////////// 576 577 578 /** 579 * Initializes a thread object which is associated with a static 580 * D function. 581 * 582 * Params: 583 * fn = The thread function. 584 * sz = The stack size for this thread. 585 * 586 * In: 587 * fn must not be null. 588 */ 589 this( void function() fn, size_t sz = 0 ) 590 in 591 { 592 assert( fn ); 593 } 594 body 595 { 596 m_fn = fn; 597 m_sz = sz; 598 m_call = Call.FN; 599 m_curr = &m_main; 600 } 601 602 603 /** 604 * Initializes a thread object which is associated with a dynamic 605 * D function. 606 * 607 * Params: 608 * dg = The thread function. 609 * sz = The stack size for this thread. 610 * 611 * In: 612 * dg must not be null. 613 */ 614 this( void delegate() dg, size_t sz = 0 ) 615 in 616 { 617 assert( dg ); 618 } 619 body 620 { 621 m_dg = dg; 622 m_sz = sz; 623 m_call = Call.DG; 624 m_curr = &m_main; 625 } 626 627 628 /** 629 * Cleans up any remaining resources used by this object. 630 */ 631 ~this() 632 { 633 if( m_addr == m_addr.init ) 634 { 635 return; 636 } 637 638 version( Win32 ) 639 { 640 m_addr = m_addr.init; 641 CloseHandle( m_hndl ); 642 m_hndl = m_hndl.init; 643 } 644 else version( Posix ) 645 { 646 pthread_detach( m_addr ); 647 m_addr = m_addr.init; 648 } 649 } 650 651 652 //////////////////////////////////////////////////////////////////////////// 653 // General Actions 654 //////////////////////////////////////////////////////////////////////////// 655 656 657 /** 658 * Starts the thread and invokes the function or delegate passed upon 659 * construction. 660 * 661 * In: 662 * This routine may only be called once per thread instance. 663 * 664 * Throws: 665 * ThreadException if the thread fails to start. 666 */ 667 final void start() 668 in 669 { 670 assert( !next && !prev ); 671 } 672 body 673 { 674 version( Win32 ) {} else 675 version( Posix ) 676 { 677 pthread_attr_t attr; 678 679 if( pthread_attr_init( &attr ) ) 680 throw new ThreadException( "Error initializing thread attributes" ); 681 if( m_sz && pthread_attr_setstacksize( &attr, m_sz ) ) 682 throw new ThreadException( "Error initializing thread stack size" ); 683 if( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) ) 684 throw new ThreadException( "Error setting thread joinable" ); 685 } 686 687 // NOTE: This operation needs to be synchronized to avoid a race 688 // condition with the GC. Without this lock, the thread 689 // could start and allocate memory before being added to 690 // the global thread list, preventing it from being scanned 691 // and causing memory to be collected that is still in use. 692 synchronized( slock ) 693 { 694 volatile multiThreadedFlag = true; 695 version( Win32 ) 696 { 697 m_hndl = cast(HANDLE) _beginthreadex( null, m_sz, &thread_entryPoint, cast(void*) this, 0, &m_addr ); 698 if( cast(size_t) m_hndl == 0 ) 699 throw new ThreadException( "Error creating thread" ); 700 } 701 else version( Posix ) 702 { 703 m_isRunning = true; 704 scope( failure ) m_isRunning = false; 705 if( pthread_create( &m_addr, &attr, &thread_entryPoint, cast(void*) this ) != 0 ) 706 throw new ThreadException( "Error creating thread" ); 707 } 708 add( this ); 709 } 710 } 711 712 713 /** 714 * Waits for this thread to complete. If the thread terminated as the 715 * result of an unhandled exception, this exception will be rethrown. 716 * 717 * Params: 718 * rethrow = Rethrow any unhandled exception which may have caused this 719 * thread to terminate. 720 * 721 * Throws: 722 * ThreadException if the operation fails. 723 * Any exception not handled by the joined thread. 724 * 725 * Returns: 726 * Any exception not handled by this thread if rethrow = false, null 727 * otherwise. 728 */ 729 final Object join( bool rethrow = true ) 730 { 731 if(!isRunning()) 732 return null; 733 734 version( Win32 ) 735 { 736 if( WaitForSingleObject( m_hndl, INFINITE ) != WAIT_OBJECT_0 ) 737 throw new ThreadException( "Unable to join thread" ); 738 // NOTE: m_addr must be cleared before m_hndl is closed to avoid 739 // a race condition with isRunning. The operation is labeled 740 // volatile to prevent compiler reordering. 741 volatile m_addr = m_addr.init; 742 CloseHandle( m_hndl ); 743 m_hndl = m_hndl.init; 744 } 745 else version( Posix ) 746 { 747 if( pthread_join( m_addr, null ) != 0 ) 748 throw new ThreadException( "Unable to join thread" ); 749 // NOTE: pthread_join acts as a substitute for pthread_detach, 750 // which is normally called by the dtor. Setting m_addr 751 // to zero ensures that pthread_detach will not be called 752 // on object destruction. 753 volatile m_addr = m_addr.init; 754 } 755 if( m_unhandled ) 756 { 757 if( rethrow ) 758 throw m_unhandled; 759 return m_unhandled; 760 } 761 return null; 762 } 763 764 765 //////////////////////////////////////////////////////////////////////////// 766 // General Properties 767 //////////////////////////////////////////////////////////////////////////// 768 769 770 /** 771 * Gets the user-readable label for this thread. 772 * 773 * Returns: 774 * The name of this thread. 775 */ 776 const final const(char[]) name() 777 { 778 synchronized( this ) 779 { 780 return m_name; 781 } 782 } 783 784 785 /** 786 * Sets the user-readable label for this thread. 787 * 788 * Params: 789 * val = The new name of this thread. 790 */ 791 final void name( const(char[]) val ) 792 { 793 synchronized( this ) 794 { 795 m_name = val.dup; 796 } 797 } 798 799 800 /** 801 * Gets the daemon status for this thread. While the runtime will wait for 802 * all normal threads to complete before tearing down the process, daemon 803 * threads are effectively ignored and thus will not prevent the process 804 * from terminating. In effect, daemon threads will be terminated 805 * automatically by the OS when the process exits. 806 * 807 * Returns: 808 * true if this is a daemon thread. 809 */ 810 const final bool isDaemon() 811 { 812 synchronized( this ) 813 { 814 return m_isDaemon; 815 } 816 } 817 818 819 /** 820 * Sets the daemon status for this thread. While the runtime will wait for 821 * all normal threads to complete before tearing down the process, daemon 822 * threads are effectively ignored and thus will not prevent the process 823 * from terminating. In effect, daemon threads will be terminated 824 * automatically by the OS when the process exits. 825 * 826 * Params: 827 * val = The new daemon status for this thread. 828 */ 829 final void isDaemon( bool val ) 830 { 831 synchronized( this ) 832 { 833 m_isDaemon = val; 834 } 835 } 836 837 838 /** 839 * Tests whether this thread is running. 840 * 841 * Returns: 842 * true if the thread is running, false if not. 843 */ 844 const final bool isRunning() 845 { 846 if( m_addr == m_addr.init ) 847 { 848 return false; 849 } 850 851 version( Win32 ) 852 { 853 uint ecode = 0; 854 GetExitCodeThread( m_hndl, &ecode ); 855 return ecode == STILL_ACTIVE; 856 } 857 else version( Posix ) 858 { 859 // NOTE: It should be safe to access this value without 860 // memory barriers because word-tearing and such 861 // really isn't an issue for boolean values. 862 return m_isRunning; 863 } 864 } 865 866 //////////////////////////////////////////////////////////////////////////// 867 // Thread Priority Actions 868 //////////////////////////////////////////////////////////////////////////// 869 870 871 /** 872 * The minimum scheduling priority that may be set for a thread. On 873 * systems where multiple scheduling policies are defined, this value 874 * represents the minimum valid priority for the scheduling policy of 875 * the process. 876 */ 877 static __gshared const int PRIORITY_MIN; 878 879 880 /** 881 * The maximum scheduling priority that may be set for a thread. On 882 * systems where multiple scheduling policies are defined, this value 883 * represents the minimum valid priority for the scheduling policy of 884 * the process. 885 */ 886 static __gshared const int PRIORITY_MAX; 887 888 889 /** 890 * Gets the scheduling priority for the associated thread. 891 * 892 * Returns: 893 * The scheduling priority of this thread. 894 */ 895 const final int priority() 896 { 897 version( Win32 ) 898 { 899 return GetThreadPriority( m_hndl ); 900 } 901 else version( Posix ) 902 { 903 int policy; 904 sched_param param; 905 906 if( pthread_getschedparam( m_addr, &policy, ¶m ) ) 907 throw new ThreadException( "Unable to get thread priority" ); 908 return param.sched_priority; 909 } 910 } 911 912 913 /** 914 * Sets the scheduling priority for the associated thread. 915 * 916 * Params: 917 * val = The new scheduling priority of this thread. 918 */ 919 final void priority( int val ) 920 { 921 version( Win32 ) 922 { 923 if( !SetThreadPriority( m_hndl, val ) ) 924 throw new ThreadException( "Unable to set thread priority" ); 925 } 926 else version( Posix ) 927 { 928 // NOTE: pthread_setschedprio is not implemented on linux, so use 929 // the more complicated get/set sequence below. 930 //if( pthread_setschedprio( m_addr, val ) ) 931 // throw new ThreadException( "Unable to set thread priority" ); 932 933 int policy; 934 sched_param param; 935 936 if( pthread_getschedparam( m_addr, &policy, ¶m ) ) 937 throw new ThreadException( "Unable to set thread priority" ); 938 param.sched_priority = val; 939 if( pthread_setschedparam( m_addr, policy, ¶m ) ) 940 throw new ThreadException( "Unable to set thread priority" ); 941 } 942 } 943 944 945 //////////////////////////////////////////////////////////////////////////// 946 // Actions on Calling Thread 947 //////////////////////////////////////////////////////////////////////////// 948 949 950 /** 951 * Suspends the calling thread for at least the supplied time, up to a 952 * maximum of (uint.max - 1) milliseconds. 953 * 954 * Params: 955 * period = The minimum duration the calling thread should be suspended, 956 * in seconds. Sub-second durations are specified as fractional 957 * values. 958 * 959 * In: 960 * period must be less than (uint.max - 1) milliseconds. 961 * 962 * Example: 963 * ------------------------------------------------------------------------- 964 * Thread.sleep( 0.05 ); // sleep for 50 milliseconds 965 * Thread.sleep( 5 ); // sleep for 5 seconds 966 * ------------------------------------------------------------------------- 967 */ 968 static void sleep( double period ) 969 in 970 { 971 // NOTE: The fractional value added to period is to correct fp error. 972 assert( period * 1000 + 0.1 < uint.max - 1 ); 973 } 974 body 975 { 976 version( Win32 ) 977 { 978 Sleep( cast(uint)( period * 1000 + 0.1 ) ); 979 } 980 else version( Posix ) 981 { 982 timespec tin = void; 983 timespec tout = void; 984 985 period += 0.000_000_000_1; 986 987 if( tin.tv_sec.max < period ) 988 { 989 tin.tv_sec = tin.tv_sec.max; 990 tin.tv_nsec = 0; 991 } 992 else 993 { 994 tin.tv_sec = cast(typeof(tin.tv_sec)) period; 995 tin.tv_nsec = cast(typeof(tin.tv_nsec)) ((period % 1.0) * 1_000_000_000); 996 } 997 998 while( true ) 999 { 1000 if( !nanosleep( &tin, &tout ) ) 1001 return; 1002 if( getErrno() != EINTR ) 1003 throw new ThreadException( "Unable to sleep for specified duration" ); 1004 tin = tout; 1005 } 1006 } 1007 } 1008 1009 1010 /+ 1011 /** 1012 * Suspends the calling thread for at least the supplied time, up to a 1013 * maximum of (uint.max - 1) milliseconds. 1014 * 1015 * Params: 1016 * period = The minimum duration the calling thread should be suspended. 1017 * 1018 * In: 1019 * period must be less than (uint.max - 1) milliseconds. 1020 * 1021 * Example: 1022 * ------------------------------------------------------------------------- 1023 * Thread.sleep( TimeSpan.milliseconds( 50 ) ); // sleep for 50 milliseconds 1024 * Thread.sleep( TimeSpan.seconds( 5 ) ); // sleep for 5 seconds 1025 * ------------------------------------------------------------------------- 1026 */ 1027 static void sleep( TimeSpan period ) 1028 in 1029 { 1030 assert( period.milliseconds < uint.max - 1 ); 1031 } 1032 body 1033 { 1034 version( Win32 ) 1035 { 1036 Sleep( cast(uint)( period.milliseconds ) ); 1037 } 1038 else version( Posix ) 1039 { 1040 timespec tin = void; 1041 timespec tout = void; 1042 1043 if( tin.tv_sec.max < period.seconds ) 1044 { 1045 tin.tv_sec = tin.tv_sec.max; 1046 tin.tv_nsec = 0; 1047 } 1048 else 1049 { 1050 tin.tv_sec = cast(typeof(tin.tv_sec)) period.seconds; 1051 tin.tv_nsec = cast(typeof(tin.tv_nsec)) period.nanoseconds % 1_000_000_000; 1052 } 1053 1054 while( true ) 1055 { 1056 if( !nanosleep( &tin, &tout ) ) 1057 return; 1058 if( getErrno() != EINTR ) 1059 throw new ThreadException( "Unable to sleep for specified duration" ); 1060 tin = tout; 1061 } 1062 } 1063 } 1064 1065 1066 /** 1067 * Suspends the calling thread for at least the supplied time, up to a 1068 * maximum of (uint.max - 1) milliseconds. 1069 * 1070 * Params: 1071 * period = The minimum duration the calling thread should be suspended, 1072 * in seconds. Sub-second durations are specified as fractional 1073 * values. Please note that because period is a floating-point 1074 * number, some accuracy may be lost for certain intervals. For 1075 * this reason, the TimeSpan overload is preferred in instances 1076 * where an exact interval is required. 1077 * 1078 * In: 1079 * period must be less than (uint.max - 1) milliseconds. 1080 * 1081 * Example: 1082 * ------------------------------------------------------------------------- 1083 * Thread.sleep( 0.05 ); // sleep for 50 milliseconds 1084 * Thread.sleep( 5 ); // sleep for 5 seconds 1085 * ------------------------------------------------------------------------- 1086 */ 1087 static void sleep( double period ) 1088 { 1089 sleep( TimeSpan.interval( period ) ); 1090 } 1091 +/ 1092 1093 1094 /** 1095 * Forces a context switch to occur away from the calling thread. 1096 */ 1097 static void yield() 1098 { 1099 version( Win32 ) 1100 { 1101 // NOTE: Sleep(1) is necessary because Sleep(0) does not give 1102 // lower priority threads any timeslice, so looping on 1103 // Sleep(0) could be resource-intensive in some cases. 1104 Sleep( 1 ); 1105 } 1106 else version( Posix ) 1107 { 1108 sched_yield(); 1109 } 1110 } 1111 1112 1113 //////////////////////////////////////////////////////////////////////////// 1114 // Thread Accessors 1115 //////////////////////////////////////////////////////////////////////////// 1116 1117 1118 /** 1119 * Provides a reference to the calling thread. 1120 * 1121 * Returns: 1122 * The thread object representing the calling thread. The result of 1123 * deleting this object is undefined. 1124 */ 1125 static Thread getThis() 1126 { 1127 // NOTE: This function may not be called until thread_init has 1128 // completed. See thread_suspendAll for more information 1129 // on why this might occur. 1130 version( Win32 ) 1131 { 1132 return cast(Thread) TlsGetValue( sm_this ); 1133 } 1134 else version( Posix ) 1135 { 1136 return cast(Thread) pthread_getspecific( sm_this ); 1137 } 1138 } 1139 1140 1141 /** 1142 * Provides a list of all threads currently being tracked by the system. 1143 * 1144 * Returns: 1145 * An array containing references to all threads currently being 1146 * tracked by the system. The result of deleting any contained 1147 * objects is undefined. 1148 */ 1149 static Thread[] getAll() 1150 { 1151 Thread[] buf; 1152 while(1){ 1153 if (buf) delete buf; 1154 buf = new Thread[sm_tlen]; 1155 synchronized( slock ) 1156 { 1157 size_t pos = 0; 1158 if (buf.length<sm_tlen) { 1159 continue; 1160 } else { 1161 buf.length=sm_tlen; 1162 } 1163 foreach( Thread t; Thread ) 1164 { 1165 buf[pos++] = t; 1166 } 1167 return buf; 1168 } 1169 } 1170 } 1171 1172 1173 /** 1174 * Operates on all threads currently being tracked by the system. The 1175 * result of deleting any Thread object is undefined. 1176 * 1177 * Params: 1178 * dg = The supplied code as a delegate. 1179 * 1180 * Returns: 1181 * Zero if all elemented are visited, nonzero if not. 1182 */ 1183 static int opApply(scope int delegate( ref Thread ) dg ) 1184 { 1185 synchronized( slock ) 1186 { 1187 int ret = 0; 1188 1189 for( Thread t = sm_tbeg; t; t = t.next ) 1190 { 1191 ret = dg( t ); 1192 if( ret ) 1193 break; 1194 } 1195 return ret; 1196 } 1197 } 1198 1199 1200 //////////////////////////////////////////////////////////////////////////// 1201 // Local Storage Actions 1202 //////////////////////////////////////////////////////////////////////////// 1203 1204 1205 /** 1206 * Indicates the number of local storage pointers available at program 1207 * startup. It is recommended that this number be at least 64. 1208 */ 1209 static const uint LOCAL_MAX = 64; 1210 1211 1212 /** 1213 * Reserves a local storage pointer for use and initializes this location 1214 * to null for all running threads. 1215 * 1216 * Returns: 1217 * A key representing the array offset of this memory location. 1218 */ 1219 static uint createLocal() 1220 { 1221 synchronized( slock ) 1222 { 1223 foreach( uint key, ref bool set; sm_local ) 1224 { 1225 if( !set ) 1226 { 1227 //foreach( Thread t; sm_tbeg ) Bug in GDC 0.24 SVN (r139) 1228 for( Thread t = sm_tbeg; t; t = t.next ) 1229 { 1230 t.m_local[key] = null; 1231 } 1232 set = true; 1233 return key; 1234 } 1235 } 1236 throw new ThreadException( "No more local storage slots available" ); 1237 } 1238 } 1239 1240 1241 /** 1242 * Marks the supplied key as available and sets the associated location 1243 * to null for all running threads. It is assumed that any key passed 1244 * to this function is valid. The result of calling this function for 1245 * a key which is still in use is undefined. 1246 * 1247 * Params: 1248 * key = The key to delete. 1249 */ 1250 static void deleteLocal( uint key ) 1251 { 1252 synchronized( slock ) 1253 { 1254 sm_local[key] = false; 1255 // foreach( Thread t; sm_tbeg ) Bug in GDC 0.24 SVN (r139) 1256 for( Thread t = sm_tbeg; t; t = t.next ) 1257 { 1258 t.m_local[key] = null; 1259 } 1260 } 1261 } 1262 1263 1264 /** 1265 * Loads the value stored at key within a thread-local static array. It is 1266 * assumed that any key passed to this function is valid. 1267 * 1268 * Params: 1269 * key = The location which holds the desired data. 1270 * 1271 * Returns: 1272 * The data associated with the supplied key. 1273 */ 1274 static void* getLocal( uint key ) 1275 { 1276 return getThis().m_local[key]; 1277 } 1278 1279 1280 /** 1281 * Stores the supplied value at key within a thread-local static array. It 1282 * is assumed that any key passed to this function is valid. 1283 * 1284 * Params: 1285 * key = The location to store the supplied data. 1286 * val = The data to store. 1287 * 1288 * Returns: 1289 * A copy of the data which has just been stored. 1290 */ 1291 static void* setLocal( uint key, void* val ) 1292 { 1293 return getThis().m_local[key] = val; 1294 } 1295 1296 1297 //////////////////////////////////////////////////////////////////////////// 1298 // Static Initalizer 1299 //////////////////////////////////////////////////////////////////////////// 1300 1301 1302 /** 1303 * This initializer is used to set thread constants. All functional 1304 * initialization occurs within thread_init(). 1305 */ 1306 static this() 1307 { 1308 version( Win32 ) 1309 { 1310 PRIORITY_MIN = -15; 1311 PRIORITY_MAX = 15; 1312 } 1313 else version( Posix ) 1314 { 1315 int policy; 1316 sched_param param; 1317 pthread_t self = pthread_self(); 1318 1319 int status = pthread_getschedparam( self, &policy, ¶m ); 1320 assert( status == 0 ); 1321 1322 PRIORITY_MIN = sched_get_priority_min( policy ); 1323 assert( PRIORITY_MIN != -1 ); 1324 1325 PRIORITY_MAX = sched_get_priority_max( policy ); 1326 assert( PRIORITY_MAX != -1 ); 1327 } 1328 } 1329 1330 1331 private: 1332 // 1333 // Initializes a thread object which has no associated executable function. 1334 // This is used for the main thread initialized in thread_init(). 1335 // 1336 this() 1337 { 1338 m_call = Call.NO; 1339 m_curr = &m_main; 1340 } 1341 1342 1343 // 1344 // Thread entry point. Invokes the function or delegate passed on 1345 // construction (if any). 1346 // 1347 final void run() 1348 { 1349 switch( m_call ) 1350 { 1351 case Call.FN: 1352 m_fn(); 1353 break; 1354 case Call.DG: 1355 m_dg(); 1356 break; 1357 default: 1358 break; 1359 } 1360 } 1361 1362 1363 private: 1364 // 1365 // The type of routine passed on thread construction. 1366 // 1367 enum Call 1368 { 1369 NO, 1370 FN, 1371 DG 1372 } 1373 1374 1375 // 1376 // Standard types 1377 // 1378 version( Win32 ) 1379 { 1380 alias uint TLSKey; 1381 alias uint ThreadAddr; 1382 } 1383 else version( Posix ) 1384 { 1385 alias pthread_key_t TLSKey; 1386 alias pthread_t ThreadAddr; 1387 } 1388 1389 1390 // 1391 // Local storage 1392 // 1393 static __gshared bool[LOCAL_MAX] sm_local; 1394 static __gshared TLSKey sm_this; 1395 1396 void*[LOCAL_MAX] m_local; 1397 1398 1399 // 1400 // Standard thread data 1401 // 1402 version( Win32 ) 1403 { 1404 HANDLE m_hndl; 1405 } 1406 public ThreadAddr m_addr; 1407 Call m_call; 1408 const(char)[] m_name; 1409 union 1410 { 1411 void function() m_fn; 1412 void delegate() m_dg; 1413 } 1414 size_t m_sz; 1415 version( Posix ) 1416 { 1417 bool m_isRunning; 1418 } 1419 bool m_isDaemon; 1420 public Throwable m_unhandled; 1421 1422 1423 private: 1424 //////////////////////////////////////////////////////////////////////////// 1425 // Storage of Active Thread 1426 //////////////////////////////////////////////////////////////////////////// 1427 1428 1429 // 1430 // Sets a thread-local reference to the current thread object. 1431 // 1432 static void setThis( Thread t ) 1433 { 1434 version( Win32 ) 1435 { 1436 TlsSetValue( sm_this, cast(void*) t ); 1437 } 1438 else version( Posix ) 1439 { 1440 pthread_setspecific( sm_this, cast(void*) t ); 1441 } 1442 } 1443 1444 1445 private: 1446 //////////////////////////////////////////////////////////////////////////// 1447 // Thread Context and GC Scanning Support 1448 //////////////////////////////////////////////////////////////////////////// 1449 1450 1451 final void pushContext( Context* c ) 1452 in 1453 { 1454 assert( !c.within ); 1455 } 1456 body 1457 { 1458 c.within = m_curr; 1459 m_curr = c; 1460 } 1461 1462 1463 final void popContext() 1464 in 1465 { 1466 assert( m_curr && m_curr.within ); 1467 } 1468 body 1469 { 1470 Context* c = m_curr; 1471 m_curr = c.within; 1472 c.within = null; 1473 } 1474 1475 1476 public final Context* topContext() 1477 in 1478 { 1479 assert( m_curr ); 1480 } 1481 body 1482 { 1483 return m_curr; 1484 } 1485 1486 1487 public static struct Context 1488 { 1489 void* bstack, 1490 tstack; 1491 Context* within; 1492 Context* next, 1493 prev; 1494 } 1495 1496 1497 Context m_main; 1498 Context* m_curr; 1499 bool m_lock; 1500 1501 version( Win32 ) 1502 { 1503 uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax 1504 } 1505 1506 1507 private: 1508 //////////////////////////////////////////////////////////////////////////// 1509 // GC Scanning Support 1510 //////////////////////////////////////////////////////////////////////////// 1511 1512 1513 // NOTE: The GC scanning process works like so: 1514 // 1515 // 1. Suspend all threads. 1516 // 2. Scan the stacks of all suspended threads for roots. 1517 // 3. Resume all threads. 1518 // 1519 // Step 1 and 3 require a list of all threads in the system, while 1520 // step 2 requires a list of all thread stacks (each represented by 1521 // a Context struct). Traditionally, there was one stack per thread 1522 // and the Context structs were not necessary. However, Fibers have 1523 // changed things so that each thread has its own 'main' stack plus 1524 // an arbitrary number of nested stacks (normally referenced via 1525 // m_curr). Also, there may be 'free-floating' stacks in the system, 1526 // which are Fibers that are not currently executing on any specific 1527 // thread but are still being processed and still contain valid 1528 // roots. 1529 // 1530 // To support all of this, the Context struct has been created to 1531 // represent a stack range, and a global list of Context structs has 1532 // been added to enable scanning of these stack ranges. The lifetime 1533 // (and presence in the Context list) of a thread's 'main' stack will 1534 // be equivalent to the thread's lifetime. So the Ccontext will be 1535 // added to the list on thread entry, and removed from the list on 1536 // thread exit (which is essentially the same as the presence of a 1537 // Thread object in its own global list). The lifetime of a Fiber's 1538 // context, however, will be tied to the lifetime of the Fiber object 1539 // itself, and Fibers are expected to add/remove their Context struct 1540 // on construction/deletion. 1541 1542 1543 // 1544 // All use of the global lists should synchronize on this lock. 1545 // 1546 static Object slock() 1547 { 1548 return Thread.classinfo; 1549 } 1550 1551 1552 static __gshared Context* sm_cbeg; 1553 static __gshared size_t sm_clen; 1554 1555 static __gshared Thread sm_tbeg; 1556 static __gshared size_t sm_tlen; 1557 1558 // 1559 // Used for ordering threads in the global thread list. 1560 // 1561 Thread prev; 1562 Thread next; 1563 1564 1565 //////////////////////////////////////////////////////////////////////////// 1566 // Global Context List Operations 1567 //////////////////////////////////////////////////////////////////////////// 1568 1569 1570 // 1571 // Add a context to the global context list. 1572 // 1573 static void add( Context* c ) 1574 in 1575 { 1576 assert( c ); 1577 assert( !c.next && !c.prev ); 1578 } 1579 body 1580 { 1581 synchronized( slock ) 1582 { 1583 if( sm_cbeg ) 1584 { 1585 c.next = sm_cbeg; 1586 sm_cbeg.prev = c; 1587 } 1588 sm_cbeg = c; 1589 ++sm_clen; 1590 } 1591 } 1592 1593 1594 // 1595 // Remove a context from the global context list. 1596 // 1597 static void remove( Context* c ) 1598 in 1599 { 1600 assert( c ); 1601 assert( c.next || c.prev ); 1602 } 1603 body 1604 { 1605 synchronized( slock ) 1606 { 1607 if( c.prev ) 1608 c.prev.next = c.next; 1609 if( c.next ) 1610 c.next.prev = c.prev; 1611 if( sm_cbeg == c ) 1612 sm_cbeg = c.next; 1613 --sm_clen; 1614 } 1615 // NOTE: Don't null out c.next or c.prev because opApply currently 1616 // follows c.next after removing a node. This could be easily 1617 // addressed by simply returning the next node from this function, 1618 // however, a context should never be re-added to the list anyway 1619 // and having next and prev be non-null is a good way to 1620 // ensure that. 1621 } 1622 1623 1624 //////////////////////////////////////////////////////////////////////////// 1625 // Global Thread List Operations 1626 //////////////////////////////////////////////////////////////////////////// 1627 1628 1629 // 1630 // Add a thread to the global thread list. 1631 // 1632 static void add( Thread t ) 1633 in 1634 { 1635 assert( t ); 1636 assert( !t.next && !t.prev ); 1637 assert( t.isRunning ); 1638 } 1639 body 1640 { 1641 synchronized( slock ) 1642 { 1643 if( sm_tbeg ) 1644 { 1645 t.next = sm_tbeg; 1646 sm_tbeg.prev = t; 1647 } 1648 sm_tbeg = t; 1649 ++sm_tlen; 1650 } 1651 } 1652 1653 1654 // 1655 // Remove a thread from the global thread list. 1656 // 1657 static void remove( Thread t ) 1658 in 1659 { 1660 assert( t ); 1661 assert( t.next || t.prev ); 1662 } 1663 body 1664 { 1665 synchronized( slock ) 1666 { 1667 // NOTE: When a thread is removed from the global thread list its 1668 // main context is invalid and should be removed as well. 1669 // It is possible that t.m_curr could reference more 1670 // than just the main context if the thread exited abnormally 1671 // (if it was terminated), but we must assume that the user 1672 // retains a reference to them and that they may be re-used 1673 // elsewhere. Therefore, it is the responsibility of any 1674 // object that creates contexts to clean them up properly 1675 // when it is done with them. 1676 remove( &t.m_main ); 1677 1678 if( t.prev ) 1679 t.prev.next = t.next; 1680 if( t.next ) 1681 t.next.prev = t.prev; 1682 if( sm_tbeg == t ) 1683 sm_tbeg = t.next; 1684 --sm_tlen; 1685 } 1686 // NOTE: Don't null out t.next or t.prev because opApply currently 1687 // follows t.next after removing a node. This could be easily 1688 // addressed by simply returning the next node from this function, 1689 // however, a thread should never be re-added to the list anyway 1690 // and having next and prev be non-null is a good way to 1691 // ensure that. 1692 } 1693 } 1694 1695 1696 //////////////////////////////////////////////////////////////////////////////// 1697 // GC Support Routines 1698 //////////////////////////////////////////////////////////////////////////////// 1699 1700 1701 /** 1702 * Initializes the thread module. This function must be called by the 1703 * garbage collector on startup and before any other thread routines 1704 * are called. 1705 */ 1706 extern (C) void thread_init() 1707 { 1708 // NOTE: If thread_init itself performs any allocations then the thread 1709 // routines reserved for garbage collector use may be called while 1710 // thread_init is being processed. However, since no memory should 1711 // exist to be scanned at this point, it is sufficient for these 1712 // functions to detect the condition and return immediately. 1713 1714 version( Win32 ) 1715 { 1716 Thread.sm_this = TlsAlloc(); 1717 assert( Thread.sm_this != TLS_OUT_OF_INDEXES ); 1718 Fiber.sm_this = TlsAlloc(); 1719 assert( Thread.sm_this != TLS_OUT_OF_INDEXES ); 1720 } 1721 else version( Posix ) 1722 { 1723 int status; 1724 sigaction_t sigusr1 = void; 1725 sigaction_t sigusr2 = void; 1726 sigaction_t sigabrt = void; 1727 1728 // This is a quick way to zero-initialize the structs without using 1729 // memset or creating a link dependency on their static initializer. 1730 (cast(byte*) &sigusr1)[0 .. sigaction_t.sizeof] = 0; 1731 (cast(byte*) &sigusr2)[0 .. sigaction_t.sizeof] = 0; 1732 (cast(byte*) &sigabrt)[0 .. sigaction_t.sizeof] = 0; 1733 1734 // NOTE: SA_RESTART indicates that system calls should restart if they 1735 // are interrupted by a signal, but this is not available on all 1736 // Posix systems, even those that support multithreading. 1737 static if( is( typeof( SA_RESTART ) ) ) 1738 sigusr1.sa_flags = SA_RESTART; 1739 else 1740 sigusr1.sa_flags = 0; 1741 sigusr1.sa_handler = &thread_suspendHandler; 1742 // NOTE: We want to ignore all signals while in this handler, so fill 1743 // sa_mask to indicate this. 1744 status = sigfillset( &sigusr1.sa_mask ); 1745 assert( status == 0 ); 1746 status = sigdelset( &sigusr1.sa_mask , SIGABRT); 1747 assert( status == 0 ); 1748 1749 // NOTE: Since SIGUSR2 should only be issued for threads within the 1750 // suspend handler, we don't want this signal to trigger a 1751 // restart. 1752 sigusr2.sa_flags = 0; 1753 sigusr2.sa_handler = &thread_resumeHandler; 1754 // NOTE: We want to ignore all signals while in this handler, so fill 1755 // sa_mask to indicate this. 1756 status = sigfillset( &sigusr2.sa_mask ); 1757 assert( status == 0 ); 1758 status = sigdelset( &sigusr2.sa_mask , SIGABRT); 1759 assert( status == 0 ); 1760 1761 status = sigaction( SIGUSR1, &sigusr1, null ); 1762 assert( status == 0 ); 1763 1764 status = sigaction( SIGUSR2, &sigusr2, null ); 1765 assert( status == 0 ); 1766 1767 // NOTE: SA_RESTART indicates that system calls should restart if they 1768 // are interrupted by a signal, but this is not available on all 1769 // Posix systems, even those that support multithreading. 1770 static if( is( typeof( SA_RESTART ) ) ) 1771 sigabrt.sa_flags = SA_RESTART; 1772 else 1773 sigabrt.sa_flags = 0; 1774 sigabrt.sa_handler = &thread_abortHandler; 1775 // NOTE: We want to ignore all signals while in this handler, so fill 1776 // sa_mask to indicate this. 1777 status = sigfillset( &sigabrt.sa_mask ); 1778 assert( status == 0 ); 1779 1780 status = sigaction( SIGABRT, &sigabrt, null ); 1781 assert( status == 0 ); 1782 1783 version(AtomicSuspendCount){ 1784 suspendCount=0; 1785 } else { 1786 status = sem_init( &suspendCount, 0, 0 ); 1787 } 1788 assert( status == 0 ); 1789 1790 status = pthread_key_create( &Thread.sm_this, null ); 1791 assert( status == 0 ); 1792 status = pthread_key_create( &Fiber.sm_this, null ); 1793 assert( status == 0 ); 1794 } 1795 1796 thread_attachThis(); 1797 } 1798 1799 1800 /** 1801 * Registers the calling thread for use with Tango. If this routine is called 1802 * for a thread which is already registered, the result is undefined. 1803 */ 1804 extern (C) void thread_attachThis() 1805 { 1806 version( Win32 ) 1807 { 1808 Thread thisThread = new Thread(); 1809 Thread.Context* thisContext = &thisThread.m_main; 1810 assert( thisContext == thisThread.m_curr ); 1811 1812 thisThread.m_addr = GetCurrentThreadId(); 1813 thisThread.m_hndl = GetCurrentThreadHandle(); 1814 thisContext.bstack = getStackBottom(); 1815 thisContext.tstack = thisContext.bstack; 1816 1817 thisThread.m_isDaemon = true; 1818 1819 Thread.setThis( thisThread ); 1820 } 1821 else version( Posix ) 1822 { 1823 Thread thisThread = new Thread(); 1824 Thread.Context* thisContext = thisThread.m_curr; 1825 assert( thisContext == &thisThread.m_main ); 1826 1827 thisThread.m_addr = pthread_self(); 1828 thisContext.bstack = getStackBottom(); 1829 thisContext.tstack = thisContext.bstack; 1830 1831 thisThread.m_isRunning = true; 1832 thisThread.m_isDaemon = true; 1833 1834 Thread.setThis( thisThread ); 1835 } 1836 1837 Thread.add( thisThread ); 1838 Thread.add( thisContext ); 1839 } 1840 1841 1842 /** 1843 * Deregisters the calling thread from use with Tango. If this routine is 1844 * called for a thread which is already registered, the result is undefined. 1845 */ 1846 extern (C) void thread_detachThis() 1847 { 1848 Thread.remove( Thread.getThis() ); 1849 } 1850 1851 1852 /** 1853 * Joins all non-daemon threads that are currently running. This is done by 1854 * performing successive scans through the thread list until a scan consists 1855 * of only daemon threads. 1856 */ 1857 extern (C) void thread_joinAll() 1858 { 1859 1860 while( true ) 1861 { 1862 Thread nonDaemon = null; 1863 1864 foreach( t; Thread ) 1865 { 1866 if( !t.isDaemon ) 1867 { 1868 nonDaemon = t; 1869 break; 1870 } 1871 } 1872 if( nonDaemon is null ) 1873 return; 1874 nonDaemon.join(); 1875 } 1876 } 1877 1878 1879 /** 1880 * Performs intermediate shutdown of the thread module. 1881 */ 1882 static ~this() 1883 { 1884 // NOTE: The functionality related to garbage collection must be minimally 1885 // operable after this dtor completes. Therefore, only minimal 1886 // cleanup may occur. 1887 1888 for( Thread t = Thread.sm_tbeg; t; t = t.next ) 1889 { 1890 if( !t.isRunning ) 1891 Thread.remove( t ); 1892 } 1893 } 1894 1895 1896 // Used for needLock below 1897 private bool multiThreadedFlag = false; 1898 1899 1900 /** 1901 * This function is used to determine whether the the process is 1902 * multi-threaded. Optimizations may only be performed on this 1903 * value if the programmer can guarantee that no path from the 1904 * enclosed code will start a thread. 1905 * 1906 * Returns: 1907 * True if Thread.start() has been called in this process. 1908 */ 1909 extern (C) bool thread_needLock() 1910 { 1911 return multiThreadedFlag; 1912 } 1913 1914 1915 // Used for suspendAll/resumeAll below 1916 private uint suspendDepth = 0; 1917 1918 /** 1919 * Suspend all threads but the calling thread for "stop the world" garbage 1920 * collection runs. This function may be called multiple times, and must 1921 * be followed by a matching number of calls to thread_resumeAll before 1922 * processing is resumed. 1923 * 1924 * Throws: 1925 * ThreadException if the suspend operation fails for a running thread. 1926 */ 1927 extern (C) void thread_suspendAll() 1928 { 1929 int suspendedCount=0; 1930 /** 1931 * Suspend the specified thread and load stack and register information for 1932 * use by thread_scanAll. If the supplied thread is the calling thread, 1933 * stack and register information will be loaded but the thread will not 1934 * be suspended. If the suspend operation fails and the thread is not 1935 * running then it will be removed from the global thread list, otherwise 1936 * an exception will be thrown. 1937 * 1938 * Params: 1939 * t = The thread to suspend. 1940 * 1941 * Throws: 1942 * ThreadException if the suspend operation fails for a running thread. 1943 */ 1944 void suspend( Thread t ) 1945 { 1946 version( Win32 ) 1947 { 1948 if( t.m_addr != GetCurrentThreadId() && SuspendThread( t.m_hndl ) == 0xFFFFFFFF ) 1949 { 1950 if( !t.isRunning ) 1951 { 1952 Thread.remove( t ); 1953 return; 1954 } 1955 throw new ThreadException( "Unable to suspend thread" ); 1956 } 1957 1958 CONTEXT context = void; 1959 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; 1960 1961 if( !GetThreadContext( t.m_hndl, &context ) ) 1962 throw new ThreadException( "Unable to load thread context" ); 1963 if( !t.m_lock ) 1964 t.m_curr.tstack = cast(void*) context.Esp; 1965 // edi,esi,ebp,esp,ebx,edx,ecx,eax 1966 t.m_reg[0] = context.Edi; 1967 t.m_reg[1] = context.Esi; 1968 t.m_reg[2] = context.Ebp; 1969 t.m_reg[3] = context.Esp; 1970 t.m_reg[4] = context.Ebx; 1971 t.m_reg[5] = context.Edx; 1972 t.m_reg[6] = context.Ecx; 1973 t.m_reg[7] = context.Eax; 1974 } 1975 else version( Posix ) 1976 { 1977 if( t.m_addr != pthread_self() ) 1978 { 1979 if( pthread_kill( t.m_addr, SIGUSR1 ) != 0 ) 1980 { 1981 if( !t.isRunning ) 1982 { 1983 Thread.remove( t ); 1984 return; 1985 } 1986 throw new ThreadException( "Unable to suspend thread" ); 1987 } 1988 version (AtomicSuspendCount){ 1989 ++suspendedCount; 1990 version(AtomicSuspendCount){ 1991 version(SuspendOneAtTime){ // when debugging suspending all threads at once might give "lost" signals 1992 int icycle=0; 1993 suspendLoop: while (flagGet(suspendCount)!=suspendedCount){ 1994 for (size_t i=1000;i!=0;--i){ 1995 if (flagGet(suspendCount)==suspendedCount) break suspendLoop; 1996 if (++icycle==100_000){ 1997 debug(Thread) 1998 printf("waited %d cycles for thread suspension, suspendCount=%d, should be %d\nAtomic ops do not work?\nContinuing wait...\n",icycle,suspendCount,suspendedCount); 1999 } 2000 Thread.yield(); 2001 } 2002 Thread.sleep(0.0001); 2003 } 2004 } 2005 } 2006 2007 } else { 2008 sem_wait( &suspendCount ); 2009 // shouldn't the return be checked and maybe a loop added for further interrupts 2010 // as in Semaphore.d ? 2011 } 2012 } 2013 else if( !t.m_lock ) 2014 { 2015 t.m_curr.tstack = getStackTop(); 2016 } 2017 } 2018 } 2019 2020 2021 // NOTE: We've got an odd chicken & egg problem here, because while the GC 2022 // is required to call thread_init before calling any other thread 2023 // routines, thread_init may allocate memory which could in turn 2024 // trigger a collection. Thus, thread_suspendAll, thread_scanAll, 2025 // and thread_resumeAll must be callable before thread_init completes, 2026 // with the assumption that no other GC memory has yet been allocated 2027 // by the system, and thus there is no risk of losing data if the 2028 // global thread list is empty. The check of Thread.sm_tbeg 2029 // below is done to ensure thread_init has completed, and therefore 2030 // that calling Thread.getThis will not result in an error. For the 2031 // short time when Thread.sm_tbeg is null, there is no reason 2032 // not to simply call the multithreaded code below, with the 2033 // expectation that the foreach loop will never be entered. 2034 if( !multiThreadedFlag && Thread.sm_tbeg ) 2035 { 2036 if( ++suspendDepth == 1 ) { 2037 suspend( Thread.getThis() ); 2038 } 2039 return; 2040 } 2041 _d_monitorenter(Thread.slock); 2042 { 2043 if( ++suspendDepth > 1 ) 2044 return; 2045 // NOTE: I'd really prefer not to check isRunning within this loop but 2046 // not doing so could be problematic if threads are termianted 2047 // abnormally and a new thread is created with the same thread 2048 // address before the next GC run. This situation might cause 2049 // the same thread to be suspended twice, which would likely 2050 // cause the second suspend to fail, the garbage collection to 2051 // abort, and Bad Things to occur. 2052 for( Thread t = Thread.sm_tbeg; t; t = t.next ) 2053 { 2054 if( t.isRunning ){ 2055 suspend( t ); 2056 } else 2057 Thread.remove( t ); 2058 } 2059 2060 version( Posix ) 2061 { 2062 version(AtomicSuspendCount){ 2063 int icycle=0; 2064 suspendLoop2: while (flagGet(suspendCount)!=suspendedCount){ 2065 for (size_t i=1000;i!=0;--i){ 2066 if (flagGet(suspendCount)==suspendedCount) break suspendLoop2; 2067 if (++icycle==1000_000){ 2068 debug(Thread) 2069 printf("waited %d cycles for thread suspension, suspendCount=%d, should be %d\nAtomic ops do not work?\nContinuing wait...\n",icycle,suspendCount,suspendedCount); 2070 } 2071 Thread.yield(); 2072 } 2073 Thread.sleep(0.0001); 2074 } 2075 } 2076 } 2077 } 2078 } 2079 2080 2081 /** 2082 * Resume all threads but the calling thread for "stop the world" garbage 2083 * collection runs. This function must be called once for each preceding 2084 * call to thread_suspendAll before the threads are actually resumed. 2085 * 2086 * In: 2087 * This routine must be preceded by a call to thread_suspendAll. 2088 * 2089 * Throws: 2090 * ThreadException if the resume operation fails for a running thread. 2091 */ 2092 extern (C) void thread_resumeAll() 2093 in 2094 { 2095 assert( suspendDepth > 0 ); 2096 } 2097 body 2098 { 2099 version(AtomicSuspendCount) version(SuspendOneAtTime) auto suspendedCount=flagGet(suspendCount); 2100 /** 2101 * Resume the specified thread and unload stack and register information. 2102 * If the supplied thread is the calling thread, stack and register 2103 * information will be unloaded but the thread will not be resumed. If 2104 * the resume operation fails and the thread is not running then it will 2105 * be removed from the global thread list, otherwise an exception will be 2106 * thrown. 2107 * 2108 * Params: 2109 * t = The thread to resume. 2110 * 2111 * Throws: 2112 * ThreadException if the resume fails for a running thread. 2113 */ 2114 void resume( Thread t ) 2115 { 2116 version( Win32 ) 2117 { 2118 if( t.m_addr != GetCurrentThreadId() && ResumeThread( t.m_hndl ) == 0xFFFFFFFF ) 2119 { 2120 if( !t.isRunning ) 2121 { 2122 Thread.remove( t ); 2123 return; 2124 } 2125 throw new ThreadException( "Unable to resume thread" ); 2126 } 2127 2128 if( !t.m_lock ) 2129 t.m_curr.tstack = t.m_curr.bstack; 2130 t.m_reg[0 .. $] = 0; 2131 } 2132 else version( Posix ) 2133 { 2134 if( t.m_addr != pthread_self() ) 2135 { 2136 if( pthread_kill( t.m_addr, SIGUSR2 ) != 0 ) 2137 { 2138 if( !t.isRunning ) 2139 { 2140 Thread.remove( t ); 2141 return; 2142 } 2143 throw new ThreadException( "Unable to resume thread" ); 2144 } 2145 version (AtomicSuspendCount){ 2146 version(SuspendOneAtTime){ // when debugging suspending all threads at once might give "lost" signals 2147 --suspendedCount; 2148 int icycle=0; 2149 recoverLoop: while(flagGet(suspendCount)>suspendedCount){ 2150 for (size_t i=1000;i!=0;--i){ 2151 if (flagGet(suspendCount)==suspendedCount) break recoverLoop; 2152 if (++icycle==100_000){ 2153 debug(Thread) 2154 printf("waited %d cycles for thread recover, suspendCount=%d, should be %d\nAtomic ops do not work?\nContinuing wait...\n",icycle,suspendCount,suspendedCount); 2155 } 2156 Thread.yield(); 2157 } 2158 Thread.sleep(0.0001); 2159 } 2160 } 2161 } else { 2162 sem_wait( &suspendCount ); 2163 // shouldn't the return be checked and maybe a loop added for further interrupts 2164 // as in Semaphore.d ? 2165 } 2166 } 2167 else if( !t.m_lock ) 2168 { 2169 t.m_curr.tstack = t.m_curr.bstack; 2170 } 2171 } 2172 } 2173 2174 2175 // NOTE: See thread_suspendAll for the logic behind this. 2176 if( !multiThreadedFlag && Thread.sm_tbeg ) 2177 { 2178 if( --suspendDepth == 0 ) 2179 resume( Thread.getThis() ); 2180 return; 2181 } 2182 2183 { 2184 scope(exit) _d_monitorexit(Thread.slock); 2185 if( --suspendDepth > 0 ) 2186 return; 2187 { 2188 for( Thread t = Thread.sm_tbeg; t; t = t.next ) 2189 { 2190 resume( t ); 2191 } 2192 version(AtomicSuspendCount){ 2193 int icycle=0; 2194 recoverLoop2: while(flagGet(suspendCount)>0){ 2195 for (size_t i=1000;i!=0;--i){ 2196 Thread.yield(); 2197 if (flagGet(suspendCount)==0) break recoverLoop2; 2198 if (++icycle==100_000){ 2199 debug(Thread) 2200 printf("waited %d cycles for thread recovery, suspendCount=%d, should be %d\nAtomic ops do not work?\nContinuing wait...\n",icycle,suspendCount,0); 2201 } 2202 } 2203 Thread.sleep(0.0001); 2204 } 2205 } 2206 } 2207 } 2208 } 2209 2210 2211 private alias scope void delegate( void*, void* ) scanAllThreadsFn; 2212 2213 2214 /** 2215 * The main entry point for garbage collection. The supplied delegate 2216 * will be passed ranges representing both stack and register values. 2217 * 2218 * Params: 2219 * scan = The scanner function. It should scan from p1 through p2 - 1. 2220 * curStackTop = An optional pointer to the top of the calling thread's stack. 2221 * 2222 * In: 2223 * This routine must be preceded by a call to thread_suspendAll. 2224 */ 2225 extern (C) void thread_scanAll( scanAllThreadsFn scan, void* curStackTop = null ) 2226 in 2227 { 2228 assert( suspendDepth > 0 ); 2229 } 2230 body 2231 { 2232 Thread thisThread = null; 2233 void* oldStackTop = null; 2234 2235 if( curStackTop && Thread.sm_tbeg ) 2236 { 2237 thisThread = Thread.getThis(); 2238 if( thisThread && (!thisThread.m_lock) ) 2239 { 2240 oldStackTop = thisThread.m_curr.tstack; 2241 thisThread.m_curr.tstack = curStackTop; 2242 } 2243 } 2244 2245 scope( exit ) 2246 { 2247 if( curStackTop && Thread.sm_tbeg ) 2248 { 2249 if( thisThread && (!thisThread.m_lock) ) 2250 { 2251 thisThread.m_curr.tstack = oldStackTop; 2252 } 2253 } 2254 } 2255 2256 // NOTE: Synchronizing on Thread.slock is not needed because this 2257 // function may only be called after all other threads have 2258 // been suspended from within the same lock. 2259 for( Thread.Context* c = Thread.sm_cbeg; c; c = c.next ) 2260 { 2261 version( StackGrowsDown ) 2262 { 2263 // NOTE: We can't index past the bottom of the stack 2264 // so don't do the "+1" for StackGrowsDown. 2265 if( c.tstack && c.tstack < c.bstack ) 2266 scan( c.tstack, c.bstack ); 2267 } 2268 else 2269 { 2270 if( c.bstack && c.bstack < c.tstack ) 2271 scan( c.bstack, c.tstack + 1 ); 2272 } 2273 } 2274 version( Win32 ) 2275 { 2276 for( Thread t = Thread.sm_tbeg; t; t = t.next ) 2277 { 2278 scan( &t.m_reg[0], &t.m_reg[0] + t.m_reg.length ); 2279 } 2280 } 2281 } 2282 2283 2284 //////////////////////////////////////////////////////////////////////////////// 2285 // Thread Local 2286 //////////////////////////////////////////////////////////////////////////////// 2287 2288 2289 /** 2290 * This class encapsulates the operations required to initialize, access, and 2291 * destroy thread local data. 2292 */ 2293 class ThreadLocal( T ) 2294 { 2295 //////////////////////////////////////////////////////////////////////////// 2296 // Initialization 2297 //////////////////////////////////////////////////////////////////////////// 2298 2299 2300 /** 2301 * Initializes thread local storage for the indicated value which will be 2302 * initialized to def for all threads. 2303 * 2304 * Params: 2305 * def = The default value to return if no value has been explicitly set. 2306 */ 2307 this( T def = T.init ) 2308 { 2309 m_def = def; 2310 m_key = Thread.createLocal(); 2311 } 2312 2313 2314 ~this() 2315 { 2316 Thread.deleteLocal( m_key ); 2317 } 2318 2319 2320 //////////////////////////////////////////////////////////////////////////// 2321 // Accessors 2322 //////////////////////////////////////////////////////////////////////////// 2323 2324 2325 /** 2326 * Gets the value last set by the calling thread, or def if no such value 2327 * has been set. 2328 * 2329 * Returns: 2330 * The stored value or def if no value is stored. 2331 */ 2332 T val() 2333 { 2334 Wrap* wrap = cast(Wrap*) Thread.getLocal( m_key ); 2335 2336 return wrap ? wrap.val : m_def; 2337 } 2338 2339 2340 /** 2341 * Copies newval to a location specific to the calling thread, and returns 2342 * newval. 2343 * 2344 * Params: 2345 * newval = The value to set. 2346 * 2347 * Returns: 2348 * The value passed to this function. 2349 */ 2350 T val( T newval ) 2351 { 2352 Wrap* wrap = cast(Wrap*) Thread.getLocal( m_key ); 2353 2354 if( wrap is null ) 2355 { 2356 wrap = new Wrap; 2357 Thread.setLocal( m_key, wrap ); 2358 } 2359 wrap.val = newval; 2360 return newval; 2361 } 2362 2363 2364 private: 2365 // 2366 // A wrapper for the stored data. This is needed for determining whether 2367 // set has ever been called for this thread (and therefore whether the 2368 // default value should be returned) and also to flatten the differences 2369 // between data that is smaller and larger than (void*).sizeof. The 2370 // obvious tradeoff here is an extra per-thread allocation for each 2371 // ThreadLocal value as compared to calling the Thread routines directly. 2372 // 2373 struct Wrap 2374 { 2375 T val; 2376 } 2377 2378 2379 T m_def; 2380 uint m_key; 2381 } 2382 2383 2384 //////////////////////////////////////////////////////////////////////////////// 2385 // Thread Group 2386 //////////////////////////////////////////////////////////////////////////////// 2387 2388 2389 /** 2390 * This class is intended to simplify certain common programming techniques. 2391 */ 2392 class ThreadGroup 2393 { 2394 /** 2395 * Creates and starts a new Thread object that executes fn and adds it to 2396 * the list of tracked threads. 2397 * 2398 * Params: 2399 * fn = The thread function. 2400 * 2401 * Returns: 2402 * A reference to the newly created thread. 2403 */ 2404 final Thread create( void function() fn ) 2405 { 2406 Thread t = new Thread( fn ); 2407 2408 t.start(); 2409 synchronized( this ) 2410 { 2411 m_all[t] = t; 2412 } 2413 return t; 2414 } 2415 2416 2417 /** 2418 * Creates and starts a new Thread object that executes dg and adds it to 2419 * the list of tracked threads. 2420 * 2421 * Params: 2422 * dg = The thread function. 2423 * 2424 * Returns: 2425 * A reference to the newly created thread. 2426 */ 2427 final Thread create( void delegate() dg ) 2428 { 2429 Thread t = new Thread( dg ); 2430 2431 t.start(); 2432 synchronized( this ) 2433 { 2434 m_all[t] = t; 2435 } 2436 return t; 2437 } 2438 2439 2440 /** 2441 * Add t to the list of tracked threads if it is not already being tracked. 2442 * 2443 * Params: 2444 * t = The thread to add. 2445 * 2446 * In: 2447 * t must not be null. 2448 */ 2449 final void add( Thread t ) 2450 in 2451 { 2452 assert( t ); 2453 } 2454 body 2455 { 2456 synchronized( this ) 2457 { 2458 m_all[t] = t; 2459 } 2460 } 2461 2462 2463 /** 2464 * Removes t from the list of tracked threads. No operation will be 2465 * performed if t is not currently being tracked by this object. 2466 * 2467 * Params: 2468 * t = The thread to remove. 2469 * 2470 * In: 2471 * t must not be null. 2472 */ 2473 final void remove( Thread t ) 2474 in 2475 { 2476 assert( t ); 2477 } 2478 body 2479 { 2480 synchronized( this ) 2481 { 2482 m_all.remove( t ); 2483 } 2484 } 2485 2486 2487 /** 2488 * Operates on all threads currently tracked by this object. 2489 */ 2490 final int opApply( scope int delegate( ref Thread ) dg ) 2491 { 2492 synchronized( this ) 2493 { 2494 int ret = 0; 2495 2496 // NOTE: This loop relies on the knowledge that m_all uses the 2497 // Thread object for both the key and the mapped value. 2498 foreach( Thread t; m_all.keys ) 2499 { 2500 ret = dg( t ); 2501 if( ret ) 2502 break; 2503 } 2504 return ret; 2505 } 2506 } 2507 2508 2509 /** 2510 * Iteratively joins all tracked threads. This function will block add, 2511 * remove, and opApply until it completes. 2512 * 2513 * Params: 2514 * rethrow = Rethrow any unhandled exception which may have caused the 2515 * current thread to terminate. 2516 * 2517 * Throws: 2518 * Any exception not handled by the joined threads. 2519 */ 2520 final void joinAll( bool rethrow = true ) 2521 { 2522 synchronized( this ) 2523 { 2524 // NOTE: This loop relies on the knowledge that m_all uses the 2525 // Thread object for both the key and the mapped value. 2526 foreach( Thread t; m_all.keys ) 2527 { 2528 t.join( rethrow ); 2529 } 2530 } 2531 } 2532 2533 2534 private: 2535 Thread[Thread] m_all; 2536 } 2537 2538 2539 //////////////////////////////////////////////////////////////////////////////// 2540 // Fiber Platform Detection and Memory Allocation 2541 //////////////////////////////////////////////////////////////////////////////// 2542 2543 2544 private 2545 { 2546 version( D_InlineAsm_X86 ) 2547 { 2548 version( X86_64 ) 2549 { 2550 // Shouldn't an x64 compiler be setting D_InlineAsm_X86_64 instead? 2551 } 2552 else 2553 { 2554 version( Win32 ) 2555 version = AsmX86_Win32; 2556 else version( Posix ) 2557 version = AsmX86_Posix; 2558 } 2559 } 2560 else version( D_InlineAsm_X86_64 ) 2561 { 2562 version( Posix ) 2563 version = AsmX86_64_Posix; 2564 } 2565 else version( PPC ) 2566 { 2567 version( Posix ) 2568 version = AsmPPC_Posix; 2569 } 2570 2571 version( Posix ) 2572 { 2573 import tango.stdc.posix.unistd; // for sysconf 2574 import tango.stdc.posix.sys.mman; // for mmap 2575 import tango.stdc.posix.stdlib; // for malloc, valloc, free 2576 2577 version( AsmX86_Win32 ) {} else 2578 version( AsmX86_Posix ) {} else 2579 version( AsmX86_64_Posix ) {} else 2580 version( AsmPPC_Posix ) {} else 2581 { 2582 // NOTE: The ucontext implementation requires architecture specific 2583 // data definitions to operate so testing for it must be done 2584 // by checking for the existence of ucontext_t rather than by 2585 // a version identifier. Please note that this is considered 2586 // an obsolescent feature according to the POSIX spec, so a 2587 // custom solution is still preferred. 2588 import tango.stdc.posix.ucontext; 2589 static assert( is( ucontext_t ), "Unknown fiber implementation"); 2590 } 2591 } 2592 const size_t PAGESIZE; 2593 } 2594 2595 static this() 2596 { 2597 static if( is( typeof( GetSystemInfo ) ) ) 2598 { 2599 SYSTEM_INFO info; 2600 GetSystemInfo( &info ); 2601 2602 PAGESIZE = info.dwPageSize; 2603 assert( PAGESIZE < int.max ); 2604 } 2605 else static if( is( typeof( sysconf ) ) && 2606 is( typeof( _SC_PAGESIZE ) ) ) 2607 { 2608 PAGESIZE = cast(size_t) sysconf( _SC_PAGESIZE ); 2609 assert( PAGESIZE < int.max ); 2610 } 2611 else 2612 { 2613 version( PPC ) 2614 PAGESIZE = 8192; 2615 else 2616 PAGESIZE = 4096; 2617 } 2618 } 2619 2620 //////////////////////////////////////////////////////////////////////////////// 2621 // Fiber Entry Point and Context Switch 2622 //////////////////////////////////////////////////////////////////////////////// 2623 2624 2625 private 2626 { 2627 extern (C) void fiber_entryPoint() 2628 { 2629 Fiber obj = Fiber.getThis(); 2630 assert( obj ); 2631 2632 assert( Thread.getThis().m_curr is obj.m_ctxt ); 2633 volatile Thread.getThis().m_lock = false; 2634 obj.m_ctxt.tstack = obj.m_ctxt.bstack; 2635 obj.m_state = Fiber.State.EXEC; 2636 2637 try 2638 { 2639 obj.run(); 2640 } 2641 catch( Throwable o ) 2642 { 2643 obj.m_unhandled = o; 2644 } 2645 2646 static if( is( ucontext_t ) ) 2647 obj.m_ucur = &obj.m_utxt; 2648 2649 obj.m_state = Fiber.State.TERM; 2650 obj.switchOut(); 2651 } 2652 2653 2654 // NOTE: If AsmPPC_Posix is defined then the context switch routine will 2655 // be defined externally until GDC supports inline PPC ASM. 2656 version( AsmPPC_Posix ) 2657 extern (C) void fiber_switchContext( void** oldp, void* newp ); 2658 else 2659 extern (C) void fiber_switchContext( void** oldp, void* newp ) 2660 { 2661 // NOTE: The data pushed and popped in this routine must match the 2662 // default stack created by Fiber.initStack or the initial 2663 // switch into a new context will fail. 2664 2665 version( AsmX86_Win32 ) 2666 { 2667 asm 2668 { 2669 naked; 2670 2671 // save current stack state 2672 push EBP; 2673 mov EBP, ESP; 2674 push EAX; 2675 push dword ptr FS:[0]; 2676 push dword ptr FS:[4]; 2677 push dword ptr FS:[8]; 2678 push EBX; 2679 push ESI; 2680 push EDI; 2681 2682 // store oldp again with more accurate address 2683 mov EAX, dword ptr 8[EBP]; 2684 mov [EAX], ESP; 2685 // load newp to begin context switch 2686 mov ESP, dword ptr 12[EBP]; 2687 2688 // load saved state from new stack 2689 pop EDI; 2690 pop ESI; 2691 pop EBX; 2692 pop dword ptr FS:[8]; 2693 pop dword ptr FS:[4]; 2694 pop dword ptr FS:[0]; 2695 pop EAX; 2696 pop EBP; 2697 2698 // 'return' to complete switch 2699 ret; 2700 } 2701 } 2702 else version( AsmX86_Posix ) 2703 { 2704 asm 2705 { 2706 naked; 2707 2708 // save current stack state 2709 push EBP; 2710 mov EBP, ESP; 2711 push EAX; 2712 push EBX; 2713 push ECX; 2714 push ESI; 2715 push EDI; 2716 2717 // store oldp again with more accurate address 2718 mov EAX, dword ptr 8[EBP]; 2719 mov [EAX], ESP; 2720 // load newp to begin context switch 2721 mov ESP, dword ptr 12[EBP]; 2722 2723 // load saved state from new stack 2724 pop EDI; 2725 pop ESI; 2726 pop ECX; 2727 pop EBX; 2728 pop EAX; 2729 pop EBP; 2730 2731 // 'return' to complete switch 2732 ret; 2733 } 2734 } 2735 else version( AsmX86_64_Posix ) 2736 { 2737 version( DigitalMars ) const dmdgdc = true; 2738 else version (GNU) const dmdgdc = true; 2739 else const dmdgdc = false; 2740 2741 static if (dmdgdc == true) asm 2742 { 2743 naked; 2744 2745 // save current stack state 2746 push RBP; 2747 mov RBP, RSP; 2748 push RBX; 2749 push R12; 2750 push R13; 2751 push R14; 2752 push R15; 2753 sub RSP, 4; 2754 stmxcsr [RSP]; 2755 sub RSP, 4; 2756 //version(SynchroFloatExcept){ 2757 fstcw [RSP]; 2758 fwait; 2759 //} else { 2760 // fnstcw [RSP]; 2761 // fnclex; 2762 //} 2763 2764 // store oldp again with more accurate address 2765 mov [RDI], RSP; 2766 // load newp to begin context switch 2767 mov RSP, RSI; 2768 2769 // load saved state from new stack 2770 fldcw [RSP]; 2771 add RSP, 4; 2772 ldmxcsr [RSP]; 2773 add RSP, 4; 2774 pop R15; 2775 pop R14; 2776 pop R13; 2777 pop R12; 2778 2779 pop RBX; 2780 pop RBP; 2781 2782 // 'return' to complete switch 2783 ret; 2784 2785 } 2786 else asm 2787 { 2788 naked; 2789 2790 // save current stack state 2791 pushq RBP; 2792 mov RBP, RSP; 2793 pushq RBX; 2794 pushq R12; 2795 pushq R13; 2796 pushq R14; 2797 pushq R15; 2798 sub RSP, 4; 2799 stmxcsr [RSP]; 2800 sub RSP, 4; 2801 //version(SynchroFloatExcept){ 2802 fstcw [RSP]; 2803 fwait; 2804 //} else { 2805 // fnstcw [RSP]; 2806 // fnclex; 2807 //} 2808 2809 // store oldp again with more accurate address 2810 mov [RDI], RSP; 2811 // load newp to begin context switch 2812 mov RSP, RSI; 2813 2814 // load saved state from new stack 2815 fldcw [RSP]; 2816 add RSP, 4; 2817 ldmxcsr [RSP]; 2818 add RSP, 4; 2819 popq R15; 2820 popq R14; 2821 popq R13; 2822 popq R12; 2823 2824 popq RBX; 2825 popq RBP; 2826 2827 // 'return' to complete switch 2828 ret; 2829 } 2830 } 2831 else static if( is( ucontext_t ) ) 2832 { 2833 Fiber cfib = Fiber.getThis(); 2834 void* ucur = cfib.m_ucur; 2835 2836 *oldp = &ucur; 2837 swapcontext( **(cast(ucontext_t***) oldp), 2838 *(cast(ucontext_t**) newp) ); 2839 } 2840 } 2841 } 2842 2843 2844 //////////////////////////////////////////////////////////////////////////////// 2845 // Fiber 2846 //////////////////////////////////////////////////////////////////////////////// 2847 2848 private char[] ptrToStr(size_t addr,char[]buf){ 2849 __gshared immutable char[] digits="0123456789ABCDEF".dup; 2850 enum{ nDigits=size_t.sizeof*2 } 2851 if (nDigits>buf.length) assert(0); 2852 char[] res=buf[0..nDigits]; 2853 size_t addrAtt=addr; 2854 for (int i=nDigits;i!=0;--i){ 2855 res[i-1]=digits[addrAtt&0xF]; 2856 addrAtt>>=4; 2857 } 2858 return res; 2859 } 2860 2861 /** 2862 * This class provides a cooperative concurrency mechanism integrated with the 2863 * threading and garbage collection functionality. Calling a fiber may be 2864 * considered a blocking operation that returns when the fiber yields (via 2865 * Fiber.yield()). Execution occurs within the context of the calling thread 2866 * so synchronization is not necessary to guarantee memory visibility so long 2867 * as the same thread calls the fiber each time. Please note that there is no 2868 * requirement that a fiber be bound to one specific thread. Rather, fibers 2869 * may be freely passed between threads so long as they are not currently 2870 * executing. Like threads, a new fiber thread may be created using either 2871 * derivation or composition, as in the following example. 2872 * 2873 * Example: 2874 * ---------------------------------------------------------------------- 2875 * class DerivedFiber : Fiber 2876 * { 2877 * this() 2878 * { 2879 * super( &run ); 2880 * } 2881 * 2882 * private : 2883 * void run() 2884 * { 2885 * printf( "Derived fiber running.\n" ); 2886 * } 2887 * } 2888 * 2889 * void fiberFunc() 2890 * { 2891 * printf( "Composed fiber running.\n" ); 2892 * Fiber.yield(); 2893 * printf( "Composed fiber running.\n" ); 2894 * } 2895 * 2896 * // create instances of each type 2897 * Fiber derived = new DerivedFiber(); 2898 * Fiber composed = new Fiber( &fiberFunc ); 2899 * 2900 * // call both fibers once 2901 * derived.call(); 2902 * composed.call(); 2903 * printf( "Execution returned to calling context.\n" ); 2904 * composed.call(); 2905 * 2906 * // since each fiber has run to completion, each should have state TERM 2907 * assert( derived.state == Fiber.State.TERM ); 2908 * assert( composed.state == Fiber.State.TERM ); 2909 * ---------------------------------------------------------------------- 2910 * 2911 * Authors: Based on a design by Mikola Lysenko. 2912 */ 2913 2914 class Fiber 2915 { 2916 static class Scheduler 2917 { 2918 alias void* Handle; 2919 2920 enum Type {Read=1, Write=2, Accept=3, Connect=4, Transfer=5} 2921 2922 void pause (uint ms) {} 2923 2924 void ready (Fiber fiber) {} 2925 2926 void open (Handle fd, char[] name) {} 2927 2928 void close (Handle fd, char[] name) {} 2929 2930 void await (Handle fd, Type t, uint timeout) {} 2931 2932 void spawn (char[] name, void delegate() dg, size_t stack=8192) {} 2933 } 2934 2935 struct Event // scheduler support 2936 { 2937 uint idx; // support for timer removal 2938 Fiber next; // linked list of elapsed fibers 2939 void* data; // data to exchange 2940 ulong clock; // request timeout duration 2941 Scheduler.Handle handle; // IO request handle 2942 Scheduler scheduler; // associated scheduler (may be null) 2943 } 2944 /+ 2945 final override int opCmp (Object o) 2946 { 2947 throw new Exception ("Invalid opCmp in Fiber"); 2948 2949 auto other = cast(Fiber) cast(void*) o; 2950 if (other) 2951 { 2952 auto x = cast(long) event.clock - cast(long) other.event.clock; 2953 return (x < 0 ? -1 : x is 0 ? 0 : 1); 2954 } 2955 return 1; 2956 } 2957 +/ 2958 2959 final static Scheduler scheduler () 2960 { 2961 return getThis.event.scheduler; 2962 } 2963 2964 //////////////////////////////////////////////////////////////////////////// 2965 // Initialization 2966 //////////////////////////////////////////////////////////////////////////// 2967 2968 /** 2969 * Initializes an empty fiber object 2970 * 2971 * (useful to reset it) 2972 */ 2973 this(size_t sz){ 2974 m_dg = null; 2975 m_fn = null; 2976 m_call = Call.NO; 2977 m_state = State.TERM; 2978 m_unhandled = null; 2979 2980 allocStack( sz ); 2981 } 2982 2983 /** 2984 * Initializes a fiber object which is associated with a static 2985 * D function. 2986 * 2987 * Params: 2988 * fn = The thread function. 2989 * sz = The stack size for this fiber. 2990 * 2991 * In: 2992 * fn must not be null. 2993 */ 2994 this( void function() fn, size_t sz = PAGESIZE) 2995 in 2996 { 2997 assert( fn ); 2998 } 2999 body 3000 { 3001 m_fn = fn; 3002 m_call = Call.FN; 3003 m_state = State.HOLD; 3004 allocStack( sz ); 3005 initStack(); 3006 } 3007 3008 3009 /** 3010 * Initializes a fiber object which is associated with a dynamic 3011 * D function. 3012 * 3013 * Params: 3014 * dg = The thread function. 3015 * sz = The stack size for this fiber. 3016 * 3017 * In: 3018 * dg must not be null. 3019 */ 3020 this( void delegate() dg, size_t sz = PAGESIZE, Scheduler s = null ) 3021 in 3022 { 3023 assert( dg ); 3024 } 3025 body 3026 { 3027 event.scheduler = s; 3028 3029 m_dg = dg; 3030 m_call = Call.DG; 3031 m_state = State.HOLD; 3032 allocStack(sz); 3033 initStack(); 3034 } 3035 3036 3037 /** 3038 * Cleans up any remaining resources used by this object. 3039 */ 3040 ~this() 3041 { 3042 // NOTE: A live reference to this object will exist on its associated 3043 // stack from the first time its call() method has been called 3044 // until its execution completes with State.TERM. Thus, the only 3045 // times this dtor should be called are either if the fiber has 3046 // terminated (and therefore has no active stack) or if the user 3047 // explicitly deletes this object. The latter case is an error 3048 // but is not easily tested for, since State.HOLD may imply that 3049 // the fiber was just created but has never been run. There is 3050 // not a compelling case to create a State.INIT just to offer a 3051 // means of ensuring the user isn't violating this object's 3052 // contract, so for now this requirement will be enforced by 3053 // documentation only. 3054 freeStack(); 3055 } 3056 3057 3058 //////////////////////////////////////////////////////////////////////////// 3059 // General Actions 3060 //////////////////////////////////////////////////////////////////////////// 3061 3062 3063 /** 3064 * Transfers execution to this fiber object. The calling context will be 3065 * suspended until the fiber calls Fiber.yield() or until it terminates 3066 * via an unhandled exception. 3067 * 3068 * Params: 3069 * rethrow = Rethrow any unhandled exception which may have caused this 3070 * fiber to terminate. 3071 * 3072 * In: 3073 * This fiber must be in state HOLD. 3074 * 3075 * Throws: 3076 * Any exception not handled by the joined thread. 3077 * 3078 * Returns: 3079 * Any exception not handled by this fiber if rethrow = false, null 3080 * otherwise. 3081 */ 3082 final Object call( bool rethrow = true ) 3083 in 3084 { 3085 assert( m_state == State.HOLD ); 3086 } 3087 body 3088 { 3089 Fiber cur = getThis(); 3090 3091 static if( is( ucontext_t ) ) 3092 m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt; 3093 3094 setThis( this ); 3095 this.switchIn(); 3096 setThis( cur ); 3097 3098 static if( is( ucontext_t ) ) 3099 m_ucur = null; 3100 3101 // NOTE: If the fiber has terminated then the stack pointers must be 3102 // reset. This ensures that the stack for this fiber is not 3103 // scanned if the fiber has terminated. This is necessary to 3104 // prevent any references lingering on the stack from delaying 3105 // the collection of otherwise dead objects. The most notable 3106 // being the current object, which is referenced at the top of 3107 // fiber_entryPoint. 3108 if( m_state == State.TERM ) 3109 { 3110 m_ctxt.tstack = m_ctxt.bstack; 3111 } 3112 if( m_unhandled ) 3113 { 3114 Throwable obj = m_unhandled; 3115 m_unhandled = null; 3116 if( rethrow ) 3117 throw obj; 3118 return obj; 3119 } 3120 return null; 3121 } 3122 3123 3124 /** 3125 * Resets this fiber so that it may be re-used with the same function. 3126 * This routine may only be 3127 * called for fibers that have terminated, as doing otherwise could result 3128 * in scope-dependent functionality that is not executed. Stack-based 3129 * classes, for example, may not be cleaned up properly if a fiber is reset 3130 * before it has terminated. 3131 * 3132 * In: 3133 * This fiber must be in state TERM, and have a valid function/delegate. 3134 */ 3135 final void reset() 3136 in 3137 { 3138 assert( m_call != Call.NO ); 3139 assert( m_state == State.TERM ); 3140 assert( m_ctxt.tstack == m_ctxt.bstack ); 3141 } 3142 body 3143 { 3144 m_state = State.HOLD; 3145 initStack(); 3146 m_unhandled = null; 3147 } 3148 3149 /** 3150 * Reinitializes a fiber object which is associated with a static 3151 * D function. 3152 * 3153 * Params: 3154 * fn = The thread function. 3155 * 3156 * In: 3157 * This fiber must be in state TERM. 3158 * fn must not be null. 3159 */ 3160 final void reset( void function() fn ) 3161 in 3162 { 3163 assert( fn ); 3164 assert( m_state == State.TERM ); 3165 assert( m_ctxt.tstack == m_ctxt.bstack ); 3166 } 3167 body 3168 { 3169 m_fn = fn; 3170 m_call = Call.FN; 3171 m_state = State.HOLD; 3172 initStack(); 3173 m_unhandled = null; 3174 } 3175 3176 3177 /** 3178 * Reinitializes a fiber object which is associated with a dynamic 3179 * D function. 3180 * 3181 * Params: 3182 * dg = The thread function. 3183 * 3184 * In: 3185 * This fiber must be in state TERM. 3186 * dg must not be null. 3187 */ 3188 final void reset( void delegate() dg ) 3189 in 3190 { 3191 assert( dg ); 3192 assert( m_state == State.TERM ); 3193 assert( m_ctxt.tstack == m_ctxt.bstack ); 3194 } 3195 body 3196 { 3197 m_dg = dg; 3198 m_call = Call.DG; 3199 m_state = State.HOLD; 3200 initStack(); 3201 m_unhandled = null; 3202 } 3203 3204 /** 3205 * Clears the fiber from all references to a previous call (unhandled exceptions, delegate) 3206 * 3207 * In: 3208 * This fiber must be in state TERM. 3209 */ 3210 final void clear() 3211 in 3212 { 3213 assert( m_state == State.TERM ); 3214 assert( m_ctxt.tstack == m_ctxt.bstack ); 3215 } 3216 body 3217 { 3218 if (m_state != State.TERM){ 3219 char[20] buf; 3220 throw new Exception("Fiber@"~ptrToStr(cast(size_t)cast(void*)this,buf).idup~" in unexpected state "~ptrToStr(m_state,buf).idup,__FILE__,__LINE__); 3221 } 3222 if (m_ctxt.tstack != m_ctxt.bstack){ 3223 char[20] buf; 3224 throw new Exception("Fiber@"~ptrToStr(cast(size_t)cast(void*)this,buf).idup~" bstack="~ptrToStr(cast(size_t)cast(void*)m_ctxt.bstack,buf).idup~" != tstack="~ptrToStr(cast(size_t)cast(void*)m_ctxt.tstack,buf).idup,__FILE__,__LINE__); 3225 } 3226 m_dg = null; 3227 m_fn = null; 3228 m_call = Call.NO; 3229 m_state = State.TERM; 3230 m_unhandled = null; 3231 } 3232 3233 3234 //////////////////////////////////////////////////////////////////////////// 3235 // General Properties 3236 //////////////////////////////////////////////////////////////////////////// 3237 3238 3239 /** 3240 * A fiber may occupy one of three states: HOLD, EXEC, and TERM. The HOLD 3241 * state applies to any fiber that is suspended and ready to be called. 3242 * The EXEC state will be set for any fiber that is currently executing. 3243 * And the TERM state is set when a fiber terminates. Once a fiber 3244 * terminates, it must be reset before it may be called again. 3245 */ 3246 enum State 3247 { 3248 HOLD, /// 3249 EXEC, /// 3250 TERM /// 3251 } 3252 3253 3254 /** 3255 * Gets the current state of this fiber. 3256 * 3257 * Returns: 3258 * The state of this fiber as an enumerated value. 3259 */ 3260 const final State state() 3261 { 3262 return m_state; 3263 } 3264 3265 const size_t stackSize(){ 3266 return m_size; 3267 } 3268 3269 3270 //////////////////////////////////////////////////////////////////////////// 3271 // Actions on Calling Fiber 3272 //////////////////////////////////////////////////////////////////////////// 3273 3274 3275 /** 3276 * Forces a context switch to occur away from the calling fiber. 3277 */ 3278 final void cede () 3279 { 3280 assert( m_state == State.EXEC ); 3281 3282 static if( is( ucontext_t ) ) 3283 m_ucur = &m_utxt; 3284 3285 m_state = State.HOLD; 3286 switchOut(); 3287 m_state = State.EXEC; 3288 } 3289 3290 3291 /** 3292 * Forces a context switch to occur away from the calling fiber. 3293 */ 3294 static void yield() 3295 { 3296 Fiber cur = getThis; 3297 assert( cur, "Fiber.yield() called with no active fiber" ); 3298 if (cur.event.scheduler) 3299 cur.event.scheduler.pause (0); 3300 else 3301 cur.cede; 3302 } 3303 3304 /** 3305 * Forces a context switch to occur away from the calling fiber and then 3306 * throws obj in the calling fiber. 3307 * 3308 * Params: 3309 * obj = The object to throw. 3310 * 3311 * In: 3312 * obj must not be null. 3313 */ 3314 static void yieldAndThrow( Throwable obj ) 3315 in 3316 { 3317 assert( obj ); 3318 } 3319 body 3320 { 3321 Fiber cur = getThis(); 3322 assert( cur, "Fiber.yield(obj) called with no active fiber" ); 3323 cur.m_unhandled = obj; 3324 if (cur.event.scheduler) 3325 cur.event.scheduler.pause (0); 3326 else 3327 cur.cede; 3328 } 3329 3330 3331 //////////////////////////////////////////////////////////////////////////// 3332 // Fiber Accessors 3333 //////////////////////////////////////////////////////////////////////////// 3334 3335 3336 /** 3337 * Provides a reference to the calling fiber or null if no fiber is 3338 * currently active. 3339 * 3340 * Returns: 3341 * The fiber object representing the calling fiber or null if no fiber 3342 * is currently active. The result of deleting this object is undefined. 3343 */ 3344 static Fiber getThis() 3345 { 3346 version( Win32 ) 3347 { 3348 return cast(Fiber) TlsGetValue( sm_this ); 3349 } 3350 else version( Posix ) 3351 { 3352 return cast(Fiber) pthread_getspecific( sm_this ); 3353 } 3354 } 3355 3356 3357 //////////////////////////////////////////////////////////////////////////// 3358 // Static Initialization 3359 //////////////////////////////////////////////////////////////////////////// 3360 3361 3362 static this() 3363 { 3364 version( Win32 ) 3365 { 3366 sm_this = TlsAlloc(); 3367 assert( sm_this != TLS_OUT_OF_INDEXES ); 3368 } 3369 else version( Posix ) 3370 { 3371 int status; 3372 3373 status = pthread_key_create( &sm_this, null ); 3374 assert( status == 0 ); 3375 3376 static if( is( ucontext_t ) ) 3377 { 3378 status = getcontext( &sm_utxt ); 3379 assert( status == 0 ); 3380 } 3381 } 3382 } 3383 3384 3385 private: 3386 // 3387 // Initializes a fiber object which has no associated executable function. 3388 // 3389 this() 3390 { 3391 m_call = Call.NO; 3392 } 3393 3394 3395 // 3396 // Fiber entry point. Invokes the function or delegate passed on 3397 // construction (if any). 3398 // 3399 final void run() 3400 { 3401 switch( m_call ) 3402 { 3403 case Call.FN: 3404 m_fn(); 3405 break; 3406 case Call.DG: 3407 m_dg(); 3408 break; 3409 default: 3410 break; 3411 } 3412 } 3413 3414 3415 private: 3416 // 3417 // The type of routine passed on fiber construction. 3418 // 3419 enum Call 3420 { 3421 NO, 3422 FN, 3423 DG 3424 } 3425 3426 3427 // 3428 // Standard fiber data 3429 // 3430 Call m_call; 3431 union 3432 { 3433 void function() m_fn; 3434 void delegate() m_dg; 3435 } 3436 bool m_isRunning; 3437 Throwable m_unhandled; 3438 State m_state; 3439 char[] m_name; 3440 public: 3441 Event event; 3442 3443 3444 private: 3445 //////////////////////////////////////////////////////////////////////////// 3446 // Stack Management 3447 //////////////////////////////////////////////////////////////////////////// 3448 3449 3450 // 3451 // Allocate a new stack for this fiber. 3452 // 3453 final void allocStack( size_t sz ) 3454 in 3455 { 3456 assert( !m_pmem && !m_ctxt ); 3457 } 3458 body 3459 { 3460 // adjust alloc size to a multiple of PAGESIZE 3461 sz += PAGESIZE - 1; 3462 sz -= sz % PAGESIZE; 3463 3464 // NOTE: This instance of Thread.Context is dynamic so Fiber objects 3465 // can be collected by the GC so long as no user level references 3466 // to the object exist. If m_ctxt were not dynamic then its 3467 // presence in the global context list would be enough to keep 3468 // this object alive indefinitely. An alternative to allocating 3469 // room for this struct explicitly would be to mash it into the 3470 // base of the stack being allocated below. However, doing so 3471 // requires too much special logic to be worthwhile. 3472 m_ctxt = new Thread.Context; 3473 3474 static if( is( typeof( VirtualAlloc ) ) ) 3475 { 3476 // reserve memory for stack 3477 m_pmem = VirtualAlloc( null, 3478 sz + PAGESIZE, 3479 MEM_RESERVE, 3480 PAGE_NOACCESS ); 3481 if( !m_pmem ) 3482 { 3483 throw new FiberException( "Unable to reserve memory for stack" ); 3484 } 3485 3486 version( StackGrowsDown ) 3487 { 3488 void* stack = m_pmem + PAGESIZE; 3489 void* guard = m_pmem; 3490 void* pbase = stack + sz; 3491 } 3492 else 3493 { 3494 void* stack = m_pmem; 3495 void* guard = m_pmem + sz; 3496 void* pbase = stack; 3497 } 3498 3499 // allocate reserved stack segment 3500 stack = VirtualAlloc( stack, 3501 sz, 3502 MEM_COMMIT, 3503 PAGE_READWRITE ); 3504 if( !stack ) 3505 { 3506 throw new FiberException( "Unable to allocate memory for stack" ); 3507 } 3508 3509 // allocate reserved guard page 3510 guard = VirtualAlloc( guard, 3511 PAGESIZE, 3512 MEM_COMMIT, 3513 PAGE_READWRITE | PAGE_GUARD ); 3514 if( !guard ) 3515 { 3516 throw new FiberException( "Unable to create guard page for stack" ); 3517 } 3518 3519 m_ctxt.bstack = pbase; 3520 m_ctxt.tstack = pbase; 3521 m_size = sz; 3522 } 3523 else 3524 { static if( is( typeof( mmap ) ) ) 3525 { 3526 m_pmem = mmap( null, 3527 sz, 3528 PROT_READ | PROT_WRITE, 3529 MAP_PRIVATE | MAP_ANON, 3530 -1, 3531 0 ); 3532 if( m_pmem == MAP_FAILED ) 3533 m_pmem = null; 3534 } 3535 else static if( is( typeof( valloc ) ) ) 3536 { 3537 m_pmem = valloc( sz ); 3538 } 3539 else static if( is( typeof( malloc ) ) ) 3540 { 3541 m_pmem = malloc( sz ); 3542 } 3543 else 3544 { 3545 m_pmem = null; 3546 } 3547 3548 if( !m_pmem ) 3549 { 3550 throw new FiberException( "Unable to allocate memory for stack" ); 3551 } 3552 3553 version( StackGrowsDown ) 3554 { 3555 m_ctxt.bstack = m_pmem + sz; 3556 m_ctxt.tstack = m_pmem + sz; 3557 } 3558 else 3559 { 3560 m_ctxt.bstack = m_pmem; 3561 m_ctxt.tstack = m_pmem; 3562 } 3563 m_size = sz; 3564 } 3565 3566 Thread.add( m_ctxt ); 3567 } 3568 3569 3570 // 3571 // Free this fiber's stack. 3572 // 3573 final void freeStack() 3574 in 3575 { 3576 assert( m_pmem && m_ctxt ); 3577 } 3578 body 3579 { 3580 // NOTE: Since this routine is only ever expected to be called from 3581 // the dtor, pointers to freed data are not set to null. 3582 3583 // NOTE: m_ctxt is guaranteed to be alive because it is held in the 3584 // global context list. 3585 Thread.remove( m_ctxt ); 3586 3587 static if( is( typeof( VirtualAlloc ) ) ) 3588 { 3589 VirtualFree( m_pmem, 0, MEM_RELEASE ); 3590 } 3591 else static if( is( typeof( mmap ) ) ) 3592 { 3593 munmap( m_pmem, m_size ); 3594 } 3595 else static if( is( typeof( valloc ) ) ) 3596 { 3597 free( m_pmem ); 3598 } 3599 else static if( is( typeof( malloc ) ) ) 3600 { 3601 free( m_pmem ); 3602 } 3603 delete m_ctxt; 3604 } 3605 3606 3607 // 3608 // Initialize the allocated stack. 3609 // 3610 final void initStack() 3611 in 3612 { 3613 assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack ); 3614 assert( cast(size_t) m_ctxt.bstack % (void*).sizeof == 0 ); 3615 } 3616 body 3617 { 3618 void* pstack = m_ctxt.tstack; 3619 scope( exit ) m_ctxt.tstack = pstack; 3620 3621 void push( size_t val ) 3622 { 3623 version( StackGrowsDown ) 3624 { 3625 pstack -= size_t.sizeof; 3626 *(cast(size_t*) pstack) = val; 3627 } 3628 else 3629 { 3630 pstack += size_t.sizeof; 3631 *(cast(size_t*) pstack) = val; 3632 } 3633 } 3634 3635 // NOTE: On OS X the stack must be 16-byte aligned according to the 3636 // IA-32 call spec. 3637 version(OSX) 3638 { 3639 pstack = cast(void*)(cast(uint)(pstack) - (cast(uint)(pstack) & 0x0F)); 3640 } 3641 3642 version( AsmX86_Win32 ) 3643 { 3644 push( cast(size_t) &fiber_entryPoint ); // EIP 3645 push( 0xFFFFFFFF ); // EBP 3646 push( 0x00000000 ); // EAX 3647 push( 0xFFFFFFFF ); // FS:[0] 3648 version( StackGrowsDown ) 3649 { 3650 push( cast(size_t) m_ctxt.bstack ); // FS:[4] 3651 push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8] 3652 } 3653 else 3654 { 3655 push( cast(size_t) m_ctxt.bstack ); // FS:[4] 3656 push( cast(size_t) m_ctxt.bstack + m_size ); // FS:[8] 3657 } 3658 push( 0x00000000 ); // EBX 3659 push( 0x00000000 ); // ESI 3660 push( 0x00000000 ); // EDI 3661 } 3662 else version( AsmX86_Posix ) 3663 { 3664 push( 0x00000000 ); // strange pre EIP 3665 push( cast(size_t) &fiber_entryPoint ); // EIP 3666 push( (cast(size_t)pstack)+8 ); // EBP 3667 push( 0x00000000 ); // EAX 3668 push( getEBX() ); // EBX used for PIC code 3669 push( 0x00000000 ); // ECX just to have it aligned... 3670 push( 0x00000000 ); // ESI 3671 push( 0x00000000 ); // EDI 3672 } 3673 else version( AsmX86_64_Posix ) 3674 { 3675 push( 0x00000000 ); // strange pre EIP 3676 push( cast(size_t) &fiber_entryPoint ); // RIP 3677 push( (cast(size_t)pstack)+8 ); // RBP 3678 push( 0x00000000_00000000 ); // RBX 3679 push( 0x00000000_00000000 ); // R12 3680 push( 0x00000000_00000000 ); // R13 3681 push( 0x00000000_00000000 ); // R14 3682 push( 0x00000000_00000000 ); // R15 3683 push( 0x00001f80_0000037f ); // MXCSR (32 bits), unused (16 bits) , x87 control (16 bits) 3684 } 3685 else version( AsmPPC_Posix ) 3686 { 3687 version( StackGrowsDown ) 3688 { 3689 pstack -= int.sizeof * 5; 3690 } 3691 else 3692 { 3693 pstack += int.sizeof * 5; 3694 } 3695 3696 push( cast(size_t) &fiber_entryPoint ); // link register 3697 push( 0x00000000 ); // control register 3698 push( 0x00000000 ); // old stack pointer 3699 3700 // GPR values 3701 version( StackGrowsDown ) 3702 { 3703 pstack -= int.sizeof * 20; 3704 } 3705 else 3706 { 3707 pstack += int.sizeof * 20; 3708 } 3709 3710 assert( (cast(uint) pstack & 0x0f) == 0 ); 3711 } 3712 else static if( is( ucontext_t ) ) 3713 { 3714 getcontext( &m_utxt ); 3715 // patch from #1707 - thanks to jerdfelt 3716 //m_utxt.uc_stack.ss_sp = m_ctxt.bstack; 3717 m_utxt.uc_stack.ss_sp = m_pmem; 3718 m_utxt.uc_stack.ss_size = m_size; 3719 makecontext( &m_utxt, &fiber_entryPoint, 0 ); 3720 // NOTE: If ucontext is being used then the top of the stack will 3721 // be a pointer to the ucontext_t struct for that fiber. 3722 push( cast(size_t) &m_utxt ); 3723 } 3724 } 3725 3726 3727 public Thread.Context* m_ctxt; 3728 public size_t m_size; 3729 void* m_pmem; 3730 3731 static if( is( ucontext_t ) ) 3732 { 3733 // NOTE: The static ucontext instance is used to represent the context 3734 // of the main application thread. 3735 static __gshared ucontext_t sm_utxt = void; 3736 ucontext_t m_utxt = void; 3737 ucontext_t* m_ucur = null; 3738 } 3739 3740 3741 private: 3742 //////////////////////////////////////////////////////////////////////////// 3743 // Storage of Active Fiber 3744 //////////////////////////////////////////////////////////////////////////// 3745 3746 3747 // 3748 // Sets a thread-local reference to the current fiber object. 3749 // 3750 static void setThis( Fiber f ) 3751 { 3752 version( Win32 ) 3753 { 3754 TlsSetValue( sm_this, cast(void*) f ); 3755 } 3756 else version( Posix ) 3757 { 3758 pthread_setspecific( sm_this, cast(void*) f ); 3759 } 3760 } 3761 3762 3763 static __gshared Thread.TLSKey sm_this; 3764 3765 3766 private: 3767 //////////////////////////////////////////////////////////////////////////// 3768 // Context Switching 3769 //////////////////////////////////////////////////////////////////////////// 3770 3771 3772 // 3773 // Switches into the stack held by this fiber. 3774 // 3775 final void switchIn() 3776 { 3777 Thread tobj = Thread.getThis(); 3778 void** oldp = &tobj.m_curr.tstack; 3779 void* newp = m_ctxt.tstack; 3780 3781 // NOTE: The order of operations here is very important. The current 3782 // stack top must be stored before m_lock is set, and pushContext 3783 // must not be called until after m_lock is set. This process 3784 // is intended to prevent a race condition with the suspend 3785 // mechanism used for garbage collection. If it is not followed, 3786 // a badly timed collection could cause the GC to scan from the 3787 // bottom of one stack to the top of another, or to miss scanning 3788 // a stack that still contains valid data. The old stack pointer 3789 // oldp will be set again before the context switch to guarantee 3790 // that it points to exactly the correct stack location so the 3791 // successive pop operations will succeed. 3792 *oldp = getStackTop(); 3793 volatile tobj.m_lock = true; 3794 tobj.pushContext( m_ctxt ); 3795 3796 fiber_switchContext( oldp, newp ); 3797 3798 // NOTE: As above, these operations must be performed in a strict order 3799 // to prevent Bad Things from happening. 3800 tobj.popContext(); 3801 volatile tobj.m_lock = false; 3802 tobj.m_curr.tstack = tobj.m_curr.bstack; 3803 } 3804 3805 3806 // 3807 // Switches out of the current stack and into the enclosing stack. 3808 // 3809 final void switchOut() 3810 { 3811 Thread tobj = Thread.getThis(); 3812 void** oldp = &m_ctxt.tstack; 3813 void* newp = tobj.m_curr.within.tstack; 3814 3815 // NOTE: The order of operations here is very important. The current 3816 // stack top must be stored before m_lock is set, and pushContext 3817 // must not be called until after m_lock is set. This process 3818 // is intended to prevent a race condition with the suspend 3819 // mechanism used for garbage collection. If it is not followed, 3820 // a badly timed collection could cause the GC to scan from the 3821 // bottom of one stack to the top of another, or to miss scanning 3822 // a stack that still contains valid data. The old stack pointer 3823 // oldp will be set again before the context switch to guarantee 3824 // that it points to exactly the correct stack location so the 3825 // successive pop operations will succeed. 3826 *oldp = getStackTop(); 3827 volatile tobj.m_lock = true; 3828 3829 fiber_switchContext( oldp, newp ); 3830 3831 // NOTE: As above, these operations must be performed in a strict order 3832 // to prevent Bad Things from happening. 3833 tobj=Thread.getThis(); 3834 volatile tobj.m_lock = false; 3835 tobj.m_curr.tstack = tobj.m_curr.bstack; 3836 } 3837 } 3838 } 3839 +/