Compare commits

...
Author SHA1 Message Date
sebres bac7d95422 merge 8-5-event-perf-branch 2022-05-31 13:25:48 +00:00
sebres 9b0968b734 [windows] avoid overflow by calculation of perf-counter to 100ns as signed wide int (also fixes negative initial base for monotonic time and incorrect time distance by very large offsets) 2022-05-31 13:22:56 +00:00
sebres 67dabc08b1 correct comment for TclpGetUTimeMonotonic 2022-05-31 13:20:45 +00:00
sebres 3136db14ad merge 8.6 & 8.5-event-perf-branch (3way, conflicts resolved) 2019-05-22 09:39:19 +00:00
sebres aebbbf1416 merge 8.5 2019-05-22 09:18:43 +00:00
sebres 0c8bcac14f merge 8-5-event-perf-branch 2019-03-09 21:22:31 +00:00
sebres 42ef0efbad NRT relevant only: provides self-calibrating mechanism for TclpUSleep (the actual time of usleep/nanosleep may be longer, due to system latencies and possible limitations in the timer resolution of the hardware);
forces context switch or yield the processor if sleep time to small.
2019-03-09 21:04:55 +00:00
sebres eb195382ca non-native time scale fixed: do scale time only once (not in cycle, since all other times used in calculation already scaled). 2019-03-09 20:07:08 +00:00
sebres 27e53946ba macosx: Tcl_Sleep -> TclpUSleep (provides microseconds sleep, fixed unresolved external "TclpUSleep" bug). 2019-03-09 19:07:38 +00:00
sebres f765969711 TclpScaleUTime fixed typo, only if native scaling used (tclScaleTimeProcPtr == NativeScaleTime) the relationship is 1:1 and nothing has to be done. 2019-03-09 18:41:57 +00:00
sebres f85c7eeaf8 merge 8-5-event-perf-branch (no monotonic time fallbacks) 2019-03-08 15:39:32 +00:00
sebres ac0c06864a unix: monotonic time (performance counter/wide-clicks) - implements several fallbacks for platforms without monotonic time (older Darwin, MacOSX or xcode) 2019-03-08 15:38:05 +00:00
sebres e5d47fe818 merge 8-5-event-perf-branch (restore merge point after complex merge across branches 8.6/8.6-timerate/etc) 2019-03-08 11:37:34 +00:00
sebres b2bb2605af 3-way merge 8.6 - timerate is part of Tcl now (since TIP#527 got merged), conflicts resolved, merge-points restored, configure rebuilt, etc 2019-03-08 11:34:11 +00:00
sebres 8abf59b9b4 merge 8.5 (fixed performance test for short exec-time) 2019-03-08 04:38:19 +00:00
sebres 9f8a01e5f2 cpu-count (performance counter) recognition is rewritten (also resolves gcc warning: suggest parentheses around '&&' within '||') 2019-03-08 04:37:12 +00:00
sebres b19f192517 merge 8.5 - timerate is part of Tcl now (since TIP#527 got merged), conflicts resolved, merge-points restored 2019-03-08 04:08:17 +00:00
sebres 986c4cd50c merge sebres-8-5-timerate (prepare for consolidation with 8.5) 2019-03-08 04:05:12 +00:00
sebres 64868abf4f merge sebres-event-perf-fix-busy-wait 2017-07-13 16:04:31 +00:00
sebres 54bd87018c merge (integrate) sebres-event-perf-fix-busy-wait 2017-07-13 15:46:42 +00:00
sebres efb54576ca avoid busy wait if new short block-time will be set within service event-cycle (Tcl_DoOneEvent) produced in event-cycle without processing 2017-07-13 15:43:42 +00:00
sebres cd593b31a8 timer: fixed setup of the block-time to 0.0 by pending timer of new generation, example:
tclsh -c "proc test {} {after 1000 test}; test; vwait infinite"
2017-07-13 15:41:57 +00:00
sebres 7cc472b9ec merge core-8-6-branch 2017-07-10 11:31:03 +00:00
sebres 604246ba34 merge core-8-5-branch 2017-07-10 11:17:31 +00:00
sebres 29f2fde4ef merge sebres-8-5-event-perf-branch 2017-07-10 11:12:32 +00:00
sebres c0f37e88f4 avoid blocking wait if we've some retarded events (from last event-cycle) and max blocking time was not set outside an event source traversal. 2017-07-10 10:58:42 +00:00
sebres f5896b1435 reintegrate sebres-8-6-event-perf-branch back to 8.5 2017-07-10 10:56:21 +00:00
sebres 5ac5e7e0f2 tclUnixNotfy.c: we should wait for notifier at least once in case of waitForFiles (regardless of the too short timeout), e. g. test case "chan-io-53.8", etc. 2017-07-10 08:59:08 +00:00
sebres ea33ec285f Stability fix: queue epoch to guarantee avoid broken queue, service level to avoid reset block time by nested event-cycles (if Tcl_SetTimer does not create it), etc.
Fixed retarded events (using new retarded list, the involve of the retarded events occurs only after checking of all event sources now).
Two opportunities to retard event:
- lazy, using the same event-object: in the handler set event->proc to new (or the same) handler (fast, possible only if not entering new event-cycle in handler);
- create the event with new position "TCL_QUEUE_RETARDED";
New inline functions TclpQueueEventClientData / TclpQueueEventEx to fast creating resp. queuing event with extra data.
2017-07-10 08:55:58 +00:00
sebres 5fd912f410 merge resp. reintegrate sebres-8-5-event-perf-branch to 8.6 2017-07-10 08:53:28 +00:00
sebres d7726170f8 performance test cases extended 2017-07-03 13:32:33 +00:00
sebres b927c80c2d cleanup... 2017-07-03 13:32:29 +00:00
sebres b2fccb4530 after at: added simple workaround for absolute timers/sleep ("after at real-time"): because we use monotonic time in all wait functions, so to avoid too long wait by the absolute timers (to be able to trigger it) if time jumped to the expected absolute time, just let block for maximal 1 second if absolute timers available.
test-cases: time-jumps (TIP #302) test covered now.
Note: on some platforms it is only possible if the user has corresponding privileges to change system date and time.
Ex.:  sudo LD_LIBRARY_PATH=. ./tclsh ../tests/timer.test -match timer-20.*
2017-07-03 13:32:24 +00:00
sebres 16fcaa5d3a code review and small optimizations 2017-07-03 13:32:19 +00:00
sebres 3e620d567b fix check event source threshold (corresponds 100-ns ranges, if the wide-clicks supported);
because of variable width of 1 wide-click: windows - frequency dependent, unix - nanoseconds, darwin/osx - tb.numer / tb.denom nanoseconds.
2017-07-03 13:32:15 +00:00
sebres 06a11ad743 unix: implements wide-clicks on unix (1 wide-click == 0.001 microseconds (1 nanosecond)), so more precise now (e. g. by time measurement etc.);
unix/configure: regenerated (autoconf)
2017-07-03 13:32:11 +00:00
sebres 99b3de0698 [unix] fixes conditional-wait: timeout is monotonic based; 2017-07-03 13:32:06 +00:00
sebres 0a42a5fe3a Introduced monotonic time as ultimate fix for time-jump issue (fixed for windows and unix now, TIP #302 fully implemented now);
Usage of monotonic time instead of adjustment via timeJump/timeJumpEpoch is more precise and effective.
New sub-command "clock monotonic" to provide monotonic time facility for tcl-level.
2017-07-03 13:32:02 +00:00
sebres d668a02838 don't cancel scheduled event as long as the event list is not bidirectional (too slow by large queue) - rewritten to cancel delayed (by execute it). 2017-07-03 13:31:53 +00:00
sebres ce98b00663 fixed timer-marker handling: timer should be always executed after queued event (of the same generation), it was marked (be sure it marked to immediate execution in corresponding checkProc only).
tclIO: scheduled event rewritten using Tcl_Event instead of timer event (IO is not timer, e. g. executed also by usage of `vwait -notimer ...`, etc).
2017-07-03 13:31:45 +00:00
sebres 33f0088fcc interim commit: trying to pass test-cases - timer-3.*, chan-io-29.34, socket-2.12 (busy prompt timer, unexpected too early timer event, wrong timer-marker usage) 2017-07-03 13:31:40 +00:00
sebres b760247052 Merge branch 'fix-busy-prompt-timers' into event-perf-branch:
* Amend to timer-marker: dualize special state of timer-marker (to differentiate between timer generations), so:
  INT2PTR(-1) - exec immediate (marker reached);
  INT2PTR(-2) - check in the next-cycle (marker reached only if no other events available);
Avoids permanent busy execution of prompt-events (always busy in timer), if they regenerate itself continuously for waiting for other events (like writable/readable), see e. g. socket-2.12.
2017-07-03 13:31:13 +00:00
sebres 15fcf0b7ba "after at" set factor to 1000000 (seconds), test cases fixed 2017-07-03 13:30:36 +00:00
sebres 33851b7f36 code review, rewrite tclTimer, prolongation, etc. 2017-07-03 13:30:30 +00:00
sebres 91e0326db8 interim commit: code review, rewrite tclTimer, etc. 2017-07-03 13:30:12 +00:00
sebres 1dfe753462 revert dual lists (relative/absolute) back to single list (because of better handling, a bit faster, etc.) 2017-07-03 13:30:08 +00:00
sebres 7d840e941c amend with review 2017-07-03 13:30:05 +00:00
sebres 6fbf38944b don't use tolerance in vwait, because of dual usage, it causes canceling of wait before end-time, on small timeout values (like 0.5, etc.) 2017-07-03 13:29:58 +00:00
sebres b5a1844e1f interim commit: trying to resolve time-freezes with new facilities timeJump/timeJumpEpoch 2017-07-03 13:29:50 +00:00
sebres 2d12e7c898 interim commit: trying to resolve time-freezes with new facilities timeJump/timeJumpEpoch 2017-07-03 13:29:42 +00:00
sebres 741679e635 call TclWinResetTimerResolution at end of sleep resp. wait for event (no calibration thread anymore) 2017-07-03 13:29:33 +00:00
sebres c5e7df0399 win: calibration cycle completely rewritten (no calibration thread needed, soft drifts within 250ms intervals, fewer discrepancy and fewer virtual time gradation, etc).
todo: implement resetting timer-resolution to original value (without calibration thread now).
2017-07-03 13:29:29 +00:00
sebres 5083075a48 interim commit: amend with optimization 2017-07-03 13:29:24 +00:00
sebres 4ab56fa512 interim commit: time-drift backwards fix... (try to resolve using counter variance + frequency factor) 2017-07-03 13:29:20 +00:00
sebres e64ebe1e2f interim commit: time-drift backwards fix... (try to resolve using frequency factor) 2017-07-03 13:29:16 +00:00
sebres ecba820579 interim commit: time-drift backwards fix... 2017-07-03 13:29:12 +00:00
sebres eade5586f0 interim commit: try to fix time-drift backwards (calibration? something else?)... 2017-07-03 13:29:07 +00:00
sebres 93ca48c01a interim commit: try to fix time-drift backwards (too long offset after calibration? something else?)... 2017-07-03 13:29:03 +00:00
sebres 422ee51c0c extended performance test-cases (test-nrt-capability): RTS-near sleeps with very brief sleep-time. 2017-07-03 13:28:17 +00:00
sebres 53c0b2b00e chanio.test: optimize several tests cases running too long (shorten unwanted large sleeps) 2017-07-03 13:28:09 +00:00
sebres 6ab0cbe861 bug fix: prevent setting of negative block-time by too few initial wait-time, that may expire immediately (for example vwait 0.0001 test). 2017-07-03 13:28:04 +00:00
sebres 587bcb0602 extended performance test-cases (test-nrt-capability): covering of brief wait-times and other RTS-near constructs. 2017-07-03 13:27:47 +00:00
sebres 31c3f2f9da [unix] optimized Tcl_WaitForEvent similar to windows changes (makes Tcl for *nix more "RTS" resp. NRT-capable):
- more precise waiting now (e.g. still microseconds by time up to 0.005 ms), important since after/vwait accepting microseconds (double);
- avoids too long waiting on *nix wait/sleep primitives, e. g. by `timerate {vwait 0 a}` - 1.5µs now vs. 31.9µs before;
- extended with new internal function TclpSleep (in contrast to Tcl_Sleep accept Tcl_Time, so microseconds);
2017-07-03 13:27:33 +00:00
sebres 1965a89571 added performance test-cases to cover timer-events speed resp. event-driven tcl-handling
(cherry-picked and back-ported from tclSE-9)
2017-07-03 13:27:20 +00:00
sebres 43560cb286 fix sporadic errors on some fast cpu/platforms (because bgerror executed in background and it is an idle-event, give enough time to process it (resp. wait until last idle event is done); 2017-07-03 13:26:19 +00:00
sebres 74a050a430 make timer test-case more precise and time-independent, ignores short tolerance (deviation by waiting);
several time-independent test-cases optimized (wait shorter now) + some new cases to cover more situations.
2017-07-03 13:26:06 +00:00
sebres de9efce993 after info, after cancel: compare interpreter of the timer-events by direct retrieving via internal representation (ignore foreign events), test cases extended. 2017-07-03 13:25:52 +00:00
sebres 4139f96329 resolved some warnings / fixed unix resp. x64 compilation 2017-07-03 13:25:45 +00:00
sebres 1640c780c0 code review + better usage of the waiting tolerance (fewer CPU-greedy now, avoid busy-wait if the rest of wait-time too small and can be neglected);
TMR_RES_TOLERANCE can be defined to use wait-tolerance on *nix platforms (currently windows only as relation resp. deviation between default timer resolution 15.600 in exact milliseconds, means 15600/15000 + small overhead);
Decreasing of TMR_RES_TOLERANCE (up to 0) makes tcl more "RTS" resp. NRT-capable (very precise wait-intervals, but more CPU-hungry).
2017-07-03 13:25:40 +00:00
sebres 3fac560e1a [win] fallback to replace C++ keyword "inline" with C keyword "__inline"
Otherwise depending on the VC-version, context, include-order it can cause:
  error C2054: expected '(' to follow 'inline'
2017-07-03 13:25:32 +00:00
sebres 22a93826f0 [win32] use timer resolution handling in Tcl_Sleep also; 2017-07-03 13:25:25 +00:00
sebres f1c50ac724 Use auto-reset event object (system automatically resets the event state to nonsignaled after wake-up), avoids unwanted reset if wake-up for some other reasons (timeout/aio/message). 2017-07-03 13:25:20 +00:00
sebres dc494be758 optimization of Tcl_LimitExceeded by internal usage (tclInt header) 2017-07-03 13:25:11 +00:00
sebres e7aa7663aa dynamic increase of timer resolution corresponding wait-time;
non-blocking wait for event - if block-time set outside an event source traversal, use it as timeout, so can return with result 0 (no events);
2017-07-03 13:25:01 +00:00
sebres c648fba3f0 [enhancement] extend "vwait" with same options as "update", new syntax "vwait ?options? ?timeout? varname".
some small improvements and fixing:
- Tcl_DoOneEvent can wait for block time that was set with Tcl_SetMaxBlockTime outside an event source traversal,
  and stop waiting if Tcl_SetMaxBlockTime was called outside an event source (another event occurs and interrupt waiting loop), etc;
- safer more precise pre-lookup by options (use TclObjIsIndexOfTable instead of simply comparison of type with tclIndexType);
test cases extended to cover conditional "vwait" usage;
2017-07-03 13:24:53 +00:00
sebres 2b5f5fbefe interim commit: try to extend "vwait" with same options as "update" 2017-07-03 13:24:42 +00:00
sebres 5b3bc219ba [performance] do one event (update / event servicing) cycle optimized (introduced threshold to prevent sourcing resp. waiting for new events by no-wait).
[enhancement] new event type introduced: TCL_ASYNC_EVENTS, command "update" becomes options to process only specified types, resp. to bypass some event types (including -idle/-noidle that in opposite to "idletasks" does not included window events);
test cases extended.
2017-07-03 13:24:36 +00:00
sebres b6f5caf13e command "vwait" extended with timeout argument (in ms), 0 could be used to process pending events only (without wait), negative value equivalent execution of "vwait" without timeout (infinite);
test cases fixed and extended;
2017-07-03 13:24:12 +00:00
sebres 0d02e93910 [performance] much better handling for timer events within Tcl_ServiceEvent using timer marker in the queue and direct call of TclServiceTimerEvents if marker reached (instead of continuous adding handler event, polling it in the queue and removing hereafter);
this provides double performance increase in the service cycle;
2017-07-03 13:24:04 +00:00
sebres 41b3d45cfe [performance] large performance increase by event servicing cycles (3x - 5x faster now);
[win] prevent listen using PeekMessage twice, and no wait anymore for too short timeouts (because windows can wait too long), compare 0µs with up-to 100µs overhead within MsgWaitForMultipleObjectsEx;
[bad behavior] process idle events only as long as no other events available (now TclPeekEventQueued will be used to check new events are available in service idle cycle);
[enhancement] new option "noidletasks" for command "update", so "update noidle" means "process all events but not idle";
2017-07-03 13:23:45 +00:00
sebres c5ad1c88af [performance] introduced additional queue for prompt timer events (after 0) that should be executed immediately (no time);
normalizes timer, prompt and idle events structures using common TimerEntry structure for all types;
2017-07-03 13:23:40 +00:00
sebres 82c68cbe3d bug fix: wrong release of after-id tcl-object if it switch type (object leak) 2017-07-03 13:23:31 +00:00
sebres 25c8ef9477 [bug/stable fix] don't execute TimerSetupProc directly (may be unwanted, because changes the blocking time, also if TCL_TIMER_EVENTS|TCL_IDLE_EVENTS not set), so let do that within Tcl_DoOneEvent cycle only (we have registered an event source).
[performance] optimization for "after 0" as immediately execution without time (invoke as soon as possible) - generation and invocation of such timers twice faster now.
[performance] leave handler-event in the queue as long as pending timers still available (with expired time or immediate timers) by generation lock, resp. changed/not invalidated timer-queue) - so fewer event/allocations and guarantee to be executed within the next event cycle;
2017-07-03 13:23:20 +00:00
sebres 58358848bb after-id: introduced object of type "afterObjType" as self-referenced weak pointer to timer/idle event, used for fast access to the "after" event (cancel, info etc.);
test cases extended to cover it additionally
2017-07-03 13:22:04 +00:00
sebres 41aab74fa0 rewrite interpreter limit handling using new timer event handling (with delete callback) 2017-07-03 13:21:57 +00:00
sebres e25427b7d7 timer resp. idle events optimized: better handling using doubly linked lists, prevents allocating memory twice for the "after" events (use memory inside timer/idle event for the "after" structure), etc. 2017-07-03 13:21:34 +00:00
sebres 88d0bfbe6b [performance] after-event list optimized (interp-assoc switched to doubly linked list, because requires handling from both ends of the list) 2017-07-03 13:21:24 +00:00
25 changed files with 4865 additions and 3018 deletions
+3 -1
View File
@@ -755,6 +755,7 @@ typedef void (Tcl_PanicProc) (const char *format, ...);
typedef void (Tcl_TcpAcceptProc) (ClientData callbackData, Tcl_Channel chan,
char *address, int port);
typedef void (Tcl_TimerProc) (ClientData clientData);
typedef void (Tcl_TimerDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef int (Tcl_SetFromAnyProc) (Tcl_Interp *interp, struct Tcl_Obj *objPtr);
typedef void (Tcl_UpdateStringProc) (struct Tcl_Obj *objPtr);
typedef char * (Tcl_VarTraceProc) (ClientData clientData, Tcl_Interp *interp,
@@ -1359,6 +1360,7 @@ typedef struct {
* events:
*/
#define TCL_ASYNC_EVENTS (1<<0)
#define TCL_DONT_WAIT (1<<1)
#define TCL_WINDOW_EVENTS (1<<2)
#define TCL_FILE_EVENTS (1<<3)
@@ -1385,7 +1387,7 @@ struct Tcl_Event {
*/
typedef enum {
TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK
TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK, TCL_QUEUE_RETARDED
} Tcl_QueuePosition;
/*
+38
View File
@@ -184,6 +184,9 @@ static int ClockMicrosecondsObjCmd(
static int ClockMillisecondsObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
static int ClockMonotonicObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
static int ClockParseformatargsObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
@@ -257,6 +260,7 @@ TclClockInit(
{"format", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0},
{"microseconds", ClockMicrosecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(1), 0},
{"milliseconds", ClockMillisecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(2), 0},
{"monotonic", ClockMonotonicObjCmd, NULL, NULL, NULL, 0},
{"scan", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL , 0},
{"seconds", ClockSecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(3), 0},
{NULL, NULL, NULL, NULL, NULL, 0}
@@ -1850,6 +1854,40 @@ ClockMicrosecondsObjCmd(
return TCL_OK;
}
/*----------------------------------------------------------------------
*
* ClockMonotonicObjCmd -
*
* Returns a count of microseconds since some starting point.
* This represents monotonic time not affected from the time-jumps.
*
* Results:
* Returns a standard Tcl result.
*
* Side effects:
* None.
*
* This function implements the 'clock monotonic' Tcl command. Refer to the
* user documentation for details on what it does.
*
*----------------------------------------------------------------------
*/
int
ClockMonotonicObjCmd(
ClientData clientData, /* Client data is unused */
Tcl_Interp* interp, /* Tcl interpreter */
int objc, /* Parameter count */
Tcl_Obj* const* objv) /* Parameter values */
{
if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, NULL);
return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetUTimeMonotonic()));
return TCL_OK;
}
/*
*-----------------------------------------------------------------------------
*
+176 -38
View File
@@ -1368,6 +1368,62 @@ TclInThreadExit(void)
}
return tsdPtr->inExit;
}
static CONST char *updateEventOptions[] = {
"-idle", "-noidle", /* new options */
"-timer", "-notimer",
"-file", "-nofile",
"-window", "-nowindow",
"-async", "-noasync",
"-nowait", "-wait",
"idletasks", /* backwards compat. */
NULL
};
static int
GetEventFlagsFromOpts(
Tcl_Interp *interp, /* Used for error reporting if not NULL. */
int objc, /* Number of arguments. */
Tcl_Obj *CONST objv[], /* Arguments containing the option to lookup. */
int *flagsPtr) /* Input and resulting flags. */
{
int i, optionIndex, result = TCL_ERROR;
int flags = *flagsPtr; /* default flags */
static CONST struct {
int mask;
int flags;
} *updateFlag, updateFlags[] = {
{0, TCL_IDLE_EVENTS}, {TCL_IDLE_EVENTS, 0}, /* -idle, -noidle */
{0, TCL_TIMER_EVENTS}, {TCL_TIMER_EVENTS, 0}, /* -timer, -notimer */
{0, TCL_FILE_EVENTS}, {TCL_FILE_EVENTS, 0}, /* -file, -nofile */
{0, TCL_WINDOW_EVENTS}, {TCL_WINDOW_EVENTS, 0}, /* -window, -nowindow */
{0, TCL_ASYNC_EVENTS}, {TCL_ASYNC_EVENTS, 0}, /* -async, -noasync */
{0, TCL_DONT_WAIT}, {TCL_DONT_WAIT, 0}, /* -nowait, -wait */
{TCL_ALL_EVENTS, TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS}, /* idletasks */
{0, 0} /* dummy / place holder */
};
for (i = 0; i < objc; i++) {
if (Tcl_GetIndexFromObj(interp, objv[i], updateEventOptions,
"option", 0, &optionIndex) != TCL_OK) {
goto done;
}
updateFlag = &updateFlags[optionIndex];
/* pure positive option and still default,
* reset all events (only this flag) */
if (!updateFlag->mask && flags == *flagsPtr) {
flags &= ~TCL_ALL_EVENTS;
}
flags &= ~updateFlag->mask;
flags |= updateFlag->flags;
}
result = TCL_OK;
done:
*flagsPtr = flags;
return result;
}
/*
*----------------------------------------------------------------------
@@ -1394,45 +1450,129 @@ Tcl_VwaitObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
int done, foundEvent;
int done = 0, foundEvent = 1, checktime = 0;
int flags = TCL_ALL_EVENTS; /* default flags */
const char *nameString;
int optc = objc - 2; /* options count without cmd and varname */
Tcl_WideInt usec = -1;
Tcl_WideInt now = 0, wakeup = 0;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "name");
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "?options? ?timeout? name");
return TCL_ERROR;
}
nameString = Tcl_GetString(objv[1]);
/* if arguments available - wrap options to flags */
if (objc >= 3) {
/* first try to recognize options up to the possible end, thereby
* we assume that option is not an integer, try to get numeric timeout
*/
if (!TclObjIsIndexOfTable(objv[optc], updateEventOptions)
&& TclpGetUTimeFromObj(NULL, objv[optc], &usec, 1000) == TCL_OK) {
if (usec < 0) { usec = 0; };
optc--;
}
/* now try to parse options (if available) */
if ( optc > 0
&& GetEventFlagsFromOpts(interp, optc, objv+1, &flags) != TCL_OK
) {
return TCL_ERROR;
}
}
/*
* If timeout specified - create timer event or no-wait by 0ms.
* Note the time can be switched (time-jump), so use monotonic time here.
*/
if (usec != -1) {
if (usec > 0) {
now = TclpGetUTimeMonotonic();
wakeup = now + usec;
} else {
flags |= TCL_DONT_WAIT;
}
}
nameString = Tcl_GetString(objv[objc-1]);
if (Tcl_TraceVar2(interp, nameString, NULL,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
VwaitVarProc, &done) != TCL_OK) {
return TCL_ERROR;
};
done = 0;
foundEvent = 1;
while (!done && foundEvent) {
foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
do {
/* if wait - set blocking time */
if (usec > 0) {
Tcl_Time blockTime;
Tcl_WideInt diff;
now = TclpGetUTimeMonotonic();
/* calculate blocking time */
diff = wakeup - now;
diff -= 1; /* overhead for this code (e. g. Tcl_TraceVar/Tcl_UntraceVar) */
/* be sure process at least one event */
if (diff <= 0) {
/* timeout occurs */
if (checktime) {
done = -1;
break;
}
/* expired, be sure non-negative values here */
diff = 0;
checktime = 1;
}
blockTime.sec = diff / 1000000;
blockTime.usec = diff % 1000000;
Tcl_SetMaxBlockTime(&blockTime);
}
if ((foundEvent = Tcl_DoOneEvent(flags)) <= 0) {
/*
* If don't wait flag set - no error, and two cases:
* option -nowait for vwait means - we don't wait for events;
* if no timeout (0) - just stop waiting (no more events)
*/
if (foundEvent == 0 && (flags & TCL_DONT_WAIT || usec != -1)) {
foundEvent = 1;
if (usec == 0) { /* timeout occurs */
done = -1;
break;
}
}
/* don't stop wait - no event expected here
* (stop only on error case foundEvent <= 0). */
if (foundEvent < 0) {
done = -2;
}
}
/* check canceled or interpreter limit exceeded */
if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) {
done = -3;
break;
}
if (Tcl_LimitExceeded(interp)) {
Tcl_ResetResult(interp);
Tcl_SetObjResult(interp, Tcl_NewStringObj("limit exceeded", -1));
done = -4;
break;
}
}
} while (!done);
Tcl_UntraceVar2(interp, nameString, NULL,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
VwaitVarProc, &done);
if (!foundEvent) {
Tcl_ResetResult(interp);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"can't wait for variable \"%s\": would wait forever",
nameString));
Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_SOURCES", NULL);
return TCL_ERROR;
}
if (!done) {
/* if some error */
if (done <= -2) {
if (done == -2) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"can't wait for variable \"%s\": would wait forever",
nameString));
Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_SOURCES", NULL);
return TCL_ERROR;
}
/*
* The interpreter's result was already set to the right error message
* prior to exiting the loop above.
@@ -1441,6 +1581,16 @@ Tcl_VwaitObjCmd(
return TCL_ERROR;
}
/* if timeout specified (and no errors) */
if (usec != -1) {
Tcl_Obj *objPtr;
/* done - true, timeout false */
TclNewLongObj(objPtr, (done > 0));
Tcl_SetObjResult(interp, objPtr);
return TCL_OK;
}
/*
* Clear out the interpreter's result, since it may have been set by event
* handlers.
@@ -1492,28 +1642,13 @@ Tcl_UpdateObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
int optionIndex;
int flags = 0; /* Initialized to avoid compiler warning. */
static const char *const updateOptions[] = {"idletasks", NULL};
enum updateOptions {OPT_IDLETASKS};
int flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; /* default flags */
if (objc == 1) {
flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;
} else if (objc == 2) {
if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions,
"option", 0, &optionIndex) != TCL_OK) {
/* if arguments available - wrap options to flags */
if (objc > 1) {
if (GetEventFlagsFromOpts(interp, objc-1, objv+1, &flags) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum updateOptions) optionIndex) {
case OPT_IDLETASKS:
flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT;
break;
default:
Tcl_Panic("Tcl_UpdateObjCmd: bad option index to UpdateOptions");
}
} else {
Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?");
return TCL_ERROR;
}
while (Tcl_DoOneEvent(flags) != 0) {
@@ -1525,6 +1660,9 @@ Tcl_UpdateObjCmd(
Tcl_SetObjResult(interp, Tcl_NewStringObj("limit exceeded", -1));
return TCL_ERROR;
}
/* be sure not to produce infinite wait (wait only once) */
flags |= TCL_DONT_WAIT;
}
/*
+74 -34
View File
@@ -156,7 +156,7 @@ static void PreserveChannelBuffer(ChannelBuffer *bufPtr);
static void ReleaseChannelBuffer(ChannelBuffer *bufPtr);
static int IsShared(ChannelBuffer *bufPtr);
static void ChannelFree(Channel *chanPtr);
static void ChannelTimerProc(ClientData clientData);
static int ChannelScheduledProc(Tcl_Event *evPtr, int flags);
static int ChanRead(Channel *chanPtr, char *dst, int dstSize);
static int CheckChannelErrors(ChannelState *statePtr,
int direction);
@@ -1697,7 +1697,7 @@ Tcl_CreateChannel(
statePtr->interestMask = 0;
statePtr->scriptRecordPtr = NULL;
statePtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
statePtr->timer = NULL;
statePtr->schedEvent = NULL;
statePtr->csPtrR = NULL;
statePtr->csPtrW = NULL;
statePtr->outputStage = NULL;
@@ -3085,10 +3085,17 @@ CloseChannel(
}
/*
* Cancel any outstanding timer.
* Cancel any outstanding scheduled event.
*/
Tcl_DeleteTimerHandler(statePtr->timer);
if (statePtr->schedEvent) {
/* reset channel in event (cancel delayed) */
*(Channel**)(statePtr->schedEvent+1) = NULL;
#if 0
TclpCancelEvent(statePtr->schedEvent);
#endif
statePtr->schedEvent = NULL;
}
/*
* Mark the channel as deleted by clearing the type structure.
@@ -3881,10 +3888,17 @@ Tcl_ClearChannelHandlers(
chanPtr = statePtr->topChanPtr;
/*
* Cancel any outstanding timer.
* Cancel any outstanding scheduled event.
*/
Tcl_DeleteTimerHandler(statePtr->timer);
if (statePtr->schedEvent) {
/* reset channel in event (cancel delayed) */
*(Channel**)(statePtr->schedEvent+1) = NULL;
#if 0
TclpCancelEvent(statePtr->schedEvent);
#endif
statePtr->schedEvent = NULL;
}
/*
* Remove any references to channel handlers for this channel that may be
@@ -4820,7 +4834,7 @@ Tcl_GetsObj(
/*
* We didn't get a complete line so we need to indicate to UpdateInterest
* that the gets blocked. It will wait for more data instead of firing a
* timer, avoiding a busy wait. This is where we are assuming that the
* event, avoiding a busy wait. This is where we are assuming that the
* next operation is a gets. No more file events will be delivered on this
* channel until new data arrives or some operation is performed on the
* channel (e.g. gets, read, fconfigure) that changes the blocking state.
@@ -5105,7 +5119,7 @@ TclGetsObjBinary(
/*
* We didn't get a complete line so we need to indicate to UpdateInterest
* that the gets blocked. It will wait for more data instead of firing a
* timer, avoiding a busy wait. This is where we are assuming that the
* event, avoiding a busy wait. This is where we are assuming that the
* next operation is a gets. No more file events will be delivered on this
* channel until new data arrives or some operation is performed on the
* channel (e.g. gets, read, fconfigure) that changes the blocking state.
@@ -8389,6 +8403,21 @@ Tcl_NotifyChannel(
tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
}
static inline Tcl_Event *
CreateChannelScheduledEvent(
Channel *chanPtr)
{
#ifdef SYNTHETIC_EVENT_TIME
Tcl_Time blckTime;
blckTime.sec = SYNTHETIC_EVENT_TIME / 1000000;
blckTime.usec = SYNTHETIC_EVENT_TIME % 1000000;
Tcl_SetMaxBlockTime(&blckTime);
#endif
return TclpQueueEventClientData(ChannelScheduledProc, chanPtr,
TCL_QUEUE_RETARDED);
}
/*
*----------------------------------------------------------------------
*
@@ -8401,7 +8430,7 @@ Tcl_NotifyChannel(
* None.
*
* Side effects:
* May schedule a timer or driver handler.
* May schedule a event or driver handler.
*
*----------------------------------------------------------------------
*/
@@ -8430,7 +8459,7 @@ UpdateInterest(
/*
* If there is data in the input queue, and we aren't waiting for more
* data, then we need to schedule a timer so we don't block in the
* data, then we need to schedule an event so we don't block in the
* notifier. Also, cancel the read interest so we don't get duplicate
* events.
*/
@@ -8459,7 +8488,7 @@ UpdateInterest(
*
* - Tcl drops READABLE here, because it has data in its own
* buffers waiting to be read by the extension.
* - A READABLE event is syntesized via timer.
* - A READABLE event is syntesized via tcl-event (on queue tail).
* - The OS still reports the EXCEPTION condition on the file.
* - And the extension gets the EXCPTION event first, and handles
* this as EOF.
@@ -8481,9 +8510,8 @@ UpdateInterest(
mask &= ~TCL_EXCEPTION;
if (!statePtr->timer) {
statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
ChannelTimerProc, chanPtr);
if (!statePtr->schedEvent) {
statePtr->schedEvent = CreateChannelScheduledEvent(chanPtr);
}
}
}
@@ -8493,9 +8521,9 @@ UpdateInterest(
/*
*----------------------------------------------------------------------
*
* ChannelTimerProc --
* ChannelScheduledProc --
*
* Timer handler scheduled by UpdateInterest to monitor the channel
* Event handler scheduled by UpdateInterest to monitor the channel
* buffers until they are empty.
*
* Results:
@@ -8507,32 +8535,41 @@ UpdateInterest(
*----------------------------------------------------------------------
*/
static void
ChannelTimerProc(
ClientData clientData)
static int
ChannelScheduledProc(
Tcl_Event *evPtr, int flags)
{
Channel *chanPtr = clientData;
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
Channel *chanPtr = *(Channel**)(evPtr+1);
ChannelState *statePtr; /* State info for channel */
if (!chanPtr) { /* channel deleted */
return 1;
}
statePtr = chanPtr->state;
if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA)
&& (statePtr->interestMask & TCL_READABLE)
&& (statePtr->inQueueHead != NULL)
&& IsBufferReady(statePtr->inQueueHead)) {
/*
* Restart the timer in case a channel handler reenters the event loop
* Prolong the event in case a channel handler reenters the event loop
* before UpdateInterest gets called by Tcl_NotifyChannel.
*/
statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
ChannelTimerProc,chanPtr);
statePtr->schedEvent = CreateChannelScheduledEvent(chanPtr);
Tcl_Preserve(statePtr);
Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);
Tcl_Release(statePtr);
} else {
statePtr->timer = NULL;
UpdateInterest(chanPtr);
return 1; /* next cycle */
}
statePtr->schedEvent = NULL; /* event done. */
UpdateInterest(chanPtr);
return 1;
}
/*
@@ -8987,9 +9024,9 @@ Tcl_FileEventObjCmd(
/*
*----------------------------------------------------------------------
*
* ZeroTransferTimerProc --
* ZeroTransferEventProc --
*
* Timer handler scheduled by TclCopyChannel so that -command is
* Event handler scheduled by TclCopyChannel so that -command is
* called asynchronously even when -size is 0.
*
* Results:
@@ -9001,14 +9038,17 @@ Tcl_FileEventObjCmd(
*----------------------------------------------------------------------
*/
static void
ZeroTransferTimerProc(
ClientData clientData)
static int
ZeroTransferEventProc(
Tcl_Event *evPtr, int flags)
{
/* calling CopyData with mask==0 still implies immediate invocation of the
* -command callback, and completion of the fcopy.
*/
ClientData clientData = *(ClientData*)(evPtr+1);
CopyData(clientData, 0);
return 1;
}
/*
@@ -9153,7 +9193,7 @@ TclCopyChannel(
*/
if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) {
Tcl_CreateTimerHandler(0, ZeroTransferTimerProc, csPtr);
TclpQueueEventClientData(ZeroTransferEventProc, csPtr, TCL_QUEUE_TAIL);
return 0;
}
+1 -1
View File
@@ -187,7 +187,7 @@ typedef struct ChannelState {
/* Chain of all scripts registered for event
* handlers ("fileevent") on this channel. */
int bufSize; /* What size buffers to allocate? */
Tcl_TimerToken timer; /* Handle to wakeup timer for this channel. */
Tcl_Event *schedEvent; /* Scheduler event to wakeup this channel. */
struct CopyState *csPtrR; /* State of background copy for which channel
* is input, or NULL. */
struct CopyState *csPtrW; /* State of background copy for which channel
+46 -9
View File
@@ -43,7 +43,7 @@ static void PrintUsage(Tcl_Interp *interp,
* that can be invoked by generic object code.
*/
static const Tcl_ObjType indexType = {
const Tcl_ObjType tclIndexType = {
"index", /* name */
FreeIndex, /* freeIntRepProc */
DupIndex, /* dupIntRepProc */
@@ -76,6 +76,43 @@ typedef struct {
#define EXPAND_OF(indexRep) \
STRING_AT((indexRep)->tablePtr, (indexRep)->offset*(indexRep)->index)
/*
*----------------------------------------------------------------------
*
* TclObjIsIndexOfStruct --
*
* This function looks up an object's is a index of given table.
*
* Used for fast lookup by dynamic options count to check for other
* object types.
*
* Results:
* 1 if object is an option of table, otherwise 0.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TclObjIsIndexOfStruct(
Tcl_Obj *objPtr, /* Object containing the string to lookup. */
const void *tablePtr) /* Array of strings to compare against the
* value of objPtr; last entry must be NULL
* and there must not be duplicate entries. */
{
IndexRep *indexRep;
if (objPtr->typePtr != &tclIndexType) {
return 0;
}
indexRep = objPtr->internalRep.twoPtrValue.ptr1;
if (indexRep->tablePtr != (void *) tablePtr) {
return 0;
}
return 1;
}
/*
*----------------------------------------------------------------------
*
@@ -121,7 +158,7 @@ Tcl_GetIndexFromObj(
* the common case where the result is cached).
*/
if (objPtr->typePtr == &indexType) {
if (objPtr->typePtr == &tclIndexType) {
IndexRep *indexRep = objPtr->internalRep.twoPtrValue.ptr1;
/*
@@ -279,7 +316,7 @@ Tcl_GetIndexFromObjStruct(
* See if there is a valid cached result from a previous lookup.
*/
if (objPtr->typePtr == &indexType) {
if (objPtr->typePtr == &tclIndexType) {
indexRep = objPtr->internalRep.twoPtrValue.ptr1;
if (indexRep->tablePtr==tablePtr && indexRep->offset==offset) {
*indexPtr = indexRep->index;
@@ -340,13 +377,13 @@ Tcl_GetIndexFromObjStruct(
* operation.
*/
if (objPtr->typePtr == &indexType) {
if (objPtr->typePtr == &tclIndexType) {
indexRep = objPtr->internalRep.twoPtrValue.ptr1;
} else {
TclFreeIntRep(objPtr);
indexRep = ckalloc(sizeof(IndexRep));
objPtr->internalRep.twoPtrValue.ptr1 = indexRep;
objPtr->typePtr = &indexType;
objPtr->typePtr = &tclIndexType;
}
indexRep->tablePtr = (void *) tablePtr;
indexRep->offset = offset;
@@ -488,7 +525,7 @@ DupIndex(
memcpy(dupIndexRep, srcIndexRep, sizeof(IndexRep));
dupPtr->internalRep.twoPtrValue.ptr1 = dupIndexRep;
dupPtr->typePtr = &indexType;
dupPtr->typePtr = &tclIndexType;
}
/*
@@ -960,7 +997,7 @@ Tcl_WrongNumArgs(
* Add the element, quoting it if necessary.
*/
if (origObjv[i]->typePtr == &indexType) {
if (origObjv[i]->typePtr == &tclIndexType) {
register IndexRep *indexRep =
origObjv[i]->internalRep.twoPtrValue.ptr1;
@@ -1010,7 +1047,7 @@ Tcl_WrongNumArgs(
* Otherwise, just use the string rep.
*/
if (objv[i]->typePtr == &indexType) {
if (objv[i]->typePtr == &tclIndexType) {
register IndexRep *indexRep = objv[i]->internalRep.twoPtrValue.ptr1;
Tcl_AppendStringsToObj(objPtr, EXPAND_OF(indexRep), NULL);
@@ -1458,7 +1495,7 @@ TclGetCompletionCodeFromObj(
"ok", "error", "return", "break", "continue", NULL
};
if ((value->typePtr != &indexType)
if ((value->typePtr != &tclIndexType)
&& TclGetIntFromObj(NULL, value, codePtr) == TCL_OK) {
return TCL_OK;
}
+213 -5
View File
@@ -55,6 +55,16 @@
typedef int ptrdiff_t;
#endif
/*
* [MSVC] fallback to replace C++ keyword "inline" with C keyword "__inline"
* Otherwise depending on the VC-version, context, include-order it can cause:
* error C2054: expected '(' to follow 'inline'
*/
#if defined(_MSC_VER) && !defined(inline)
# define inline __inline
#endif
/*
* Ensure WORDS_BIGENDIAN is defined correctly:
* Needs to happen here in addition to configure to work with fat compiles on
@@ -123,6 +133,58 @@ typedef int ptrdiff_t;
# define vsnprintf _vsnprintf
#endif
/*
*----------------------------------------------------------------
* Data structures related to timer / idle events.
*----------------------------------------------------------------
*/
#define TCL_TMREV_PROMPT (1 << 0) /* Mark immediate event (0 microseconds) */
#define TCL_TMREV_AT (1 << 1) /* Mark timer event to execute verbatim
* at the due-time (regardless any
* time-jumps). */
#define TCL_TMREV_IDLE (1 << 3) /* Mark idle event */
#define TCL_TMREV_LISTED (1 << 5) /* Event listed (attached to queue). */
#define TCL_TMREV_DELETE (1 << 7) /* Event will be deleted. */
/*
* This structure used for handling of timer events (with or without time to
* invoke, e. g. created with "after 0") or declared in a call to Tcl_DoWhenIdle
* (created with "after idle"). All of the currently-active handlers are linked
* together into corresponding list.
*
* For each timer callback that's pending there is one record of the following
* type. The normal handlers (created by Tcl_CreateTimerHandler) are chained
* together in a list via TclTimerEvent sorted by time (earliest event first).
*/
typedef struct TclTimerEvent {
Tcl_TimerProc *proc; /* Function to call timer/idle event */
Tcl_TimerDeleteProc *deleteProc; /* Function to cleanup idle event */
ClientData clientData; /* Argument to pass to proc and deleteProc */
int flags; /* Flags, OR-ed combination of flags/states
* TCL_TMREV_PROMPT ... TCL_TMREV_DELETE */
Tcl_WideInt time; /* When timer is to fire (absolute/relative). */
Tcl_TimerToken token; /* Identifies handler so it can be deleted. */
size_t generation; /* Used to distinguish older handlers from
* recently-created ones. */
size_t refCount; /* Used to preserve for deletion (nested exec
* resp. prolongation). */
struct TclTimerEvent *nextPtr;/* Next and prev event in idle queue, */
struct TclTimerEvent *prevPtr;/* or NULL for end/start of the queue. */
/* variable ExtraData */ /* If extraDataSize supplied to create event. */
} TclTimerEvent;
/*
* Macros to wrap ExtraData and TclTimerEvent (and vice versa)
*/
#define TclpTimerEvent2ExtraData(ptr) \
( (ClientData)(((TclTimerEvent *)(ptr))+1) )
#define TclpExtraData2TimerEvent(ptr) \
( ((TclTimerEvent *)(ptr))-1 )
/*
* The following procedures allow namespaces to be customized to support
* special name resolution rules for commands/variables.
@@ -1975,8 +2037,7 @@ typedef struct Interp {
* reached. */
int timeGranularity; /* Mod factor used to determine how often to
* evaluate the limit check. */
Tcl_TimerToken timeEvent;
/* Handle for a timer callback that will occur
TclTimerEvent *timeEvent;/* Handle for a timer callback that will occur
* when the time-limit is exceeded. */
Tcl_HashTable callbacks;/* Mapping from (interp,type) pair to data
@@ -2172,18 +2233,37 @@ typedef struct Interp {
* existence of struct items 'prevPtr' and 'nextPtr'.
*
* a = element to add or remove.
* b = list head.
* b = list head (points to the first element).
* e = list tail (points to the last element).
*
* TclSpliceIn adds to the head of the list.
* TclSpliceTail adds to the tail of the list.
*/
#define TclSpliceIn(a,b) \
(a)->nextPtr = (b); \
if ((b) != NULL) { \
if (((a)->nextPtr = (b)) != NULL) { \
(b)->prevPtr = (a); \
} \
(a)->prevPtr = NULL, (b) = (a);
#define TclSpliceInEx(a,b,e) \
TclSpliceIn(a,b); \
if ((e) == NULL) { \
(e) = (a); \
}
#define TclSpliceTail(a,e) \
if (((a)->prevPtr = (e)) != NULL) { \
(e)->nextPtr = (a); \
} \
(a)->nextPtr = NULL, (e) = (a);
#define TclSpliceTailEx(a,b,e) \
TclSpliceTail(a,e); \
if ((b) == NULL) { \
(b) = (a); \
}
#define TclSpliceOut(a,b) \
if ((a)->prevPtr != NULL) { \
(a)->prevPtr->nextPtr = (a)->nextPtr; \
@@ -2194,6 +2274,11 @@ typedef struct Interp {
(a)->nextPtr->prevPtr = (a)->prevPtr; \
}
#define TclSpliceOutEx(a,b,e) \
TclSpliceOut(a,b) else { \
(e) = (e)->prevPtr; \
}
/*
* EvalFlag bits for Interp structures:
*
@@ -2689,6 +2774,7 @@ MODULE_SCOPE const Tcl_ObjType tclByteArrayType;
MODULE_SCOPE const Tcl_ObjType tclByteCodeType;
MODULE_SCOPE const Tcl_ObjType tclDoubleType;
MODULE_SCOPE const Tcl_ObjType tclEndOffsetType;
MODULE_SCOPE const Tcl_ObjType tclIndexType;
MODULE_SCOPE const Tcl_ObjType tclIntType;
MODULE_SCOPE const Tcl_ObjType tclListType;
MODULE_SCOPE const Tcl_ObjType tclDictType;
@@ -2848,6 +2934,12 @@ struct Tcl_LoadHandle_ {
*----------------------------------------------------------------
*/
MODULE_SCOPE int TclObjIsIndexOfStruct(Tcl_Obj *objPtr,
const void *tablePtr);
#define TclObjIsIndexOfTable(objPtr, tablePtr) \
((objPtr->typePtr == &tclIndexType) \
&& TclObjIsIndexOfStruct(objPtr, tablePtr))
MODULE_SCOPE void TclAppendBytesToByteArray(Tcl_Obj *objPtr,
const unsigned char *bytes, int len);
MODULE_SCOPE int TclNREvalCmd(Tcl_Interp *interp, Tcl_Obj *objPtr,
@@ -3213,9 +3305,70 @@ MODULE_SCOPE Tcl_WideInt TclpGetWideClicks(void);
MODULE_SCOPE double TclpWideClickInMicrosec(void);
# define TclpWideClicksToNanoseconds(clicks) \
((double)(clicks) * TclpWideClickInMicrosec() * 1000)
/* Tolerance (in percent), prevents entering busy wait, but has fewer accuracy
* because can wait a bit shorter as wanted. Currently experimental value
* (4.5% equivalent to 15600 / 15000 with small overhead) */
# ifndef TMR_RES_TOLERANCE
# define TMR_RES_TOLERANCE 4.5
# endif
# endif
#endif
MODULE_SCOPE Tcl_WideInt TclpGetMicroseconds(void);
MODULE_SCOPE Tcl_WideInt TclpGetUTimeMonotonic(void);
MODULE_SCOPE int TclpGetUTimeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
Tcl_WideInt *timePtr, int factor);
MODULE_SCOPE void TclpScaleUTime(Tcl_WideInt *usec);
MODULE_SCOPE void TclpUSleep(Tcl_WideInt usec);
/*
* Helper macros for working with times. TCL_TIME_BEFORE encodes how to write
* the ordering relation on (normalized) times, and TCL_TIME_DIFF_MS resp.
* TCL_TIME_DIFF_US compute the number of milliseconds or microseconds difference
* between two times. Both macros use both of their arguments multiple times,
* so make sure they are cheap and side-effect free.
* Macro TCL_TIME_TO_USEC converts Tcl_Time to microseconds.
* The "prototypes" for these macros are:
*
* static int TCL_TIME_BEFORE(Tcl_Time t1, Tcl_Time t2);
* static Tcl_WideInt TCL_TIME_DIFF_MS(Tcl_Time t1, Tcl_Time t2);
* static Tcl_WideInt TCL_TIME_DIFF_US(Tcl_Time t1, Tcl_Time t2);
* static Tcl_WideInt TCL_TIME_TO_USEC(Tcl_Time t)
*/
#define TCL_TIME_BEFORE(t1, t2) \
(((t1).sec<(t2).sec) || ((t1).sec==(t2).sec && (t1).usec<(t2).usec))
#define TCL_TIME_DIFF_MS(t1, t2) \
(1000*((Tcl_WideInt)(t1).sec - (Tcl_WideInt)(t2).sec) + \
((long)(t1).usec - (long)(t2).usec)/1000)
#define TCL_TIME_DIFF_US(t1, t2) \
(1000000*((Tcl_WideInt)(t1).sec - (Tcl_WideInt)(t2).sec) + \
((long)(t1).usec - (long)(t2).usec))
#define TCL_TIME_TO_USEC(t) \
(((Tcl_WideInt)(t).sec)*1000000 + (t).usec)
static inline void
TclTimeSetMilliseconds(
register Tcl_Time *timePtr,
register double ms
) {
timePtr->sec = (long)(ms / 1000);
timePtr->usec = (((long)ms) % 1000) * 1000 + (((long)(ms*1000)) % 1000);
}
static inline void
TclTimeAddMilliseconds(
register Tcl_Time *timePtr,
register double ms
) {
timePtr->sec += (long)(ms / 1000);
timePtr->usec += (((long)ms) % 1000) * 1000 + (((long)(ms*1000)) % 1000);
if (timePtr->usec > 1000000) {
timePtr->usec -= 1000000;
timePtr->sec++;
}
}
MODULE_SCOPE int TclZlibInit(Tcl_Interp *interp);
MODULE_SCOPE void * TclpThreadCreateKey(void);
@@ -3278,9 +3431,53 @@ MODULE_SCOPE int Tcl_ConcatObjCmd(ClientData clientData,
MODULE_SCOPE int Tcl_ContinueObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE void TclSetTimerEventMarker(int flags);
MODULE_SCOPE int TclServiceTimerEvents(void);
MODULE_SCOPE int TclServiceIdleEx(int flags, int count);
MODULE_SCOPE void TclpCancelEvent(Tcl_Event *evPtr);
static inline Tcl_Event*
TclpQueueEventEx(
Tcl_EventProc *proc, /* Event function to call if it servicing. */
ClientData extraData, /* Event extra data to be included and its */
size_t extraDataSize, /* extra size (to allocate and copy into). */
Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
* TCL_QUEUE_MARK or TCL_QUEUE_RETARDED. */
{
Tcl_Event *evPtr = ckalloc(sizeof(Tcl_Event) + extraDataSize);
evPtr->proc = proc;
memcpy((evPtr+1), extraData, extraDataSize);
Tcl_QueueEvent(evPtr, position);
return evPtr;
}
static inline Tcl_Event*
TclpQueueEventClientData(
Tcl_EventProc *proc, /* Event function to call if it servicing. */
ClientData clientData, /* Event extra data to be included. */
Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
* TCL_QUEUE_MARK or TCL_QUEUE_RETARDED. */
{
Tcl_Event *evPtr = ckalloc(sizeof(Tcl_Event) + sizeof(clientData));
evPtr->proc = proc;
*(ClientData*)(evPtr+1) = clientData;
Tcl_QueueEvent(evPtr, position);
return evPtr;
}
MODULE_SCOPE TclTimerEvent* TclpCreateTimerEvent(Tcl_WideInt usec,
Tcl_TimerProc *proc, Tcl_TimerDeleteProc *delProc,
size_t extraDataSize, int flags);
MODULE_SCOPE TclTimerEvent* TclpCreatePromptTimerEvent(
Tcl_TimerProc *proc, Tcl_TimerDeleteProc *delProc,
size_t extraDataSize, int flags);
MODULE_SCOPE Tcl_TimerToken TclCreateTimerHandler(
Tcl_Time *timePtr, Tcl_TimerProc *proc,
ClientData clientData, int flags);
MODULE_SCOPE Tcl_TimerToken TclCreateAbsoluteTimerHandler(
Tcl_Time *timePtr, Tcl_TimerProc *proc,
ClientData clientData);
MODULE_SCOPE void TclpDeleteTimerEvent(TclTimerEvent *tmrEvent);
MODULE_SCOPE TclTimerEvent* TclpProlongTimerEvent(TclTimerEvent *tmrEvent,
Tcl_WideInt usec, int flags);
MODULE_SCOPE int TclPeekEventQueued(int flags);
MODULE_SCOPE int TclDefaultBgErrorHandlerObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
@@ -4781,6 +4978,17 @@ MODULE_SCOPE Tcl_PackageInitProc Procbodytest_SafeInit;
#define TclLimitExceeded(limit) ((limit).exceeded != 0)
static inline int
TclInlLimitExceeded(
register Tcl_Interp *interp)
{
return (((Interp *)interp)->limit.exceeded != 0);
}
#ifdef Tcl_LimitExceeded
# undef Tcl_LimitExceeded
#endif
#define Tcl_LimitExceeded(interp) TclInlLimitExceeded(interp)
#define TclLimitReady(limit) \
(((limit).active == 0) ? 0 : \
(++(limit).granularityTicker, \
+55 -12
View File
@@ -3276,6 +3276,8 @@ Tcl_MakeSafe(
*----------------------------------------------------------------------
*/
#undef Tcl_LimitExceeded
int
Tcl_LimitExceeded(
Tcl_Interp *interp)
@@ -3747,7 +3749,7 @@ TclLimitRemoveAllHandlers(
*/
if (iPtr->limit.timeEvent != NULL) {
Tcl_DeleteTimerHandler(iPtr->limit.timeEvent);
TclpDeleteTimerEvent(iPtr->limit.timeEvent);
iPtr->limit.timeEvent = NULL;
}
}
@@ -3917,15 +3919,26 @@ Tcl_LimitGetCommands(
return iPtr->limit.cmdCount;
}
static void
TimeLimitDeleteCallback(
ClientData clientData)
{
Interp *iPtr = clientData;
iPtr->limit.timeEvent = NULL;
}
/*
*----------------------------------------------------------------------
*
* Tcl_LimitSetTime --
* Tcl_LimitSetTime --, TclpLimitSetTimeOffs --
*
* Set the time limit for an interpreter by copying it from the value
* pointed to by the timeLimitPtr argument.
*
* TclpLimitSetTimeOffs opposite to Tcl_LimitSetTime set the limit as
* relative time.
*
* Results:
* None.
*
@@ -3943,22 +3956,52 @@ Tcl_LimitSetTime(
Tcl_Time *timeLimitPtr)
{
Interp *iPtr = (Interp *) interp;
Tcl_Time nextMoment;
Tcl_WideInt nextMoment;
memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time));
nextMoment = TCL_TIME_TO_USEC(*timeLimitPtr) + 10;
if (iPtr->limit.timeEvent != NULL) {
Tcl_DeleteTimerHandler(iPtr->limit.timeEvent);
iPtr->limit.timeEvent = TclpProlongTimerEvent(iPtr->limit.timeEvent,
nextMoment, TCL_TMREV_AT);
if (iPtr->limit.timeEvent) {
return;
}
}
nextMoment.sec = timeLimitPtr->sec;
nextMoment.usec = timeLimitPtr->usec+10;
if (nextMoment.usec >= 1000000) {
nextMoment.sec++;
nextMoment.usec -= 1000000;
}
iPtr->limit.timeEvent = TclCreateAbsoluteTimerHandler(&nextMoment,
TimeLimitCallback, interp);
iPtr->limit.timeEvent = TclpCreateTimerEvent(nextMoment,
TimeLimitCallback, TimeLimitDeleteCallback, 0, TCL_TMREV_AT);
iPtr->limit.timeEvent->clientData = interp;
iPtr->limit.exceeded &= ~TCL_LIMIT_TIME;
}
#if 0
void
TclpLimitSetTimeOffs(
Tcl_Interp *interp,
Tcl_WideInt timeOffs)
{
Interp *iPtr = (Interp *) interp;
Tcl_GetTime(&iPtr->limit.time);
iPtr->limit.time.sec += timeOffs / 1000000;
iPtr->limit.time.usec += timeOffs % 1000000;
if (iPtr->limit.time.usec > 1000000) {
iPtr->limit.time.usec -= 1000000;
iPtr->limit.time.sec++;
}
timeOffs += 10;
/* we should use relative time (because of the timeout meaning) */
if (iPtr->limit.timeEvent != NULL) {
iPtr->limit.timeEvent = TclpProlongTimerEvent(iPtr->limit.timeEvent,
timeOffs, 0);
if (iPtr->limit.timeEvent) {
return;
}
}
iPtr->limit.timeEvent = TclpCreateTimerEvent(timeOffs,
TimeLimitCallback, TimeLimitDeleteCallback, 0, 0);
iPtr->limit.timeEvent->clientData = interp;
iPtr->limit.exceeded &= ~TCL_LIMIT_TIME;
}
#endif
/*
*----------------------------------------------------------------------
+768 -185
View File
File diff suppressed because it is too large Load Diff
+1235 -453
View File
File diff suppressed because it is too large Load Diff
+10 -14
View File
@@ -1487,9 +1487,9 @@ OnOffWaitingList(
/*
*----------------------------------------------------------------------
*
* Tcl_Sleep --
* TclpUSleep --
*
* Delay execution for the specified number of milliseconds.
* Delay execution for the specified time (in microseconds).
*
* Results:
* None.
@@ -1501,24 +1501,20 @@ OnOffWaitingList(
*/
void
Tcl_Sleep(
int ms) /* Number of milliseconds to sleep. */
TclpUSleep(
Tcl_WideInt usec) /* Time to sleep. */
{
Tcl_Time vdelay;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (ms <= 0) {
return;
if (usec <= 0) { /* force one context switch */
usec = 0;
}
/*
* TIP #233: Scale from virtual time to real-time.
*/
vdelay.sec = ms / 1000;
vdelay.usec = (ms % 1000) * 1000;
tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
TclpScaleUTime(&usec);
if (tsdPtr->runLoop) {
CFTimeInterval waitTime;
@@ -1526,7 +1522,7 @@ Tcl_Sleep(
CFAbsoluteTime nextTimerFire = 0, waitEnd, now;
SInt32 runLoopStatus;
waitTime = vdelay.sec + 1.0e-6 * vdelay.usec;
waitTime = (usec / 1000000) + 1.0e-6 * (usec % 1000000);
now = CFAbsoluteTimeGetCurrent();
waitEnd = now + waitTime;
@@ -1563,8 +1559,8 @@ Tcl_Sleep(
} else {
struct timespec waitTime;
waitTime.tv_sec = vdelay.sec;
waitTime.tv_nsec = vdelay.usec * 1000;
waitTime.tv_sec = (usec / 1000000);
waitTime.tv_nsec = (usec % 1000000) * 1000;
while (nanosleep(&waitTime, &waitTime));
}
}
+66 -2
View File
@@ -131,7 +131,29 @@ proc test-exec {{reptime 1000}} {
}
}
proc test-nrt-capability {{reptime 1000}} {
proc test-exec-new {{reptime 1000}} {
_test_run $reptime {
# conditional update pure idle only (without window):
{update -idle}
# conditional update without idle events:
{update -noidle}
# conditional update timers only:
{update -timer}
# conditional update AIO only:
{update -async}
# conditional vwait with zero timeout: pure idle only (without window):
{vwait -idle 0 x}
# conditional vwait with zero timeout: without idle events:
{vwait -noidle 0 x}
# conditional vwait with zero timeout: timers only:
{vwait -timer 0 x}
# conditional vwait with zero timeout: AIO only:
{vwait -async 0 x}
}
}
proc test-nrt-capability-old {{reptime 1000}} {
_test_run $reptime {
# comparison values:
{after 0 {set a 5}; update}
@@ -143,6 +165,43 @@ proc test-nrt-capability {{reptime 1000}} {
}
}
proc test-nrt-capability {{reptime 1000}} {
_test_run $reptime {
# comparison values:
{after 0 {set a 5}; update}
{after 0 {set a 5}; vwait a}
# conditional vwait with very brief wait-time:
{vwait 1 a}
{vwait 0.5 a}
{vwait 0.2 a}
{vwait 0.1 a}
{vwait 0.05 a}
{vwait 0.02 a}
{vwait 0.01 a}
{vwait 0.005 a}
{vwait 0.001 a}
# NRT sleep / very brief delays (0.5 - 0.005):
{after 0.5}
{after 0.05}
{after 0.005}
# NRT sleep / very brief delays (0.1 - 0.001):
{after 0.1}
{after 0.01}
{after 0.001}
# comparison of update's executing event:
{after idle {set a 5}; update -idle -timer}
{after 0 {set a 5}; update -idle -timer}
{after idle {set a 5}; update -idle}
# comparison of vwait's executing event:
{after idle {set a 5}; vwait -idle -timer a}
{after 0 {set a 5}; vwait -idle -timer a}
{after idle {set a 5}; vwait -idle a}
}
}
proc test-long {{reptime 1000}} {
_test_run $reptime {
# in-between important event by amount of idle events:
@@ -159,7 +218,12 @@ proc test {{reptime 1000}} {
foreach howmuch {5000 50000} {
test-access [list $reptime $howmuch]
}
test-nrt-capability $reptime
if {![catch {update -noidle}]} {
test-exec-new $reptime
test-nrt-capability $reptime
} else {
test-nrt-capability-old $reptime
}
test-long $reptime
puts ""
+10 -9
View File
@@ -1715,7 +1715,7 @@ test chan-io-14.9 {reuse of stdio special channels} -setup {
# Added delay to give Windows time to stop the spawned process and clean
# up its grip on the file test1. Added delete as proper test cleanup.
# The failing tests were 18.1 and 18.2 as first re-users of file "test1".
after [expr {[testConstraint win] ? 10000 : 500}]
after [expr {[testConstraint win] ? 1000 : 500}]
file delete $path(script)
file delete $path(test1)
} -result hello
@@ -6969,17 +6969,17 @@ test chan-io-53.7 {CopyData: Flooding chan copy from pipe} -setup {
set fcopyTestCount 0
set f1 [open $path(pipe) w]
chan puts $f1 {
# Write 10 bytes / 10 msec
# Write 10 bytes / 1 msec
proc Write {count} {
chan puts -nonewline "1234567890"
if {[incr count -1]} {
after 10 [list Write $count]
after 1 [list Write $count]
} else {
set ::ready 1
}
}
chan configure stdout -buffering none
Write 345 ;# 3450 bytes ~3.45 sec
Write 345 ;# 3450 bytes ~0.345 sec
vwait ready
exit 0
}
@@ -7089,11 +7089,11 @@ test chan-io-53.9 {CopyData: -size and event interaction, Bug 780533} -setup {
chan puts $pipe {
chan configure stdout -translation binary -buffering line
chan puts stderr Waiting...
after 1000
after 100
foreach x {a b c} {
chan puts stderr Looping...
chan puts $x
after 500
after 50
}
proc bye args {
if {[chan gets stdin line]<0} {
@@ -7221,7 +7221,10 @@ test chan-io-54.1 {Recursive channel events} {socket fileevent} {
lappend result $next
if {$next == 1} {
chan event $s readable [namespace code [list readit $s 2]]
vwait [namespace which -variable x]
if {![vwait 2000 [namespace which -variable x]]} {
set x failure
error "timeout: too long wait"
}
}
incr x
}
@@ -7250,9 +7253,7 @@ test chan-io-54.1 {Recursive channel events} {socket fileevent} {
lappend result [chan gets $cs]
chan configure $cs -blocking off
chan event $cs readable [namespace code [list readit $cs 1]]
set a [after 2000 [namespace code { set x failure }]]
vwait [namespace which -variable x]
after cancel $a
chan close $as
chan close $ss
chan close $cs
+240 -6
View File
@@ -423,6 +423,38 @@ foo
("after" script)
}
test event-7.10 {after at - absolute time} {
set result {}
# 1st test simple delay (and mix generation / recursive processing)
set attm [expr {[clock milliseconds]/1000.0 + 0.150}]
after 0 {lappend result 1}
after 100 {
update; # this don't catch event 3
lappend result 2a
after at $attm; # delay to 150ms from start
update; # this still don't catch event 3 also
lappend result 2b
after at [expr {$attm + 0.100}]; # delay to 250ms from start
update; # this should catch event 3
lappend result 2c
}
after 200 {lappend result 3; set a done}
vwait a
# 2nd test events "at" (mix due-times between relative/absolute events)
lappend result --
set sttm [expr {[clock milliseconds]/1000.0}]
after 200 {lappend result 4; set a done}
after 120 {lappend result 5}
after 40 {lappend result 6}
after at [expr {$sttm+0.160}] {lappend result at-1}
after at [expr {$sttm+0.080}] {lappend result at-2}
after at [expr {$sttm+0.005}] {lappend result at-3}
after 2000 {lappend result [set a timeout]}
after 0 {lappend result 7}
vwait a
set result
} {1 2a 2b 3 2c -- 7 at-3 6 at-2 5 at-1 4}
# someday : add a test checking that when there is no bgerror, an error msg
# goes to stderr ideally one would use sub interp and transfer a fake stderr
# to it, unfortunatly the current interp tcl API does not allow that. The
@@ -509,10 +541,10 @@ test event-10.1 {Tcl_Exit procedure} {stdio} {
test event-11.1 {Tcl_VwaitCmd procedure} -returnCodes error -body {
vwait
} -result {wrong # args: should be "vwait name"}
} -result {wrong # args: should be "vwait ?options? ?timeout? name"}
test event-11.2 {Tcl_VwaitCmd procedure} -returnCodes error -body {
vwait a b
} -result {wrong # args: should be "vwait name"}
} -result {bad option "a": must be -idle, -noidle, -timer, -notimer, -file, -nofile, -window, -nowindow, -async, -noasync, -nowait, -wait, or idletasks}
test event-11.3 {Tcl_VwaitCmd procedure} -setup {
catch {unset x}
} -body {
@@ -539,6 +571,155 @@ test event-11.4 {Tcl_VwaitCmd procedure} -setup {
after cancel $i
}
} -result {{} x-done y-done before q-done}
test event-11.4.0 {vwait - interp limit precedence} {} {
foreach i [after info] {
after cancel $i
}
set result {}
set i [interp create]
$i bgerror {lappend errors}; # prevent stdout background errors;
# limit should be exceeded (wait infinite):
$i limit time -milliseconds 0
lappend result 1. [catch {$i eval {vwait x}} msg] $msg
# no limit in between:
$i limit time -seconds {} -milliseconds {}
lappend result 2. [catch {$i eval {vwait 0 x}} msg] $msg
# limit should be exceeded: (wait infinite)
$i limit time -milliseconds 0
lappend result 3. [catch {$i eval {vwait x}} msg] $msg
# limit should be exceeded (wait too long - 1000ms):
$i limit time -milliseconds 0
lappend result 4. [catch {$i eval {vwait 1000 x}} msg] $msg
set tout [clock seconds]; incr tout 10
# wait timeout (before limit):
$i limit time -seconds $tout
lappend result 5. [catch {$i eval {vwait 0 x}} msg] $msg
# wait timeout (before limit):
$i limit time -seconds $tout
lappend result 6. [catch {$i eval {vwait 10 x}} msg] $msg
# wait successful (before limit):
$i limit time -seconds $tout
lappend result 7. [catch {$i eval {after 0 {set x ""}; vwait 10 x}} msg] $msg
interp delete $i
set result
} [list \
1. 1 {limit exceeded} \
2. 0 0 \
3. 1 {limit exceeded} \
4. 1 {limit exceeded} \
5. 0 0 \
6. 0 0 \
7. 0 1 \
]
test event-11.4.0 {vwait conditional with timeout (bypass timer)} {} {
set x {}
after 1000 {lappend x "error-too-slow"}
after 0 {lappend x 1-timer}
after 1 {lappend x 2-timer}
after idle {lappend x 3-idle}
vwait -async 50 x; # ignore all except async (timer also)
lappend x 4-async
vwait -idle 50 x; # ignore all except idle (timer also)
lappend x 5-idle
after idle {lappend x 6-idle}
vwait 100 x; # now we accept timer events
lappend x 7-idle
vwait 100 x;
# cleanup:
foreach i [after info] {
after cancel $i
}
set x
} {4-async 3-idle 5-idle 1-timer 2-timer 7-idle 6-idle}
test event-11.4.1 {vwait with timeout} {} {
foreach i [after info] {
after cancel $i
}
set z {}
set x {}
# success cases:
after 0 {lappend z 0}
after 50 {lappend x 1}
after 50 {lappend x 2}
after 250 {lappend x 3}
after 1000 {lappend x "error-too-slow"}
vwait 0 x; # no-wait
lappend z $x; # 0 {} - (x still empty)
vwait 200 x; # wait up-to 200ms
lappend z $x; # 0 {} {1 2}
vwait x; # infinite wait
lappend z $x; # 0 {} {1 2} {1 2 3}
foreach i [after info] {
after cancel $i
}
set z
} {0 {} {1 2} {1 2 3}}
test event-11.4.2 {cancel} {} {
foreach i [after info] {
after cancel $i
}
set x {}
# success cases:
after 10 {lappend x 1}
after 10 {lappend x 2}
after 10 {lappend x 3}
# cancel via object representation (4-6) and searching by id (7-9):
foreach i [list \
[after 0 {lappend x 4-unexpected}] \
[after 5 {lappend x 5-unexpected}] \
[after 10 {lappend x 6-unexpected}] \
[string trim " [after 0 {lappend x 7-unexpected}] "] \
[string trim " [after 5 {lappend x 8-unexpected}] "] \
[string trim " [after 10 {lappend x 9-unexpected}] "] \
] {
after cancel $i
}
after 20 {set y done}
list [vwait y] $x $y
} {{} {1 2 3} done}
test event-11.4.3 {cancel twice and info} {} {
foreach i [after info] {
after cancel $i
}
set x {}
# success cases:
after 10 {lappend x 1}
after 10 {lappend x 2}
after 10 {lappend x 3}
# cancel via object representation (4-6) and searching by id (7-9):
foreach i [list \
[after 0 {lappend x 4-unexpected}] \
[after 5 {lappend x 5-unexpected}] \
[after 10 {lappend x 6-unexpected}] \
[string trim " [after 0 {lappend x 7-unexpected}] "] \
[string trim " [after 5 {lappend x 8-unexpected}] "] \
[string trim " [after 10 {lappend x 9-unexpected}] "] \
] {
after cancel $i
# just to test possible segfault:
after cancel $i
if {![catch {after info $i} i]} {; # unexpected (event doesn't exist)
error "\"after info\" returns \"$i\" - should be an error"
}
}
after 20 {set y done}
list [vwait y] $x $y
} {{} {1 2 3} done}
foreach i [after info] {
after cancel $i
}
test event-11.5 {Tcl_VwaitCmd procedure: round robin scheduling, 2 sources} -setup {
set test1file [makeFile "" test1]
} -constraints {socket} -body {
@@ -612,12 +793,9 @@ test event-11.8 {Bug 16828b3744} -setup {
A destroy
} -result {}
test event-12.1 {Tcl_UpdateCmd procedure} -returnCodes error -body {
update a b
} -result {wrong # args: should be "update ?idletasks?"}
test event-12.2 {Tcl_UpdateCmd procedure} -returnCodes error -body {
update bogus
} -result {bad option "bogus": must be idletasks}
} -result {bad option "bogus": must be -idle, -noidle, -timer, -notimer, -file, -nofile, -window, -nowindow, -async, -noasync, -nowait, -wait, or idletasks}
test event-12.3 {Tcl_UpdateCmd procedure} -setup {
foreach i [after info] {
after cancel $i
@@ -657,6 +835,62 @@ test event-12.4 {Tcl_UpdateCmd procedure} -setup {
}
} -result {x-done before z-done}
test event-12.5 {update -idle, update -noidle} {
foreach i [after info] {
after cancel $i
}
set x {}
after idle {lappend x idle}
update -noidle
after 0 {lappend x 0}
update -noidle
after 50 {lappend x 1}
update -noidle
lappend x 2
update -idle
lappend x 3
after idle {lappend x idle}
after 0 {lappend x 4}
after 0 {lappend x 5}
update -idle
lappend x 6
update
lappend x res:[vwait 500 x]
set x
} {0 2 idle 3 idle 6 4 5 1 res:1}
test event-12.6 {update -timer, update -notimer} {
foreach i [after info] {
after cancel $i
}
set x {}
after idle {lappend x idle.0}
update -timer
after 0 {lappend x 0a}
update -notimer
after idle {
lappend x idle.1a;
after 0 {lappend x 0b};
after idle {lappend x idle.1b}
}
after 50 {lappend x 1; after idle {lappend x idle.2}}
update -timer
lappend x 2
update -timer -idle
lappend x 3
after idle {lappend x idle.3}
after 0 {lappend x 4}
after 0 {lappend x 5}
update -timer -idle
lappend x 6
update
lappend x res:[vwait 500 x]
update -noidle
lappend x 7
update
set x
} {idle.0 0a 2 idle.1a 0b idle.1b 3 4 5 idle.3 6 1 res:1 7 idle.2}
test event-13.1 {Tcl_WaitForFile procedure, readable} -setup {
foreach i [after info] {
after cancel $i
+16 -9
View File
@@ -3590,24 +3590,31 @@ test interp-36.7 {SlaveBgerror sets error handler of slave [1999035]} -setup {
interp create slave
slave alias handler handler
slave bgerror handler
variable result {untouched}
variable result {}
proc handler {args} {
variable result
set result [lindex $args 0]
variable result
lappend result [lindex $args 0]
}
} -body {
slave eval {
variable done {}
after 0 error foo
after 10 [list ::set [namespace which -variable done] {}]
vwait [namespace which -variable done]
# because bgerror executed in background and it is idle-event, give enough
# time to process it (resp. wait until last idle event is done).
slave eval {; # via vwait
after 0 error foo-1
after 10 {after idle {set done {}}}
vwait done
}
lappend result between
slave eval {; # via update
after 1 error foo-2
after 10
update
}
set result
} -cleanup {
variable result {}
unset -nocomplain result
interp delete slave
} -result foo
} -result {foo-1 between foo-2}
test interp-37.1 {safe interps and min() and max(): Bug 2895741} -setup {
catch {interp delete a}
+15 -3
View File
@@ -2905,7 +2905,8 @@ test io-29.33b {TIP#398, no implicit flush of nonblocking on exit} {exec} {
close $ff
expr {($t2-$t1)/2000 ? $t2-$t1 : 0}
} 0
test io-29.34 {Tcl_Close, async flush on close, using sockets} {socket tempNotMac fileevent} {
proc io-29.34 {wait} {
variable c 0
variable x running
set l abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
@@ -2934,14 +2935,25 @@ test io-29.34 {Tcl_Close, async flush on close, using sockets} {socket tempNotMa
}
set ss [socket -server [namespace code accept] -myaddr 127.0.0.1 0]
set cs [socket 127.0.0.1 [lindex [fconfigure $ss -sockname] 2]]
vwait [namespace which -variable x]
{*}$wait [namespace which -variable x]
if {$x ne "accepted"} {error "timeout, state: $x"}
fconfigure $cs -blocking off
writelots $cs $l
close $cs
close $ss
vwait [namespace which -variable x]
{*}$wait [namespace which -variable x]
if {$x ne "done"} {error "timeout, state: $x"}
set c
}
test io-29.34.1 {Tcl_Close, async flush on close, using sockets} {socket tempNotMac fileevent} {
io-29.34 {vwait 5000}
} 9000
test io-29.34.2 {same as io-29.34 / IO without timer events} {socket tempNotMac fileevent} {
# this test will fail if IO uses timer event for scheduled execution.
io-29.34 {vwait -notimer 5000}
} 9000
test io-29.35 {Tcl_Close vs fileevent vs multiple interpreters} {socket tempNotMac fileevent} {
# On Mac, this test screws up sockets such that subsequent tests using port 2828
# either cause errors or panic().
+323 -142
View File
@@ -17,23 +17,40 @@ if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest 2
namespace import -force ::tcltest::*
}
# On some platforms the short tolerance (in percent to wait-time) used to avoid
# busy waiting, so it may cause on the fast mashines, that waiting interupts a bit
# earlier as expected ("rounded" to this tolerance boundary).
#
# Following routines guarantee the timer event always occured in such cases.
variable tolerance 5
proc tl-after {ms args} {
variable tolerance
uplevel [list after [expr {$ms + double($ms*$tolerance/100)}] {*}$args]
}
proc tl-vwait {args} {
variable tolerance
set ms [lindex $args end-1]
set vn [lindex $args end]
uplevel [list vwait [expr {$ms + double($ms*$tolerance/100)}] {*}[lrange $args 0 end-2] $vn]
}
test timer-1.1 {Tcl_CreateTimerHandler procedure} -setup {
proc clean-up-events {} {
foreach i [after info] {
after cancel $i
}
}
test timer-1.1 {Tcl_CreateTimerHandler procedure} -setup {
clean-up-events
} -body {
set x ""
foreach i {100 200 1000 50 150} {
after $i lappend x $i
}
after 200 set done 1
vwait done
tl-vwait 200 done
return $x
} -cleanup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -result {50 100 150 200}
test timer-2.1 {Tcl_DeleteTimerHandler procedure} -setup {
@@ -47,8 +64,7 @@ test timer-2.1 {Tcl_DeleteTimerHandler procedure} -setup {
}
after cancel lappend x 150
after cancel lappend x 50
after 200 set done 1
vwait done
tl-vwait 200 done
return $x
} -result {100 200}
@@ -65,70 +81,60 @@ test timer-3.1 {TimerHandlerEventProc procedure: event masks} {
lappend result $x
} {start fired}
test timer-3.2 {TimerHandlerEventProc procedure: multiple timers} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
foreach i {200 600 1000} {
after $i lappend x $i
}
after 200
tl-after 200
set result ""
set x ""
update
lappend result $x
after 400
tl-after 400
update
lappend result $x
after 400
tl-after 400
update
lappend result $x
} -result {200 {200 600} {200 600 1000}}
test timer-3.3 {TimerHandlerEventProc procedure: reentrant timer deletion} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x {}
after 100 lappend x 100
set i [after 300 lappend x 300]
after 200 after cancel $i
after 400
tl-after 400
update
return $x
} -result 100
test timer-3.4 {TimerHandlerEventProc procedure: all expired timers fire} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x {}
after 100 lappend x a
after 200 lappend x b
after 300 lappend x c
after 300
tl-after 300
vwait x
return $x
} -result {a b c}
test timer-3.5 {TimerHandlerEventProc procedure: reentrantly added timers don't fire} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x {}
after 100 {lappend x a; after 0 lappend x b}
after 100
after 10 {lappend x a; after 0 lappend x b}
after 50
vwait x
return $x
} -result a
test timer-3.6 {TimerHandlerEventProc procedure: reentrantly added timers don't fire} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x {}
after 100 {lappend x a; after 100 lappend x b; after 100}
after 100
tl-after 100
vwait x
set result $x
vwait x
@@ -139,9 +145,7 @@ test timer-3.6 {TimerHandlerEventProc procedure: reentrantly added timers don't
# below.
test timer-4.1 {Tcl_CancelIdleCall procedure} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x before
set y before
@@ -154,9 +158,7 @@ test timer-4.1 {Tcl_CancelIdleCall procedure} -setup {
list $x $y $z
} -result {after1 before after3}
test timer-4.2 {Tcl_CancelIdleCall procedure} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x before
set y before
@@ -170,9 +172,7 @@ test timer-4.2 {Tcl_CancelIdleCall procedure} -setup {
} -result {before after2 after3}
test timer-5.1 {Tcl_ServiceIdle, self-rescheduling handlers} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x 1
set y 23
@@ -189,27 +189,34 @@ test timer-6.1 {Tcl_AfterCmd procedure, basics} -returnCodes error -body {
} -result {wrong # args: should be "after option ?arg ...?"}
test timer-6.2 {Tcl_AfterCmd procedure, basics} -returnCodes error -body {
after 2x
} -result {bad argument "2x": must be cancel, idle, info, or an integer}
} -result {bad argument "2x": must be at, cancel, idle, info or a time}
test timer-6.3 {Tcl_AfterCmd procedure, basics} -returnCodes error -body {
after gorp
} -result {bad argument "gorp": must be cancel, idle, info, or an integer}
} -result {bad argument "gorp": must be at, cancel, idle, info or a time}
test timer-6.4 {Tcl_AfterCmd procedure, ms argument} {
set x before
after 400 {set x after}
# cover prompt events also (immediate handling differs now):
after 0 {lappend x immediate}
lappend x before-immediate
update
lappend x after-immediate
after 100 {lappend x after}
after 10
update
lappend x nothing
after 200
lappend x before-after
update
set y $x
after 400
update
list $y $x
} {before after}
lappend x after-after
set x
} {before before-immediate immediate after-immediate nothing before-after after after-after}
test timer-6.5 {Tcl_AfterCmd procedure, ms argument} {
set x before
after 400 set x after
after 200
after 100 set x after
after 10
update
set y $x
after 400
after 200
update
list $y $x
} {before after}
@@ -223,50 +230,42 @@ test timer-6.8 {Tcl_AfterCmd procedure, cancel option} {
after cancel {foo bar}
} {}
test timer-6.9 {Tcl_AfterCmd procedure, cancel option} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x before
set y [after 100 set x after]
set y [after 10 set x after]
after cancel $y
after 200
after 100
update
return $x
} -result {before}
test timer-6.10 {Tcl_AfterCmd procedure, cancel option} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x before
after 100 set x after
after 10 set x after
after cancel {set x after}
after 200
after 100
update
return $x
} -result {before}
test timer-6.11 {Tcl_AfterCmd procedure, cancel option} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x before
after 100 set x after
set id [after 300 set x after]
after 10 set x after-10
set id [after 30 set x after-30]
after cancel $id
after 200
after 100
update
set y $x
set x cleared
after 200
after 100
update
list $y $x
} -result {after cleared}
} -result {after-10 cleared}
test timer-6.12 {Tcl_AfterCmd procedure, cancel option} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x first
after idle lappend x second
@@ -278,9 +277,7 @@ test timer-6.12 {Tcl_AfterCmd procedure, cancel option} -setup {
return $x
} -result {first third}
test timer-6.13 {Tcl_AfterCmd procedure, cancel option, multiple arguments for command} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x first
after idle lappend x second
@@ -292,22 +289,19 @@ test timer-6.13 {Tcl_AfterCmd procedure, cancel option, multiple arguments for c
return $x
} -result {first third}
test timer-6.14 {Tcl_AfterCmd procedure, cancel option, cancel during handler, used to dump core} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set id [
after 100 {
after 10 {
set x done
after cancel $id
after idle {set y done}
}
]
vwait x
} -result {}
list [tl-vwait 1000 x] [tl-vwait 100 y]
} -result {1 1}
test timer-6.15 {Tcl_AfterCmd procedure, cancel option, multiple interps} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
interp create x
x eval {set a before; set b before; after idle {set a a-after};
@@ -348,23 +342,47 @@ set childEvent [x eval {after idle event in child}]
test timer-6.19 {Tcl_AfterCmd, info option} {
lsort [after info]
} [lsort "$event1 $event2"]
test timer-6.19.1 {Tcl_AfterCmd, info option (all events in child)} {
x eval {after info}
} [list $childEvent]
test timer-6.20 {Tcl_AfterCmd, info option} -returnCodes error -body {
after info a b
} -result {wrong # args: should be "after info ?id?"}
test timer-6.21 {Tcl_AfterCmd, info option} -returnCodes error -body {
after info $childEvent
} -result "event \"$childEvent\" doesn't exist"
test timer-6.21.1 {Tcl_AfterCmd, info option (internal representation)} {
list [catch {x eval [list after info $childEvent]} msg] $msg
} {0 {{event in child} idle}}
test timer-6.21.1 {Tcl_AfterCmd, info option (internal representation)} {
list [catch {x eval [list after info $childEvent]} msg] $msg
} {0 {{event in child} idle}}
test timer-6.21.2 {Tcl_AfterCmd, info option (search using string representation)} {
list [catch {x eval [list after info [string trim " $childEvent "]]} msg] $msg
} {0 {{event in child} idle}}
test timer-6.22 {Tcl_AfterCmd, info option} {
list [after info $event1] [after info $event2]
} {{{event 1} idle} {{event 2} timer}}
test timer-6.22.1 {Tcl_AfterCmd, cancel option (internal representation)} {
after cancel $childEvent; # foreign event - does nothing
# check still available:
list [catch {x eval [list after info $childEvent]} msg] $msg
} {0 {{event in child} idle}}
test timer-6.22.2 {Tcl_AfterCmd, cancel option (search using string representation)} {
after cancel [string trim " $childEvent "]; # foreign event - does nothing
# check still available:
set lst [list [catch {x eval [list after info $childEvent]} msg] $msg]
# cancel again but in child:
x eval [list after cancel [string trim " $childEvent "]]
# check it was canceled:
lappend lst {*}[list [catch {x eval [list after info $childEvent]} msg] $msg]
} [list 0 {{event in child} idle} 1 "event \"$childEvent\" doesn't exist"]
after cancel $event1
after cancel $event2
interp delete x
test timer-6.23 {Tcl_AfterCmd procedure, no option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
after 1 "set x ab\0cd"
@@ -373,9 +391,7 @@ test timer-6.23 {Tcl_AfterCmd procedure, no option, script with NUL} -setup {
string length $x
} -result {5}
test timer-6.24 {Tcl_AfterCmd procedure, no option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
after 1 set x ab\0cd
@@ -384,37 +400,27 @@ test timer-6.24 {Tcl_AfterCmd procedure, no option, script with NUL} -setup {
string length $x
} -result {5}
test timer-6.25 {Tcl_AfterCmd procedure, cancel option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
after 1 set x ab\0cd
after cancel "set x ab\0ef"
llength [after info]
} -cleanup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -result {1}
test timer-6.26 {Tcl_AfterCmd procedure, cancel option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
after 1 set x ab\0cd
after cancel set x ab\0ef
llength [after info]
} -cleanup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -result {1}
test timer-6.27 {Tcl_AfterCmd procedure, idle option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
after idle "set x ab\0cd"
@@ -422,9 +428,7 @@ test timer-6.27 {Tcl_AfterCmd procedure, idle option, script with NUL} -setup {
string length $x
} -result {5}
test timer-6.28 {Tcl_AfterCmd procedure, idle option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
after idle set x ab\0cd
@@ -432,9 +436,7 @@ test timer-6.28 {Tcl_AfterCmd procedure, idle option, script with NUL} -setup {
string length $x
} -result {5}
test timer-6.29 {Tcl_AfterCmd procedure, info option, script with NUL} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
set x "hello world"
set id junk
@@ -442,9 +444,7 @@ test timer-6.29 {Tcl_AfterCmd procedure, info option, script with NUL} -setup {
update
string length [lindex [lindex [after info $id] 0] 2]
} -cleanup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -result 5
set event [after idle foo bar]
@@ -473,8 +473,8 @@ test timer-8.1 {AfterProc procedure} {
set x before
proc foo {} {
set x untouched
after 100 {set x after}
after 200
after 10 {set x after}
after 100
update
return $x
}
@@ -488,8 +488,8 @@ test timer-8.2 {AfterProc procedure} -setup {
set handler [interp bgerror {}]
interp bgerror {} [namespace which myHandler]
} -body {
after 100 {error "After error"}
after 200
after 10 {error "After error"}
after 100
set y $x
update
list $y $x
@@ -500,9 +500,7 @@ test timer-8.2 {AfterProc procedure} -setup {
"error "After error""
("after" script)}}}
test timer-8.3 {AfterProc procedure, deleting handler from itself} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
proc foo {} {
global x
@@ -513,14 +511,13 @@ test timer-8.3 {AfterProc procedure, deleting handler from itself} -setup {
after cancel foo
}
after idle foo
after 1000 {error "I shouldn't ever have executed"}
after 0 {error "I shouldn't ever have executed"}
update idletasks
clean-up-events
return $x
} -result {{{error "I shouldn't ever have executed"} timer}}
test timer-8.4 {AfterProc procedure, deleting handler from itself} -setup {
foreach i [after info] {
after cancel $i
}
clean-up-events
} -body {
proc foo {} {
global x
@@ -530,15 +527,14 @@ test timer-8.4 {AfterProc procedure, deleting handler from itself} -setup {
}
after cancel foo
}
after 1000 {error "I shouldn't ever have executed"}
after 0 {error "I shouldn't ever have executed"}
after 1 {error "I also shouldn't ever have executed"}
after idle foo
update idletasks
return $x
} -result {{{error "I shouldn't ever have executed"} timer}}
lsort $x
} -result [lsort {{{error "I shouldn't ever have executed"} timer} {{error "I also shouldn't ever have executed"} timer}}]
foreach i [after info] {
after cancel $i
}
clean-up-events
# No test for FreeAfterPtr, since it is already tested above.
@@ -546,23 +542,23 @@ test timer-9.1 {AfterCleanupProc procedure} -setup {
catch {interp delete x}
} -body {
interp create x
x eval {after 200 {
x eval {after 10 {
lappend x after
puts "part 1: this message should not appear"
}}
after 200 {lappend x after2}
x eval {after 200 {
after 10 {lappend x after2}
x eval {after 10 {
lappend x after3
puts "part 2: this message should not appear"
}}
after 200 {lappend x after4}
x eval {after 200 {
after 10 {lappend x after4}
x eval {after 10 {
lappend x after5
puts "part 3: this message should not appear"
}}
interp delete x
set x before
after 300
after 100
update
return $x
} -result {before after2 after4}
@@ -579,15 +575,15 @@ test timer-10.1 {Bug 1016167: [after] overwrites imports} -setup {
interp delete slave
} -result ::after
test timer-11.1 {Bug 1350291: [after] overflowing 32-bit field} -body {
test timer-11.1 {Bug 1350291: [after] overflowing 32-bit field} -setup {
clean-up-events
} -body {
set b ok
set a [after 0x100000001 {set b "after fired early"}]
after 100 set done 1
vwait done
return $b
list [vwait 100 b] [set b]
} -cleanup {
catch {after cancel $a}
} -result ok
} -result {0 ok}
test timer-11.2 {Bug 1350293: [after] negative argument} -body {
set l {}
after 100 {lappend l 100; set done 1}
@@ -595,6 +591,191 @@ test timer-11.2 {Bug 1350293: [after] negative argument} -body {
vwait done
return $l
} -result {-1 100}
test timer-11.3 {[after] correct timer ordering (insert ahead)} -setup {
clean-up-events
} -body {
after 10 {set done 1}
foreach l {1 0.75 0.5 0.25 0.1 0} {
after $l [list lappend l "ev:$l"]
}
set l {}
vwait done
set l
} -result {ev:0 ev:0.1 ev:0.25 ev:0.5 ev:0.75 ev:1}
# -----------------------------------------------------------------------------
#
# timer-20.x --
#
# Following test-cases cover event-driven functionality during time-jump's
#
# Note: on some platforms it is only possible if the user has corresponding
# privileges to change system date and time.
#
# Ex.: sudo LD_LIBRARY_PATH=. ./tclsh ../tests/timer.test -match timer-20.*
# -----------------------------------------------------------------------------
proc timejump {args} {
set tnow [clock clicks]
set tm [clock format [clock add [clock seconds] {*}$args] -format %H:%M:%S]
#puts -nonewline "***[format \[%04X\] [pid]]*** jump to $tm ($args) "
if {$::tcl_platform(platform) eq "windows"} {
exec $::env(COMSPEC) /c time $tm
} else {
exec date +%T -s $tm
}
#puts "***[format \[%04X\] [pid]]*** [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}] =="
}
testConstraint ChangeTimePrivilege [expr {![catch { timejump 0 minutes }]}]
testConstraint NewVWaitAfter [expr {![catch { vwait 0 nothing }]}]
# provide monotonic command, if "clock monotonic" not available (old core):
if {![catch {::tcl::clock::monotonic}]} {
namespace inscope ::tcl::clock {namespace export monotonic}
namespace import ::tcl::clock::monotonic
} else {
proc monotonic {} {
variable tickFactor
# warming-up:
clock clicks; clock microseconds
# calc tick2usec factor:
set tick [::tcl::clock::clicks]; set usec [::tcl::clock::microseconds]
after 100
set tickFactor [expr {double([::tcl::clock::clicks] - $tick) / ([::tcl::clock::microseconds] - $usec)}]
proc monotonic {} {
variable tickFactor
expr {wide([::tcl::clock::clicks] / $tickFactor)}
}
monotonic
}
}
# asynchronous function doing 3 time jumps (+1 min, +1 min, -2 minutes) in 250-ms:
proc delayedtimejump {delay {async 1} {startms {}}} {
# async code (executed in separate process):
if {$async} {
set code ""
foreach pr {timejump delayedtimejump} {
append code [list proc $pr [info args $pr] [info body $pr]] \n
}
append code "delayedtimejump $delay 0 [::tcl::clock::milliseconds]"
exec [info nameofexecutable] << $code &
return
}
# time-jumps (1st in 250-ms, 2nd and 3rd in given delay):
set delay1 250
if {$startms ne {}} {
set delay1 [expr {250 - ([::tcl::clock::milliseconds] - $startms)}]
if {$delay1 < 0} { set delay1 0 }
}
after $delay1; timejump +1 minutes
after $delay; timejump +1 minutes
after $delay; timejump -2 minutes
}
proc test-timer-with-jump {code maxCount expMT {maxOffs 100}} {
set mitm [set sttm [clock seconds]]
set stmt [monotonic]
set expMT [expr {$expMT * 1000}]
set maxOffs [expr {$maxOffs * 1000}]
set res {}
set jumped 0
for {set i 0} {$i < $maxCount} {incr i} {
set rt [::tcl::clock::microseconds]
set mt [monotonic]
# execute:
if 1 $code
# verify monotonic time:
set mt [expr {[monotonic] - $mt}]
set rt [expr {[::tcl::clock::microseconds] - $rt}]
# give +/- 100-ms for some slow or busy systems:
if {$mt < $expMT - $maxOffs || $mt > $expMT + $maxOffs} {
lappend res "$i. too large discrepancy mt: $mt ~ $expMT (rt: $rt)"
} else {
#lappend res "$i. debug, # mt: $mt, rt: $rt"
}
# be sure we are in the future and then comming back:
if {$jumped > 2} break; # we are already back
if {[clock seconds] - $mitm > 30 || [clock seconds] - $mitm < -30} {
set mitm [clock seconds]
incr jumped
}
}
# wait for time-jump back (from the future):
set future [clock add $sttm +30 seconds]
set aftto [after 10000 {set tout 1}]
while {[clock seconds] >= $future} {
if {[vwait 100 tout]} {
error "unexpected case: too long wait for time-reset."
}
}
after cancel $aftto
after 10
# result:
if {[llength $res]} {
return [join $res \n]
}
return $jumped
}
test timer-20.1 {time jump: after relative} {ChangeTimePrivilege} {
delayedtimejump 250; # delayed time jump in 250-ms
test-timer-with-jump {
after 150 {set x 1}; vwait x
} 20 150
} 3
test timer-20.2 {time jump: vwait timeout} {ChangeTimePrivilege NewVWaitAfter} {
delayedtimejump 250; # delayed time jump in 250-ms
test-timer-with-jump {
vwait -timer 150 nothing; # we want listen timer only
} 20 150
} 3
test timer-20.3 {time jump: simple sleep} {ChangeTimePrivilege} {
delayedtimejump 250; # delayed time jump in 250-ms
test-timer-with-jump {
after 150
} 20 150
} 3
# Increase delay between time-jumps (1.5s) for possible retarded reaction of
# the notifier by absolute timers (retarded recognition of the time-jump so
# the time can jump again (also backwards) without recognition.
# Note also we cannot test absolute timers by the backwards time-jump.
test timer-20.4 {time jump: after absolute time - "after at real-time code"} {ChangeTimePrivilege NewVWaitAfter} {
set sttm [clock seconds]
set i 0
foreach tm [list \
[clock add $sttm +1 minute -2 second] \
[clock add $sttm +2 minute -2 second] \
] {
after at $tm [list set x$i 1]
incr i
}
delayedtimejump 1500; # delayed time jump in 1.5s
test-timer-with-jump {
# we want listen timer only:
if {![vwait -timer 10000 x$i]} {
error "too long wait for \"x$i\" (should be maximal 1 second)"
}
} 2 250 3000; # max 3 seconds difference (compared to 1 minute)
} 2
test timer-20.5 {time jump: sleep to absolute time - "after at real-time"} {ChangeTimePrivilege NewVWaitAfter} {
set sttm [clock seconds]
set offsLst [list \
[clock add $sttm +1 minute] \
[clock add $sttm +2 minute] \
]
delayedtimejump 1500; # delayed time jump in 1.5s
test-timer-with-jump {
upvar offsLst offsLst
after at [lindex $offsLst $i]
} 2 250 3000; # max 3 seconds difference (compared to 1 minute)
} 2
# cleanup
::tcltest::cleanupTests
-920
View File
@@ -1,920 +0,0 @@
#! /bin/bash
# Generated by configure.
# Run this file to recreate the current configuration.
# Compiler output produced by configure, useful for debugging
# configure, is in config.log if it exists.
debug=false
ac_cs_recheck=false
ac_cs_silent=false
SHELL=${CONFIG_SHELL-/bin/bash}
## --------------------- ##
## M4sh Initialization. ##
## --------------------- ##
# Be Bourne compatible
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
# Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
set -o posix
fi
DUALCASE=1; export DUALCASE # for MKS sh
# Support unset when possible.
if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
as_unset=unset
else
as_unset=false
fi
# Work around bugs in pre-3.0 UWIN ksh.
$as_unset ENV MAIL MAILPATH
PS1='$ '
PS2='> '
PS4='+ '
# NLS nuisances.
for as_var in \
LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
LC_TELEPHONE LC_TIME
do
if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
eval $as_var=C; export $as_var
else
$as_unset $as_var
fi
done
# Required to use basename.
if expr a : '\(a\)' >/dev/null 2>&1; then
as_expr=expr
else
as_expr=false
fi
if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
as_basename=basename
else
as_basename=false
fi
# Name of the executable.
as_me=`$as_basename "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)$' \| \
. : '\(.\)' 2>/dev/null ||
echo X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
/^X\/\(\/\/\)$/{ s//\1/; q; }
/^X\/\(\/\).*/{ s//\1/; q; }
s/.*/./; q'`
# PATH needs CR, and LINENO needs CR and PATH.
# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
echo "#! /bin/sh" >conf$$.sh
echo "exit 0" >>conf$$.sh
chmod +x conf$$.sh
if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
PATH_SEPARATOR=';'
else
PATH_SEPARATOR=:
fi
rm -f conf$$.sh
fi
as_lineno_1=103
as_lineno_2=104
as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
test "x$as_lineno_1" != "x$as_lineno_2" &&
test "x$as_lineno_3" = "x$as_lineno_2" || {
# Find who we are. Look in the path if we contain no path at all
# relative or not.
case $0 in
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
done
;;
esac
# We did not find ourselves, most probably we were run as `sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
{ { echo "$as_me:128: error: cannot find myself; rerun with an absolute path" >&5
echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
{ (exit 1); exit 1; }; }
fi
case $CONFIG_SHELL in
'')
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for as_base in sh bash ksh sh5; do
case $as_dir in
/*)
if ("$as_dir/$as_base" -c '
as_lineno_1=143
as_lineno_2=144
as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
test "x$as_lineno_1" != "x$as_lineno_2" &&
test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
$as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
$as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
CONFIG_SHELL=$as_dir/$as_base
export CONFIG_SHELL
exec "$CONFIG_SHELL" "$0" ${1+"$@"}
fi;;
esac
done
done
;;
esac
# Create $as_me.lineno as a copy of $as_myself, but with 160
# uniformly replaced by the line number. The first 'sed' inserts a
# line-number line before each line; the second 'sed' does the real
# work. The second script uses 'N' to pair each line-number line
# with the numbered line, and appends trailing '-' during
# substitution so that 165 is not a special case at line end.
# (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
# second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
sed '=' <$as_myself |
sed '
N
s,$,-,
: loop
s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
t loop
s,-$,,
s,^['$as_cr_digits']*\n,,
' >$as_me.lineno &&
chmod +x $as_me.lineno ||
{ { echo "$as_me:179: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
{ (exit 1); exit 1; }; }
# Don't try to exec as it changes $[0], causing all sort of problems
# (the dirname of $[0] is not the place where we might find the
# original and so on. Autoconf is especially sensible to this).
. ./$as_me.lineno
# Exit status is that of the last command.
exit
}
case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
*c*,-n*) ECHO_N= ECHO_C='
' ECHO_T=' ' ;;
*c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
*) ECHO_N= ECHO_C='\c' ECHO_T= ;;
esac
if expr a : '\(a\)' >/dev/null 2>&1; then
as_expr=expr
else
as_expr=false
fi
rm -f conf$$ conf$$.exe conf$$.file
echo >conf$$.file
if ln -s conf$$.file conf$$ 2>/dev/null; then
# We could just check for DJGPP; but this test a) works b) is more generic
# and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
if test -f conf$$.exe; then
# Don't use ln at all; we don't have any links
as_ln_s='cp -p'
else
as_ln_s='ln -s'
fi
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
as_ln_s='cp -p'
fi
rm -f conf$$ conf$$.exe conf$$.file
if mkdir -p . 2>/dev/null; then
as_mkdir_p=:
else
test -d ./-p && rmdir ./-p
as_mkdir_p=false
fi
as_executable_p="test -f"
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
# IFS
# We need space, tab and new line, in precisely that order.
as_nl='
'
IFS=" $as_nl"
# CDPATH.
$as_unset CDPATH
exec 6>&1
# Open the log real soon, to keep \$[0] and so on meaningful, and to
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. Logging --version etc. is OK.
exec 5>>config.log
{
echo
sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
## Running $as_me. ##
_ASBOX
} >&5
cat >&5 <<_CSEOF
This file was extended by tcl $as_me 8.6, which was
generated by GNU Autoconf 2.59. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
CONFIG_LINKS = $CONFIG_LINKS
CONFIG_COMMANDS = $CONFIG_COMMANDS
$ $0 $@
_CSEOF
echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
echo >&5
config_files=" Makefile:../unix/Makefile.in dltest/Makefile:../unix/dltest/Makefile.in tclConfig.sh:../unix/tclConfig.sh.in tcl.pc:../unix/tcl.pc.in"
ac_cs_usage="\
\`$as_me' instantiates files from templates according to the
current configuration.
Usage: $0 [OPTIONS] [FILE]...
-h, --help print this help, then exit
-V, --version print version number, then exit
-q, --quiet do not print progress messages
-d, --debug don't remove temporary files
--recheck update $as_me by reconfiguring in the same conditions
--file=FILE[:TEMPLATE]
instantiate the configuration file FILE
Configuration files:
$config_files
Configuration commands:
$config_commands
Report bugs to <bug-autoconf@gnu.org>."
ac_cs_version="\
tcl config.status 8.6
configured by ./configure, generated by GNU Autoconf 2.59,
with options \"'--prefix=/home/jima/space/ins/lin64/dynamic/main'\"
Copyright (C) 2003 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
srcdir=/home/jima/space/bui/lin64/dynamic/main/tcl/unix
# If no file are specified by the user, then we need to provide default
# value. By we need to know if files were specified by the user.
ac_need_defaults=:
while test $# != 0
do
case $1 in
--*=*)
ac_option=`expr "x$1" : 'x\([^=]*\)='`
ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
ac_shift=:
;;
-*)
ac_option=$1
ac_optarg=$2
ac_shift=shift
;;
*) # This is not an option, so the user has probably given explicit
# arguments.
ac_option=$1
ac_need_defaults=false;;
esac
case $ac_option in
# Handling of the options.
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
ac_cs_recheck=: ;;
--version | --vers* | -V )
echo "$ac_cs_version"; exit 0 ;;
--he | --h)
# Conflict between --help and --header
{ { echo "$as_me:336: error: ambiguous option: $1
Try \`$0 --help' for more information." >&5
echo "$as_me: error: ambiguous option: $1
Try \`$0 --help' for more information." >&2;}
{ (exit 1); exit 1; }; };;
--help | --hel | -h )
echo "$ac_cs_usage"; exit 0 ;;
--debug | --d* | -d )
debug=: ;;
--file | --fil | --fi | --f )
$ac_shift
CONFIG_FILES="$CONFIG_FILES $ac_optarg"
ac_need_defaults=false;;
--header | --heade | --head | --hea )
$ac_shift
CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
ac_need_defaults=false;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil | --si | --s)
ac_cs_silent=: ;;
# This is an error.
-*) { { echo "$as_me:358: error: unrecognized option: $1
Try \`$0 --help' for more information." >&5
echo "$as_me: error: unrecognized option: $1
Try \`$0 --help' for more information." >&2;}
{ (exit 1); exit 1; }; } ;;
*) ac_config_targets="$ac_config_targets $1" ;;
esac
shift
done
ac_configure_extra_args=
if $ac_cs_silent; then
exec 6>/dev/null
ac_configure_extra_args="$ac_configure_extra_args --silent"
fi
if $ac_cs_recheck; then
echo "running /bin/bash ./configure " '--prefix=/home/jima/space/ins/lin64/dynamic/main' $ac_configure_extra_args " --no-create --no-recursion" >&6
exec /bin/bash ./configure '--prefix=/home/jima/space/ins/lin64/dynamic/main' $ac_configure_extra_args --no-create --no-recursion
fi
#
# INIT-COMMANDS section.
#
VERSION=8.6
for ac_config_target in $ac_config_targets
do
case "$ac_config_target" in
# Handling of arguments.
"Tcl-Info.plist" ) CONFIG_FILES="$CONFIG_FILES Tcl-Info.plist:../macosx/Tcl-Info.plist.in" ;;
"Tclsh-Info.plist" ) CONFIG_FILES="$CONFIG_FILES Tclsh-Info.plist:../macosx/Tclsh-Info.plist.in" ;;
"Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile:../unix/Makefile.in" ;;
"dltest/Makefile" ) CONFIG_FILES="$CONFIG_FILES dltest/Makefile:../unix/dltest/Makefile.in" ;;
"tclConfig.sh" ) CONFIG_FILES="$CONFIG_FILES tclConfig.sh:../unix/tclConfig.sh.in" ;;
"tcl.pc" ) CONFIG_FILES="$CONFIG_FILES tcl.pc:../unix/tcl.pc.in" ;;
"Tcl.framework" ) CONFIG_COMMANDS="$CONFIG_COMMANDS Tcl.framework" ;;
*) { { echo "$as_me:399: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
{ (exit 1); exit 1; }; };;
esac
done
# If the user did not use the arguments to specify the items to instantiate,
# then the envvar interface is used. Set only those that are not.
# We use the long form for the default assignment because of an extremely
# bizarre bug on SunOS 4.1.3.
if $ac_need_defaults; then
test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
fi
# Have a temporary directory for convenience. Make it in the build tree
# simply because there is no reason to put it here, and in addition,
# creating and moving files from /tmp can sometimes cause problems.
# Create a temporary directory, and hook for its removal unless debugging.
$debug ||
{
trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
trap '{ (exit 1); exit 1; }' 1 2 13 15
}
# Create a (secure) tmp directory for tmp files.
{
tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
test -n "$tmp" && test -d "$tmp"
} ||
{
tmp=./confstat$$-$RANDOM
(umask 077 && mkdir $tmp)
} ||
{
echo "$me: cannot create a temporary directory in ." >&2
{ (exit 1); exit 1; }
}
#
# CONFIG_FILES section.
#
# No need to generate the scripts if there are no CONFIG_FILES.
# This happens for instance when ./config.status config.h
if test -n "$CONFIG_FILES"; then
# Protect against being on the right side of a sed subst in config.status.
sed 's/,@/@@/; s/@,/@@/; s/,;t t$/@;t t/; /@;t t$/s/[\\&,]/\\&/g;
s/@@/,@/; s/@@/@,/; s/@;t t$/,;t t/' >$tmp/subs.sed <<\CEOF
s,@SHELL@,/bin/bash,;t t
s,@PATH_SEPARATOR@,:,;t t
s,@PACKAGE_NAME@,tcl,;t t
s,@PACKAGE_TARNAME@,tcl,;t t
s,@PACKAGE_VERSION@,8.6,;t t
s,@PACKAGE_STRING@,tcl 8.6,;t t
s,@PACKAGE_BUGREPORT@,,;t t
s,@exec_prefix@,/home/jima/space/ins/lin64/dynamic/main,;t t
s,@prefix@,/home/jima/space/ins/lin64/dynamic/main,;t t
s,@program_transform_name@,s,x,x,,;t t
s,@bindir@,${exec_prefix}/bin,;t t
s,@sbindir@,${exec_prefix}/sbin,;t t
s,@libexecdir@,${exec_prefix}/libexec,;t t
s,@datadir@,${prefix}/share,;t t
s,@sysconfdir@,${prefix}/etc,;t t
s,@sharedstatedir@,${prefix}/com,;t t
s,@localstatedir@,${prefix}/var,;t t
s,@libdir@,/home/jima/space/ins/lin64/dynamic/main/lib,;t t
s,@includedir@,${prefix}/include,;t t
s,@oldincludedir@,/usr/include,;t t
s,@infodir@,${prefix}/info,;t t
s,@mandir@,${prefix}/man,;t t
s,@build_alias@,,;t t
s,@host_alias@,,;t t
s,@target_alias@,,;t t
s,@DEFS@,-DPACKAGE_NAME=\"tcl\" -DPACKAGE_TARNAME=\"tcl\" -DPACKAGE_VERSION=\"8.6\" -DPACKAGE_STRING=\"tcl\ 8.6\" -DPACKAGE_BUGREPORT=\"\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_PARAM_H=1 -DUSE_THREAD_ALLOC=1 -D_REENTRANT=1 -D_THREAD_SAFE=1 -DHAVE_PTHREAD_ATTR_SETSTACKSIZE=1 -DHAVE_PTHREAD_ATFORK=1 -DTCL_THREADS=1 -DTCL_CFGVAL_ENCODING=\"iso8859-1\" -DHAVE_ZLIB=1 -DMODULE_SCOPE=extern\ __attribute__\(\(__visibility__\(\"hidden\"\)\)\) -DHAVE_HIDDEN=1 -DHAVE_CAST_TO_UNION=1 -DTCL_SHLIB_EXT=\".so\" -DNDEBUG=1 -DTCL_CFG_OPTIMIZED=1 -DTCL_TOMMATH=1 -DMP_PREC=4 -D_LARGEFILE64_SOURCE=1 -DTCL_WIDE_INT_IS_LONG=1 -DHAVE_GETCWD=1 -DHAVE_MKSTEMP=1 -DHAVE_OPENDIR=1 -DHAVE_STRTOL=1 -DHAVE_WAITPID=1 -DHAVE_GETNAMEINFO=1 -DHAVE_GETADDRINFO=1 -DHAVE_FREEADDRINFO=1 -DHAVE_GAI_STRERROR=1 -DHAVE_STRUCT_ADDRINFO=1 -DHAVE_STRUCT_IN6_ADDR=1 -DHAVE_STRUCT_SOCKADDR_IN6=1 -DHAVE_STRUCT_SOCKADDR_STORAGE=1 -DHAVE_GETPWUID_R_5=1 -DHAVE_GETPWUID_R=1 -DHAVE_GETPWNAM_R_5=1 -DHAVE_GETPWNAM_R=1 -DHAVE_GETGRGID_R_5=1 -DHAVE_GETGRGID_R=1 -DHAVE_GETGRNAM_R_5=1 -DHAVE_GETGRNAM_R=1 -DHAVE_GETHOSTBYNAME_R_6=1 -DHAVE_GETHOSTBYNAME_R=1 -DHAVE_GETHOSTBYADDR_R_8=1 -DHAVE_GETHOSTBYADDR_R=1 -DHAVE_TERMIOS_H=1 -DHAVE_SYS_IOCTL_H=1 -DHAVE_SYS_TIME_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_GMTIME_R=1 -DHAVE_LOCALTIME_R=1 -DHAVE_MKTIME=1 -DHAVE_TM_GMTOFF=1 -DHAVE_TIMEZONE_VAR=1 -DHAVE_STRUCT_STAT_ST_BLOCKS=1 -DHAVE_STRUCT_STAT_ST_BLKSIZE=1 -DHAVE_BLKCNT_T=1 -DHAVE_INTPTR_T=1 -DHAVE_UINTPTR_T=1 -DNO_UNION_WAIT=1 -DHAVE_SIGNED_CHAR=1 -DHAVE_LANGINFO=1 -DHAVE_MKSTEMPS=1 -DHAVE_FTS=1 -DHAVE_SYS_IOCTL_H=1 -DTCL_UNLOAD_DLLS=1 -DHAVE_CPUID=1 ,;t t
s,@ECHO_C@,,;t t
s,@ECHO_N@,-n,;t t
s,@ECHO_T@,,;t t
s,@LIBS@,-lz -lpthread,;t t
s,@MAN_FLAGS@,,;t t
s,@CC@,gcc,;t t
s,@CFLAGS@, -pipe ,;t t
s,@LDFLAGS@, -Wl,--export-dynamic ,;t t
s,@CPPFLAGS@,,;t t
s,@ac_ct_CC@,gcc,;t t
s,@EXEEXT@,,;t t
s,@OBJEXT@,o,;t t
s,@CPP@,gcc -E,;t t
s,@EGREP@,grep -E,;t t
s,@TCL_THREADS@,1,;t t
s,@TCLSH_PROG@,/home/jima/space/ins/lin64/dynamic/main/bin/tclsh8.6,;t t
s,@ZLIB_OBJS@,,;t t
s,@ZLIB_SRCS@,,;t t
s,@ZLIB_INCLUDE@,,;t t
s,@RANLIB@,ranlib,;t t
s,@ac_ct_RANLIB@,ranlib,;t t
s,@AR@,ar,;t t
s,@ac_ct_AR@,ar,;t t
s,@LIBOBJS@,,;t t
s,@TCL_LIBS@,-ldl -lz -lpthread -lieee -lm,;t t
s,@DL_LIBS@,-ldl,;t t
s,@DL_OBJS@,tclLoadDl.o,;t t
s,@PLAT_OBJS@,,;t t
s,@PLAT_SRCS@,,;t t
s,@LDAIX_SRC@,,;t t
s,@CFLAGS_DEBUG@,-g,;t t
s,@CFLAGS_OPTIMIZE@,-O2,;t t
s,@CFLAGS_WARNING@,-Wall,;t t
s,@LDFLAGS_DEBUG@,,;t t
s,@LDFLAGS_OPTIMIZE@,,;t t
s,@CC_SEARCH_FLAGS@,-Wl,-rpath,${LIB_RUNTIME_DIR},;t t
s,@LD_SEARCH_FLAGS@,-Wl,-rpath,${LIB_RUNTIME_DIR},;t t
s,@STLIB_LD@,${AR} cr,;t t
s,@SHLIB_LD@,${CC} ${CFLAGS} ${LDFLAGS} -shared,;t t
s,@TCL_SHLIB_LD_EXTRAS@,,;t t
s,@TK_SHLIB_LD_EXTRAS@,,;t t
s,@SHLIB_LD_LIBS@,${LIBS},;t t
s,@SHLIB_CFLAGS@,-fPIC,;t t
s,@SHLIB_SUFFIX@,.so,;t t
s,@MAKE_LIB@,${SHLIB_LD} -o $@ ${OBJS} ${SHLIB_LD_LIBS} ${TCL_SHLIB_LD_EXTRAS} ${TK_SHLIB_LD_EXTRAS} ${LD_SEARCH_FLAGS},;t t
s,@MAKE_STUB_LIB@,${STLIB_LD} $@ ${STUB_LIB_OBJS} ; ${RANLIB} $@,;t t
s,@INSTALL_LIB@,$(INSTALL_LIBRARY) $(LIB_FILE) "$(LIB_INSTALL_DIR)/$(LIB_FILE)",;t t
s,@DLL_INSTALL_DIR@,$(LIB_INSTALL_DIR),;t t
s,@INSTALL_STUB_LIB@,$(INSTALL_LIBRARY) $(STUB_LIB_FILE) "$(LIB_INSTALL_DIR)/$(STUB_LIB_FILE)",;t t
s,@CFLAGS_DEFAULT@,$(CFLAGS_OPTIMIZE),;t t
s,@LDFLAGS_DEFAULT@,$(LDFLAGS_OPTIMIZE),;t t
s,@DTRACE@,,;t t
s,@TCL_VERSION@,8.6,;t t
s,@TCL_MAJOR_VERSION@,8,;t t
s,@TCL_MINOR_VERSION@,6,;t t
s,@TCL_PATCH_LEVEL@,.8,;t t
s,@TCL_YEAR@,,;t t
s,@PKG_CFG_ARGS@,'--prefix=/home/jima/space/ins/lin64/dynamic/main' ,;t t
s,@TCL_LIB_FILE@,libtcl8.6.so,;t t
s,@TCL_LIB_FLAG@,-ltcl8.6,;t t
s,@TCL_LIB_SPEC@,-L/home/jima/space/ins/lin64/dynamic/main/lib -ltcl8.6,;t t
s,@TCL_STUB_LIB_FILE@,libtclstub8.6.a,;t t
s,@TCL_STUB_LIB_FLAG@,-ltclstub8.6,;t t
s,@TCL_STUB_LIB_SPEC@,-L/home/jima/space/ins/lin64/dynamic/main/lib -ltclstub8.6,;t t
s,@TCL_STUB_LIB_PATH@,/home/jima/space/ins/lin64/dynamic/main/lib/libtclstub8.6.a,;t t
s,@TCL_INCLUDE_SPEC@,-I/home/jima/space/ins/lin64/dynamic/main/include,;t t
s,@TCL_BUILD_STUB_LIB_SPEC@,-L/home/jima/space/bui/lin64/dynamic/main/tcl/unix -ltclstub8.6,;t t
s,@TCL_BUILD_STUB_LIB_PATH@,/home/jima/space/bui/lin64/dynamic/main/tcl/unix/libtclstub8.6.a,;t t
s,@TCL_SRC_DIR@,/home/jima/space/bui/lin64/dynamic/main/tcl,;t t
s,@CFG_TCL_SHARED_LIB_SUFFIX@,${VERSION}.so,;t t
s,@CFG_TCL_UNSHARED_LIB_SUFFIX@,${VERSION}.a,;t t
s,@TCL_SHARED_BUILD@,1,;t t
s,@LD_LIBRARY_PATH_VAR@,LD_LIBRARY_PATH,;t t
s,@TCL_BUILD_LIB_SPEC@,-L/home/jima/space/bui/lin64/dynamic/main/tcl/unix -ltcl8.6,;t t
s,@TCL_LIB_VERSIONS_OK@,ok,;t t
s,@TCL_SHARED_LIB_SUFFIX@,${VERSION}${SHLIB_SUFFIX},;t t
s,@TCL_UNSHARED_LIB_SUFFIX@,${VERSION}.a,;t t
s,@TCL_HAS_LONGLONG@,,;t t
s,@INSTALL_TZDATA@,,;t t
s,@DTRACE_SRC@,,;t t
s,@DTRACE_HDR@,,;t t
s,@DTRACE_OBJ@,,;t t
s,@MAKEFILE_SHELL@,/bin/sh,;t t
s,@BUILD_DLTEST@,$(DLTEST_TARGETS),;t t
s,@TCL_PACKAGE_PATH@,/home/jima/space/ins/lin64/dynamic/main/lib ,;t t
s,@TCL_MODULE_PATH@,,;t t
s,@TCL_LIBRARY@,$(prefix)/lib/tcl$(VERSION),;t t
s,@PRIVATE_INCLUDE_DIR@,$(includedir),;t t
s,@HTML_DIR@,$(DISTDIR)/html,;t t
s,@PACKAGE_DIR@,/home/jima/space/ins/lin64/dynamic/main/lib,;t t
s,@EXTRA_CC_SWITCHES@,,;t t
s,@EXTRA_APP_CC_SWITCHES@,,;t t
s,@EXTRA_INSTALL@,,;t t
s,@EXTRA_INSTALL_BINARIES@,@:,;t t
s,@EXTRA_BUILD_HTML@,@:,;t t
s,@EXTRA_TCLSH_LIBS@,,;t t
s,@DLTEST_LD@,${SHLIB_LD},;t t
s,@DLTEST_SUFFIX@,,;t t
CEOF
# Split the substitutions into bite-sized pieces for seds with
# small command number limits, like on Digital OSF/1 and HP-UX.
ac_max_sed_lines=48
ac_sed_frag=1 # Number of current file.
ac_beg=1 # First line for current file.
ac_end=$ac_max_sed_lines # Line after last line for current file.
ac_more_lines=:
ac_sed_cmds=
while $ac_more_lines; do
if test $ac_beg -gt 1; then
sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
else
sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
fi
if test ! -s $tmp/subs.frag; then
ac_more_lines=false
else
# The purpose of the label and of the branching condition is to
# speed up the sed processing (if there are no `@' at all, there
# is no need to browse any of the substitutions).
# These are the two extra sed commands mentioned above.
(echo ':t
/@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
if test -z "$ac_sed_cmds"; then
ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
else
ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
fi
ac_sed_frag=`expr $ac_sed_frag + 1`
ac_beg=$ac_end
ac_end=`expr $ac_end + $ac_max_sed_lines`
fi
done
if test -z "$ac_sed_cmds"; then
ac_sed_cmds=cat
fi
fi # test -n "$CONFIG_FILES"
for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
# Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
case $ac_file in
- | *:- | *:-:* ) # input from stdin
cat >$tmp/stdin
ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
*:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
* ) ac_file_in=$ac_file.in ;;
esac
# Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
ac_dir=`(dirname "$ac_file") 2>/dev/null ||
$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_file" : 'X\(//\)[^/]' \| \
X"$ac_file" : 'X\(//\)$' \| \
X"$ac_file" : 'X\(/\)' \| \
. : '\(.\)' 2>/dev/null ||
echo X"$ac_file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
/^X\(\/\/\)[^/].*/{ s//\1/; q; }
/^X\(\/\/\)$/{ s//\1/; q; }
/^X\(\/\).*/{ s//\1/; q; }
s/.*/./; q'`
{ if $as_mkdir_p; then
mkdir -p "$ac_dir"
else
as_dir="$ac_dir"
as_dirs=
while test ! -d "$as_dir"; do
as_dirs="$as_dir $as_dirs"
as_dir=`(dirname "$as_dir") 2>/dev/null ||
$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| \
. : '\(.\)' 2>/dev/null ||
echo X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
/^X\(\/\/\)[^/].*/{ s//\1/; q; }
/^X\(\/\/\)$/{ s//\1/; q; }
/^X\(\/\).*/{ s//\1/; q; }
s/.*/./; q'`
done
test ! -n "$as_dirs" || mkdir $as_dirs
fi || { { echo "$as_me:660: error: cannot create directory \"$ac_dir\"" >&5
echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
{ (exit 1); exit 1; }; }; }
ac_builddir=.
if test "$ac_dir" != .; then
ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
# A "../" for each directory in $ac_dir_suffix.
ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
else
ac_dir_suffix= ac_top_builddir=
fi
case $srcdir in
.) # No --srcdir option. We are building in place.
ac_srcdir=.
if test -z "$ac_top_builddir"; then
ac_top_srcdir=.
else
ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
fi ;;
[\\/]* | ?:[\\/]* ) # Absolute path.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir ;;
*) # Relative path.
ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_builddir$srcdir ;;
esac
# Do not use `cd foo && pwd` to compute absolute paths, because
# the directories may not exist.
case `pwd` in
.) ac_abs_builddir="$ac_dir";;
*)
case "$ac_dir" in
.) ac_abs_builddir=`pwd`;;
[\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
*) ac_abs_builddir=`pwd`/"$ac_dir";;
esac;;
esac
case $ac_abs_builddir in
.) ac_abs_top_builddir=${ac_top_builddir}.;;
*)
case ${ac_top_builddir}. in
.) ac_abs_top_builddir=$ac_abs_builddir;;
[\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
*) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
esac;;
esac
case $ac_abs_builddir in
.) ac_abs_srcdir=$ac_srcdir;;
*)
case $ac_srcdir in
.) ac_abs_srcdir=$ac_abs_builddir;;
[\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
*) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
esac;;
esac
case $ac_abs_builddir in
.) ac_abs_top_srcdir=$ac_top_srcdir;;
*)
case $ac_top_srcdir in
.) ac_abs_top_srcdir=$ac_abs_builddir;;
[\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
*) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
esac;;
esac
if test x"$ac_file" != x-; then
{ echo "$as_me:732: creating $ac_file" >&5
echo "$as_me: creating $ac_file" >&6;}
rm -f "$ac_file"
fi
# Let's still pretend it is `configure' which instantiates (i.e., don't
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
if test x"$ac_file" = x-; then
configure_input=
else
configure_input="$ac_file. "
fi
configure_input=$configure_input"Generated from `echo $ac_file_in |
sed 's,.*/,,'` by configure."
# First look for the input files in the build tree, otherwise in the
# src tree.
ac_file_inputs=`IFS=:
for f in $ac_file_in; do
case $f in
-) echo $tmp/stdin ;;
[\\/$]*)
# Absolute (can't be DOS-style, as IFS=:)
test -f "$f" || { { echo "$as_me:755: error: cannot find input file: $f" >&5
echo "$as_me: error: cannot find input file: $f" >&2;}
{ (exit 1); exit 1; }; }
echo "$f";;
*) # Relative
if test -f "$f"; then
# Build tree
echo "$f"
elif test -f "$srcdir/$f"; then
# Source tree
echo "$srcdir/$f"
else
# /dev/null tree
{ { echo "$as_me:768: error: cannot find input file: $f" >&5
echo "$as_me: error: cannot find input file: $f" >&2;}
{ (exit 1); exit 1; }; }
fi;;
esac
done` || { (exit 1); exit 1; }
sed "
:t
/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
s,@configure_input@,$configure_input,;t t
s,@srcdir@,$ac_srcdir,;t t
s,@abs_srcdir@,$ac_abs_srcdir,;t t
s,@top_srcdir@,$ac_top_srcdir,;t t
s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
s,@builddir@,$ac_builddir,;t t
s,@abs_builddir@,$ac_abs_builddir,;t t
s,@top_builddir@,$ac_top_builddir,;t t
s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
rm -f $tmp/stdin
if test x"$ac_file" != x-; then
mv $tmp/out $ac_file
else
cat $tmp/out
rm -f $tmp/out
fi
done
#
# CONFIG_COMMANDS section.
#
for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_dest" : 'X\(//\)[^/]' \| \
X"$ac_dest" : 'X\(//\)$' \| \
X"$ac_dest" : 'X\(/\)' \| \
. : '\(.\)' 2>/dev/null ||
echo X"$ac_dest" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
/^X\(\/\/\)[^/].*/{ s//\1/; q; }
/^X\(\/\/\)$/{ s//\1/; q; }
/^X\(\/\).*/{ s//\1/; q; }
s/.*/./; q'`
{ if $as_mkdir_p; then
mkdir -p "$ac_dir"
else
as_dir="$ac_dir"
as_dirs=
while test ! -d "$as_dir"; do
as_dirs="$as_dir $as_dirs"
as_dir=`(dirname "$as_dir") 2>/dev/null ||
$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| \
. : '\(.\)' 2>/dev/null ||
echo X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
/^X\(\/\/\)[^/].*/{ s//\1/; q; }
/^X\(\/\/\)$/{ s//\1/; q; }
/^X\(\/\).*/{ s//\1/; q; }
s/.*/./; q'`
done
test ! -n "$as_dirs" || mkdir $as_dirs
fi || { { echo "$as_me:837: error: cannot create directory \"$ac_dir\"" >&5
echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
{ (exit 1); exit 1; }; }; }
ac_builddir=.
if test "$ac_dir" != .; then
ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
# A "../" for each directory in $ac_dir_suffix.
ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
else
ac_dir_suffix= ac_top_builddir=
fi
case $srcdir in
.) # No --srcdir option. We are building in place.
ac_srcdir=.
if test -z "$ac_top_builddir"; then
ac_top_srcdir=.
else
ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
fi ;;
[\\/]* | ?:[\\/]* ) # Absolute path.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir ;;
*) # Relative path.
ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_builddir$srcdir ;;
esac
# Do not use `cd foo && pwd` to compute absolute paths, because
# the directories may not exist.
case `pwd` in
.) ac_abs_builddir="$ac_dir";;
*)
case "$ac_dir" in
.) ac_abs_builddir=`pwd`;;
[\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
*) ac_abs_builddir=`pwd`/"$ac_dir";;
esac;;
esac
case $ac_abs_builddir in
.) ac_abs_top_builddir=${ac_top_builddir}.;;
*)
case ${ac_top_builddir}. in
.) ac_abs_top_builddir=$ac_abs_builddir;;
[\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
*) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
esac;;
esac
case $ac_abs_builddir in
.) ac_abs_srcdir=$ac_srcdir;;
*)
case $ac_srcdir in
.) ac_abs_srcdir=$ac_abs_builddir;;
[\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
*) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
esac;;
esac
case $ac_abs_builddir in
.) ac_abs_top_srcdir=$ac_top_srcdir;;
*)
case $ac_top_srcdir in
.) ac_abs_top_srcdir=$ac_abs_builddir;;
[\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
*) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
esac;;
esac
{ echo "$as_me:907: executing $ac_dest commands" >&5
echo "$as_me: executing $ac_dest commands" >&6;}
case $ac_dest in
Tcl.framework ) n=Tcl &&
f=$n.framework && v=Versions/$VERSION &&
rm -rf $f && mkdir -p $f/$v/Resources &&
ln -s $v/$n $v/Resources $f && ln -s ../../../$n $f/$v &&
ln -s ../../../../$n-Info.plist $f/$v/Resources/Info.plist &&
unset n f v
;;
esac
done
{ (exit 0); exit 0; }
+8 -5
View File
@@ -17384,11 +17384,6 @@ cat >>confdefs.h <<\_ACEOF
_ACEOF
cat >>confdefs.h <<\_ACEOF
#define TCL_WIDE_CLICKS 1
_ACEOF
for ac_header in AvailabilityMacros.h
do
as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
@@ -17694,6 +17689,14 @@ else
DLTEST_SUFFIX=""
fi
# Completelly supports on unix (linux/darwin)
cat >>confdefs.h <<\_ACEOF
#define TCL_WIDE_CLICKS 1
_ACEOF
#--------------------------------------------------------------------
# Check for support of fts functions (readdir replacement)
#--------------------------------------------------------------------
+5 -2
View File
@@ -565,8 +565,6 @@ if test "`uname -s`" = "Darwin" ; then
[Are we to override what our default encoding is?])
AC_DEFINE(TCL_LOAD_FROM_MEMORY, 1,
[Can this platform load code from memory?])
AC_DEFINE(TCL_WIDE_CLICKS, 1,
[Does this platform have wide high-resolution clicks?])
AC_CHECK_HEADERS(AvailabilityMacros.h)
if test "$ac_cv_header_AvailabilityMacros_h" = yes; then
AC_CACHE_CHECK([if weak import is available], tcl_cv_cc_weak_import, [
@@ -614,6 +612,11 @@ else
DLTEST_SUFFIX=""
fi
# Completelly supports on unix (linux/darwin)
AC_DEFINE(TCL_WIDE_CLICKS, 1,
[Does this platform have wide high-resolution clicks?])
#--------------------------------------------------------------------
# Check for support of fts functions (readdir replacement)
#--------------------------------------------------------------------
-72
View File
@@ -12,79 +12,7 @@
#include "tclInt.h"
#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is
* in tclMacOSXNotify.c */
/*
*----------------------------------------------------------------------
*
* Tcl_Sleep --
*
* Delay execution for the specified number of milliseconds.
*
* Results:
* None.
*
* Side effects:
* Time passes.
*
*----------------------------------------------------------------------
*/
void
Tcl_Sleep(
int ms) /* Number of milliseconds to sleep. */
{
struct timeval delay;
Tcl_Time before, after, vdelay;
/*
* The only trick here is that select appears to return early under some
* conditions, so we have to check to make sure that the right amount of
* time really has elapsed. If it's too early, go back to sleep again.
*/
Tcl_GetTime(&before);
after = before;
after.sec += ms/1000;
after.usec += (ms%1000)*1000;
if (after.usec > 1000000) {
after.usec -= 1000000;
after.sec += 1;
}
while (1) {
/*
* TIP #233: Scale from virtual time to real-time for select.
*/
vdelay.sec = after.sec - before.sec;
vdelay.usec = after.usec - before.usec;
if (vdelay.usec < 0) {
vdelay.usec += 1000000;
vdelay.sec -= 1;
}
if ((vdelay.sec != 0) || (vdelay.usec != 0)) {
tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
}
delay.tv_sec = vdelay.sec;
delay.tv_usec = vdelay.usec;
/*
* Special note: must convert delay.tv_sec to int before comparing to
* zero, since delay.tv_usec is unsigned on some platforms.
*/
if ((((int) delay.tv_sec) < 0)
|| ((delay.tv_usec == 0) && (delay.tv_sec == 0))) {
break;
}
(void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
(SELECT_MASK *) 0, &delay);
Tcl_GetTime(&before);
}
}
#endif /* HAVE_COREFOUNDATION */
/*
* Local Variables:
+480 -283
View File
@@ -102,9 +102,10 @@ typedef struct ThreadSpecificData {
pthread_cond_t waitCV; /* Any other thread alerts a notifier that an
* event is ready to be processed by signaling
* this condition variable. */
int waitCVMono; /* Mark wait monotonic based */
#endif /* __CYGWIN__ */
int waitCVinitialized; /* Variable to flag initialization of the structure */
int eventReady; /* True if an event is ready to be processed.
int eventReady; /* > 0 if an event is ready to be processed.
* Used as condition flag together with waitCV
* above. */
#endif /* TCL_THREADS */
@@ -324,68 +325,76 @@ StartNotifierThread(const char *proc)
ClientData
Tcl_InitNotifier(void)
{
if (tclNotifierHooks.initNotifierProc) {
return tclNotifierHooks.initNotifierProc();
} else {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (tclNotifierHooks.initNotifierProc) {
return tclNotifierHooks.initNotifierProc();
} else {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
pthread_condattr_t wcvAttr, *wcvAttrPtr = NULL;
#ifdef TCL_THREADS
tsdPtr->eventReady = 0;
tsdPtr->eventReady = 0;
/*
* Initialize thread specific condition variable for this thread.
*/
if (tsdPtr->waitCVinitialized == 0) {
/*
* Initialize thread specific condition variable for this thread.
*/
if (tsdPtr->waitCVinitialized == 0) {
#ifdef __CYGWIN__
WNDCLASS class;
WNDCLASS class;
class.style = 0;
class.cbClsExtra = 0;
class.cbWndExtra = 0;
class.hInstance = TclWinGetTclInstance();
class.hbrBackground = NULL;
class.lpszMenuName = NULL;
class.lpszClassName = NotfyClassName;
class.lpfnWndProc = NotifierProc;
class.hIcon = NULL;
class.hCursor = NULL;
class.style = 0;
class.cbClsExtra = 0;
class.cbWndExtra = 0;
class.hInstance = TclWinGetTclInstance();
class.hbrBackground = NULL;
class.lpszMenuName = NULL;
class.lpszClassName = NotfyClassName;
class.lpfnWndProc = NotifierProc;
class.hIcon = NULL;
class.hCursor = NULL;
RegisterClassW(&class);
tsdPtr->hwnd = CreateWindowExW(NULL, class.lpszClassName,
class.lpszClassName, 0, 0, 0, 0, 0, NULL, NULL,
TclWinGetTclInstance(), NULL);
tsdPtr->event = CreateEventW(NULL, 1 /* manual */,
0 /* !signaled */, NULL);
#else
pthread_cond_init(&tsdPtr->waitCV, NULL);
#endif /* __CYGWIN__ */
tsdPtr->waitCVinitialized = 1;
RegisterClassW(&class);
tsdPtr->hwnd = CreateWindowExW(NULL, class.lpszClassName,
class.lpszClassName, 0, 0, 0, 0, 0, NULL, NULL,
TclWinGetTclInstance(), NULL);
tsdPtr->event = CreateEventW(NULL, 1 /* manual */,
0 /* !signaled */, NULL);
#else /* !__CYGWIN__ */
pthread_condattr_init(&wcvAttr);
if (pthread_condattr_setclock(&wcvAttr, CLOCK_MONOTONIC) == 0) {
/* we can use conditional wait monotonic based */
wcvAttrPtr = &wcvAttr;
tsdPtr->waitCVMono = 1;
}
pthread_cond_init(&tsdPtr->waitCV, wcvAttrPtr);
(void)pthread_condattr_destroy(&wcvAttr);
#endif /* !__CYGWIN__ */
tsdPtr->waitCVinitialized = 1;
}
pthread_mutex_lock(&notifierInitMutex);
pthread_mutex_lock(&notifierInitMutex);
#if defined(HAVE_PTHREAD_ATFORK)
/*
* Install pthread_atfork handlers to clean up the notifier in the
* child of a fork.
*/
/*
* Install pthread_atfork handlers to clean up the notifier in the
* child of a fork.
*/
if (!atForkInit) {
int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
if (!atForkInit) {
int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
if (result) {
Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
}
atForkInit = 1;
if (result) {
Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
}
atForkInit = 1;
}
#endif /* HAVE_PTHREAD_ATFORK */
notifierCount++;
notifierCount++;
pthread_mutex_unlock(&notifierInitMutex);
pthread_mutex_unlock(&notifierInitMutex);
#endif /* TCL_THREADS */
return tsdPtr;
}
return tsdPtr;
}
}
/*
@@ -498,13 +507,13 @@ Tcl_AlertNotifier(
ThreadSpecificData *tsdPtr = clientData;
pthread_mutex_lock(&notifierMutex);
tsdPtr->eventReady = 1;
tsdPtr->eventReady++;
# ifdef __CYGWIN__
PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
# else
# else /* !__CYGWIN__ */
pthread_cond_broadcast(&tsdPtr->waitCV);
# endif /* __CYGWIN__ */
# endif /* !__CYGWIN__ */
pthread_mutex_unlock(&notifierMutex);
#endif /* TCL_THREADS */
}
@@ -832,12 +841,125 @@ NotifierProc(
* Process all of the runnable events.
*/
tsdPtr->eventReady = 1;
tsdPtr->eventReady++;
Tcl_ServiceAll();
return 0;
}
#endif /* TCL_THREADS && __CYGWIN__ */
/*
* Several minimal sleep and wait values, corresponding unix timer resolution
*
* Note: Adjusting of this values may increase NRT-capability, but also may
* increase CPU load (resp. busy waits on brief intervals).
*/
#ifndef TCL_TMR_MIN_DELAY
# define TCL_TMR_MIN_DELAY 100
# define TCL_TMR_MIN_USLEEP 50
# define TCL_TMR_MIN_NSLEEP 1
# define TCL_TMR_OVERHEAD 50
#endif
/*
*----------------------------------------------------------------------
*
* TclpUSleep --
*
* Delay execution for the specified time (in microseconds).
*
* Results:
* None.
*
* Side effects:
* Time passes.
*
*----------------------------------------------------------------------
*/
static unsigned int tclMinDelay = TCL_TMR_MIN_DELAY;
static unsigned int tclMinUSleep = TCL_TMR_MIN_USLEEP;
static unsigned int tclMinNSleep = TCL_TMR_MIN_NSLEEP;
void
TclpUSleep(
Tcl_WideInt usec) /* Time to sleep. */
{
Tcl_WideInt now, desired;
unsigned int *calibValue;
if (usec < 0) {
usec = 0;
}
/*
* TIP #233: Scale from virtual time to real-time for select/usleep.
*/
TclpScaleUTime(&usec);
/*
* The only trick here is that select appears to return early under some
* conditions, so we have to check to make sure that the right amount of
* time really has elapsed. If it's too early, go back to sleep again.
*
* Note the time can be switched (time-jump), so use monotonic time here.
*/
now = TclpGetUTimeMonotonic();
if ((desired = (now + usec)) < now) { /* overflow */
desired = 0x7FFFFFFFFFFFFFFFL;
}
while (1) {
if (usec >= tclMinDelay) {
struct timeval delay;
calibValue = &tclMinDelay;
usec -= (usec % tclMinDelay);
delay.tv_sec = usec / 1000000;
delay.tv_usec = usec % 1000000;
(void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
(SELECT_MASK *) 0, &delay);
} else if (usec >= tclMinUSleep) {
calibValue = &tclMinUSleep;
usec -= (usec % tclMinUSleep);
usleep((useconds_t)usec);
} else if (usec >= tclMinNSleep) {
struct timespec delay;
calibValue = &tclMinNSleep;
usec -= (usec % tclMinNSleep);
delay.tv_sec = usec / 1000000;
delay.tv_nsec = (usec % 1000000) * 1000; /* usec to nsec */
nanosleep(&delay, NULL);
} else {
calibValue = NULL;
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield(); /* yield the processor (force context switch). */
#endif
}
now = TclpGetUTimeMonotonic();
if ((usec = (desired - now)) <= 0 /* or tolerance */) {
/*
* Self calibration - sleep was too long, so adjust related value.
*/
if (calibValue && (unsigned int)-usec > *calibValue) {
*calibValue = (*calibValue + (unsigned int)-usec) / 2;
}
/* end of sleep */
break;
}
}
}
/*
*----------------------------------------------------------------------
*
@@ -860,262 +982,337 @@ int
Tcl_WaitForEvent(
const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
if (tclNotifierHooks.waitForEventProc) {
return tclNotifierHooks.waitForEventProc(timePtr);
} else {
FileHandler *filePtr;
int mask;
Tcl_Time vTime;
if (tclNotifierHooks.waitForEventProc) {
return tclNotifierHooks.waitForEventProc(timePtr);
} else {
FileHandler *filePtr;
int mask, canWait = 1;
Tcl_WideInt endTime = 0, waitTime = 0;
#ifdef TCL_THREADS
int waitForFiles;
# ifdef __CYGWIN__
MSG msg;
# endif /* __CYGWIN__ */
int waitForFiles = 0;
# ifdef __CYGWIN__
MSG msg;
# endif /* __CYGWIN__ */
#else
/*
* Impl. notes: timeout & timeoutPtr are used if, and only if threads
* are not enabled. They are the arguments for the regular select()
* used when the core is not thread-enabled.
*/
/*
* Impl. notes: timeout & timeoutPtr are used if, and only if threads
* are not enabled. They are the arguments for the regular select()
* used when the core is not thread-enabled.
*/
struct timeval timeout, *timeoutPtr;
int numFound;
struct timeval timeout, *timeoutPtr;
int numFound;
#endif /* TCL_THREADS */
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
ThreadSpecificData *tsdPtr;
tsdPtr = TCL_TSD_INIT(&dataKey);
/*
* Set up the timeout structure. Note that if there are no events to
* check for, we return with a negative result rather than blocking
* forever.
*/
if (timePtr != NULL) {
waitTime = TCL_TIME_TO_USEC(*timePtr);
/* TIP #233: Scale from virtual time to real-time */
TclpScaleUTime(&waitTime);
/*
* Set up the timeout structure. Note that if there are no events to
* check for, we return with a negative result rather than blocking
* forever.
* Note the time can be switched (time-jump), so use monotonic time here.
*/
endTime = TclpGetUTimeMonotonic() + waitTime;
/*
* If short wait or no wait at all, just process events already available
* right now, avoid waiting too long somewhere (NRT-capability fix).
*/
if (!timePtr->sec && timePtr->usec < TCL_TMR_MIN_DELAY) {
canWait = 0;
}
#ifndef TCL_THREADS
timeout.tv_sec = timePtr->sec;
timeout.tv_usec = timePtr->usec;
timeoutPtr = &timeout;
} else if (tsdPtr->numFdBits == 0) {
/*
* If there are no threads, no timeout, and no fds registered,
* then there are no events possible and we must avoid deadlock.
* Note that this is not entirely correct because there might be a
* signal that could interrupt the select call, but we don't
* handle that case if we aren't using threads.
*/
if (timePtr != NULL) {
/*
* TIP #233 (Virtualized Time). Is virtual time in effect? And do
* we actually have something to scale? If yes to both then we
* call the handler to do this scaling.
*/
if (timePtr->sec != 0 || timePtr->usec != 0) {
vTime = *timePtr;
tclScaleTimeProcPtr(&vTime, tclTimeClientData);
timePtr = &vTime;
}
#ifndef TCL_THREADS
timeout.tv_sec = timePtr->sec;
timeout.tv_usec = timePtr->usec;
timeoutPtr = &timeout;
} else if (tsdPtr->numFdBits == 0) {
/*
* If there are no threads, no timeout, and no fds registered,
* then there are no events possible and we must avoid deadlock.
* Note that this is not entirely correct because there might be a
* signal that could interrupt the select call, but we don't
* handle that case if we aren't using threads.
*/
return -1;
} else {
timeoutPtr = NULL;
return -1;
} else {
timeoutPtr = NULL;
#endif /* !TCL_THREADS */
}
}
#ifdef TCL_THREADS
/*
* Start notifier thread and place this thread on the list of
* interested threads, signal the notifier thread, and wait for a
* response or a timeout.
*/
StartNotifierThread("Tcl_WaitForEvent");
pthread_mutex_lock(&notifierMutex);
waitForFiles = (tsdPtr->numFdBits > 0);
/* if cannot wait (but not really necessary to wait), bypass triggering pipe */
if (!canWait && (!waitForFiles || tsdPtr->eventReady)) {
goto nowait;
}
if (!canWait) {
/*
* Start notifier thread and place this thread on the list of
* interested threads, signal the notifier thread, and wait for a
* response or a timeout.
* Cannot emulate a polling select with a polling condition
* variable. Instead, pretend to wait for files and tell the
* notifier thread what we are doing. The notifier thread makes
* sure it goes through select with its select mask in the same
* state as ours currently is. We block until that happens.
*/
StartNotifierThread("Tcl_WaitForEvent");
pthread_mutex_lock(&notifierMutex);
if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
#if defined(__APPLE__) && defined(__LP64__)
/*
* On 64-bit Darwin, pthread_cond_timedwait() appears to have
* a bug that causes it to wait forever when passed an
* absolute time which has already been exceeded by the system
* time; as a workaround, when given a very brief timeout,
* just do a poll. [Bug 1457797]
*/
|| timePtr->usec < 10
#endif /* __APPLE__ && __LP64__ */
)) {
/*
* Cannot emulate a polling select with a polling condition
* variable. Instead, pretend to wait for files and tell the
* notifier thread what we are doing. The notifier thread makes
* sure it goes through select with its select mask in the same
* state as ours currently is. We block until that happens.
*/
waitForFiles = 1;
tsdPtr->pollState = POLL_WANT;
timePtr = NULL;
} else {
waitForFiles = (tsdPtr->numFdBits > 0);
tsdPtr->pollState = 0;
}
if (waitForFiles) {
/*
* Add the ThreadSpecificData structure of this thread to the list
* of ThreadSpecificData structures of all threads that are
* waiting on file events.
*/
tsdPtr->pollState = POLL_WANT;
canWait = 1;
}
} else {
tsdPtr->pollState = 0;
}
tsdPtr->nextPtr = waitingListPtr;
if (waitingListPtr) {
waitingListPtr->prevPtr = tsdPtr;
if (waitForFiles) {
/*
* Add the ThreadSpecificData structure of this thread to the list
* of ThreadSpecificData structures of all threads that are
* waiting on file events.
*/
tsdPtr->nextPtr = waitingListPtr;
if (waitingListPtr) {
waitingListPtr->prevPtr = tsdPtr;
}
tsdPtr->prevPtr = 0;
waitingListPtr = tsdPtr;
tsdPtr->onList = 1;
if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) {
Tcl_Panic("Tcl_WaitForEvent: %s",
"unable to write to triggerPipe");
}
}
nowait:
FD_ZERO(&tsdPtr->readyMasks.readable);
FD_ZERO(&tsdPtr->readyMasks.writable);
FD_ZERO(&tsdPtr->readyMasks.exception);
while (!tsdPtr->eventReady) {
if (canWait && timePtr) {
waitTime = endTime - TclpGetUTimeMonotonic();
/* Note: we should wait at least once if waitForFiles set */
if (waitTime <= 0) {
if (!waitForFiles) {
break; /* end of wait */
}
waitForFiles = 0;
waitTime = 0;
}
tsdPtr->prevPtr = 0;
waitingListPtr = tsdPtr;
tsdPtr->onList = 1;
if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) {
Tcl_Panic("Tcl_WaitForEvent: %s",
"unable to write to triggerPipe");
else
if (waitTime <= TCL_TMR_OVERHEAD) {
if (!waitForFiles) {
canWait = 0;
}
waitForFiles = 0;
}
}
# ifdef __CYGWIN__
if (!PeekMessageW(&msg, NULL, 0, 0, 0)) {
DWORD timeout;
if (timePtr) {
timeout = waitTime / 1000;
} else {
timeout = 0xFFFFFFFF;
}
pthread_mutex_unlock(&notifierMutex);
MsgWaitForMultipleObjects(1, &tsdPtr->event, 0, timeout, 1279);
pthread_mutex_lock(&notifierMutex);
}
# else
/* prevent too long waiting (NRT-capability) */
if ( !canWait ) {
/* short sleep */
if (waitTime) {
TclpUSleep(waitTime);
}
break; /* end of wait */
}
else
if (timePtr) {
struct timespec ptime;
clock_gettime(tsdPtr->waitCVMono ?
CLOCK_MONOTONIC : CLOCK_REALTIME, &ptime);
ptime.tv_sec += waitTime / 1000000;
ptime.tv_nsec += (waitTime % 1000000) * 1000;
if (ptime.tv_nsec > 1000000*1000) {
ptime.tv_nsec -= 1000000*1000;
ptime.tv_sec++;
}
# if defined(__APPLE__) && defined(__LP64__)
/*
* On 64-bit Darwin, pthread_cond_timedwait() appears to have
* a bug that causes it to wait forever when passed an
* absolute time which has already been exceeded by the system
* time; as a workaround, when given a very brief timeout,
* just increment a bit the waiting-time from now. [Bug 1457797]
*/
if (waitTime <= 0) {
ptime.tv_sec = now.sec;
ptime.tv_nsec = 1000 * now.usec + 10; /* + 10 nanosecond */
}
# else
/* remove overhead in nsec */
if (ptime.tv_nsec < TCL_TMR_OVERHEAD * 1000) {
ptime.tv_nsec += 1000000*1000;
ptime.tv_sec--;
}
ptime.tv_nsec -= TCL_TMR_OVERHEAD * 1000;
# endif /* __APPLE__ && __LP64__ */
if (ptime.tv_nsec > 1000000*1000) {
ptime.tv_nsec -= 1000000*1000;
ptime.tv_sec++;
}
if (pthread_cond_timedwait(&tsdPtr->waitCV, &notifierMutex,
&ptime) == ETIMEDOUT) {
continue; /* repeat wait (if not yet real timeout) */
};
} else {
pthread_cond_wait(&tsdPtr->waitCV, &notifierMutex);
}
# endif /* __CYGWIN__ */
break; /* end of wait */
}
if (tsdPtr->eventReady > 0) {
tsdPtr->eventReady--;
}
# ifdef __CYGWIN__
while (PeekMessageW(&msg, NULL, 0, 0, 0)) {
/*
* Retrieve and dispatch the message.
*/
DWORD result = GetMessageW(&msg, NULL, 0, 0);
if (result == 0) {
PostQuitMessage(msg.wParam);
/* What to do here? */
} else if (result != (DWORD) -1) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
ResetEvent(tsdPtr->event);
# endif /* __CYGWIN__ */
if (tsdPtr->onList) {
/*
* Remove the ThreadSpecificData structure of this thread from the
* waiting list. Alert the notifier thread to recompute its select
* masks - skipping this caused a hang when trying to close a pipe
* which the notifier thread was still doing a select on.
*/
if (tsdPtr->prevPtr) {
tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
} else {
waitingListPtr = tsdPtr->nextPtr;
}
if (tsdPtr->nextPtr) {
tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
}
tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
tsdPtr->onList = 0;
if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) {
Tcl_Panic("Tcl_WaitForEvent: %s",
"unable to write to triggerPipe");
}
}
#else /* !TCL_THREADS */
tsdPtr->readyMasks = tsdPtr->checkMasks;
numFound = select(tsdPtr->numFdBits, &tsdPtr->readyMasks.readable,
&tsdPtr->readyMasks.writable, &tsdPtr->readyMasks.exception,
timeoutPtr);
/*
* Some systems don't clear the masks after an error, so we have to do
* it here.
*/
if (numFound == -1) {
FD_ZERO(&tsdPtr->readyMasks.readable);
FD_ZERO(&tsdPtr->readyMasks.writable);
FD_ZERO(&tsdPtr->readyMasks.exception);
if (!tsdPtr->eventReady) {
#ifdef __CYGWIN__
if (!PeekMessageW(&msg, NULL, 0, 0, 0)) {
DWORD timeout;
if (timePtr) {
timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
} else {
timeout = 0xFFFFFFFF;
}
pthread_mutex_unlock(&notifierMutex);
MsgWaitForMultipleObjects(1, &tsdPtr->event, 0, timeout, 1279);
pthread_mutex_lock(&notifierMutex);
}
#else
if (timePtr != NULL) {
Tcl_Time now;
struct timespec ptime;
Tcl_GetTime(&now);
ptime.tv_sec = timePtr->sec + now.sec + (timePtr->usec + now.usec) / 1000000;
ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000);
pthread_cond_timedwait(&tsdPtr->waitCV, &notifierMutex, &ptime);
} else {
pthread_cond_wait(&tsdPtr->waitCV, &notifierMutex);
}
#endif /* __CYGWIN__ */
}
tsdPtr->eventReady = 0;
#ifdef __CYGWIN__
while (PeekMessageW(&msg, NULL, 0, 0, 0)) {
/*
* Retrieve and dispatch the message.
*/
DWORD result = GetMessageW(&msg, NULL, 0, 0);
if (result == 0) {
PostQuitMessage(msg.wParam);
/* What to do here? */
} else if (result != (DWORD) -1) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
ResetEvent(tsdPtr->event);
#endif /* __CYGWIN__ */
if (waitForFiles && tsdPtr->onList) {
/*
* Remove the ThreadSpecificData structure of this thread from the
* waiting list. Alert the notifier thread to recompute its select
* masks - skipping this caused a hang when trying to close a pipe
* which the notifier thread was still doing a select on.
*/
if (tsdPtr->prevPtr) {
tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
} else {
waitingListPtr = tsdPtr->nextPtr;
}
if (tsdPtr->nextPtr) {
tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
}
tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
tsdPtr->onList = 0;
if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) {
Tcl_Panic("Tcl_WaitForEvent: %s",
"unable to write to triggerPipe");
}
}
#else
tsdPtr->readyMasks = tsdPtr->checkMasks;
numFound = select(tsdPtr->numFdBits, &tsdPtr->readyMasks.readable,
&tsdPtr->readyMasks.writable, &tsdPtr->readyMasks.exception,
timeoutPtr);
/*
* Some systems don't clear the masks after an error, so we have to do
* it here.
*/
if (numFound == -1) {
FD_ZERO(&tsdPtr->readyMasks.readable);
FD_ZERO(&tsdPtr->readyMasks.writable);
FD_ZERO(&tsdPtr->readyMasks.exception);
}
#endif /* TCL_THREADS */
/*
* Queue all detected file events before returning.
*/
for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
filePtr = filePtr->nextPtr) {
mask = 0;
if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.readable)) {
mask |= TCL_READABLE;
}
if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.writable)) {
mask |= TCL_WRITABLE;
}
if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.exception)) {
mask |= TCL_EXCEPTION;
}
if (!mask) {
continue;
}
/*
* Don't bother to queue an event if the mask was previously
* non-zero since an event must still be on the queue.
*/
if (filePtr->readyMask == 0) {
FileHandlerEvent *fileEvPtr =
ckalloc(sizeof(FileHandlerEvent));
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
}
filePtr->readyMask = mask;
}
#ifdef TCL_THREADS
pthread_mutex_unlock(&notifierMutex);
#endif /* TCL_THREADS */
return 0;
}
#endif /* !TCL_THREADS */
/*
* Queue all detected file events before returning.
*/
for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
filePtr = filePtr->nextPtr) {
mask = 0;
if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.readable)) {
mask |= TCL_READABLE;
}
if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.writable)) {
mask |= TCL_WRITABLE;
}
if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.exception)) {
mask |= TCL_EXCEPTION;
}
if (!mask) {
continue;
}
/*
* Don't bother to queue an event if the mask was previously
* non-zero since an event must still be on the queue.
*/
if (filePtr->readyMask == 0) {
FileHandlerEvent *fileEvPtr =
ckalloc(sizeof(FileHandlerEvent));
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
}
filePtr->readyMask = mask;
}
#ifdef TCL_THREADS
pthread_mutex_unlock(&notifierMutex);
#endif /* TCL_THREADS */
return 0;
}
}
#ifdef TCL_THREADS
@@ -1282,7 +1479,7 @@ NotifierThreadProc(
}
if (found || (tsdPtr->pollState & POLL_DONE)) {
tsdPtr->eventReady = 1;
tsdPtr->eventReady++;
if (tsdPtr->onList) {
/*
* Remove the ThreadSpecificData structure of this thread
+154 -47
View File
@@ -101,10 +101,93 @@ TclpGetSeconds(void)
Tcl_WideInt
TclpGetMicroseconds(void)
{
Tcl_Time time;
if (tclGetTimeProcPtr == NativeGetTime) {
struct timeval tv;
tclGetTimeProcPtr(&time, tclTimeClientData);
return ((Tcl_WideInt)time.sec)*1000000 + time.usec;
(void) gettimeofday(&tv, NULL);
return ((Tcl_WideInt)tv.tv_sec)*1000000 + tv.tv_usec;
} else {
Tcl_Time time;
tclGetTimeProcPtr(&time, tclTimeClientData);
return ((Tcl_WideInt)time.sec)*1000000 + time.usec;
}
}
/*
*----------------------------------------------------------------------
*
* TclpGetUTimeMonotonic --
*
* This procedure returns the number of microseconds from some unspecified
* starting point.
* This time is monotonic (not affected by the time-jumps), so can be used
* for relative wait purposes and relative time calculation.
*
* Results:
* Monotonic time in microseconds.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Tcl_WideInt
TclpGetUTimeMonotonic(void)
{
if (tclGetTimeProcPtr == NativeGetTime) {
#if defined(CLOCK_MONOTONIC)
/* monotonic time available on this platform */
struct timespec mntv;
if (clock_gettime(CLOCK_MONOTONIC, &mntv) != 0) {
/* fallback to non-monotonic real-time. */
return TclpGetMicroseconds();
}
/* monotonic time since some starting point in microseconds */
return ((Tcl_WideInt)mntv.tv_sec)*1000000 + mntv.tv_nsec / 1000;
#elif defined(MAC_OSX_TCL)
/* absolute time related (tb.numer / tb.denom / 1000) microseconds */
static mach_timebase_info_data_t tb;
static uint64_t maxClicksForUInt64;
uint64_t clicks = mach_absolute_time();
if (!tb.denom) {
mach_timebase_info(&tb);
maxClicksForUInt64 = UINT64_MAX / tb.numer;
}
if ((uint64_t) clicks < maxClicksForUInt64) {
return (Tcl_WideInt)(((uint64_t) clicks) * tb.numer / 1000 / tb.denom);
} else {
return (Tcl_WideInt)(((uint64_t) clicks) / 1000 * tb.numer / tb.denom);
}
#else /* no MAC_OSX_TCL and no monotonic time (xcode etc) */
/* fallback to non-monotonic real-time (microseconds) */
return TclpGetMicroseconds();
/*
* TODO: may be the seconds could be obtained over the uptime (since last boot),
* like here (https://stackoverflow.com/questions/20817311/ios-real-monotonic-clock-other-than-mach-absolute-time):
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
time_t now;
time_t uptime = -1;
(void)time(&now);
if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0)
{
uptime = now - (boottime.tv_sec);
}
* but I don't think this could provide even micro- or milliseconds granularity,
* so a not back-drifting (monotonic) mix-in would be necessary.
*/
#endif
} else {
Tcl_Time time;
tclGetTimeProcPtr(&time, tclTimeClientData);
return ((Tcl_WideInt)time.sec)*1000000 + time.usec;
}
}
/*
@@ -129,30 +212,8 @@ TclpGetMicroseconds(void)
unsigned long
TclpGetClicks(void)
{
unsigned long now;
#ifdef NO_GETTOD
if (tclGetTimeProcPtr != NativeGetTime) {
Tcl_Time time;
tclGetTimeProcPtr(&time, tclTimeClientData);
now = time.sec*1000000 + time.usec;
} else {
/*
* A semi-NativeGetTime, specialized to clicks.
*/
struct tms dummy;
now = (unsigned long) times(&dummy);
}
#else
Tcl_Time time;
tclGetTimeProcPtr(&time, tclTimeClientData);
now = time.sec*1000000 + time.usec;
#endif
return now;
/* clicks should provide monotonic intervals */
return (unsigned long) TclpGetUTimeMonotonic();
}
#ifdef TCL_WIDE_CLICKS
@@ -180,17 +241,26 @@ TclpGetWideClicks(void)
{
Tcl_WideInt now;
if (tclGetTimeProcPtr != NativeGetTime) {
if (tclGetTimeProcPtr == NativeGetTime) {
#if defined(CLOCK_MONOTONIC)
/* 1 wide click == 0.001 microseconds (1 nanosecond) */
struct timespec mntv;
(void)clock_gettime(CLOCK_MONOTONIC, &mntv);
return ((Tcl_WideInt)mntv.tv_sec)*1000000*1000 + mntv.tv_nsec;
#elif defined(MAC_OSX_TCL)
/* 1 wide click == (tb.numer / tb.denom / 1000) microseconds */
return (Tcl_WideInt) (mach_absolute_time() & INT64_MAX);
#else /* no MAC_OSX_TCL and no monotonic time (xcode etc) */
/* 1 wide click == 1 microsecond (1000 nanoseconds) */
return TclpGetMicroseconds();
#endif
} else {
/* 1 wide click == 1 microsecond (1000 nanoseconds) */
Tcl_Time time;
tclGetTimeProcPtr(&time, tclTimeClientData);
now = ((Tcl_WideInt)time.sec)*1000000 + time.usec;
} else {
#ifdef MAC_OSX_TCL
now = (Tcl_WideInt) (mach_absolute_time() & INT64_MAX);
#else
#error Wide high-resolution clicks not implemented on this platform
#endif
}
return now;
@@ -219,10 +289,12 @@ TclpWideClicksToNanoseconds(
{
double nsec;
if (tclGetTimeProcPtr != NativeGetTime) {
nsec = clicks * 1000;
} else {
#ifdef MAC_OSX_TCL
if (tclGetTimeProcPtr == NativeGetTime) {
#if defined(CLOCK_MONOTONIC)
/* 1 wide click == 0.001 microseconds (1 nanosecond) */
return clicks;
#elif defined(MAC_OSX_TCL)
/* 1 wide click == (tb.numer / tb.denom) nanoseconds */
static mach_timebase_info_data_t tb;
static uint64_t maxClicksForUInt64;
@@ -235,9 +307,13 @@ TclpWideClicksToNanoseconds(
} else {
nsec = ((long double) (uint64_t) clicks) * tb.numer / tb.denom;
}
#else
#error Wide high-resolution clicks not implemented on this platform
#else /* no MAC_OSX_TCL and no monotonic time (xcode etc) */
/* 1 wide click == 1 microsecond (1000 nanoseconds) */
return clicks * 1000;
#endif
} else {
/* 1 wide click == 1 microsecond (1000 nanoseconds) */
nsec = clicks * 1000;
}
return nsec;
@@ -248,7 +324,7 @@ TclpWideClicksToNanoseconds(
*
* TclpWideClickInMicrosec --
*
* This procedure return scale to convert click values from the
* This procedure return scale to convert click values from the
* TclpGetWideClicks native resolution to microsecond resolution
* and back.
*
@@ -264,10 +340,12 @@ TclpWideClicksToNanoseconds(
double
TclpWideClickInMicrosec(void)
{
if (tclGetTimeProcPtr != NativeGetTime) {
return 1.0;
} else {
#ifdef MAC_OSX_TCL
if (tclGetTimeProcPtr == NativeGetTime) {
#ifndef MAC_OSX_TCL
/* 1 wide click == 0.001 microseconds (1 nanosecond) */
return 0.001;
#else /* MAC_OSX_TCL: */
/* 1 wide click == (tb.numer / tb.denom / 1000) microseconds */
static int initialized = 0;
static double scale = 0.0;
@@ -282,9 +360,10 @@ TclpWideClickInMicrosec(void)
initialized = 1;
return scale;
}
#else
#error Wide high-resolution clicks not implemented on this platform
#endif
} else {
/* 1 wide click == 1 microsecond (1000 nanoseconds) */
return 1.0;
}
}
#endif /* TCL_WIDE_CLICKS */
@@ -510,6 +589,34 @@ NativeScaleTime(
/* Native scale is 1:1. Nothing is done */
}
/*
*----------------------------------------------------------------------
*
* TclpScaleUTime --
*
* This procedure scales number of microseconds if expected.
*
* Results:
* Number of microseconds scaled using tclScaleTimeProcPtr.
*
*----------------------------------------------------------------------
*/
void
TclpScaleUTime(
Tcl_WideInt *usec)
{
/* Native scale is 1:1. */
if (tclScaleTimeProcPtr == NativeScaleTime) {
return;
} else {
Tcl_Time scTime;
scTime.sec = *usec / 1000000;
scTime.usec = *usec % 1000000;
tclScaleTimeProcPtr(&scTime, tclTimeClientData);
*usec = ((Tcl_WideInt)scTime.sec) * 1000000 + scTime.usec;
}
}
/*
*----------------------------------------------------------------------
*
+484 -144
View File
@@ -21,6 +21,7 @@
#define WM_WAKEUP WM_USER /* Message that is send by
* Tcl_AlertNotifier. */
/*
* The following static structure contains the state information for the
* Windows implementation of the Tcl notifier. One of these structures is
@@ -79,49 +80,49 @@ static LRESULT CALLBACK NotifierProc(HWND hwnd, UINT message,
ClientData
Tcl_InitNotifier(void)
{
if (tclNotifierHooks.initNotifierProc) {
return tclNotifierHooks.initNotifierProc();
} else {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
WNDCLASS class;
if (tclNotifierHooks.initNotifierProc) {
return tclNotifierHooks.initNotifierProc();
} else {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
WNDCLASS class;
/*
* Register Notifier window class if this is the first thread to use
* this module.
*/
/*
* Register Notifier window class if this is the first thread to use this
* module.
*/
Tcl_MutexLock(&notifierMutex);
if (notifierCount == 0) {
class.style = 0;
class.cbClsExtra = 0;
class.cbWndExtra = 0;
class.hInstance = TclWinGetTclInstance();
class.hbrBackground = NULL;
class.lpszMenuName = NULL;
class.lpszClassName = classname;
class.lpfnWndProc = NotifierProc;
class.hIcon = NULL;
class.hCursor = NULL;
Tcl_MutexLock(&notifierMutex);
if (notifierCount == 0) {
class.style = 0;
class.cbClsExtra = 0;
class.cbWndExtra = 0;
class.hInstance = TclWinGetTclInstance();
class.hbrBackground = NULL;
class.lpszMenuName = NULL;
class.lpszClassName = classname;
class.lpfnWndProc = NotifierProc;
class.hIcon = NULL;
class.hCursor = NULL;
if (!RegisterClass(&class)) {
Tcl_Panic("Unable to register TclNotifier window class");
}
if (!RegisterClass(&class)) {
Tcl_Panic("Unable to register TclNotifier window class");
}
notifierCount++;
Tcl_MutexUnlock(&notifierMutex);
tsdPtr->pending = 0;
tsdPtr->timerActive = 0;
InitializeCriticalSection(&tsdPtr->crit);
tsdPtr->hwnd = NULL;
tsdPtr->thread = GetCurrentThreadId();
tsdPtr->event = CreateEvent(NULL, TRUE /* manual */,
FALSE /* !signaled */, NULL);
return tsdPtr;
}
notifierCount++;
Tcl_MutexUnlock(&notifierMutex);
tsdPtr->pending = 0;
tsdPtr->timerActive = 0;
InitializeCriticalSection(&tsdPtr->crit);
tsdPtr->hwnd = NULL;
tsdPtr->thread = GetCurrentThreadId();
tsdPtr->event = CreateEvent(NULL, FALSE /* !manual */,
FALSE /* !signaled */, NULL);
return tsdPtr;
}
}
/*
@@ -409,6 +410,216 @@ NotifierProc(
return 0;
}
/*
* Timer resolution primitives
*/
typedef int (CALLBACK* LPFN_NtQueryTimerResolution)(PULONG,PULONG,PULONG);
typedef int (CALLBACK* LPFN_NtSetTimerResolution)(ULONG,BOOLEAN,PULONG);
static LPFN_NtQueryTimerResolution NtQueryTimerResolution = NULL;
static LPFN_NtSetTimerResolution NtSetTimerResolution = NULL;
#define TMR_RES_MICROSEC (1000 / 100)
static struct {
int available; /* Availability of timer resolution functions */
ULONG minRes; /* Lowest possible resolution (in 100-ns) */
ULONG maxRes; /* Highest possible resolution (in 100-ns) */
ULONG curRes; /* Current resolution (in 100-ns units). */
ULONG resRes; /* Resolution to be restored (delayed restore) */
LONG minDelay; /* Lowest delay by max resolution (in microsecs) */
LONG maxDelay; /* Highest delay by min resolution (in microsecs) */
size_t count; /* Waiter count (used to restore the resolution) */
Tcl_WideInt timeToReset; /* Time to reset resolution (typically now + 5s) */
CRITICAL_SECTION cs; /* Mutex guarding this structure. */
} timerResolution = {
-1,
15600 * TMR_RES_MICROSEC, 500 * TMR_RES_MICROSEC,
15600 * TMR_RES_MICROSEC, 0,
500, 15600,
0,
(Tcl_WideInt)0x7FFFFFFFFFFFFFFFL
};
/*
*----------------------------------------------------------------------
*
* InitTimerResolution --
*
* This function initializes the timer resolution functionality.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
InitTimerResolution(void)
{
if (timerResolution.available == -1) {
TclpInitLock();
if (timerResolution.available == -1) {
HMODULE hLib = GetModuleHandle(TEXT("NTDLL"));
if (hLib) {
NtQueryTimerResolution =
(LPFN_NtQueryTimerResolution)GetProcAddress(hLib,
"NtQueryTimerResolution");
NtSetTimerResolution =
(LPFN_NtSetTimerResolution)GetProcAddress(hLib,
"NtSetTimerResolution");
if ( NtSetTimerResolution && NtQueryTimerResolution
&& NtQueryTimerResolution(&timerResolution.minRes,
&timerResolution.maxRes, &timerResolution.curRes) == 0
) {
InitializeCriticalSection(&timerResolution.cs);
timerResolution.resRes = timerResolution.curRes;
timerResolution.minRes -= (timerResolution.minRes % TMR_RES_MICROSEC);
timerResolution.minDelay = timerResolution.maxRes / TMR_RES_MICROSEC;
timerResolution.maxDelay = timerResolution.minRes / TMR_RES_MICROSEC;
if (timerResolution.maxRes <= 1000 * TMR_RES_MICROSEC) {
timerResolution.available = 1;
}
}
}
if (timerResolution.available <= 0) {
/* not available, set min/max to typical values on windows */
timerResolution.minRes = 15600 * TMR_RES_MICROSEC;
timerResolution.maxRes = 500 * TMR_RES_MICROSEC;
timerResolution.minDelay = timerResolution.maxRes / TMR_RES_MICROSEC;
timerResolution.maxDelay = timerResolution.minRes / TMR_RES_MICROSEC;
timerResolution.available = 0;
}
}
TclpInitUnlock();
}
}
/*
*----------------------------------------------------------------------
*
* SetTimerResolution --
*
* This is called by Tcl_WaitForEvent to increase timer resolution if wait
* for events with time smaller as the typical windows value (ca. 15ms).
*
* Results:
* Returns previous value of timer resolution, used for restoring with
* RestoreTimerResolution.
*
* Side effects:
* Note that timer resolution takes affect for the whole process (accross
* all threads).
*
*----------------------------------------------------------------------
*/
static unsigned long
SetTimerResolution(
unsigned long newResolution,
unsigned long actualResolution
) {
/* if available */
if (timerResolution.available > 0)
{
if (newResolution < timerResolution.maxRes) {
newResolution = timerResolution.maxRes;
}
EnterCriticalSection(&timerResolution.cs);
if (!actualResolution) {
timerResolution.count++;
actualResolution = timerResolution.curRes;
}
if (newResolution < timerResolution.curRes) {
ULONG curRes;
if (NtSetTimerResolution(newResolution, TRUE, &curRes) == 0) {
timerResolution.curRes = curRes;
}
}
LeaveCriticalSection(&timerResolution.cs);
return actualResolution;
}
/* resolution unchanged (and counter not increased) */
return 0;
}
/*
*----------------------------------------------------------------------
*
* RestoreTimerResolution --
*
* This is called by Tcl_WaitForEvent to restore timer resolution to
* previous value.
*
* Results:
* None.
*
* Side effects:
* Because timer resolution takes affect for the whole process, it can
* remain max resolution after execution of this function (if some thread
* still waits with the highest timer resolution).
*
*----------------------------------------------------------------------
*/
static void
RestoreTimerResolution(
unsigned long newResolution
) {
/* if available */
if (timerResolution.available > 0 && newResolution) {
EnterCriticalSection(&timerResolution.cs);
if (timerResolution.count-- <= 1) {
timerResolution.resRes = newResolution;
/* prolong time to reset resolution */
timerResolution.timeToReset = TclpGetMicroseconds() + 1000000;
}
LeaveCriticalSection(&timerResolution.cs);
}
}
/*
*----------------------------------------------------------------------
*
* TclWinResetTimerResolution --
*
* This is called to delayed reset (after 1 second) of the timer resolution
* to original value, if no more waiting workers using precise resolution.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
TclWinResetTimerResolution(void)
{
if ( timerResolution.available > 0
&& timerResolution.count == 0 && timerResolution.resRes > timerResolution.curRes
&& TclpGetMicroseconds() >= timerResolution.timeToReset
) {
EnterCriticalSection(&timerResolution.cs);
if (timerResolution.count == 0 && timerResolution.resRes > timerResolution.curRes) {
ULONG curRes;
if (NtSetTimerResolution(timerResolution.resRes, TRUE, &curRes) == 0) {
timerResolution.curRes = curRes;
};
timerResolution.timeToReset = 0x7FFFFFFFFFFFFFFFL;
}
LeaveCriticalSection(&timerResolution.cs);
}
}
/*
*----------------------------------------------------------------------
*
@@ -432,108 +643,189 @@ int
Tcl_WaitForEvent(
const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
if (tclNotifierHooks.waitForEventProc) {
return tclNotifierHooks.waitForEventProc(timePtr);
} else {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
MSG msg;
DWORD timeout, result;
int status;
if (tclNotifierHooks.waitForEventProc) {
return tclNotifierHooks.waitForEventProc(timePtr);
} else {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
MSG msg;
DWORD timeout = INFINITE, result = WAIT_TIMEOUT;
int status = 0;
Tcl_WideInt waitTime = 0;
Tcl_WideInt endTime = 0;
long tolerance = 0;
unsigned long actualResolution = 0;
/*
* Compute the timeout in milliseconds.
*/
/*
* Compute the timeout in milliseconds.
*/
if (timePtr) {
/*
* TIP #233 (Virtualized Time). Convert virtual domain delay to
* real-time.
*/
if (timePtr) {
Tcl_Time myTime;
waitTime = TCL_TIME_TO_USEC(*timePtr);
myTime.sec = timePtr->sec;
myTime.usec = timePtr->usec;
if (myTime.sec != 0 || myTime.usec != 0) {
tclScaleTimeProcPtr(&myTime, tclTimeClientData);
}
timeout = myTime.sec * 1000 + myTime.usec / 1000;
} else {
timeout = INFINITE;
/* if no wait */
if (waitTime <= 0) {
result = 0;
goto peek;
}
/*
* Check to see if there are any messages in the queue before waiting
* because MsgWaitForMultipleObjects will not wake up if there are
* events currently sitting in the queue.
* TIP #233 (Virtualized Time). Convert virtual domain delay to
* real-time.
*/
if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
/*
* Wait for something to happen (a signal from another thread, a
* message, or timeout) or loop servicing asynchronous procedure
* calls queued to this thread.
*/
again:
result = MsgWaitForMultipleObjectsEx(1, &tsdPtr->event, timeout,
QS_ALLINPUT, MWMO_ALERTABLE);
if (result == WAIT_IO_COMPLETION) {
goto again;
} else if (result == WAIT_FAILED) {
status = -1;
goto end;
}
}
TclpScaleUTime(&waitTime);
/*
* Check to see if there are any messages to process.
* Note the time can be switched (time-jump), so use monotonic time here.
*/
endTime = TclpGetUTimeMonotonic() + waitTime;
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
/*
* Retrieve and dispatch the first message.
*/
if (timerResolution.available == -1) {
InitTimerResolution();
}
#ifdef TMR_RES_TOLERANCE
/* calculate possible maximal tolerance (in usec) of original wait-time */
tolerance = ((waitTime <= 1000000) ? waitTime : 1000000) *
TMR_RES_TOLERANCE / 100;
if (tolerance > timerResolution.maxDelay) {
tolerance = timerResolution.maxDelay;
}
#endif
result = GetMessage(&msg, NULL, 0, 0);
if (result == 0) {
/*
* We received a request to exit this thread (WM_QUIT), so
* propagate the quit message and start unwinding.
*/
repeat:
PostQuitMessage((int) msg.wParam);
status = -1;
} else if (result == (DWORD)-1) {
/*
* We got an error from the system. I have no idea why this
* would happen, so we'll just unwind.
*/
status = -1;
/* No wait if timeout too small (because windows may wait too long) */
if (waitTime < (long)timerResolution.minDelay) {
timeout = 0;
/* prevent busy wait */
if (waitTime >= 10) {
goto wait;
}
Sleep(0);
goto peek;
}
if (timerResolution.available) {
long overhead = (tolerance < 100 ? tolerance/2 : 50);
Tcl_WideInt waitTimeWithOverhead = waitTime + overhead;
if (waitTimeWithOverhead > timerResolution.maxDelay) {
/* floor (truncate) using max delay as base (follow timeout better) */
timeout = (waitTimeWithOverhead
/ timerResolution.maxDelay)
* timerResolution.maxDelay / 1000;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
status = 1;
/* calculate resolution up to 1000 microseconds
* (don't use highest, because of too large CPU load) */
ULONG res;
if (waitTimeWithOverhead >= 10000) {
res = 10000 * TMR_RES_MICROSEC;
} else {
res = 1000 * TMR_RES_MICROSEC;
}
timeout = waitTimeWithOverhead / 1000;
/* set more precise timer resolution for minimal delay */
if (!actualResolution || res < timerResolution.curRes) {
actualResolution = SetTimerResolution(
res, actualResolution);
}
}
} else {
status = 0;
timeout = waitTime / 1000;
}
end:
ResetEvent(tsdPtr->event);
return status;
}
/*
* Check to see if there are any messages in the queue before waiting
* because MsgWaitForMultipleObjects will not wake up if there are events
* currently sitting in the queue.
*/
wait:
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
goto get;
}
/*
* Wait for something to happen (a signal from another thread, a
* message, or timeout) or loop servicing asynchronous procedure calls
* queued to this thread.
*/
again:
result = MsgWaitForMultipleObjectsEx(1, &tsdPtr->event, timeout,
QS_ALLINPUT, MWMO_ALERTABLE);
if (result == WAIT_IO_COMPLETION) {
goto again;
}
if (result == WAIT_FAILED) {
status = -1;
goto end;
}
/*
* Check to see if there are any messages to process.
*/
peek:
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
/*
* Retrieve and dispatch the first message.
*/
get:
result = GetMessage(&msg, NULL, 0, 0);
if (result == 0) {
/*
* We received a request to exit this thread (WM_QUIT), so
* propagate the quit message and start unwinding.
*/
PostQuitMessage((int) msg.wParam);
status = -1;
} else if (result == (DWORD)-1) {
/*
* We got an error from the system. I have no idea why this would
* happen, so we'll just unwind.
*/
status = -1;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
status = 1;
}
}
else
if (result == WAIT_TIMEOUT && timeout != INFINITE) {
/* Check the wait should be repeated, and correct time for wait */
waitTime = endTime - TclpGetUTimeMonotonic();
if (waitTime <= tolerance) {
goto end;
}
/* Repeat wait with more precise timer resolution (or using sleep) */
goto repeat;
}
end:
/* restore timer resolution */
if (actualResolution) {
RestoreTimerResolution(actualResolution);
}
/* todo: move it to the service-thread (if available at some point) */
TclWinResetTimerResolution();
return status;
}
}
/*
*----------------------------------------------------------------------
*
* Tcl_Sleep --
* TclpUSleep --
*
* Delay execution for the specified number of milliseconds.
* Delay execution for the specified number of microseconds.
*
* TclpUSleep in contrast to Tcl_Sleep is more precise (microseconds).
*
* Results:
* None.
@@ -545,8 +837,8 @@ Tcl_WaitForEvent(
*/
void
Tcl_Sleep(
int ms) /* Number of milliseconds to sleep. */
TclpUSleep(
Tcl_WideInt usec) /* Number of microseconds to sleep. */
{
/*
* Simply calling 'Sleep' for the requisite number of milliseconds can
@@ -558,45 +850,93 @@ Tcl_Sleep(
* requisite amount.
*/
Tcl_Time now; /* Current wall clock time. */
Tcl_Time desired; /* Desired wakeup time. */
Tcl_Time vdelay; /* Time to sleep, for scaling virtual ->
* real. */
Tcl_WideInt desired; /* Desired wakeup time. */
DWORD sleepTime; /* Time to sleep, real-time */
vdelay.sec = ms / 1000;
vdelay.usec = (ms % 1000) * 1000;
Tcl_GetTime(&now);
desired.sec = now.sec + vdelay.sec;
desired.usec = now.usec + vdelay.usec;
if (desired.usec > 1000000) {
++desired.sec;
desired.usec -= 1000000;
}
long tolerance = 0;
unsigned long actualResolution = 0;
/*
* TIP #233: Scale delay from virtual to real-time.
*/
TclpScaleUTime(&usec);
tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000;
if (usec <= 9) { /* too short to start whole sleep process */
do {
/* causes context switch only (shortest waiting) */
SleepEx(0, TRUE);
} while (--usec > 0);
return;
}
if (timerResolution.available == -1) {
InitTimerResolution();
}
/*
* Note the time can be switched (time-jump), so use monotonic time here.
*/
desired = TclpGetUTimeMonotonic() + usec;
#ifdef TMR_RES_TOLERANCE
/* calculate possible maximal tolerance (in usec) of original wait-time */
tolerance = ((usec <= 1000000) ? usec : 1000000) *
TMR_RES_TOLERANCE / 100;
if (tolerance > timerResolution.maxDelay) {
tolerance = timerResolution.maxDelay;
}
#endif
for (;;) {
SleepEx(sleepTime, TRUE);
Tcl_GetTime(&now);
if (now.sec > desired.sec) {
break;
} else if ((now.sec == desired.sec) && (now.usec >= desired.usec)) {
break;
/* No wait if sleep time too small (because windows may wait too long) */
if (usec < (long)timerResolution.minDelay) {
sleepTime = 0;
goto wait;
}
vdelay.sec = desired.sec - now.sec;
vdelay.usec = desired.usec - now.usec;
if (timerResolution.available) {
if (usec > timerResolution.maxDelay) {
/* floor (truncate) using max delay as base (follow timeout better) */
sleepTime = (usec
/ timerResolution.maxDelay)
* timerResolution.maxDelay / 1000;
} else {
/* calculate resolution up to 1000 microseconds
* (don't use highest, because of too large CPU load) */
ULONG res;
if (usec >= 10000) {
res = 10000 * TMR_RES_MICROSEC;
} else {
res = 1000 * TMR_RES_MICROSEC;
}
sleepTime = usec / 1000;
/* set more precise timer resolution for minimal delay */
if (!actualResolution || res < timerResolution.curRes) {
actualResolution = SetTimerResolution(
res, actualResolution);
}
}
} else {
sleepTime = usec / 1000;
}
tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000;
wait:
SleepEx(sleepTime, TRUE);
/* new difference to now (monotonic base) */
usec = desired - TclpGetUTimeMonotonic();
if (usec <= tolerance) {
break;
}
}
/* restore timer resolution */
if (actualResolution) {
RestoreTimerResolution(actualResolution);
}
/* todo: move it to the service-thread (if available at some point) */
TclWinResetTimerResolution();
}
/*
+445 -622
View File
File diff suppressed because it is too large Load Diff