Help with pthread_create not returning

Hi, in the LCDproc project we have a driver which uses pthread_create to spin off a new thread. For a reason I don't understand, that call does never returns neither success nor any error.

Even threading code that does work well as a standalone program does not work when copied into the driver, which is a shared library.

The file in question can be found here: http://lcdproc.cvs.sourceforge.net/viewvc/lcdproc/lcdproc/server/drivers/lis.c

The thread's start_routine is at line 290 (lis_read_thread) and the thread creation is at line 514, copied here for reference (but please look at the complete file linked to above):

Code:
int
lis_read_thread(void *arg)
{
	Driver *drvthis;
	PrivateData *p;
	char unsigned buffer[64];
	int size;

	drvthis = (Driver *)arg;
	p = (PrivateData *) drvthis->private_data;

	while(! p->child_flag) {
		for (size = ftdi_read_data(&p->ftdic, buffer, 64); size > 0; size = ftdi_read_data(&p->ftdic, buffer, 64))
			;
		if (size < 0) {
			p->parent_flag = 0;
			return size;
		}
	}
	p->parent_flag = 0;
	return 0;
}

// Later in init...

	// create a thread to keep a read up on the device
	err = pthread_create(	&read_thread,
				NULL,
				(void *) lis_read_thread,
				drvthis
				);
	if (err) {
		report(RPT_ERR, "%s: pthread_create() - %s", drvthis->name, strerror(err));
		goto err_framebuf;
	}
	report(RPT_INFO, "%s: read thread created");
	p->parent_flag = 1;		// show we're now a happy parent, birth successful.

The start_routine should probably use pthread_exit and may be wrong by trying to return an int, but that's not the question here.

Please, have a look at it and help us getting this to work on FreeBSD. Thank you!

System is FreeBSD 7.3-RELEASE.


Additionally I found that other drivers linked against pthread do not work, e.g. xosd:

http://lcdproc.cvs.sourceforge.net/viewvc/lcdproc/lcdproc/server/drivers/xosdlib_drv.c
 
SirDice said:
I'm by no means a kernel hacker but as far as I understood it you cannot reference functions from (external) libraries from within the kernel (that includes drivers).

The 'driver' I am talking about is a userland shared library, not a kernel driver. So it should work.

I suspect some problem with calling phtread_create from within a shared library, but I have no idea what might be the problem.
 
There is a lot of autotools magic involved. At the end this it what make runs:
Code:
darwimy@kirika:drivers> make
gcc -DHAVE_CONFIG_H -I. -I../..  -I../..  -I/usr/local/include   -I/usr/local/include   -fPIC -Wall  -g -O -Wno-unused-function -MT lis-
lis.o -MD -MP -MF .deps/lis-lis.Tpo -c -o lis-lis.o `test -f 'lis.c' || echo './'`lis.c
mv -f .deps/lis-lis.Tpo .deps/lis-lis.Po
gcc -I/usr/local/include   -I/usr/local/include   -fPIC -Wall  -g -O -Wno-unused-function -shared  -o  lis.so lis-lis.o libLCD.a -L/usr/local/lib 
-lftdi -lusb   -L/usr/local/lib -lusb   -lpthread libbignum.a -lkvm
 
Is the program running in an environment which allows multiple threads?

pthread_create should return. And probably asynchronously. And depending on your platform, maybe even before the child begins running (or vice versa). Likely the return code from the function would be set by the system/compiler and not the child thread. Did you intend to have the child thread end with pthread_exit?

Also, it's not clear to me without seeing the actual pointer values if the private data is really private. does p->parent_flag point to the same location in memory for both parent & child threads?
 
qsecofr said:
Is the program running in an environment which allows multiple threads?

What do you mean by this? Do I have to configure FreeBSD to allow use of pthreads? Currently I'm running it on a stock 7.3-RELEASE installation (without any sysctl or login profiles configured).

pthread_create should return. And probably asynchronously. And depending on your platform, maybe even before the child begins running (or vice versa). Likely the return code from the function would be set by the system/compiler and not the child thread. Did you intend to have the child thread end with pthread_exit?

The return code of the child thread doesn't matter. And according to pthread_create(3) pthread_exit is called implicitly. The child thread is intended to run until the main program terminates.

Also, it's not clear to me without seeing the actual pointer values if the private data is really private. does p->parent_flag point to the same location in memory for both parent & child threads?

I will have to verify. But the code doesn't work even if I remove any use of p.
 
If the exit status of the child is inconsequential, then an option is to create the child thread in a detached state. The use of return values in the child, as coded, made me wonder of the intent.

The pthread_cancel function might be a more readable way to end a child thread, rather than relying on a seemingly unsynchronized read to a location in memory. And both parent and child refer to
Code:
&p->ftdic
which if by chance is the same memory location, might benefit from a mutex lock.

The child thread has what appears to be a pretty tight for loop. Is it possible that a yield at the end of each iteration might benefit the overall process?

And since there doesn't appear to be any explicit synchronization, I wouldn't assume the child did not become runnable, run, and exit before the parent thread got processor timeslice again.

I've written a few pthreads applications on a system where processes by default were single-thread capabale. The jobs needed to be defined explicitly for multi-threading. Though in such cases, the return code was EBUSY or similar. But as SirDice noted, there's points in FreeBSD where that may fail as well. Just thought to ask was all..

A threaded debugger which lets you step line by line through the whole application, from thread to thread as they run, will probably help you most. All that said, it's not yet clear to me why the function call seems not to return to the main thread - unless there's some sort of hidden deadlock or unyielding behavior.
 
Years ago the whole code used clone(2) which is Linux specific. Someone then changed it to use pthread. This is why it's written that strange.

However, I found that other programs suffer from the same problem. For example the misc/xosd library does not initialize when used within one of our 'drivers' as well. It does use pthreads, too.
 
Using a device which noone has access to will probably lead us nowhere.

However, the xosd 'driver' seems to suffer from the same problem.

Upon running LCDd with the xosd driver, I see the process shown in the 'umtxn' state in top.

Running procstat -kk gives the following output:
Code:
  PID    TID COMM             TDNAME           KSTACK                       
13842 100092 LCDd             initial thread   mi_switch+0x146 sleepq_switch+0xcb 
sleepq_catch_signals+0x2fc sleepq_wait_sig+0xd _sleep+0x2db do_lock_umutex+0x5c2 
do_lock_umutex+0x4d __umtx_op_wait_umutex+0x50 _umtx_op+0x27 syscall+0x335 
Xint0x80_syscall+0x20

Anyone may try:
  • Install xosd from misc/xosd.
  • Download the latest LCDproc night tarball.
  • Untar, change into the extracted directory and run % ./configure --enable-drivers=all --enable-debug
    % make
  • Open LCDd.conf and edit the section [xosd] at the end of the file. Put in a Font available on your system, e.g. '-*-terminal-*-r-*-*-*-140-*-*-*-*-*-1'.
  • % server/LCDd -c LCDd.conf -r 4 -f
You will see the process hanging.
 
The problem is likely that libpthread does not support being loaded into a process after it has already started. You can work around this by making sure the main program is also linked with libpthread.

Note that adding a thread to a single-threaded program is not fully transparent to that program; in particular, a new thread may run signal handlers for the same signals as its parent thread (unless special arrangements are made, see pthread_sigmask(3)), which may not agree with what the program expects.
 
jilles@ said:
The problem is likely that libpthread does not support being loaded into a process after it has already started. You can work around this by making sure the main program is also linked with libpthread.

That did the trick. After adding pthreads to LDADD for the main program it does now start up.

The next steps are to find out how to get this into the autotools stuff and how to program the thread properly.
 
Back
Top