Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event performance branch #4

Open
wants to merge 78 commits into
base: core-8-5-branch
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8ae594a
[performance] after-event list optimized (interp-assoc switched to do…
sebres Jul 3, 2017
7ea8eec
timer resp. idle events optimized: better handling using doubly linke…
sebres Jul 3, 2017
59b7439
rewrite interpreter limit handling using new timer event handling (wi…
sebres Jul 3, 2017
303f179
after-id: introduced object of type "afterObjType" as self-referenced…
sebres Jul 3, 2017
f743ad6
[bug/stable fix] don't execute TimerSetupProc directly (may be unwant…
sebres Jul 3, 2017
ea640f9
bug fix: wrong release of after-id tcl-object if it switch type (obje…
sebres Jul 3, 2017
daed4df
[performance] introduced additional queue for prompt timer events (af…
sebres Jul 3, 2017
616371f
[performance] large performance increase by event servicing cycles (3…
sebres Jul 3, 2017
6cec57b
[performance] much better handling for timer events within Tcl_Servic…
sebres Jul 3, 2017
27b0e45
command "vwait" extended with timeout argument (in ms), 0 could be us…
sebres Jul 3, 2017
e3a3cb7
[performance] do one event (update / event servicing) cycle optimized…
sebres Jul 3, 2017
3505de8
interim commit: try to extend "vwait" with same options as "update"
sebres Jul 3, 2017
70f5692
[enhancement] extend "vwait" with same options as "update", new synta…
sebres Jul 3, 2017
d7b36b9
dynamic increase of timer resolution corresponding wait-time;
sebres Jul 3, 2017
74aaaff
optimization of Tcl_LimitExceeded by internal usage (tclInt header)
sebres Jul 3, 2017
5a5c7ea
Use auto-reset event object (system automatically resets the event st…
sebres Jul 3, 2017
91d77fe
[win32] use timer resolution handling in Tcl_Sleep also;
sebres Jul 3, 2017
82ba340
[win] fallback to replace C++ keyword "inline" with C keyword "__inline"
sebres Jul 3, 2017
8f7576a
code review + better usage of the waiting tolerance (fewer CPU-greedy…
sebres Jul 3, 2017
6315911
resolved some warnings / fixed unix resp. x64 compilation
sebres Jul 3, 2017
50bbd06
after info, after cancel: compare interpreter of the timer-events by …
sebres Jul 3, 2017
754a8c1
make timer test-case more precise and time-independent, ignores short…
sebres Jul 3, 2017
b9c33d0
fix sporadic errors on some fast cpu/platforms (because bgerror execu…
sebres Jul 3, 2017
7455b3f
added performance test-cases to cover timer-events speed resp. event-…
sebres Jul 3, 2017
6deb0b8
[unix] optimized Tcl_WaitForEvent similar to windows changes (makes T…
sebres Jul 3, 2017
5bc8e9f
extended performance test-cases (test-nrt-capability): covering of br…
sebres Jul 3, 2017
bfeb666
bug fix: prevent setting of negative block-time by too few initial wa…
sebres Jul 3, 2017
9148f08
chanio.test: optimize several tests cases running too long (shorten u…
sebres Jul 3, 2017
81af4a8
extended performance test-cases (test-nrt-capability): RTS-near sleep…
sebres Jul 3, 2017
0b58606
interim commit: try to fix time-drift backwards (too long offset afte…
sebres Jul 3, 2017
e57cc9e
interim commit: try to fix time-drift backwards (calibration? somethi…
sebres Jul 3, 2017
206f8c7
interim commit: time-drift backwards fix...
sebres Jul 3, 2017
6b0752e
interim commit: time-drift backwards fix... (try to resolve using fre…
sebres Jul 3, 2017
864762e
interim commit: time-drift backwards fix... (try to resolve using cou…
sebres Jul 3, 2017
17aaad4
interim commit: amend with optimization
sebres Jul 3, 2017
2a3c75f
win: calibration cycle completely rewritten (no calibration thread ne…
sebres Jul 3, 2017
e4df9d5
call TclWinResetTimerResolution at end of sleep resp. wait for event …
sebres Jul 3, 2017
45d875a
interim commit: trying to resolve time-freezes with new facilities ti…
sebres Jul 3, 2017
9c04414
interim commit: trying to resolve time-freezes with new facilities ti…
sebres Jul 3, 2017
c581bda
don't use tolerance in vwait, because of dual usage, it causes cancel…
sebres Jul 3, 2017
9a0ec98
amend with review
sebres Jul 3, 2017
9385efe
revert dual lists (relative/absolute) back to single list (because of…
sebres Jul 3, 2017
d5f19fd
interim commit: code review, rewrite tclTimer, etc.
sebres Jul 3, 2017
655c496
code review, rewrite tclTimer, prolongation, etc.
sebres Jul 3, 2017
8326183
"after at" set factor to 1000000 (seconds), test cases fixed
sebres Jul 3, 2017
6e993d0
Merge branch 'fix-busy-prompt-timers' into event-perf-branch:
sebres Jul 3, 2017
91f7185
interim commit: trying to pass test-cases - timer-3.*, chan-io-29.34,…
sebres Jul 3, 2017
35e88e5
fixed timer-marker handling: timer should be always executed after qu…
sebres Jul 3, 2017
c780afc
don't cancel scheduled event as long as the event list is not bidirec…
sebres Jul 3, 2017
6c801ea
Introduced monotonic time as ultimate fix for time-jump issue (fixed …
sebres Jul 3, 2017
1895544
[unix] fixes conditional-wait: timeout is monotonic based;
sebres Jul 3, 2017
f9d02b4
unix: implements wide-clicks on unix (1 wide-click == 0.001 microseco…
sebres Jul 3, 2017
2303ca1
fix check event source threshold (corresponds 100-ns ranges, if the w…
sebres Jul 3, 2017
7cf6ef1
code review and small optimizations
sebres Jul 3, 2017
590508d
after at: added simple workaround for absolute timers/sleep ("after a…
sebres Jul 3, 2017
4af2721
cleanup...
sebres Jul 3, 2017
1041322
performance test cases extended
sebres Jul 3, 2017
6e09c2f
merge resp. reintegrate sebres-8-5-event-perf-branch to 8.6
sebres Jul 10, 2017
96b79db
Stability fix: queue epoch to guarantee avoid broken queue, service l…
sebres Jul 10, 2017
d1fba64
tclUnixNotfy.c: we should wait for notifier at least once in case of …
sebres Jul 10, 2017
bb515d5
reintegrate sebres-8-6-event-perf-branch back to 8.5
sebres Jul 10, 2017
d2ae8f3
avoid blocking wait if we've some retarded events (from last event-cy…
sebres Jul 10, 2017
606f029
merge core-8-5-branch
sebres Jul 10, 2017
a54af9a
timer: fixed setup of the block-time to 0.0 by pending timer of new g…
sebres Jul 13, 2017
78b9602
avoid busy wait if new short block-time will be set within service ev…
sebres Jul 13, 2017
f4704c4
merge (integrate) sebres-event-perf-fix-busy-wait
sebres Jul 13, 2017
b4cbd5b
merge sebres-8-5-timerate (prepare for consolidation with 8.5)
sebres Mar 8, 2019
67ef69d
merge 8.5 - timerate is part of Tcl now (since TIP#527 got merged), c…
sebres Mar 8, 2019
d9b096a
cpu-count (performance counter) recognition is rewritten (also resolv…
sebres Mar 8, 2019
95719f8
merge 8.5 (fixed performance test for short exec-time)
sebres Mar 8, 2019
f5b24d3
unix: monotonic time (performance counter/wide-clicks) - implements s…
sebres Mar 8, 2019
f0b40b1
TclpScaleUTime fixed typo, only if native scaling used (tclScaleTimeP…
sebres Mar 9, 2019
9fb5419
macosx: Tcl_Sleep -> TclpUSleep (provides microseconds sleep, fixed u…
sebres Mar 9, 2019
2761fa9
non-native time scale fixed: do scale time only once (not in cycle, s…
sebres Mar 9, 2019
fe57079
NRT relevant only: provides self-calibrating mechanism for TclpUSleep…
sebres Mar 9, 2019
61bcbc6
merge 8.5
sebres May 22, 2019
2f096ff
correct comment for TclpGetUTimeMonotonic
sebres May 31, 2022
ef4c1ac
[windows] avoid overflow by calculation of perf-counter to 100ns as s…
sebres May 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion generic/tcl.h
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ typedef void (Tcl_PanicProc) _ANSI_ARGS_((CONST char *format, ...));
typedef void (Tcl_TcpAcceptProc) _ANSI_ARGS_((ClientData callbackData,
Tcl_Channel chan, char *address, int port));
typedef void (Tcl_TimerProc) _ANSI_ARGS_((ClientData clientData));
typedef void (Tcl_TimerDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef int (Tcl_SetFromAnyProc) _ANSI_ARGS_((Tcl_Interp *interp,
struct Tcl_Obj *objPtr));
typedef void (Tcl_UpdateStringProc) _ANSI_ARGS_((struct Tcl_Obj *objPtr));
Expand Down Expand Up @@ -1296,6 +1297,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)
Expand All @@ -1322,7 +1324,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;

/*
Expand Down
38 changes: 38 additions & 0 deletions generic/tclClock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[]);
Expand Down Expand Up @@ -212,6 +215,7 @@ static const struct ClockCommand clockCommands[] = {
{ "getenv", ClockGetenvObjCmd },
{ "microseconds", ClockMicrosecondsObjCmd },
{ "milliseconds", ClockMillisecondsObjCmd },
{ "monotonic", ClockMonotonicObjCmd },
{ "seconds", ClockSecondsObjCmd },
{ "Oldscan", TclClockOldscanObjCmd },
{ "ConvertLocalToUTC", ClockConvertlocaltoutcObjCmd },
Expand Down Expand Up @@ -1816,6 +1820,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;
}

/*
*-----------------------------------------------------------------------------
*
Expand Down
231 changes: 189 additions & 42 deletions generic/tclEvent.c
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,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;
}

/*
*----------------------------------------------------------------------
Expand All @@ -1324,46 +1380,149 @@ Tcl_VwaitObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *CONST objv[]) /* Argument objects. */
{
int done, foundEvent;
char *nameString;

if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "name");
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, "?options? ?timeout? name");
return TCL_ERROR;
}
nameString = Tcl_GetString(objv[1]);
if (Tcl_TraceVar(interp, nameString,

/* 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, (ClientData) &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 interpreter limit exceeded */
if (Tcl_LimitExceeded(interp)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("limit exceeded", -1));
done = -4;
break;
}
}
Tcl_UntraceVar(interp, nameString,
} while (!done);

Tcl_UntraceVar2(interp, nameString, NULL,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
VwaitVarProc, (ClientData) &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.
*/

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.
*/

Tcl_ResetResult(interp);
if (!foundEvent) {
Tcl_AppendResult(interp, "can't wait for variable \"", nameString,
"\": would wait forever", NULL);
return TCL_ERROR;
}
if (!done) {
Tcl_AppendResult(interp, "limit exceeded", NULL);
return TCL_ERROR;
}
return TCL_OK;
}

Expand Down Expand Up @@ -1409,28 +1568,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 *updateOptions[] = {"idletasks", NULL};
enum updateOptions {REGEXP_IDLETASKS};

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) {
int flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; /* default flags */

/* 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 REGEXP_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) {
Expand All @@ -1439,6 +1583,9 @@ Tcl_UpdateObjCmd(
Tcl_AppendResult(interp, "limit exceeded", NULL);
return TCL_ERROR;
}

/* be sure not to produce infinite wait (wait only once) */
flags |= TCL_DONT_WAIT;
}

/*
Expand Down
Loading