Tk Source Code

Artifact [117a974c]
Login

Artifact 117a974c70453f75e968be579e71b2cf49b1a4eaab6a483b710294a0d7022959:

Attachment "a0c1a9c6c6_sample.tcl" to ticket [a0c1a9c6] added by serhiy.storchaka 2026-09-29 13:07:28.
# Sample for Tk bug a0c1a9c6c6: text layout is not thread safe when base
# chunks are used (macOS, and with bidi rendering also X11 and Windows).
# Several threads lay out text widgets at the same time.  Without the fix
# wish crashes within seconds; with the fix it prints "ok".
#
# Xft is not thread safe itself, so every thread is initialized in turn and
# uses its own font family, to not crash in Xft instead.
#
# Run with: wish a0c1a9c6c6_sample.tcl ?nthreads? ?seconds?

package require Thread

set nthreads [expr {$argc > 0 ? [lindex $argv 0] : 4}]
set seconds [expr {$argc > 1 ? [lindex $argv 1] : 10}]

set setup {
    load {} Tk
    tk useinputmethods 0
    wm title . "Thread [thread::id]: $family"
    text .t -width 40 -height 20 -wrap word -font [list $family 10]
    pack .t -fill both -expand 1
    .t tag configure big -font [list $family 16]
    .t tag configure red -foreground red
    for {set i 0} {$i < 50} {incr i} {
	.t insert end "Line $i: some text " {} "big text " big \
		"red text " red "mixed [string repeat abc 10]\n"
    }
    update
    proc run {deadline} {
	set n 0
	while {[clock milliseconds] < $deadline} {
	    # Relayout all lines: change the width and edit the text.
	    .t configure -width [expr {30 + $n % 20}]
	    .t insert 1.0 "x" red
	    .t delete 1.0
	    update
	    incr n
	}
	return $n
    }
}

set families [lsort -unique [font families]]
set step [expr {[llength $families] / $nthreads}]
set tids {}
for {set i 0} {$i < $nthreads} {incr i} {
    set tid [thread::create]
    lappend tids $tid
    set family [lindex $families [expr {$i * $step}]]
    thread::send $tid [list set family $family]
    thread::send $tid $setup
}

set deadline [expr {[clock milliseconds] + 1000 * $seconds}]
foreach tid $tids {
    thread::send -async $tid [list run $deadline] ::result($tid)
}
set done 0
trace add variable ::result write {apply {args {incr ::done}}}
while {$done < $nthreads} {
    vwait ::done
}
puts "ok: [join [lmap tid $tids {set ::result($tid)}] { }] relayouts"
exit