| Ticket UUID: | 87b69745be15c2816574ec415dfe2c3c407cc5d6 | ||
| Title: | interp creation resets encoding directory search path | ||
| Type: | Bug | Created on: | 2025-07-31 05:00:27 |
| Submitter: | apnadkarni | Assigned to: | nobody |
| Subsystem: | - New Builtin Commands | Severity: | Minor |
| Priority: | 5 Medium | Last modified: | 2025-09-02 17:20:27 |
| Status: | Closed | Closed by: | dgp |
| Resolution: | Fixed | Closed on: | 2025-09-02 17:20:27 |
| Version: | 9.0 | ||
| Description: | ||||
|
| ||||
| User Comments: | ||||
apnadkarni added on 2025-07-31 05:16:21:
Looks like the bug was introduced with commit [450d12dac5]. apnadkarni added on 2025-08-06 13:40:50:
Branch bug-87b69745be contains a proposed solution to this and other zipfs related encoding bugs that previously contained point fixes.
Review requested. apnadkarni added on 2025-08-06 13:53:41:
Reminder to self - need to investigate if wish / Tk have a similar issue. sebres added on 2025-08-13 16:11:19:
After a quick review, I don't entirely agree with the changes in Additionally note that this mutex only protect the hash table of encodings and encoding' refCount, it doesn't really protect static systemEncoding (nor the defaultEncoding) directly. Let alone it'd be still affected by [f2ff05fc84] and co. Commit [bf62ce24c60c7b8a] shall fix it now. apnadkarni added on 2025-08-13 17:28:33:
@sebres, I am not in agreement with your mods. First, as I mentioned in the core mailing list, Tcl mutexes are recursive as far as I can tell. Second, I do not like the comparison of systemEncoding outside of holding the mutex. Globals shared between threads should be accessed under a lock even when reading. While I could probably create a case where that comparison of defaultEncoding and systemEncoding is a race condition, as a matter of principle the safer route is always accessing under a lock instead of proving it is safe. In fact, looking at the management of systemEncoding without locks led me to create this (unrelated) ticket about whether more fixes related to systemEncoding synchronization are needed. Of course, things are different if Tcl mutexes are not recursive and I misunderstood the manpage. sebres added on 2025-08-13 18:26:22:
Yes, as already confirmed in the core mailing list, you are right and this is indeed reentrant now (changed in 8.7+ after TIP#509).
And if it is compared by locked mutex what would it change? The only solution would be something like I provided in [f2ff05fc84], or system encoding stored per thread (TSV).
No. In this particular comparison it doesn't change something, since it is only protection against increase of FS epoch and function simply returns if it is equal now. Few nanoseconds later or earlier it may be different (no matter with or without lock). With already mentioned consequences. Feel free to revert it (mark may change as mistake branch and reset tags to previous commit), if you don't like it. But I guess in case of possible race condition, you overestimate the lock protection here and underestimate the consequences of conceptual bug (the issue by "wrong" design - thread-safety of the system encoding). apnadkarni added on 2025-08-14 11:32:48:
@sebres, I'm merging my original code. What you said about not requiring a lock may well be true but except in performance critical paths (this is not one), I prefer to stick with the lock for shared data in the general belief that ensuring access is safe without locks needs careful thought and is prone to errors (possibly even in future when code is modified). Not worth the time spent, imo, to convince oneself there are no race conditions or other effects. sebres added on 2025-08-14 11:47:28:
No problem. > to convince oneself there are no race conditions or other effects But there is a race condition issue. The possibility to overwrite a system encoding from every thread (also protected by mutex) will cause that previously set system-encoding, which can be still used by other threads with other functions (see ticket [f2ff05fc84]) may get released (by last reference). And your extra mutex doesn't protect against it at all. Sure, it makes that not more worse as it was, but the lock protection is definitely overestimated... apnadkarni added on 2025-08-14 13:29:56:
Yes of course. But tracking that as a separate bug that you have already logged (and I duplicated yesterday) as the required solution is much broader (as you also stated). The possibility of race conditions in encoding routines is much greater and has to be fixed. apnadkarni added on 2025-08-14 13:47:18:
Fixed in [0433b67adc]. Broader thread safety issues with systemEncoding to be addressed separately. jan.nijtmans added on 2025-08-18 11:50:40:
Review remark: indenting in tclEncoding.c is not consistent. All other files in this commit are fine. Fixed [ee22d2717fc9d6c3|here]. Sorry, Ashok. dgp added on 2025-08-22 18:19:08:
On core-9-0-branch, starting with checkin [0433b67adc], I see 114
failing tests in cmdAH.test and encoding.test. The first of them is
==== cmdAH-4.3.13.7F.solo.ascii.strict.a encoding convertfrom -profile strict ascii FAILED
==== Contents of test case:
encoding convertfrom -profile strict ascii
---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: unexpected byte sequence starting at index 0: '\x7F'
while executing
"encoding convertfrom -profile strict ascii "
("uplevel" body line 1)
invoked from within
"uplevel 1 $script"
---- errorCode: TCL ENCODING ILLEGALSEQUENCE 0
==== cmdAH-4.3.13.7F.solo.ascii.strict.a FAILED
How can I help unbreak the branch??
apnadkarni added on 2025-08-23 00:55:54:
What is the test configuration in which you are seeing errors? Neither my tests not Github CI showed any failures on any platform before I committed. On the current core-9-0-branch, Github CI still shows no failures and now I re-tested the following combinations on Ubuntu: ``` configure make test make test TESTFLAGS="-singleproc 1" configure --disable-shared make test make test TESTFLAGS="-singleproc 1" configure --disable-zipfs make test make test TESTFLAGS="-singleproc 1" ``` as well as the equivalents on Windows ``` nmake /f makefile.vc test nmake /f makefile.vc test TESTFLAGS="-singleproc 1" nmake /f makefile.vc OPTS=static test nmake /f makefile.vc OPTS=static test TESTFLAGS="-singleproc 1" nmake /f makefile.vc OPTS=noembed test nmake /f makefile.vc OPTS=noembed test TESTFLAGS="-singleproc 1" ``` None show any failures. Failing on ASCII input 0x7f is very strange indeed and if it seems to happen only on your system stranger still. Something uninitialized in specific circumstance...? What platform are you on and what were the specific configure and test options? dgp added on 2025-08-25 12:56:58:
Here's a more complete log of my steps to see the failures:
$ uname -a
Linux spalt.cam.nist.gov 5.15.0-131-generic #141-Ubuntu SMP Fri Jan 10 21:18:28 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
$ mkdir demo-87b697
$ cd demo-87b697/
$ fossil open ../tcl.fr 0433b6
...
$ cd unix/
$ ./configure --disable-shared
...
checking system version... (cached) Linux-5.15.0-131-generic
...
checking for macher... checking for zip... /usr/bin/zip
Found INFO Zip in environment
checking for building with zipfs... yes
...
$ make
...
$ make tcltest
...
$ make test-tcl TESTFLAGS="-file encoding.test"
...
LD_LIBRARY_PATH=`pwd`:/home/dgp/lib TCLLIBPATH="/home/dgp/fossil/demo-87b697/unix/pkgs" TCL_LIBRARY="" ./tcltest /home/dgp/fossil/demo-87b697/tests/all.tcl -file encoding.test
Tests running in interp: /local/tmp/dgp/fossil/demo-87b697/unix/tcltest
Tests located in: /local/tmp/dgp/fossil/demo-87b697/tests
Tests running in: /local/tmp/dgp/fossil/demo-87b697/unix
...
encoding.test
==== encoding-28.0 all encodings load FAILED
==== Contents of test case:
set string hello
foreach name [encoding names] {
if {$name ne "unicode"} {
incr count
}
encoding convertto -profile tcl8 $name $string
# discard the cached internal representation of Tcl_Encoding
# Unfortunately, without this, encoding 2-1 fails.
llength $name
}
return $count
---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: invalid encoding file "tis-620"
while executing
"encoding convertto -profile tcl8 $name $string"
("foreach" body line 5)
invoked from within
"foreach name [encoding names] {
if {$name ne "unicode"} {
incr count
}
encoding convertto -profile tcl8 $name $string
# discard the cac..."
("uplevel" body line 3)
invoked from within
"uplevel 1 $script"
---- errorCode: TCL LOOKUP ENCODING tis-620
==== encoding-28.0 FAILED
==== encoding-12.7 cp864 [ecafd8611d] FAILED
==== Contents of test case:
encoding convertfrom cp864 \xA7
---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: unexpected byte sequence starting at index 0: '\xA7'
while executing
"encoding convertfrom cp864 \xA7"
("uplevel" body line 2)
invoked from within
"uplevel 1 $script"
---- errorCode: TCL ENCODING ILLEGALSEQUENCE 0
==== encoding-12.7 FAILED
...
apnadkarni added on 2025-08-25 16:25:44:
Don, As seen below your build recipe works fine for me on Ubuntu 20 under WSL. It's more or less identical to one of the configs I tried before but just to be sure. I do notice one difference though. What is the purpose of the path /home/dgp/lib in your test? Where is it coming from and what does it contain? Can you eliminate that just to eliminate one variation?
sebres added on 2025-08-25 16:48:40:
@dgp, does [9338fcde504fb307] changes something in the tests, Don? (I mean if Tcl_GetEncoding fails for whatever reason, and the error would not be silently swallowed now, so could now bring more insides). By the way, like Ashok, I can't reproduce it on my linux boxes either. dgp added on 2025-08-25 18:21:54:
The checkin now on the tip of the core-9-0-branch doesn't change this failure. No opinion whether or not to keep it. dgp added on 2025-08-25 18:23:13:
When I see the failing tests, I am picking up a set of encoding files from a very old install, sometime around 2018. dgp added on 2025-08-25 18:35:25:
Relative to where I make most Tcl installs, I see $installDir/lib/tcl9.0/encoding and the contents of that directory are files placed there in 2018. That's even earlier than the release of Tcl 9.0a1, so it's some litter from pre-release development testing. I expect that if I clean up my disk drive the symptoms will disappear. Under the Tcl 8 patterns of development, those old files would have been overwritten by corrected/updated versions in installs of later Tcl 9.0 releases. But Tcl 9.0 has shifted to placing these files in the zip archive, so such installs don't happen anymore. Starting with [0433b67adc], `make test` is finding and using this litter, when it hasn't done so recently before that. The checkin somehow started looking there and using what is found there. If there's a sound reason for that, let's understand it. If not, lets find and destroy searches that might find what can only be wrong answers. dgp added on 2025-08-25 19:54:43:
Is this the place where the *.enc files get installed after a --disable-zipfs build?? Why would my --disable-shared --enable-zipfs build be searching there? sebres added on 2025-08-25 22:07:35:
Looks like a weird side effect to me. What would you see with this one (started from build folder): LD_LIBRARY_PATH=. TCL_LIBRARY="" ./tcltest <<<'puts [encoding dirs]' In particular do you see some other path before "//zipfs:/app/tcl_library/encoding" (for zipfs) or "${pwd}/../library/encoding" (for build without zipfs)? Something like: /usr/local/lib/tcl9.0/encoding ${pwd}/../library/encoding (or whatever the $installDir you mentioned)? Strange is, even if I create such directory (and put there some old encodings, e. g. from core-9-0-a1-rc), I cannot reproduce the errors in "encoding.test" anyway (no matter with or without zipfs, disabled shared or not). And I don't know what shall be so different there to force that tests fail. Moreover in build with zipfs, I see only one path in `encoding dirs` - "//zipfs:/app/tcl_library/encoding" (and since zipfs uses `Tcl_SetEncodingSearchPath` to set search path, it shall be basically sole path only). By the way, does this one returns 1? LD_LIBRARY_PATH=. ./tcltest <<<'puts [file exists "//zipfs:/app/tcl_library/encoding"]' sebres added on 2025-08-25 23:08:00:
OK, ultimately found that - [9ade301dd7122d21] shall fix it now. It avoids too earlier search for tcl-library - previously TclZipfsLocateTclLibrary has been invoked before mount of zipfs, so obviously can't find something at time point if TclZipfs_AppHook got called, and since it is called once (and not as before [0433b67adc] for any interpreter), it wouldn't initialize properly (misses a lot of inits, inclusive invocation of TclZipfsInitEncodingDirs). apnadkarni added on 2025-08-25 23:39:10:
As I mentioned on the online meet, I had the feeling Don having reported this with an earlier commit as well. Found it way back in 2023 - https://core.tcl-lang.org/tcl/tktview/894e11d7f73023089a8f. Though on the core-8-branch, essentially the same issue with outdated encodings being found. Not that it matters, but I think this issue has existed for some time and triggers under some specific circumstances. FWIW, I really do not like Tcl's propensity of "searching" for file locations, whether at build time (headers, libraries), during test, or runtime (scripts, encodings). The current error is an example but not the only one. Many users also run into this issue, finding the wrong headers or libraries while trying to build Tcl. IMO Tcl should never be searching and randomly picking up something that *might* be the right file(s). Look in a fixed location or if not found there have the user specify the location via a configure option, environment variable etc. apnadkarni added on 2025-08-26 00:11:07:
Sergey, that change seems to break --enable-shared builds. Compare before
with after
Not verified but I think it is because the
it never checks for zipfs inside the DLL, not the main exe. The test suite does not catch this because of Tcl's "helpful" nature of searching for a tcl library, it searches and finds it in the source repository when it should have really complained of not finding one in a zipfs. Working on it ... apnadkarni added on 2025-08-26 05:58:34:
I have committed a "tactical" fix in the apn-missing-shlib-zipfs-check branch. Please review. It's tactical because I think more changes are really needed for a more robust fix. Currently the mounting of zipfs archives and search for jan.nijtmans added on 2025-08-26 08:20:21:
> Sergey, that change seems to break --enable-shared builds That - indeed - broke the build. So I moved this commit to Ashok's "apn-missing-shlib-zipfs-check" branch (which also contains a fix for this breakage). @Sergey, please don't commit directly to core-9-0-branch/trunk unless you are 1000% sure it won't break anything .... @Ashok, as soon as your fixes are complete, merging it to core-9-0-branch and trunk should be possible without problems. sebres added on 2025-08-26 10:37:34:
1000%?! This game can be played by many persons, Jan. Let's count who and how often broke the mainline branches. apnadkarni added on 2025-08-27 07:30:56:
Jan wrote
@All I think prefer the fix I am working on in the apn-early-zipfs-mounts branch rather than apn-missing-shlib-zipfs-check because I really do not like the current intermixing of zipfs mounts and tcl_library searches that results in configuration and environment related outcomes. zipfs mounts, just like file system mounts, should happen before anything else. it makes reasoning simpler and behaviour more predictable and well defined. That branch (still needs to go through CI) changes are a little more widespread so needs review so it would be a couple of days. @Sergey, as an aside... I think Jan's comment about working on main trunk was really just an outcome of the last online meet where breakage from several direct commits to trunk (not by you! I know this was an exception and you almost always work in branches) so it was felt reminders when it happens might be useful. Your willingness to dive into bugs, even those not of your making :-), as well as review code is hugely appreciated. apnadkarni added on 2025-08-27 07:46:36:
@Don, it has been bothering me as to why this issue is exhibited only on your system. I think I have a plausible explanation. It was a combination of - the bug introduced by my commit (that Sergey fixed) related to zipfs initialization in static build case, and - what I would suggest is a misfeature (by design) in the initialization script's use of `pkgconfig scriptdir,runtime` The init script constructs a list of directories to search for the init.tcl file. One of these is the directory returned by `pkgconfig get scriptdir,runtime`. Because the zipfs failed to locate tcl_library early enough, and the `TCL_LIBRARY` env is not set (by design when running make test with zipfs), the test run lands up using the path returned by `pkgconfig` command. The problem is that the `scriptdir,runtime` value reflects the path **after installation** (presumably the value of `-prefix` or friends). When you ran test-tcl, it was picking up the path where the build would be installed and that path happened to contain the old encodings. That's my **theory** anyways. That pkgconfig value is pretty useless as on Windows it never reflects reality and is broken in various scenarios even on Unix, like the one above. apnadkarni added on 2025-08-27 11:21:36:
@dgp, now that it has passed github CI, could you verify the apn-early-zipfs-mounts branch to check that it works correctly in your failure scenario? apnadkarni added on 2025-09-01 03:04:26:
Closing. @dgp, please if you are still experiencing issues, please re-open or log a separate ticket. dgp added on 2025-09-02 17:20:27:
I can confirm that the tests no longer fail on the current tip of the core-9-0-branch. Thanks! From the comments it looks like there is a remaining bug, which I will open in a new ticket. | ||||
