Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | New scheme for keeping the per-process tcl_precision value in sync without the need for mutex locks on every read. Uses adapted ProcessGlobalValue machinery backported from Tcl 8.5 where it's been working without reported problems. Thanks to Phil Brooks for reporting on tests which highlight the thread performance problems raised by the old scheme, and to Clif Flynt for further testing pointing the finger at tcl_precision locks as the main culprit. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | core-8-4-branch |
Files: | files | file ages | folders |
SHA1: |
02ef6b18c8ba8bec37414a1f0286f226 |
User & Date: | dgp 2013-03-04 15:38:01 |
Context
2013-03-05
| ||
19:39 | Remove TclInitCompilation() declaration that's never had a definition (14 years!). check-in: 543d9b10bd user: dgp tags: core-8-4-branch | |
14:38 | Contributed patch from Tom Lane <[email protected]>. Merge conflicts due to different coding style a... check-in: 59666f739d user: dgp tags: bug-3606683-84 | |
2013-03-04
| ||
16:08 | merge core-8-4-branch check-in: 004c19de41 user: jan.nijtmans tags: bug-3562640 | |
15:40 | merge mark check-in: 86a398db8f user: dgp tags: core-8-5-branch | |
15:38 | New scheme for keeping the per-process tcl_precision value in sync without the need for mutex locks ... check-in: 02ef6b18c8 user: dgp tags: core-8-4-branch | |
2013-02-27
| ||
15:15 | A bit more tidiness expressing the new test expression. check-in: 099cf26224 user: dgp tags: core-8-4-branch | |
Changes
Changes to ChangeLog.
1 2 3 4 5 6 7 | 2013-02-27 Jan Nijtmans <[email protected]> * generic/regcomp.c: [Bug 3606139]: missing error check allows * tests/regexp.test: regexp to crash Tcl. Thanks to Tom Lane for providing the test-case and the patch. 2013-02-22 Don Porter <[email protected]> | > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 2013-03-04 Don Porter <[email protected]> * generic/tclUtil.c: New scheme for keeping the per-process tcl_precision value in sync without the need for mutex locks on every read. Uses adapted ProcessGlobalValue machinery backported from Tcl 8.5 where it's been working without reported problems. Thanks to Phil Brooks for reporting on tests which highlight the thread performance problems raised by the old scheme, and to Clif Flynt for further testing pointing the finger at tcl_precision locks as the main culprit. 2013-02-27 Jan Nijtmans <[email protected]> * generic/regcomp.c: [Bug 3606139]: missing error check allows * tests/regexp.test: regexp to crash Tcl. Thanks to Tom Lane for providing the test-case and the patch. 2013-02-22 Don Porter <[email protected]> |
︙ | ︙ |
Changes to generic/tclUtil.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | * BRACES_UNMATCHED - 1 means that braces aren't properly matched * in the argument. */ #define USE_BRACES 2 #define BRACES_UNMATCHED 4 /* * The following values determine the precision used when converting * floating-point values to strings. This information is linked to all * of the tcl_precision variables in all interpreters via the procedure * TclPrecTraceProc. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < < < | > > > > > > > > | 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 97 98 99 100 101 102 103 104 105 106 107 108 109 | * BRACES_UNMATCHED - 1 means that braces aren't properly matched * in the argument. */ #define USE_BRACES 2 #define BRACES_UNMATCHED 4 /* * Data structures for process-global values. */ typedef void (InitPGVProc) _ANSI_ARGS_ ((char **valuePtr, int *lengthPtr)); /* * A ProcessGlobalValue struct exists for each internal value in Tcl that is * to be shared among several threads. Each thread sees a (Tcl_Obj) copy of * the value, and the master is kept as a counted string, with epoch and mutex * control. Each ProcessGlobalValue struct should be a static variable in some * file. */ typedef struct ProcessGlobalValue { int epoch; /* Epoch counter to detect changes in the * master value. */ int numBytes; /* Length of the master string. */ char *value; /* The master string value. */ InitPGVProc *proc; /* A procedure to initialize the master string * copy when a "get" request comes in before * any "set" request has been received. */ Tcl_Mutex mutex; /* Enforce orderly access from multiple * threads. */ Tcl_ThreadDataKey key; /* Key for per-thread data holding the * (Tcl_Obj) copy for each thread. */ } PGV; /* * The following values determine the precision used when converting * floating-point values to strings. This information is linked to all * of the tcl_precision variables in all interpreters via the procedure * TclPrecTraceProc. */ static InitPGVProc InitPrecision; static PGV precision = { 0, 0, NULL, InitPrecision, NULL, NULL }; /* * Prototypes for procedures defined later in this file. */ static void ClearHash _ANSI_ARGS_((Tcl_HashTable *tablePtr)); static void FreePGV _ANSI_ARGS_((ClientData clientData)); static void FreeThreadHash _ANSI_ARGS_((ClientData clientData)); static Tcl_HashTable * GetThreadHash _ANSI_ARGS_(( Tcl_ThreadDataKey *keyPtr)); static void UpdateStringOfEndOffset _ANSI_ARGS_((Tcl_Obj* objPtr)); static int SetEndOffsetFromAny _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Obj* objPtr)); static void SetPGV _ANSI_ARGS_((PGV *pgvPtr, Tcl_Obj *newValue)); static Tcl_Obj * GetPGV _ANSI_ARGS_((PGV *pgvPtr)); /* * The following is the Tcl object type definition for an object * that represents a list index in the form, "end-offset". It is * used as a performance optimization in TclGetIntForIndex. The * internal rep is an integer, so no memory management is required * for it. |
︙ | ︙ | |||
1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 | { Tcl_DStringAppend(dsPtr, "}", -1); } /* *---------------------------------------------------------------------- * * Tcl_PrintDouble -- * * Given a floating-point value, this procedure converts it to * an ASCII string using. * * Results: * The ASCII equivalent of "value" is written at "dst". It is | > > > > > > > > > > > > > > > > > > > > > > > > > > | 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 | { Tcl_DStringAppend(dsPtr, "}", -1); } /* *---------------------------------------------------------------------- * * InitPrecision -- * * Set the default value for tcl_precision to 12. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void InitPrecision(valuePtr, lengthPtr) char **valuePtr; int *lengthPtr; { *lengthPtr = 2; *valuePtr = ckalloc(3); memcpy(*valuePtr, "12", 3); } /* *---------------------------------------------------------------------- * * Tcl_PrintDouble -- * * Given a floating-point value, this procedure converts it to * an ASCII string using. * * Results: * The ASCII equivalent of "value" is written at "dst". It is |
︙ | ︙ | |||
1898 1899 1900 1901 1902 1903 1904 1905 1906 | * printing. It's ignored now. */ double value; /* Value to print as string. */ char *dst; /* Where to store converted value; * must have at least TCL_DOUBLE_SPACE * characters. */ { char *p, c; Tcl_UniChar ch; | > > < > | < | 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 | * printing. It's ignored now. */ double value; /* Value to print as string. */ char *dst; /* Where to store converted value; * must have at least TCL_DOUBLE_SPACE * characters. */ { char *p, c; char format[10]; Tcl_UniChar ch; Tcl_Obj *precisionObj = GetPGV(&precision); sprintf(format, "%%.%sg", Tcl_GetString(precisionObj)); sprintf(dst, format, value); /* * If the ASCII result looks like an integer, add ".0" so that it * doesn't look like an integer anymore. This prevents floating-point * values from being converted to integers unintentionally. * Check for ASCII specifically to speed up the function. */ |
︙ | ︙ | |||
1980 1981 1982 1983 1984 1985 1986 | /* * When the variable is read, reset its value from our shared * value. This is needed in case the variable was modified in * some other interpreter so that this interpreter's value is * out of date. */ | < < | < | < | | | < < < | | 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 | /* * When the variable is read, reset its value from our shared * value. This is needed in case the variable was modified in * some other interpreter so that this interpreter's value is * out of date. */ if (flags & TCL_TRACE_READS) { Tcl_SetVar2Ex(interp, name1, name2, GetPGV(&precision), flags & TCL_GLOBAL_ONLY); return (char *) NULL; } /* * The variable is being written. Check the new value and disallow * it if it isn't reasonable or if this is a safe interpreter (we * don't want safe interpreters messing up the precision of other * interpreters). */ if (Tcl_IsSafe(interp)) { Tcl_SetVar2Ex(interp, name1, name2, GetPGV(&precision), flags & TCL_GLOBAL_ONLY); return "can't modify precision from a safe interpreter"; } value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); if (value == NULL) { value = ""; } prec = strtoul(value, &end, 10); if ((prec <= 0) || (prec > TCL_MAX_PREC) || (end == value) || (*end != 0)) { Tcl_SetVar2Ex(interp, name1, name2, GetPGV(&precision), flags & TCL_GLOBAL_ONLY); return "improper value for precision"; } SetPGV(&precision, Tcl_NewIntObj(prec)); return (char *) NULL; } /* *---------------------------------------------------------------------- * * TclNeedSpace -- |
︙ | ︙ | |||
2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 | (char *) NULL); } return 1; } } return 0; } /* *---------------------------------------------------------------------- * * Tcl_GetNameOfExecutable -- * * This procedure simply returns a pointer to the internal full | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 | (char *) NULL); } return 1; } } return 0; } /* *---------------------------------------------------------------------- * * ClearHash -- * * Remove all the entries in the hash table *tablePtr. * *---------------------------------------------------------------------- */ static void ClearHash(tablePtr) Tcl_HashTable *tablePtr; { Tcl_HashSearch search; Tcl_HashEntry *hPtr; for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_Obj *objPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); Tcl_DecrRefCount(objPtr); Tcl_DeleteHashEntry(hPtr); } } /* *---------------------------------------------------------------------- * * GetThreadHash -- * * Get a thread-specific (Tcl_HashTable *) associated with a thread data * key. * * Results: * The Tcl_HashTable * corresponding to *keyPtr. * * Side effects: * The first call on a keyPtr in each thread creates a new Tcl_HashTable, * and registers a thread exit handler to dispose of it. * *---------------------------------------------------------------------- */ static Tcl_HashTable * GetThreadHash(keyPtr) Tcl_ThreadDataKey *keyPtr; { Tcl_HashTable **tablePtrPtr = (Tcl_HashTable **) Tcl_GetThreadData(keyPtr, (int) sizeof(Tcl_HashTable *)); if (NULL == *tablePtrPtr) { *tablePtrPtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); Tcl_CreateThreadExitHandler(FreeThreadHash, (ClientData)*tablePtrPtr); Tcl_InitHashTable(*tablePtrPtr, TCL_ONE_WORD_KEYS); } return *tablePtrPtr; } /* *---------------------------------------------------------------------- * * FreeThreadHash -- * * Thread exit handler used by GetThreadHash to dispose of a thread hash * table. * * Side effects: * Frees a Tcl_HashTable. * *---------------------------------------------------------------------- */ static void FreeThreadHash(clientData) ClientData clientData; { Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientData; ClearHash(tablePtr); Tcl_DeleteHashTable(tablePtr); ckfree((char *) tablePtr); } /* *---------------------------------------------------------------------- * * FreePGV -- * * Exit handler used by (Set|Get)PGV to cleanup a PGV at exit. * *---------------------------------------------------------------------- */ static void FreePGV(clientData) ClientData clientData; { PGV *pgvPtr = (PGV *) clientData; pgvPtr->epoch++; pgvPtr->numBytes = 0; ckfree(pgvPtr->value); pgvPtr->value = NULL; Tcl_MutexFinalize(&pgvPtr->mutex); } /* *---------------------------------------------------------------------- * * SetPGV -- * * Utility routine to set a global value shared by all threads in the * process while keeping a thread-local copy as well. * *---------------------------------------------------------------------- */ static void SetPGV(pgvPtr, newValue) PGV *pgvPtr; Tcl_Obj *newValue; { CONST char *bytes; Tcl_HashTable *cacheMap; Tcl_HashEntry *hPtr; int dummy; Tcl_MutexLock(&pgvPtr->mutex); /* * Fill the global string value. */ pgvPtr->epoch++; if (NULL != pgvPtr->value) { ckfree(pgvPtr->value); } else { Tcl_CreateExitHandler(FreePGV, (ClientData) pgvPtr); } bytes = Tcl_GetStringFromObj(newValue, &pgvPtr->numBytes); pgvPtr->value = ckalloc((unsigned) pgvPtr->numBytes + 1); memcpy(pgvPtr->value, bytes, (unsigned) pgvPtr->numBytes + 1); /* * Fill the local thread copy directly with the Tcl_Obj value to avoid * loss of the intrep. Increment newValue refCount early to handle case * where we set a PGV to itself. */ Tcl_IncrRefCount(newValue); cacheMap = GetThreadHash(&pgvPtr->key); ClearHash(cacheMap); hPtr = Tcl_CreateHashEntry(cacheMap, (char *) pgvPtr->epoch, &dummy); Tcl_SetHashValue(hPtr, (ClientData) newValue); Tcl_MutexUnlock(&pgvPtr->mutex); } /* *---------------------------------------------------------------------- * * GetPGV -- * * Retrieve a global value shared among all threads of the process, * preferring a thread-local copy as long as it remains valid. * * Results: * Returns a (Tcl_Obj *) that holds a copy of the global value. * *---------------------------------------------------------------------- */ static Tcl_Obj * GetPGV(pgvPtr) PGV *pgvPtr; { Tcl_Obj *value = NULL; Tcl_HashTable *cacheMap; Tcl_HashEntry *hPtr; int epoch = pgvPtr->epoch; cacheMap = GetThreadHash(&pgvPtr->key); hPtr = Tcl_FindHashEntry(cacheMap, (char *) epoch); if (NULL == hPtr) { int dummy; /* * No cache for the current epoch - must be a new one. * * First, clear the cacheMap, as anything in it must refer to some * expired epoch. */ ClearHash(cacheMap); /* * If no thread has set the shared value, call the initializer. */ Tcl_MutexLock(&pgvPtr->mutex); if ((NULL == pgvPtr->value) && (pgvPtr->proc)) { pgvPtr->epoch++; (*(pgvPtr->proc))(&pgvPtr->value, &pgvPtr->numBytes); if (pgvPtr->value == NULL) { Tcl_Panic("PGV Initializer did not initialize"); } Tcl_CreateExitHandler(FreePGV, (ClientData) pgvPtr); } /* * Store a copy of the shared value in our epoch-indexed cache. */ value = Tcl_NewStringObj(pgvPtr->value, pgvPtr->numBytes); hPtr = Tcl_CreateHashEntry(cacheMap, (char *) pgvPtr->epoch, &dummy); Tcl_MutexUnlock(&pgvPtr->mutex); Tcl_SetHashValue(hPtr, (ClientData) value); Tcl_IncrRefCount(value); } return (Tcl_Obj *) Tcl_GetHashValue(hPtr); } /* *---------------------------------------------------------------------- * * Tcl_GetNameOfExecutable -- * * This procedure simply returns a pointer to the internal full |
︙ | ︙ |