From 3fb596eba977c4239cfa36c295080a52952d4161 Mon Sep 17 00:00:00 2001 From: Hector Romojaro Date: Thu, 11 Sep 2025 10:09:36 +0200 Subject: [PATCH] Fix memory leak by reworking ExitHandler conditional call to Tcl_Release(interp) This fix reworks the previous change[1] to avoid Tcl_AsyncDelete errors[2] while keeping the memory consumption low, by calling Tcl_Release(interp) only from the thread that owns "interp", instead of only when executed on the main thread. [1] 301c760fa0086cb629d09b9750e1a2287d123eef [2] such as "Fatal: Tcl_AsyncDelete: async handler deleted by the wrong thread" --- generic/nsf.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/generic/nsf.c b/generic/nsf.c index c343ab2c..dbaa1827 100644 --- a/generic/nsf.c +++ b/generic/nsf.c @@ -247,9 +247,6 @@ static Tcl_ExitProc ExitHandler; #if defined(TCL_THREADS) static Tcl_ExitProc Nsf_ThreadExitProc; -#if !defined(_WIN32) && !defined(_WIN64) -static pthread_t main_thread_id = 0; -#endif #endif @@ -35940,6 +35937,9 @@ ExitHandler(ClientData clientData) { Tcl_Interp *interp = (Tcl_Interp *)clientData; int flags; NsfRuntimeState *rst; +#if defined(TCL_THREADS) && !defined(_WIN32) && !defined(_WIN64) + Tcl_ThreadId interpThreadId = Tcl_Interp_threadId(interp); +#endif nonnull_assert(clientData != NULL); @@ -36083,15 +36083,15 @@ ExitHandler(ClientData clientData) { #if defined(TCL_THREADS) && !defined(_WIN32) && !defined(_WIN64) /* - * The interpreter of the main thread has to be deleted by Tcl. Otherwise we + * Delete interpreters only by the thread that owns them. Otherwise we * see fatal errors like the following from NaviServer, when exiting from * the call "nsd -c": * * Fatal: Tcl_AsyncDelete: async handler deleted by the wrong thread */ - /*fprintf(stderr, "+++ ExiHandler interp %p main_thread_id %p current %p\n", - (void*)interp, (void*) main_thread_id, pthread_self());*/ - if (main_thread_id == pthread_self()) { + /*fprintf(stderr, "+++ ExitHandler interp %p current %p owned by %p\n", + (void*)interp, pthread_self(), interpThreadId);*/ + if (pthread_equal((pthread_t)interpThreadId, pthread_self()) != 0) { Tcl_Release(interp); } #else @@ -36245,18 +36245,6 @@ Nsf_Init( */ NsfMutexLock(&initMutex); -#if defined(TCL_THREADS) && !defined(_WIN32) && !defined(_WIN64) - /* - * Keep the main thread id. Actually, we assume that the first thread is the - * main thread, although there is no guarantee for this. However, this - * avoids fatal errors when used in NaviServer. - */ - if (main_thread_id == 0) { - main_thread_id = pthread_self(); - /*fprintf(stderr, "+++ Init interp %p main_thread %p\n", (void*)interp, (void*) main_thread_id);*/ - } -#endif - Nsf_OT_byteCodeType = Tcl_GetObjType("bytecode"); assert(Nsf_OT_byteCodeType != NULL);