tzset() locks in Linux after fork()

M

mike

I've got an issue with an application I've developed that uses a
combination of threads (pthreads) and fork()'d processes.

In short, my parent process has a handful of threads that run to
perform various tasks; when a new request comes into the parent
process, it forks off a new child to handle the request; one of the
first things the child does is initiate a library that has a date
class that calls tzset().

My parent process also logs the requests from a thread, which writes
the date to the log using localtime_r(), which also calls tzset().

The problem, is that one of out every x child processes fork()'d
simply hangs at a lock in tzset() when the library is initialized.

I've reproduced this by creating a very simple test program, that
simply calls tzset() over and over from a thread, while it forks, and
then calls tzset() in the child process- I can get it to hang almost
immediately.

and this only seems to happen on my Linux machines (CentO/S 5.5)- my
FreeBSD (8.0) runs my test program file, without any locks.

So my question is: if I fork() while tzset() is holding a lock in the
parent process, will the lock get copied to the child process locked?
Is this a known result? is there a way around this?

Test program, which is just a super simple version of what I see in my
real app, and gdb output below, which is when I attached to a child
process that had hung.

Mike

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

static void* setter(void*)
{
while(1)
{
tzset();
}

return NULL;
}

int main(void)
{
pthread_t thread;
pthread_create(&thread, NULL, &setter, NULL);

signal(SIGCHLD, SIG_IGN);

while(1)
{
switch(fork())
{
case 0:
{
fprintf(stderr, "CHILD >> tzset()\n");
tzset();
exit(1);
}
break;
case -1:
{
fprintf(stderr, ">> failed to fork()
\n");
}
break;
default:
{
;
}
}

usleep(1000);
}

return 0;
}

Reading symbols from /home/mike/thr/test...done.
Attaching to program: /home/mike/thr/test, process 8397
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols
found)...done.
[Thread debugging using libthread_db enabled]
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /usr/lib64/libstdc++.so.6...(no debugging symbols
found)...done.
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols
found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols
found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols
found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging
symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x0000003512cdfade in __lll_lock_wait_private () from /lib64/libc.so.6
(gdb) bt
#0 0x0000003512cdfade in __lll_lock_wait_private () from /lib64/
libc.so.6
#1 0x0000003512c8d20b in _L_lock_1920 () from /lib64/libc.so.6
#2 0x0000003512c8d0f1 in tzset () from /lib64/libc.so.6
#3 0x0000000000400811 in main () at test.cpp:32
(gdb) info threads
* 1 Thread 0x2ab3c2e9dbb0 (LWP 8397) 0x0000003512cdfade in
__lll_lock_wait_private () from /lib64/libc.so.6
 
J

Jorgen Grahn

I've got an issue with an application I've developed that uses a
combination of threads (pthreads) and fork()'d processes.
[...]

Others have replied with advice, but in general you're much better off
posting to ... whatever Unix or Linux programming group has the best
audience.

This is comp.lang.c++, your program seems to be a traditional C
program, and the question would be offtopic even in comp.lang.c.

(Your question was interesting and well formulated though.)

/Jorgen
 
M

Mike Pultz

Sam has already answered your immediate question. Just a few more notes.

In Linux, the threads are implemented as processes sharing the same
memory space. When you call fork() in a multithreaded program, you will
get a copy of the current thread and a copy of the whole memory space.
This means that the memory space has lots of inaccessible stuff inside
it, like dynamic allocations from other threads, stacks of the other
threads etc. Carrying this garbage along is just not clean even if the
program could be otherwise put to work. And yes, this inaccessible memory  
can contain things like acquired user-space locks.

So yes, if you want to clone your multithreaded process, then it would be
wise to exec() yourself immediately after fork(), possibly with some
special command-line arguments showing that you are starting an internal
child, and passing over any needed information.

Thanks everyone- unfortunately, that's what I thought-

I would just use threads for everything, and no fork(), but the stupid
library I'm using uses lots of global static variables, and even has
some cases where it exit()'s from inside methods!!

I considered using execv() as well, but I'm using a socketpair() for
IPC, so it would make that pretty tough.

I guess I was just hoping for some magical "un-lock-everything()"
function I could call after I fork()'d ;)

Jorgen- you are right- this is Linux specific, though I had a tough
time finding a good (competent) Linux C group- figured I'd ask here.

Cheers,

Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,222
Members
46,810
Latest member
Kassie0918

Latest Threads

Top