Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge to feature branch |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | tip-388-impl |
Files: | files | file ages | folders |
SHA1: |
094a3c09ef5407bc83e7249c382849d4 |
User & Date: | jan.nijtmans 2011-08-22 10:18:06 |
Context
2011-08-23
| ||
07:24 | Merge to feature branch check-in: c6361c69eb user: jan.nijtmans tags: tip-388-impl | |
2011-08-22
| ||
10:18 | Merge to feature branch check-in: 094a3c09ef user: jan.nijtmans tags: tip-388-impl | |
2011-08-19
| ||
20:27 | 3393279, 3393280 ReflectClose(.) is missing Tcl_EventuallyFree() calls at some of its exits. check-in: 08b1cf9ec5 user: dgp tags: trunk | |
2011-08-18
| ||
12:25 | remove some debugging stuff check-in: 08c04d17be user: jan.nijtmans tags: tip-388-impl | |
Changes
Changes to ChangeLog.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 2011-08-18 Jan Nijtmans <[email protected]> * generic/tclUniData.c: [Bug 3393714] overflow in toupper delta * tools/uniParse.tcl * tests/utf.test 2011-08-17 Alexandre Ferrieux <[email protected]> * generic/tclIO.c: [Bug 2946474] Consistently resume backgrounded * tests/ioCmd.test: flushes+closes when exiting. 2011-08-17 Alexandre Ferrieux <[email protected]> * doc/interp.n: Document TIP 378's one-way-ness. | > > > > > > > > > > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 2011-08-19 Don Porter <[email protected]> * generic/tclIORTrans.c: [Bugs 3393279, 3393280] ReflectClose(.) is missing Tcl_EventuallyFree() calls at some of its exits. * generic/tclIO.c: [Bugs 3394654, 3393276] Revise FlushChannel() to account for the possibility that the ChanWrite() call might recycle the buffer out from under us. * generic/tclIO.c: Preserve the chanPtr during FlushChannel so that channel drivers don't yank it away before we're done with it. 2011-08-19 Alexandre Ferrieux <[email protected]> * generic/tclTest.c: [Bug 2981154] async-4.3 segfault. * tests/async.test: [Bug 1774689] async-4.3 sometimes fails. 2011-08-18 Alexandre Ferrieux <[email protected]> * generic/tclIO.c: [Bug 3096275] Sync fcopy buffers input. 2011-08-18 Jan Nijtmans <[email protected]> * generic/tclUniData.c: [Bug 3393714] overflow in toupper delta * tools/uniParse.tcl * tests/utf.test 2011-08-17 Alexandre Ferrieux <[email protected]> * generic/tclIO.c: [Bug 2946474] Consistently resume backgrounded * tests/ioCmd.test: flushes+closes when exiting. 2011-08-17 Alexandre Ferrieux <[email protected]> * doc/interp.n: Document TIP 378's one-way-ness. 2011-08-17 Don Porter <[email protected]> * generic/tclGet.c: [Bug 3393150] Overlooked free of intreps. (It matters for bignums!) 2011-08-16 Don Porter <[email protected]> |
︙ | ︙ |
Changes to generic/tclIO.c.
︙ | ︙ | |||
75 76 77 78 79 80 81 | Tcl_Interp *interp); static void DeleteScriptRecord(Tcl_Interp *interp, Channel *chanPtr, int mask); static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan); static void DiscardInputQueued(ChannelState *statePtr, int discardSavedBuffers); static void DiscardOutputQueued(ChannelState *chanPtr); | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | Tcl_Interp *interp); static void DeleteScriptRecord(Tcl_Interp *interp, Channel *chanPtr, int mask); static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan); static void DiscardInputQueued(ChannelState *statePtr, int discardSavedBuffers); static void DiscardOutputQueued(ChannelState *chanPtr); static int DoRead(Channel *chanPtr, char *srcPtr, int slen, int allowShortReads); static int DoWrite(Channel *chanPtr, const char *src, int srcLen); static int DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead, int appendFlag); static int DoWriteChars(Channel *chan, const char *src, int len); static int FilterInputBytes(Channel *chanPtr, GetsState *statePtr); static int FlushChannel(Tcl_Interp *interp, Channel *chanPtr, |
︙ | ︙ | |||
2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 | } /* * Loop over the queued buffers and attempt to flush as much as possible * of the queued output to the channel. */ while (1) { /* * If the queue is empty and there is a ready current buffer, OR if * the current buffer is full, then move the current buffer to the * queue. */ | > | 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 | } /* * Loop over the queued buffers and attempt to flush as much as possible * of the queued output to the channel. */ Tcl_Preserve(chanPtr); while (1) { /* * If the queue is empty and there is a ready current buffer, OR if * the current buffer is full, then move the current buffer to the * queue. */ |
︙ | ︙ | |||
2381 2382 2383 2384 2385 2386 2387 | /* * If we are not being called from an async flush and an async flush * is active, we just return without producing any output. */ if (!calledFromAsyncFlush && GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { | | > | 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 | /* * If we are not being called from an async flush and an async flush * is active, we just return without producing any output. */ if (!calledFromAsyncFlush && GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { errorCode = 0; goto done; } /* * If the output queue is still empty, break out of the while loop. */ if (bufPtr == NULL) { |
︙ | ︙ | |||
2504 2505 2506 2507 2508 2509 2510 | DiscardOutputQueued(statePtr); continue; } else { wroteSome = 1; } | > | > | 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 | DiscardOutputQueued(statePtr); continue; } else { wroteSome = 1; } if (!IsBufferEmpty(bufPtr)) { bufPtr->nextRemoved += written; } /* * If this buffer is now empty, recycle it. */ if (IsBufferEmpty(bufPtr)) { statePtr->outQueueHead = bufPtr->nextPtr; |
︙ | ︙ | |||
2528 2529 2530 2531 2532 2533 2534 | * We can't finish the background flush until we run out of data and the * channel becomes writable again. This ensures that all of the pending * data has been flushed at the system level. */ if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { if (wroteSome) { | | | > | > > > > | 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 | * We can't finish the background flush until we run out of data and the * channel becomes writable again. This ensures that all of the pending * data has been flushed at the system level. */ if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { if (wroteSome) { goto done; } else if (statePtr->outQueueHead == NULL) { ResetFlag(statePtr, BG_FLUSH_SCHEDULED); ChanWatch(chanPtr, statePtr->interestMask); } } /* * If the channel is flagged as closed, delete it when the refCount drops * to zero, the output queue is empty and there is no output in the * current output buffer. */ if (GotFlag(statePtr, CHANNEL_CLOSED) && (statePtr->refCount <= 0) && (statePtr->outQueueHead == NULL) && ((statePtr->curOutPtr == NULL) || IsBufferEmpty(statePtr->curOutPtr))) { errorCode = CloseChannel(interp, chanPtr, errorCode); goto done; } /* * If the write-side of the channel is flagged as closed, delete it when * the output queue is empty and there is no output in the current output * buffer. */ if (GotFlag(statePtr, CHANNEL_CLOSEDWRITE) && (statePtr->outQueueHead == NULL) && ((statePtr->curOutPtr == NULL) || IsBufferEmpty(statePtr->curOutPtr))) { errorCode = CloseChannelPart(interp, chanPtr, errorCode, TCL_CLOSE_WRITE); goto done; } done: Tcl_Release(chanPtr); return errorCode; } /* *---------------------------------------------------------------------- * * CloseChannel -- |
︙ | ︙ | |||
5440 5441 5442 5443 5444 5445 5446 | chanPtr = statePtr->topChanPtr; if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) { return -1; } | | | 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 | chanPtr = statePtr->topChanPtr; if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) { return -1; } return DoRead(chanPtr, dst, bytesToRead, 0); } /* *---------------------------------------------------------------------- * * Tcl_ReadRaw -- * |
︙ | ︙ | |||
9165 9166 9167 9168 9169 9170 9171 | || (csPtr->toRead > (Tcl_WideInt) csPtr->bufSize)) { sizeb = csPtr->bufSize; } else { sizeb = (int) csPtr->toRead; } if (inBinary || sameEncoding) { | | > | 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 | || (csPtr->toRead > (Tcl_WideInt) csPtr->bufSize)) { sizeb = csPtr->bufSize; } else { sizeb = (int) csPtr->toRead; } if (inBinary || sameEncoding) { size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb, !GotFlag(inStatePtr, CHANNEL_NONBLOCKING)); } else { size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, 0 /* No append */); } underflow = (size >= 0) && (size < sizeb); /* Input underflow */ } |
︙ | ︙ | |||
9404 9405 9406 9407 9408 9409 9410 | *---------------------------------------------------------------------- */ static int DoRead( Channel *chanPtr, /* The channel from which to read. */ char *bufPtr, /* Where to store input read. */ | | > | 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 | *---------------------------------------------------------------------- */ static int DoRead( Channel *chanPtr, /* The channel from which to read. */ char *bufPtr, /* Where to store input read. */ int toRead, /* Maximum number of bytes to read. */ int allowShortReads) /* Allow half-blocking (pipes,sockets) */ { ChannelState *statePtr = chanPtr->state; /* State info for channel */ int copied; /* How many characters were copied into the * result string? */ int copiedNow; /* How many characters were copied from the * current input buffer? */ |
︙ | ︙ | |||
9445 9446 9447 9448 9449 9450 9451 | result = GetInput(chanPtr); if (result != 0) { if (result != EAGAIN) { copied = -1; } goto done; } | > > > | | 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 | result = GetInput(chanPtr); if (result != 0) { if (result != EAGAIN) { copied = -1; } goto done; } } else if (allowShortReads) { copied += copiedNow; break; } } ResetFlag(statePtr, CHANNEL_BLOCKED); /* * Update the notifier state so we don't block while there is still data * in the buffers. |
︙ | ︙ |
Changes to generic/tclIORTrans.c.
︙ | ︙ | |||
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | * that check is not necessary. We always go through 'finalize'. */ if (HAS(rtPtr->methods, METH_DRAIN) && !rtPtr->readIsDrained) { int errorCode; if (!TransformDrain(rtPtr, &errorCode)) { return errorCode; } } if (HAS(rtPtr->methods, METH_FLUSH)) { int errorCode; if (!TransformFlush(rtPtr, &errorCode, FLUSH_WRITE)) { return errorCode; } } /* * Are we in the correct thread? */ | > > | 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | * that check is not necessary. We always go through 'finalize'. */ if (HAS(rtPtr->methods, METH_DRAIN) && !rtPtr->readIsDrained) { int errorCode; if (!TransformDrain(rtPtr, &errorCode)) { Tcl_EventuallyFree (rtPtr, (Tcl_FreeProc *) FreeReflectedTransform); return errorCode; } } if (HAS(rtPtr->methods, METH_FLUSH)) { int errorCode; if (!TransformFlush(rtPtr, &errorCode, FLUSH_WRITE)) { Tcl_EventuallyFree (rtPtr, (Tcl_FreeProc *) FreeReflectedTransform); return errorCode; } } /* * Are we in the correct thread? */ |
︙ | ︙ |
Changes to generic/tclTest.c.
︙ | ︙ | |||
70 71 72 73 74 75 76 77 78 79 80 81 82 83 | int id; /* Identifier for this handler. */ Tcl_AsyncHandler handler; /* Tcl's token for the handler. */ char *command; /* Command to invoke when the handler is * invoked. */ struct TestAsyncHandler *nextPtr; /* Next is list of handlers. */ } TestAsyncHandler; static TestAsyncHandler *firstHandler = NULL; /* * The dynamic string below is used by the "testdstring" command to test the * dynamic string facilities. */ | > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | int id; /* Identifier for this handler. */ Tcl_AsyncHandler handler; /* Tcl's token for the handler. */ char *command; /* Command to invoke when the handler is * invoked. */ struct TestAsyncHandler *nextPtr; /* Next is list of handlers. */ } TestAsyncHandler; TCL_DECLARE_MUTEX(asyncTestMutex); static TestAsyncHandler *firstHandler = NULL; /* * The dynamic string below is used by the "testdstring" command to test the * dynamic string facilities. */ |
︙ | ︙ | |||
787 788 789 790 791 792 793 794 795 796 | return TCL_ERROR; } if (strcmp(argv[1], "create") == 0) { if (argc != 3) { goto wrongNumArgs; } asyncPtr = ckalloc(sizeof(TestAsyncHandler)); asyncPtr->id = nextId; nextId++; asyncPtr->handler = Tcl_AsyncCreate(AsyncHandlerProc, | > > > | < < > > > > > | 789 790 791 792 793 794 795 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 | return TCL_ERROR; } if (strcmp(argv[1], "create") == 0) { if (argc != 3) { goto wrongNumArgs; } asyncPtr = ckalloc(sizeof(TestAsyncHandler)); asyncPtr->command = ckalloc(strlen(argv[2]) + 1); strcpy(asyncPtr->command, argv[2]); Tcl_MutexLock(&asyncTestMutex); asyncPtr->id = nextId; nextId++; asyncPtr->handler = Tcl_AsyncCreate(AsyncHandlerProc, (ClientData) asyncPtr->id); asyncPtr->nextPtr = firstHandler; firstHandler = asyncPtr; Tcl_MutexUnlock(&asyncTestMutex); Tcl_SetObjResult(interp, Tcl_NewIntObj(asyncPtr->id)); } else if (strcmp(argv[1], "delete") == 0) { if (argc == 2) { Tcl_MutexLock(&asyncTestMutex); while (firstHandler != NULL) { asyncPtr = firstHandler; firstHandler = asyncPtr->nextPtr; Tcl_AsyncDelete(asyncPtr->handler); ckfree(asyncPtr->command); ckfree(asyncPtr); } Tcl_MutexUnlock(&asyncTestMutex); return TCL_OK; } if (argc != 3) { goto wrongNumArgs; } if (Tcl_GetInt(interp, argv[2], &id) != TCL_OK) { return TCL_ERROR; } Tcl_MutexLock(&asyncTestMutex); for (prevPtr = NULL, asyncPtr = firstHandler; asyncPtr != NULL; prevPtr = asyncPtr, asyncPtr = asyncPtr->nextPtr) { if (asyncPtr->id != id) { continue; } if (prevPtr == NULL) { firstHandler = asyncPtr->nextPtr; } else { prevPtr->nextPtr = asyncPtr->nextPtr; } Tcl_AsyncDelete(asyncPtr->handler); ckfree(asyncPtr->command); ckfree(asyncPtr); break; } Tcl_MutexUnlock(&asyncTestMutex); } else if (strcmp(argv[1], "mark") == 0) { if (argc != 5) { goto wrongNumArgs; } if ((Tcl_GetInt(interp, argv[2], &id) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &code) != TCL_OK)) { return TCL_ERROR; |
︙ | ︙ | |||
858 859 860 861 862 863 864 | return TCL_ERROR; } for (asyncPtr = firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { if (asyncPtr->id == id) { Tcl_ThreadId threadID; if (Tcl_CreateThread(&threadID, AsyncThreadProc, | | | 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 | return TCL_ERROR; } for (asyncPtr = firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { if (asyncPtr->id == id) { Tcl_ThreadId threadID; if (Tcl_CreateThread(&threadID, AsyncThreadProc, (ClientData) id, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) { Tcl_SetResult(interp, "can't create thread", TCL_STATIC); return TCL_ERROR; } break; } } |
︙ | ︙ | |||
882 883 884 885 886 887 888 | #endif } return TCL_OK; } static int AsyncHandlerProc( | | > | > > > > > > > > > > > > > | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 | #endif } return TCL_OK; } static int AsyncHandlerProc( ClientData clientData, /* If of TestAsyncHandler structure. * in global list. */ Tcl_Interp *interp, /* Interpreter in which command was * executed, or NULL. */ int code) /* Current return code from command. */ { TestAsyncHandler *asyncPtr; int id = (int) clientData; const char *listArgv[4], *cmd; char string[TCL_INTEGER_SPACE]; Tcl_MutexLock(&asyncTestMutex); for (asyncPtr = firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { if (asyncPtr->id == id) break; } Tcl_MutexUnlock(&asyncTestMutex); if (!asyncPtr) { /* Woops - this one was deleted between the AsyncMark and now */ return TCL_OK; } TclFormatInt(string, code); listArgv[0] = asyncPtr->command; listArgv[1] = Tcl_GetString(Tcl_GetObjResult(interp)); listArgv[2] = string; listArgv[3] = NULL; cmd = Tcl_Merge(3, listArgv); |
︙ | ︙ | |||
928 929 930 931 932 933 934 | * *---------------------------------------------------------------------- */ #ifdef TCL_THREADS static Tcl_ThreadCreateType AsyncThreadProc( | | | > > > > > > | > > > > | 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 | * *---------------------------------------------------------------------- */ #ifdef TCL_THREADS static Tcl_ThreadCreateType AsyncThreadProc( ClientData clientData) /* Parameter is the id of a * TestAsyncHandler, defined above. */ { TestAsyncHandler *asyncPtr; int id = (int) clientData; Tcl_Sleep(1); Tcl_MutexLock(&asyncTestMutex); for (asyncPtr = firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { if (asyncPtr->id == id) { Tcl_AsyncMark(asyncPtr->handler); break; } } Tcl_MutexUnlock(&asyncTestMutex); Tcl_ExitThread(TCL_OK); TCL_THREAD_CREATE_RETURN; } #endif /* *---------------------------------------------------------------------- |
︙ | ︙ | |||
7050 7051 7052 7053 7054 7055 7056 7057 7058 | } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ | > > | 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 | } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * tab-width: 8 * indent-tabs-mode: nil * End: */ |
Changes to tests/async.test.
︙ | ︙ | |||
192 193 194 195 196 197 198 | set hm [testasync create async3] } -body { apply [list {handle} [concat { global aresult set aresult {Async event not delivered} testasync marklater $handle set i 0 | | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | set hm [testasync create async3] } -body { apply [list {handle} [concat { global aresult set aresult {Async event not delivered} testasync marklater $handle set i 0 } "[string repeat {;incr i;} 1500000]after 10;" { return $aresult }]] $hm } -result {test pattern} -cleanup { testasync delete $hm } # cleanup |
︙ | ︙ |