Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Reimplemented Tcl_ConditionNotify and Tcl_ConditionWait based on an algorithm contributed by Jim Davidson. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | core-8-1-branch-old |
Files: | files | file ages | folders |
SHA1: |
87deb82e27548abdf5aae8a7f5a91786 |
User & Date: | welch 1999-04-01 23:25:19.000 |
Context
1999-04-02
| ||
00:54 | Fix previous patch on Solaris, need to provide the Tcl package before calling Tcl_InitStubs(). check-in: c8e8856000 user: redman tags: core-8-1-branch-old | |
1999-04-01
| ||
23:25 | Reimplemented Tcl_ConditionNotify and Tcl_ConditionWait based on an algorithm contributed by Jim Dav... check-in: 87deb82e27 user: welch tags: core-8-1-branch-old | |
23:23 | Deleted unused code. check-in: e6c75f634f user: welch tags: core-8-1-branch-old | |
Changes
Changes to win/tclWinThrd.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * tclWinThread.c -- * * This file implements the Windows-specific thread operations. * * Copyright (c) 1998 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tclWinThrd.c 1.13 98/02/18 14:00:23 */ | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* * tclWinThread.c -- * * This file implements the Windows-specific thread operations. * * Copyright (c) 1998 by Sun Microsystems, Inc. * Copyright (c) 1999 by Scriptics Corporation * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tclWinThrd.c 1.13 98/02/18 14:00:23 */ |
︙ | ︙ | |||
32 33 34 35 36 37 38 | * This is the master lock used to serialize initialization and finalization * of Tcl as a whole. */ static CRITICAL_SECTION initLock; /* | > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | * This is the master lock used to serialize initialization and finalization * of Tcl as a whole. */ static CRITICAL_SECTION initLock; /* * Condition variables are implemented with a combination of a * per-thread Windows Event and a per-condition waiting queue. * The idea is that each thread has its own Event that it waits * on when it is doing a ConditionWait; it uses the same event for * all condition variables because it only waits on one at a time. * Each condition variable has a queue of waiting threads, and a * mutex used to serialize access to this queue. * * Special thanks to David Nichols and * Jim Davidson for advice on the Condition Variable implementation. */ /* * The per-thread event and queue pointers. */ typedef struct ThreadSpecificData { HANDLE condEvent; /* Per-thread condition event */ struct ThreadSpecificData *nextPtr; /* Queue pointers */ struct ThreadSpecificData *prevPtr; int flags; /* See flags below */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; /* * State bits for the thread. * WIN_THREAD_UNINIT Uninitialized. Must be zero because * of the way ThreadSpecificData is created. * WIN_THREAD_RUNNING Running, not waiting. * WIN_THREAD_BLOCKED Waiting, or trying to wait. * WIN_THREAD_DEAD Dying - no per-thread event anymore. */ #define WIN_THREAD_UNINIT 0x0 #define WIN_THREAD_RUNNING 0x1 #define WIN_THREAD_BLOCKED 0x2 #define WIN_THREAD_DEAD 0x4 /* * The per condition queue pointers and the * Mutex used to serialize access to the queue. */ typedef struct WinCondition { CRITICAL_SECTION condLock; /* Lock to serialize queuing on the condition */ struct ThreadSpecificData *firstPtr; /* Queue pointers */ struct ThreadSpecificData *lastPtr; } WinCondition; static void *FinalizeConditionEvent(ClientData data); /* *---------------------------------------------------------------------- * * TclpThreadCreate -- * |
︙ | ︙ | |||
409 410 411 412 413 414 415 | * *---------------------------------------------------------------------- */ void TclpThreadDataKeyInit(keyPtr) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, | | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | * *---------------------------------------------------------------------- */ void TclpThreadDataKeyInit(keyPtr) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, * really (DWORD **) */ { DWORD *indexPtr; MASTER_LOCK; if (*keyPtr == NULL) { indexPtr = (DWORD *)ckalloc(sizeof(DWORD)); *indexPtr = TlsAlloc(); |
︙ | ︙ | |||
574 575 576 577 578 579 580 | * and initialize this the first time this Tcl_Condition is used. * *---------------------------------------------------------------------- */ void Tcl_ConditionWait(condPtr, mutexPtr, timePtr) | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | > | | | | > | > | | > > > > | < < < < < > > | < < < | > > > > > > | > > > > > > > > | | > | > > | > > > > > > > > > | > > > > > > > > > > > > > | > > | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | * and initialize this the first time this Tcl_Condition is used. * *---------------------------------------------------------------------- */ void Tcl_ConditionWait(condPtr, mutexPtr, timePtr) Tcl_Condition *condPtr; /* Really (WinCondition **) */ Tcl_Mutex *mutexPtr; /* Really (CRITICAL_SECTION **) */ Tcl_Time *timePtr; /* Timeout on waiting period */ { WinCondition *winCondPtr; /* Per-condition queue head */ CRITICAL_SECTION *csPtr; /* Caller's Mutex, after casting */ DWORD wtime; /* Windows time value */ int timeout; /* True if we got a timeout */ int doExit = 0; /* True if we need to do exit setup */ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (tsdPtr->flags & WIN_THREAD_DEAD) { /* * No more per-thread event on which to wait. */ return; } /* * Self initialize the two parts of the contition. * The per-condition and per-thread parts need to be * handled independently. */ if (tsdPtr->flags == WIN_THREAD_UNINIT) { MASTER_LOCK; /* * Create the per-thread event and queue pointers. */ if (tsdPtr->flags == WIN_THREAD_UNINIT) { tsdPtr->condEvent = CreateEvent(NULL, TRUE /* manual reset */, FALSE /* non signaled */, NULL); tsdPtr->nextPtr = NULL; tsdPtr->prevPtr = NULL; tsdPtr->flags = WIN_THREAD_RUNNING; doExit = 1; } MASTER_UNLOCK; if (doExit) { /* * Create a per-thread exit handler to clean up the condEvent. * We must be careful do do this outside the Master Lock * because Tcl_CreateThreadExitHandler uses its own * ThreadSpecificData, and initializing that may drop * back into the Master Lock. */ Tcl_CreateThreadExitHandler(FinalizeConditionEvent, tsdPtr); } } if (*condPtr == NULL) { MASTER_LOCK; /* * Initialize the per-condition queue pointers and Mutex. */ if (*condPtr == NULL) { winCondPtr = (WinCondition *)ckalloc(sizeof(WinCondition)); InitializeCriticalSection(&winCondPtr->condLock); winCondPtr->firstPtr = NULL; winCondPtr->lastPtr = NULL; *condPtr = (Tcl_Condition)winCondPtr; TclRememberCondition(condPtr); } MASTER_UNLOCK; } csPtr = *((CRITICAL_SECTION **)mutexPtr); winCondPtr = *((WinCondition **)condPtr); if (timePtr == NULL) { wtime = INFINITE; } else { wtime = timePtr->sec * 1000 + timePtr->usec / 1000; } /* * Queue the thread on the condition, using * the per-condition lock for serialization. */ tsdPtr->flags = WIN_THREAD_BLOCKED; tsdPtr->nextPtr = NULL; EnterCriticalSection(&winCondPtr->condLock); tsdPtr->prevPtr = winCondPtr->lastPtr; /* A: */ winCondPtr->lastPtr = tsdPtr; if (tsdPtr->prevPtr != NULL) { tsdPtr->prevPtr->nextPtr = tsdPtr; } if (winCondPtr->firstPtr == NULL) { winCondPtr->firstPtr = tsdPtr; } /* * Unlock the caller's mutex and wait for the condition, or a timeout. * There is a minor issue here in that we don't count down the * timeout if we get notified, but another thread grabs the condition * before we do. In that race condition we'll wait again for the * full timeout. Timed waits are dubious anyway. Either you have * the locking protocol wrong and are masking a deadlock, * or you are using conditions to pause your thread. */ LeaveCriticalSection(csPtr); timeout = 0; while (!timeout && (tsdPtr->flags & WIN_THREAD_BLOCKED)) { ResetEvent(tsdPtr->condEvent); LeaveCriticalSection(&winCondPtr->condLock); if (WaitForSingleObject(tsdPtr->condEvent, wtime) == WAIT_TIMEOUT) { timeout = 1; } EnterCriticalSection(&winCondPtr->condLock); } /* * Be careful on timeouts because the signal might arrive right around * time time limit and someone else could have taken us off the queue. */ if (timeout) { if (tsdPtr->flags & WIN_THREAD_RUNNING) { timeout = 0; } else { /* * When dequeuing, we can leave the tsdPtr->nextPtr * and tsdPtr->prevPtr with dangling pointers because * they are reinitialilzed w/out reading them when the * thread is enqueued later. */ if (winCondPtr->firstPtr == tsdPtr) { winCondPtr->firstPtr = tsdPtr->nextPtr; } else { tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; } if (winCondPtr->lastPtr == tsdPtr) { winCondPtr->lastPtr = tsdPtr->prevPtr; } else { tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; } tsdPtr->flags = WIN_THREAD_RUNNING; } } LeaveCriticalSection(&winCondPtr->condLock); EnterCriticalSection(csPtr); } /* *---------------------------------------------------------------------- * * Tcl_ConditionNotify -- |
︙ | ︙ | |||
662 663 664 665 666 667 668 | *---------------------------------------------------------------------- */ void Tcl_ConditionNotify(condPtr) Tcl_Condition *condPtr; { | | > | < | | | < < > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | *---------------------------------------------------------------------- */ void Tcl_ConditionNotify(condPtr) Tcl_Condition *condPtr; { WinCondition *winCondPtr; ThreadSpecificData *tsdPtr; if (condPtr != NULL) { winCondPtr = *((WinCondition **)condPtr); /* * Loop through all the threads waiting on the condition * and notify them (i.e., broadcast semantics). The queue * manipulation is guarded by the per-condition coordinating mutex. */ EnterCriticalSection(&winCondPtr->condLock); while (winCondPtr->firstPtr != NULL) { tsdPtr = winCondPtr->firstPtr; winCondPtr->firstPtr = tsdPtr->nextPtr; if (winCondPtr->lastPtr == tsdPtr) { winCondPtr->lastPtr = NULL; } tsdPtr->flags = WIN_THREAD_RUNNING; tsdPtr->nextPtr = NULL; tsdPtr->prevPtr = NULL; /* Not strictly necessary, see A: */ SetEvent(tsdPtr->condEvent); } LeaveCriticalSection(&winCondPtr->condLock); } else { /* * Noone has used the condition variable, so there are no waiters. */ } } /* *---------------------------------------------------------------------- * * FinalizeConditionEvent -- * * This procedure is invoked to clean up the per-thread * event used to implement condition waiting. * This is only safe to call at the end of time. * * Results: * None. * * Side effects: * The per-thread event is closed. * *---------------------------------------------------------------------- */ static void * FinalizeConditionEvent(data) ClientData data; { ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data; tsdPtr->flags = WIN_THREAD_DEAD; CloseHandle(tsdPtr->condEvent); } /* *---------------------------------------------------------------------- * * TclpFinalizeCondition -- * * This procedure is invoked to clean up a condition variable. |
︙ | ︙ | |||
708 709 710 711 712 713 714 | *---------------------------------------------------------------------- */ void TclpFinalizeCondition(condPtr) Tcl_Condition *condPtr; { | > | > > > > > > > | < | < < | 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 | *---------------------------------------------------------------------- */ void TclpFinalizeCondition(condPtr) Tcl_Condition *condPtr; { WinCondition *winCondPtr = *(WinCondition **)condPtr; /* * Note - this is called long after the thread-local storage is * reclaimed. The per-thread condition waiting event is * reclaimed earlier in a per-thread exit handler, which is * called before thread local storage is reclaimed. */ if (winCondPtr != NULL) { ckfree((char *)winCondPtr); *condPtr = NULL; } } #endif /* TCL_THREADS */ |