Tcl Source Code

View Ticket
Login
Ticket UUID: 0221b993a19fd8d1b72db4e79286c0861e7eb83c
Title: Tcl command [update idletasks] doesn't skip main loop in Tcl_DoOneEvent
Type: Bug Version: 8.6.11
Submitter: erikleunissen Created on: 2021-03-15 19:09:33
Subsystem: 02. Event Loops Assigned To: jan.nijtmans
Priority: 5 Medium Severity: Important
Status: Closed Last Modified: 2021-06-04 20:28:02
Resolution: Fixed Closed By: dgp
    Closed on: 2021-06-04 20:28:02
Description:
Nonwithstanding the intention in the comment, the following code snippet from Tcl_DoOneEvent():

	/*
	 * If idle events are the only things to service, skip the main part
	 * of the loop and go directly to handle idle events (i.e. don't wait
	 * even if TCL_DONT_WAIT isn't set).
	 */

	if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
	    flags = flags | TCL_DONT_WAIT;
	    goto idleEvents;
	}

is bypassed for [update idletasks] because of the presence of the TCL_WINDOW_EVENTS bit in the flags argument, which Tcl_DoOneEvent() receives from  Tcl_UpdateObjCmd():

	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");
	}

I find the presence of TCL_WINDOW_EVENTS in that flags variable suspect. What window events anyway? Especially when comparing with the Tk counterpart Tk_UpdateObjCmd(). In Tk, windows do play a role, but even here the TCL_WINDOW_EVENTS bit is not passed when "update idletasks" is invoked:

    if (objc == 1) {
	flags = TCL_DONT_WAIT;
    } else if (objc == 2) {
	if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions, "option", 0,
		&index) != TCL_OK) {
	    return TCL_ERROR;
	}
	flags = TCL_IDLE_EVENTS;	<=== ***
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?");
	return TCL_ERROR;
    }
User Comments: dgp added on 2021-06-04 20:28:02:
Should tcl.h continue to define TCL_WINDOW_EVENTS ?

Should there continue to be a testing subcommand [testfilehandler windowevent] ?

jan.nijtmans added on 2021-03-19 15:01:33:

Merged to core-8-branch and trunk


jan.nijtmans added on 2021-03-18 13:21:29:

Proposal now committed to the "bug-0221b993a1" branch


jan.nijtmans added on 2021-03-17 08:30:23:
Since TCL_WINDOW_EVENTS are really Tk events, I see no harm in removing TCL_WINDOW_EVENTS, as @erikleunissen suggests. Still, to prevent any risk (maybe other extensions could register TCL_WINDOW_EVENTS ...) I suggest to make this change only in Tcl 8.7+.

Any objections?

erikleunissen added on 2021-03-16 18:15:36:
Corroborating the misbehaviour:

I've put a printf() inside the crucial code snippet in Tcl_DoOneEvent(), like:

	if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
	    printf("***\n");
	    flags = TCL_IDLE_EVENTS | TCL_DONT_WAIT;
	    goto idleEvents;
	}

This printf() is skipped by [update idletasks] in a tclsh, but after loading Tk (which has a different implementation of the "update" command), the printf() statement is executed.

This confirms my suspicion, that [update idletasks], called from tclsh (no Tk loaded) doesn't skip what it should skip inside Tcl_DoOneEVent(). The consequences are probably not as severe as that it services timer and file events, but it does carry out a bunch of useless work (harmful work not excluded), like checking inappropriate event sources.

The remedy is simple (stripping the offending TCL_WINDOW_EVENTS inside Tcl_UpdateObjCmd() ). A patch (against tcl8.6.11) is provided anyway to prevent any ambiguity.

erikleunissen added on 2021-03-15 19:34:41:
> Therefore, upgrading priority to important.

Uhmm, I mean: severity

erikleunissen added on 2021-03-15 19:33:44:
If I am right, the consequence is that all along [update idletasks] has been servicing events from sources that it should ignore: timer events and file events.

Therefore, upgrading priority to important.

Attachments: