Tcl Source Code

View Ticket
Login
Ticket UUID: 05d9a601e45db736729ed5b7a260dd675f245bb4
Title: Memory leak with creating and destroying threads from C
Type: Bug Version: 8.6.1
Submitter: rkeene Created on: 2016-04-04 16:16:55
Subsystem: 49. Threading Assigned To: nobody
Priority: 5 Medium Severity: Important
Status: Open Last Modified: 2016-05-13 13:11:26
Resolution: None Closed By: nobody
    Closed on: 2016-04-04 16:29:28
Description:
Creating threads from C and then creating a Tcl interpreter in those threads, then destroying the Tcl interpreter and then destroying the thread leaks memory.  

Simple demo attached.   This eventually exhausts all RAM on the system despite not creating many concurrent interpreters.
User Comments: gustafn2 (claiming to be Gustaf Neumann) added on 2016-05-13 13:11:26:
You should probably create the thread via Tcl_CreateThread() as shown in your updated program below. For me, this does not appear to leak.

-gustaf


============================================================================
#include <stdatomic.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <tcl.h>

_Atomic unsigned long long threadCount = 0;
_Atomic unsigned long long interpCount = 0;

static Tcl_ThreadCreateProc threadRunner;
static Tcl_ThreadCreateType threadRunner(ClientData clientData) {
	Tcl_Interp *interp;
	int tclReturnValue;

	threadCount++;

	interp = Tcl_CreateInterp();

	interpCount++;

	tclReturnValue = Tcl_Init(interp);
	if (tclReturnValue != TCL_OK) {
		fprintf(stderr, "warning: TclInit() failed.\n");
	}

	Tcl_DeleteInterp(interp);

	interpCount--;

	Tcl_FinalizeThread();

	threadCount--;
}

int main(int argc, char **argv) {
	Tcl_ThreadId threadId;
	struct timespec sleepTime;
	int pthreadCreateRet;
	int idx = 0;
	int tclMajor, tclMinor, tclPatchLevel;

	Tcl_FindExecutable(argv[0]);

	Tcl_GetVersion(&tclMajor, &tclMinor, &tclPatchLevel, NULL);

	printf("Testing Tcl version %i.%i.%i\n", tclMajor, tclMinor, tclPatchLevel);

	for (idx = 0;; idx++) {
		if ((idx % 1024) == 0) {
			idx = 0;

			printf("Thread Count: %llu, Interp Count: %llu\n", threadCount, interpCount);
		}

		pthreadCreateRet = Tcl_CreateThread(&threadId, threadRunner, NULL, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS);

		if (pthreadCreateRet != 0) {
			fprintf(stderr, "error: pthread_create() failed, exiting.\n");

			return(1);
		}

		sleepTime.tv_sec = 0;
		sleepTime.tv_nsec = 1000000;
		nanosleep(&sleepTime, NULL);
	}

	return(0);
}

rkeene added on 2016-04-04 19:20:56:
I have not tried it from tclsh since it wouldn't be relevant to my problem -- I'm creating threads from C and I cannot change that.

sebres added on 2016-04-04 18:58:53:
Sorry (input eaten, thick fingers)))

So again: no, I cannot reproduce this leak (currently still have not tried your code, but neither in tclsh with thread 2.7 module, nor within my multithreaded apps)...

Have you tried my example with tclsh?

rkeene added on 2016-04-04 18:22:59:
What ?

> I can reproduce a leak with our thread module and from my multithreaded apps (don't tried your code)...

So you can reproduce this leak ?

sebres added on 2016-04-04 18:12:29:
I can reproduce a leak with our thread module and from my multithreaded apps (don't tried your code)...

Possibly, you have something wrong in your c-code (resp. you miss something that does Tcl_AppInit, Tcl_Main, other subsystems resp. some events in Tcl_init, etc.)...

For example did you still have a leak, if you will create a new thread with "Tcl_CreateThread" instead of "pthread_create"?

Second, simply test it using thread module, just from your tclsh like here:

load thread27
time { puts [time { thread::create {thread::release} } 1000] } 10

As already said, I see no leak at all, if I do that with current 8.5, 8.6 and 8.7 (trunk)...

rkeene added on 2016-04-04 17:22:52:
No change with Tcl 8.6.5.

I did update the source to print the version number (via Tcl_GetVersion()), exit early if pthread_create() fails, and call Tcl_FindExecutable(argv[0]) -- updated source attached.



++ x86_64-generic-linux-gnu-gcc -std=gnu11 -o test-leak test-leak.c -Wall -pthread -ltcl8.6 -I./tcl8.6.5/INST/include -L./tcl8.6.5/INST/lib -Wl,-R,/home/rkeene/devel/appfs/build/tcl8.6.5/INST/lib '-DPACKAGE_NAME="tcl"' '-DPACKAGE_TARNAME="tcl"' '-DPACKAGE_VERSION="8.6"' '-DPACKAGE_STRING="tcl 8.6"' '-DPACKAGE_BUGREPORT=""' -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_PARAM_H=1 -DUSE_THREAD_ALLOC=1 -D_REENTRANT=1 -D_THREAD_SAFE=1 -DHAVE_PTHREAD_ATTR_SETSTACKSIZE=1 -DHAVE_PTHREAD_ATFORK=1 -DTCL_THREADS=1 '-DTCL_CFGVAL_ENCODING="iso8859-1"' -DHAVE_ZLIB=1 '-DMODULE_SCOPE=extern __attribute__((__visibility__("hidden")))' -DHAVE_HIDDEN=1 -DHAVE_CAST_TO_UNION=1 '-DTCL_SHLIB_EXT=".so"' -DNDEBUG=1 -DTCL_CFG_OPTIMIZED=1 -DTCL_TOMMATH=1 -DMP_PREC=4 -D_LARGEFILE64_SOURCE=1 -DTCL_WIDE_INT_IS_LONG=1 -DHAVE_GETCWD=1 -DHAVE_MKSTEMP=1 -DHAVE_OPENDIR=1 -DHAVE_STRTOL=1 -DHAVE_WAITPID=1 -DHAVE_GETNAMEINFO=1 -DHAVE_GETADDRINFO=1 -DHAVE_FREEADDRINFO=1 -DHAVE_GAI_STRERROR=1 -DHAVE_STRUCT_ADDRINFO=1 -DHAVE_STRUCT_IN6_ADDR=1 -DHAVE_STRUCT_SOCKADDR_IN6=1 -DHAVE_STRUCT_SOCKADDR_STORAGE=1 -DHAVE_GETPWUID_R_5=1 -DHAVE_GETPWUID_R=1 -DHAVE_GETPWNAM_R_5=1 -DHAVE_GETPWNAM_R=1 -DHAVE_GETGRGID_R_5=1 -DHAVE_GETGRGID_R=1 -DHAVE_GETGRNAM_R_5=1 -DHAVE_GETGRNAM_R=1 -DHAVE_GETHOSTBYNAME_R_6=1 -DHAVE_GETHOSTBYNAME_R=1 -DHAVE_GETHOSTBYADDR_R_8=1 -DHAVE_GETHOSTBYADDR_R=1 -DHAVE_TERMIOS_H=1 -DHAVE_SYS_IOCTL_H=1 -DHAVE_SYS_TIME_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_GMTIME_R=1 -DHAVE_LOCALTIME_R=1 -DHAVE_MKTIME=1 -DHAVE_TM_GMTOFF=1 -DHAVE_TIMEZONE_VAR=1 -DHAVE_STRUCT_STAT_ST_BLOCKS=1 -DHAVE_STRUCT_STAT_ST_BLKSIZE=1 -DHAVE_BLKCNT_T=1 -DHAVE_INTPTR_T=1 -DHAVE_UINTPTR_T=1 -DHAVE_SIGNED_CHAR=1 -DHAVE_LANGINFO=1 -DHAVE_MKSTEMPS=1 -DHAVE_FTS=1 -DHAVE_SYS_IOCTL_H=1 -DTCL_UNLOAD_DLLS=1 -DHAVE_CPUID=1
Testing Tcl version 8.6.5
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 0, Interp Count: 0
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 0, Interp Count: 0
error: pthread_create() failed, exiting.

sebres added on 2016-04-04 16:29:28:

Fixed and merged in trunk 2014-04-23: see http://core.tcl.tk/tcl/tktview?name=3493120 Please verify again with current 8.6


rkeene added on 2016-04-04 16:19:24:
Output of test-leak (compiled as: gcc -std=gnu11  -o test-leak test-leak.c -Wall -pthread -ltcl -I/usr/include  -L/usr/lib64):

$ ./test-leak 
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 2, Interp Count: 2
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 0, Interp Count: 0
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Thread Count: 1, Interp Count: 1
Tcl_InitNotifier: unable to start notifier thread
Aborted
$

Attachments: