* dcrt0.cc (child_info_fork::alloc_stack_hard_way): Change sense of guard test.

Increase size of stack reserved and increase size before the current stack
pointer.  Use pointers when doing arithmetic.
(dll_crt0_1): Initialize exception handler when we notice we're the child of a
fork from non-main thread.
* fork.cc (frok::parent): Make argument volatile.
(frok::child): Ditto.
(lock_signals): New class.
(lock_pthread): Ditto.
(hold_everhthing): Ditto.
(frok::parent): Move atforkprepare and atforkparent to lock_pthread class.
(fork): Make ischild boolean.  Use hold_everything variable within limited
scope to set various mutexes in such a way as to avoid deadlocks.
* thread.h (pthread_mutex::tid): New variable, active when debugging for
tracking thread id of owner.
(pthread_mutex::set_owner): Set tid when debugging.
* thread.cc (pthread_mutex::pthread_mutex): Clear tid.
(pthread_mutex::_unlock): Ditto when unlocking.
(pthread_mutex::fixup_after_fork): Set tid to special value after forking since
owner is unknown.
This commit is contained in:
Christopher Faylor
2007-02-22 12:34:55 +00:00
parent bd8f891e8a
commit 39fc0d36ae
5 changed files with 171 additions and 55 deletions

View File

@ -45,11 +45,87 @@ class frok
const char *error;
int child_pid;
int this_errno;
int __stdcall parent (void *esp);
int __stdcall child (void *esp);
int __stdcall parent (volatile char * volatile here);
int __stdcall child (volatile char * volatile here);
friend int fork ();
};
class lock_signals
{
bool worked;
public:
lock_signals ()
{
worked = sig_send (NULL, __SIGHOLD) == 0;
}
operator int () const
{
return worked;
}
void dont_bother ()
{
worked = false;
}
~lock_signals ()
{
if (worked)
sig_send (NULL, __SIGNOHOLD);
}
};
class lock_pthread
{
bool bother;
public:
lock_pthread (): bother (1)
{
pthread::atforkprepare ();
}
void dont_bother ()
{
bother = false;
}
~lock_pthread ()
{
if (bother)
pthread::atforkparent ();
}
};
class hold_everything
{
bool& ischild;
/* Note the order of the locks below. It is important,
to avoid races, that the lock order be preserved.
pthread is first because it serves as a master lock
against other forks being attempted while this one is active.
signals is next to stop signal processing for the duration
of the fork.
process is last. If it is put before signals, then a deadlock
could be introduced if the process attempts to exit due to a signal. */
lock_pthread pthread;
lock_signals signals;
lock_process process;
public:
hold_everything (bool& x): ischild (x) {}
operator int () const {return signals;}
~hold_everything()
{
if (ischild)
{
pthread.dont_bother ();
process.dont_bother ();
signals.dont_bother ();
}
}
};
static void
resume_child (HANDLE forker_finished)
{
@ -92,7 +168,7 @@ sync_with_parent (const char *s, bool hang_self)
}
int __stdcall
frok::child (void *)
frok::child (volatile char * volatile here)
{
HANDLE& hParent = ch.parent;
extern void fixup_hooks_after_fork ();
@ -213,7 +289,7 @@ slow_pid_reuse (HANDLE h)
#endif
int __stdcall
frok::parent (void *stack_here)
frok::parent (volatile char * volatile stack_here)
{
HANDLE forker_finished;
DWORD rc;
@ -224,8 +300,6 @@ frok::parent (void *stack_here)
pinfo child;
static char errbuf[256];
pthread::atforkprepare ();
int c_flags = GetPriorityClass (hMainProc);
debug_printf ("priority class %d", c_flags);
@ -271,7 +345,7 @@ frok::parent (void *stack_here)
ch.forker_finished = forker_finished;
ch.stackbottom = _tlsbase;
ch.stacktop = stack_here;
ch.stacktop = (void *) stack_here;
ch.stacksize = (char *) ch.stackbottom - (char *) stack_here;
debug_printf ("stack - bottom %p, top %p, size %d",
ch.stackbottom, ch.stacktop, ch.stacksize);
@ -492,7 +566,6 @@ frok::parent (void *stack_here)
ForceCloseHandle (forker_finished);
forker_finished = NULL;
pthread::atforkparent ();
return child_pid;
@ -516,14 +589,13 @@ extern "C" int
fork ()
{
frok grouped;
MALLOC_CHECK;
debug_printf ("entering");
grouped.first_dll = NULL;
grouped.load_dlls = 0;
int res;
int ischild;
bool ischild = false;
myself->set_has_pgid_children ();
@ -535,30 +607,28 @@ fork ()
return -1;
}
lock_process now;
if (sig_send (NULL, __SIGHOLD))
{
if (exit_state)
Sleep (INFINITE);
set_errno (EAGAIN);
return -1;
}
{
hold_everything held_everything (ischild);
ischild = setjmp (grouped.ch.jmp);
if (!held_everything)
{
if (exit_state)
Sleep (INFINITE);
set_errno (EAGAIN);
return -1;
}
void *esp;
__asm__ volatile ("movl %%esp,%0": "=r" (esp));
ischild = !!setjmp (grouped.ch.jmp);
if (ischild)
{
res = grouped.child (esp);
now.dont_bother ();
}
else
{
volatile char * volatile esp;
__asm__ volatile ("movl %%esp,%0": "=r" (esp));
if (!ischild)
res = grouped.parent (esp);
sig_send (NULL, __SIGNOHOLD);
}
else
res = grouped.child (esp);
}
MALLOC_CHECK;
if (ischild || res > 0)