Tk Source Code

View Ticket
Login
Ticket UUID: 1348333
Title: 8.5 text widget creates to many timers
Type: Bug Version: 8.6.18, 9.0.5, 9.1b1
Submitter: kjnash Created on: 2005-11-04 14:46:25
Subsystem: 69. Events Assigned To: oehhar
Priority: 8 Severity: Minor
Status: Closed Last Modified: 2026-10-28 17:33:19
Resolution: Fixed Closed By: oehhar
    Closed on: 2026-10-28 17:33:19
Description:
# Tk 8.5 cannot cope with a large number of 'idletasks'
delayed actions, and
# it hangs, consuming most of the system's CPU - even
though it is able to
# process the same actions if they are split by update
commands.
#
# ActiveTcl 8.5 beta 3 (tcl_patchLevel 8.5a2) and
ActiveTcl 8.5 beta 4 (8.5a4,
# threaded) both have this problem.
#
# Tk 8.4 does not have this problem: ActiveTcl 8.4.11.2
(8.4.11, threaded) and
# ActiveTcl 8.4.9.0 (8.4.9) tested.
#
# The attached script creates and maps a large number
of text widgets, with
# the option of using update after each widget is
created, and (separately)
# after it is mapped.

# Mapping a large number of widgets gives Tk 8.5 a problem.
# Creation of a large number of unmapped widgets also
gives Tk 8.5 a problem.

# The numbers of widgets, though large, are not
perversely large - I have apps
# with more than 150 widgets - and Tk 8.4 has no
problem with much larger
# numbers.

# 100 widgets: OK always.
# 144 to 900 widgets: OK in 8.4 always; fails in Tk 8.5
if updates are not
# switched on.
User Comments: oehhar added on 2026-10-28 17:33:19:

CI clean! Merged to:

  • main: [8cd9dc36]
  • core-9-0-branch: [057956e1]
  • core-8-6-branch: [81d65a26]

THanks Serhiy!

Harald


oehhar added on 2026-10-27 12:08:21:

Great analysis, thanks! We will see tomorrow.

Take care, Harald


serhiy.storchaka added on 2026-10-27 11:17:59:

Thank you, Harald. Unfortunately 20×20 does not catch the bug: without the fix all 400 widgets are synchronized at once. On my machine it needs about 600 text widgets to catch the bug, but 900 is a better bet for slower or faster machines.

The failure on CI was probably due to the limited screen size: 30×30 text widgets of the default font need about 600×690 pixels, so on a small screen the toplevel is clipped, and pack unmaps the frames which do not fit. An unmapped text widget never completes its line metrics update (in 8.6 and 9.0 too), so one clipped row leaves exactly 30 widgets pending. I reproduced this on Linux and on Windows with wm maxsize: 30 pending with one row clipped, 90 with three.

Reducing the font size should fix it: with -font {TkFixedFont -8} 30×30 widgets need 480×540 pixels on Linux and 360×420 on Windows, and the test still fails without the fix (900 pending) and passes with it. Done in [e283698b64], which now has the core-text-metrics-after-redraw tag.

The 10 seconds timeout happens only when the test fails. When it passes, it takes 0.1 s on my machine (0.3 s on the Windows VM).


oehhar added on 2026-10-27 09:00:10:

Unfortunately, MacOS and MS-Win still show the test failure:

==== textDisp-41.1 many text widgets complete their line metrics update - bug 1348333 FAILED
==== Contents of test case:

    for {set i 0} {$i < 30} {incr i} {
	pack [frame .tt.f$i]
	for {set j 0} {$j < 30} {incr j} {
	    text .tt.f$i.t$j -height 1 -width 2
	    .tt.f$i.t$j insert end $i$j
	    pack .tt.f$i.t$j -side left
	}
    }
    # Without the fix, no widget gets synchronized at all.  Allow up to 10
    # seconds for slow machines.
    for {set n 0} {$n < 100} {incr n} {
	pause 100
	set pending 0
	foreach f [winfo children .tt] {
	    foreach t [winfo children $f] {
		incr pending [$t pendingsync]
	    }
	}
	if {$pending == 0} {
	    break
	}
    }
    set pending

---- Result was:
30
---- Result should have been (exact matching):
0
==== textDisp-41.1 FAILED

I ran the test locally and it succeeded. I have printed the final value of n on my machine. It is 11. That is 1 second.

It is also curious, that the result is exactly 30 on both platforms.

Shall I try to just give more time?

We are not happy to have tests which take a long time, as this makes running the test suite more time consuming...

Or perhaps reduce the text widget matrix to 20x20 ? This reduces n to 5 on my machine. I try this with commit [f2efd36d].

Thanks for all, Harald


oehhar added on 2026-10-26 08:53:05:
Ah, the CI run was on the wrong checkin.
Will restart CI.

Sorry for the wrong message,
Harald

oehhar added on 2026-10-26 08:52:00:

Hi Serhiy,

CI results are here: https://github.com/tcltk/tk/actions?query=branch%3Acore-text-metrics-after-redraw

MS-Windows and Mac-OS test error:

==== textDisp-41.1 many text widgets complete their line metrics update - bug 1348333 FAILED
==== Contents of test case:

    for {set i 0} {$i < 30} {incr i} {
	pack [frame .tt.f$i]
	for {set j 0} {$j < 30} {incr j} {
	    text .tt.f$i.t$j -height 1 -width 2
	    .tt.f$i.t$j insert end $i$j
	    pack .tt.f$i.t$j -side left
	}
    }
    after 1000 {set ::done 1}
    vwait ::done
    set pending 0
    foreach f [winfo children .tt] {
	foreach t [winfo children $f] {
	    incr pending [$t pendingsync]
	}
    }
    set pending

---- Result was:
30
---- Result should have been (exact matching):
0
==== textDisp-41.1 FAILED

I was not able to reproduce locally. Is it still a timing issue?

Thanks for all, Harald


serhiy.storchaka added on 2026-10-24 08:51:16:

CI failed only in the new test textDisp-41.1 on Windows and macOS: it waited one second, which was not enough on the slower CI machines (30 of 900 widgets were still waiting). It now waits up to 10 seconds ([1f9685c952]); without the fix no widget gets synchronized at all, so the test still detects the bug. textWind-17.10 passed everywhere.


oehhar added on 2026-10-23 17:19:44:

Thanks, great! Highly appreciated!

Lets wait for CI..

Harald


nemethi (claiming to be Csaba Nemethi) added on 2026-10-23 17:09:56:

I don't see any arguments against merging the text-metrics-after-redraw branch into main, core-9-0-branch, and core-8-6-branch.


oehhar added on 2026-10-23 16:36:54:

Great, Csaba. Thanks for the test! There is no merge->yes opinion from your side, but anyway.

If CI is ok we will merge again.

Thansk for all, Harald


nemethi (claiming to be Csaba Nemethi) added on 2026-10-23 16:22:27:

I can confirm that with branch text-metrics-after-redraw merged into main, the text tests pass on macOS, too.


oehhar added on 2026-10-23 13:57:54:

Thanks, great.

I had tested the test on MS-Win before merging to main and it did not hang. I saw the beautiful big text widget matrix for a second.

I activated CI. Lets see, if this passes.

And of cause, Csabas opinion would be great...

Thanks for all, Harald


serhiy.storchaka added on 2026-10-23 13:44:44:

Sorry for the hang: I tested the previous fix only on Linux, where textWind-17.10 passes. Now I test all fixes on Linux and Windows. In that test, [update] in a -create script runs while the redisplay is still pending, and the when-idle handler of the fix kept registering itself again, so [update] never returned.

New fix on branch text-metrics-after-redraw ([009b2c8f09]): the line metrics update waits without a timer or a when-idle handler, and DisplayText() restarts it after the redisplay. The text tests pass on Linux and Windows 11, with no hang. Could someone please test it on macOS?


oehhar added on 2026-10-23 13:19:22:

Thanks, Jan. It was a try. Sorry, Harald


jan.nijtmans added on 2026-10-23 12:48:43:

It turns out that backout out the 'fix' makes everything pass again.

So, I took the action to revert this on trunk. Sorry :-(


jan.nijtmans added on 2026-10-23 12:34:46:

Same problem can also be seen in the CI build: https://github.com/tcltk/tk/actions/runs/39913119911/job/120280442353

textTag.test
textWind.test
Error: The action 'Run Tests' has timed out after 20 minutes.

It's the textWind-17.10 testcase hanging


jan.nijtmans added on 2026-10-23 12:19:50:

Unfortunately, I'm seeing a hang in textWind-17.10, both on Windows and on MacOS. So, I did a bisect:

$ fossil bisect chart
  1 BAD     2026-10-23 09:20:10 6d6d38074ea28fcb
  4 BAD     2026-10-23 08:42:11 bdb7edf927982f35
  5 BAD     2026-10-22 18:23:16 fb166f9f73106ab2
  6 BAD     2026-10-22 17:32:35 4b3b840a41badb6a
  3 GOOD    2026-10-22 15:14:16 51a8eab8eee4e121 CURRENT
  2 GOOD    2026-10-21 15:23:43 b8fb1d6c114cce4a

So, this clearly points to this ticket :-(

Please don't backport this to 9.0 yet!


oehhar added on 2026-10-22 17:40:47:

Fix is effective. Merged to main by commit [4b3b840a41].

I feel incompetent on this one. Csaba, what do you think?

Will merge to core-9-0 if main CI is clean.

Thanks for all, Serhiy!

Harald


serhiy.storchaka added on 2026-09-24 10:57:12:

Still reproducible in 8.6 and 9.x with the attached script when imax and jmax are set to 30 (900 text widgets): update never returns and the CPU stays busy. With 8.5.0 the widgets are not even displayed.

The cause is what hobbs found: while a redisplay is pending, AsyncUpdateLineMetrics polls for it with a 1 ms timer. With many text widgets these timers are always due, so the idle handlers, including the redisplays, never run.

Fix on text-metrics-idle-wait: wait for the redisplay in an idle handler instead of polling. Test textDisp-38.1.


vincentdarley added on 2006-04-03 00:39:16:
Logged In: YES 
user_id=32170

Note: timers are only used because of the bug in Tk that
'after idle' handlers cannot reschedule themselves.  The
goal here is to do some computation when Tk is idle.  It is
entirely disconnected from the drawing/redrawing of the text
widget for the good reason that the computation is referring
to things that are not (currently) to be drawn, but are
needed to know the correct size of the scrollbar, etc.

Anything that hooks into redraw mechanisms won't help,
therefore, because once the visible portion of the text
widget is ok, no more redraw handlers are going to be called.

So, what we want is a _robust_ 'compute when idle'
mechanism, which allows idle computation to schedule more,
future, idle computation.  I don't know how to achieve that
with the current Tcl, Tk APIs.

hobbs added on 2006-03-31 07:37:47:
Logged In: YES 
user_id=72656

Looking more into this, the actual problem is resource
starvation.  With the number of text widgets and the short
timer time (1), the event queue never empties.  If I up the
time to 10, I can view 400 (20x20 grid) OK, but then 900
(30x30 grid) still has resource starvation issues.

I think this timer stuff needs to be integrted into some
deferred redraw handling.

hobbs added on 2006-03-31 06:39:38:
Logged In: YES 
user_id=72656

I have confirmed that the timer handling is broken.  Please
look into how others use deferred redraw handling.  In
particular, the enclosed test for a 20x20 text grid (400
widgets) with a printf of the Tcl_CreateTimerHandler point
in AsyncUpdateLineMetrics shows that is endlessly called. 
All the timers should either delete their previous handler,
or better yet do the eventuallyredraw handling.

vincentdarley added on 2005-11-08 01:07:04:
Logged In: YES 
user_id=32170

I think I do want to use Tcl_CreateTimerHandler, (this isn't
for redrawing -- the redrawing side uses the same calls as
Tk 8.4). Each text widget should only schedule 2 timers --
one for line height calculations and one for the scrollbar.
 I just skimmed the code in tkTextDisp.c and it all looks in
order (i.e. each widget should never have more than 2 timers
active).  So, I'm interested to know things like how many
timers are active in the reported case.

hobbs added on 2005-11-08 00:35:53:
Logged In: YES 
user_id=72656

I don't think you want to use Tcl_CreateTimerHandler, or at
least not to schedule without checking what's already
scheduled.  If you look at the pattern other widgets use, it
works on delayed calcuations (like EventuallyRedraw...) that
only schedule *1* delayed operation, adjusting the
parameters to note what needs to be redrawn if multiple
calls for it come in (like increasing a bounding box or
something).

vincentdarley added on 2005-11-07 18:06:53:
Logged In: YES 
user_id=32170

The text widget uses 'Tcl_CreateTimerHandler' to make
various callbacks to do the pixel-line-height calculations.
It's not obvious to me what can go wrong with those, but
perhaps there's some sort of problem where an increasing
queue of events is never handled, because we schedule many
of these events just 1 millisecond ahead. This really needs
someone who understands the Tcl_CreateTimerHandler mechanism
to look into things.

hobbs added on 2005-11-05 06:25:29:
Logged In: YES 
user_id=72656

This appears to be a fault in the text widget (some
excessive redraws?).  If you use an entry instead, it works
fine.

Also, on Windows, if I just "touch" the "frozen" interface
by trying to resize it, it snaps out of whatever weirdness
it is in and displays all the text widgets.  Repro'd on XP
with AT 8.5.

kjnash added on 2005-11-04 21:49:02:

File Added - 155015: many_text_bug.tcl

Attachments: