Cygwin: sigproc: Allow more child processes per process
256 children per process is a bit tight in some scenarios. Fix this by revamping the `procs' array. Convert it to an extensible class child_procs and rename procs to chld_procs. Fix code throughout to use matching class methods rather than direct access. To allow a lot more child processes while trying to avoid allocations at DLL startup, maintain two arrays within class child_procs, one using a default size for 255 (i686) or 1023 (x86_64) children, the other, dynamically allocated on overflowing the first array, giving room for another 1023 (i686) or 4095 (x86_64) processes. On testing with a simple reproducer on a x86_64 machine with 4 Gigs RAM, a system memory overflow occured after forking about 1450 child processes, so this simple dynamic should suffice for a while. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
163daed37c
commit
7c963c7ba0
@ -39,8 +39,6 @@ enum child_status
|
|||||||
/* Change this value if you get a message indicating that it is out-of-sync. */
|
/* Change this value if you get a message indicating that it is out-of-sync. */
|
||||||
#define CURR_CHILD_INFO_MAGIC 0xecc930b9U
|
#define CURR_CHILD_INFO_MAGIC 0xecc930b9U
|
||||||
|
|
||||||
#define NPROCS 256
|
|
||||||
|
|
||||||
#include "pinfo.h"
|
#include "pinfo.h"
|
||||||
struct cchildren
|
struct cchildren
|
||||||
{
|
{
|
||||||
|
@ -38,19 +38,55 @@ int __sp_ln;
|
|||||||
|
|
||||||
bool no_thread_exit_protect::flag;
|
bool no_thread_exit_protect::flag;
|
||||||
|
|
||||||
char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to
|
/* Flag to sig_send that signal goes to current process but no wait is
|
||||||
// current process but no wait is required
|
required. */
|
||||||
|
char NO_COPY myself_nowait_dummy[1] = {'0'};
|
||||||
|
|
||||||
#define Static static NO_COPY
|
#define Static static NO_COPY
|
||||||
|
|
||||||
|
/* All my children info. Avoid expensive constructor ops at DLL startup */
|
||||||
|
class child_procs {
|
||||||
|
#ifdef __i386__
|
||||||
|
static const int _NPROCS = 255;
|
||||||
|
static const int _NPROCS_2 = 1023;
|
||||||
|
#else
|
||||||
|
static const int _NPROCS = 1023;
|
||||||
|
static const int _NPROCS_2 = 4095;
|
||||||
|
#endif
|
||||||
|
int _count;
|
||||||
|
uint8_t _procs[(_NPROCS + 1) * sizeof (pinfo)] __attribute__ ((__aligned__));
|
||||||
|
pinfo *_procs_2;
|
||||||
|
public:
|
||||||
|
int count () const { return _count; }
|
||||||
|
int add_one () { return ++_count; }
|
||||||
|
int del_one () { return --_count; }
|
||||||
|
int reset () { return _count = 0; }
|
||||||
|
pinfo &operator[] (int idx)
|
||||||
|
{
|
||||||
|
if (idx >= _NPROCS)
|
||||||
|
{
|
||||||
|
if (!_procs_2)
|
||||||
|
{
|
||||||
|
/* Use HeapAlloc to avoid propagating this memory area
|
||||||
|
to the child processes. */
|
||||||
|
_procs_2 = (pinfo *) HeapAlloc (GetProcessHeap (),
|
||||||
|
HEAP_GENERATE_EXCEPTIONS
|
||||||
|
| HEAP_ZERO_MEMORY,
|
||||||
|
(_NPROCS_2 + 1) * sizeof (pinfo));
|
||||||
|
}
|
||||||
|
return _procs_2[idx - _NPROCS];
|
||||||
|
}
|
||||||
|
return ((pinfo *) _procs)[idx];
|
||||||
|
}
|
||||||
|
int max_child_procs () const { return _NPROCS + _NPROCS_2; }
|
||||||
|
};
|
||||||
|
Static child_procs chld_procs;
|
||||||
|
|
||||||
Static int nprocs; // Number of deceased children
|
/* Start of queue for waiting threads. */
|
||||||
Static char cprocs[(NPROCS + 1) * sizeof (pinfo)];// All my children info
|
Static waitq waitq_head;
|
||||||
#define procs ((pinfo *) cprocs) // All this just to avoid expensive
|
|
||||||
// constructor operation at DLL startup
|
|
||||||
Static waitq waitq_head; // Start of queue for wait'ing threads
|
|
||||||
|
|
||||||
Static muto sync_proc_subproc; // Control access to subproc stuff
|
/* Controls access to subproc stuff. */
|
||||||
|
Static muto sync_proc_subproc;
|
||||||
|
|
||||||
_cygtls NO_COPY *_sig_tls;
|
_cygtls NO_COPY *_sig_tls;
|
||||||
|
|
||||||
@ -163,8 +199,8 @@ pid_exists (pid_t pid)
|
|||||||
static inline bool
|
static inline bool
|
||||||
mychild (int pid)
|
mychild (int pid)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nprocs; i++)
|
for (int i = 0; i < chld_procs.count (); i++)
|
||||||
if (procs[i]->pid == pid)
|
if (chld_procs[i]->pid == pid)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -174,6 +210,7 @@ mychild (int pid)
|
|||||||
int __reg2
|
int __reg2
|
||||||
proc_subproc (DWORD what, uintptr_t val)
|
proc_subproc (DWORD what, uintptr_t val)
|
||||||
{
|
{
|
||||||
|
int slot;
|
||||||
int rc = 1;
|
int rc = 1;
|
||||||
int potential_match;
|
int potential_match;
|
||||||
int clearing;
|
int clearing;
|
||||||
@ -197,14 +234,15 @@ proc_subproc (DWORD what, uintptr_t val)
|
|||||||
*/
|
*/
|
||||||
case PROC_ADD_CHILD:
|
case PROC_ADD_CHILD:
|
||||||
/* Filled up process table? */
|
/* Filled up process table? */
|
||||||
if (nprocs >= NPROCS)
|
if (chld_procs.count () >= chld_procs.max_child_procs ())
|
||||||
{
|
{
|
||||||
sigproc_printf ("proc table overflow: hit %d processes, pid %d\n",
|
sigproc_printf ("proc table overflow: hit %d processes, pid %d\n",
|
||||||
nprocs, vchild->pid);
|
chld_procs.count (), vchild->pid);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
set_errno (EAGAIN);
|
set_errno (EAGAIN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vchild != myself)
|
if (vchild != myself)
|
||||||
{
|
{
|
||||||
vchild->uid = myself->uid;
|
vchild->uid = myself->uid;
|
||||||
@ -219,13 +257,14 @@ proc_subproc (DWORD what, uintptr_t val)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PROC_ATTACH_CHILD:
|
case PROC_ATTACH_CHILD:
|
||||||
procs[nprocs] = vchild;
|
slot = chld_procs.count ();
|
||||||
rc = procs[nprocs].wait ();
|
chld_procs[slot] = vchild;
|
||||||
|
rc = chld_procs[slot].wait ();
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
sigproc_printf ("added pid %d to proc table, slot %d", vchild->pid,
|
sigproc_printf ("added pid %d to proc table, slot %d", vchild->pid,
|
||||||
nprocs);
|
slot);
|
||||||
nprocs++;
|
chld_procs.add_one ();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -260,7 +299,7 @@ proc_subproc (DWORD what, uintptr_t val)
|
|||||||
goto scan_wait;
|
goto scan_wait;
|
||||||
|
|
||||||
case PROC_EXEC_CLEANUP:
|
case PROC_EXEC_CLEANUP:
|
||||||
while (nprocs)
|
while (chld_procs.count ())
|
||||||
remove_proc (0);
|
remove_proc (0);
|
||||||
for (w = &waitq_head; w->next != NULL; w = w->next)
|
for (w = &waitq_head; w->next != NULL; w = w->next)
|
||||||
CloseHandle (w->next->ev);
|
CloseHandle (w->next->ev);
|
||||||
@ -274,7 +313,8 @@ proc_subproc (DWORD what, uintptr_t val)
|
|||||||
if (val)
|
if (val)
|
||||||
sigproc_printf ("clear waiting threads");
|
sigproc_printf ("clear waiting threads");
|
||||||
else
|
else
|
||||||
sigproc_printf ("looking for processes to reap, nprocs %d", nprocs);
|
sigproc_printf ("looking for processes to reap, count %d",
|
||||||
|
chld_procs.count ());
|
||||||
clearing = val;
|
clearing = val;
|
||||||
|
|
||||||
scan_wait:
|
scan_wait:
|
||||||
@ -312,7 +352,7 @@ proc_subproc (DWORD what, uintptr_t val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN)
|
if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN)
|
||||||
for (int i = 0; i < nprocs; i += remove_proc (i))
|
for (int i = 0; i < chld_procs.count (); i += remove_proc (i))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,35 +392,35 @@ _cygtls::remove_wq (DWORD wait)
|
|||||||
Called on process exit.
|
Called on process exit.
|
||||||
Also called by spawn_guts to disassociate any subprocesses from this
|
Also called by spawn_guts to disassociate any subprocesses from this
|
||||||
process. Subprocesses will then know to clean up after themselves and
|
process. Subprocesses will then know to clean up after themselves and
|
||||||
will not become procs. */
|
will not become chld_procs. */
|
||||||
void
|
void
|
||||||
proc_terminate ()
|
proc_terminate ()
|
||||||
{
|
{
|
||||||
sigproc_printf ("nprocs %d", nprocs);
|
sigproc_printf ("child_procs count %d", chld_procs.count ());
|
||||||
if (nprocs)
|
if (chld_procs.count ())
|
||||||
{
|
{
|
||||||
sync_proc_subproc.acquire (WPSP);
|
sync_proc_subproc.acquire (WPSP);
|
||||||
|
|
||||||
proc_subproc (PROC_CLEARWAIT, 1);
|
proc_subproc (PROC_CLEARWAIT, 1);
|
||||||
|
|
||||||
/* Clean out proc processes from the pid list. */
|
/* Clean out proc processes from the pid list. */
|
||||||
for (int i = 0; i < nprocs; i++)
|
for (int i = 0; i < chld_procs.count (); i++)
|
||||||
{
|
{
|
||||||
/* If we've execed then the execed process will handle setting ppid
|
/* If we've execed then the execed process will handle setting ppid
|
||||||
to 1 iff it is a Cygwin process. */
|
to 1 iff it is a Cygwin process. */
|
||||||
if (!have_execed || !have_execed_cygwin)
|
if (!have_execed || !have_execed_cygwin)
|
||||||
procs[i]->ppid = 1;
|
chld_procs[i]->ppid = 1;
|
||||||
if (procs[i].wait_thread)
|
if (chld_procs[i].wait_thread)
|
||||||
procs[i].wait_thread->terminate_thread ();
|
chld_procs[i].wait_thread->terminate_thread ();
|
||||||
/* Release memory associated with this process unless it is 'myself'.
|
/* Release memory associated with this process unless it is 'myself'.
|
||||||
'myself' is only in the procs table when we've execed. We reach
|
'myself' is only in the chld_procs table when we've execed. We
|
||||||
here when the next process has finished initializing but we still
|
reach here when the next process has finished initializing but we
|
||||||
can't free the memory used by 'myself' since it is used later on
|
still can't free the memory used by 'myself' since it is used
|
||||||
during cygwin tear down. */
|
later on during cygwin tear down. */
|
||||||
if (procs[i] != myself)
|
if (chld_procs[i] != myself)
|
||||||
procs[i].release ();
|
chld_procs[i].release ();
|
||||||
}
|
}
|
||||||
nprocs = 0;
|
chld_procs.reset ();
|
||||||
sync_proc_subproc.release ();
|
sync_proc_subproc.release ();
|
||||||
}
|
}
|
||||||
sigproc_printf ("leaving");
|
sigproc_printf ("leaving");
|
||||||
@ -866,7 +906,8 @@ cygheap_exec_info::alloc ()
|
|||||||
cygheap_exec_info *res =
|
cygheap_exec_info *res =
|
||||||
(cygheap_exec_info *) ccalloc_abort (HEAP_1_EXEC, 1,
|
(cygheap_exec_info *) ccalloc_abort (HEAP_1_EXEC, 1,
|
||||||
sizeof (cygheap_exec_info)
|
sizeof (cygheap_exec_info)
|
||||||
+ (nprocs * sizeof (children[0])));
|
+ (chld_procs.count ()
|
||||||
|
* sizeof (children[0])));
|
||||||
res->sigmask = _my_tls.sigmask;
|
res->sigmask = _my_tls.sigmask;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -942,10 +983,10 @@ child_info_spawn::cleanup ()
|
|||||||
inline void
|
inline void
|
||||||
cygheap_exec_info::record_children ()
|
cygheap_exec_info::record_children ()
|
||||||
{
|
{
|
||||||
for (nchildren = 0; nchildren < nprocs; nchildren++)
|
for (nchildren = 0; nchildren < chld_procs.count (); nchildren++)
|
||||||
{
|
{
|
||||||
children[nchildren].pid = procs[nchildren]->pid;
|
children[nchildren].pid = chld_procs[nchildren]->pid;
|
||||||
children[nchildren].p = procs[nchildren];
|
children[nchildren].p = chld_procs[nchildren];
|
||||||
/* Set inheritance of required child handles for reattach_children
|
/* Set inheritance of required child handles for reattach_children
|
||||||
in the about-to-be-execed process. */
|
in the about-to-be-execed process. */
|
||||||
children[nchildren].p.set_inheritance (true);
|
children[nchildren].p.set_inheritance (true);
|
||||||
@ -1126,13 +1167,13 @@ checkstate (waitq *parent_w)
|
|||||||
{
|
{
|
||||||
int potential_match = 0;
|
int potential_match = 0;
|
||||||
|
|
||||||
sigproc_printf ("nprocs %d", nprocs);
|
sigproc_printf ("child_procs count %d", chld_procs.count ());
|
||||||
|
|
||||||
/* Check already dead processes first to see if they match the criteria
|
/* Check already dead processes first to see if they match the criteria
|
||||||
* given in w->next. */
|
* given in w->next. */
|
||||||
int res;
|
int res;
|
||||||
for (int i = 0; i < nprocs; i++)
|
for (int i = 0; i < chld_procs.count (); i++)
|
||||||
if ((res = stopped_or_terminated (parent_w, procs[i])))
|
if ((res = stopped_or_terminated (parent_w, chld_procs[i])))
|
||||||
{
|
{
|
||||||
remove_proc (i);
|
remove_proc (i);
|
||||||
potential_match = 1;
|
potential_match = 1;
|
||||||
@ -1140,40 +1181,40 @@ checkstate (waitq *parent_w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sigproc_printf ("no matching terminated children found");
|
sigproc_printf ("no matching terminated children found");
|
||||||
potential_match = -!!nprocs;
|
potential_match = -!!chld_procs.count ();
|
||||||
|
|
||||||
out:
|
out:
|
||||||
sigproc_printf ("returning %d", potential_match);
|
sigproc_printf ("returning %d", potential_match);
|
||||||
return potential_match;
|
return potential_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove a proc from procs by swapping it with the last child in the list.
|
/* Remove a proc from chld_procs by swapping it with the last child in the list.
|
||||||
Also releases shared memory of exited processes. */
|
Also releases shared memory of exited processes. */
|
||||||
static int
|
static int
|
||||||
remove_proc (int ci)
|
remove_proc (int ci)
|
||||||
{
|
{
|
||||||
if (have_execed)
|
if (have_execed)
|
||||||
{
|
{
|
||||||
if (_my_tls._ctinfo != procs[ci].wait_thread)
|
if (_my_tls._ctinfo != chld_procs[ci].wait_thread)
|
||||||
procs[ci].wait_thread->terminate_thread ();
|
chld_procs[ci].wait_thread->terminate_thread ();
|
||||||
}
|
}
|
||||||
else if (procs[ci] && procs[ci]->exists ())
|
else if (chld_procs[ci] && chld_procs[ci]->exists ())
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
sigproc_printf ("removing procs[%d], pid %d, nprocs %d", ci, procs[ci]->pid,
|
sigproc_printf ("removing chld_procs[%d], pid %d, child_procs count %d",
|
||||||
nprocs);
|
ci, chld_procs[ci]->pid, chld_procs.count ());
|
||||||
if (procs[ci] != myself)
|
if (chld_procs[ci] != myself)
|
||||||
procs[ci].release ();
|
chld_procs[ci].release ();
|
||||||
if (ci < --nprocs)
|
if (ci < chld_procs.del_one ())
|
||||||
{
|
{
|
||||||
/* Wait for proc_waiter thread to make a copy of this element before
|
/* Wait for proc_waiter thread to make a copy of this element before
|
||||||
moving it or it may become confused. The chances are very high that
|
moving it or it may become confused. The chances are very high that
|
||||||
the proc_waiter thread has already done this by the time we
|
the proc_waiter thread has already done this by the time we
|
||||||
get here. */
|
get here. */
|
||||||
if (!have_execed && !exit_state)
|
if (!have_execed && !exit_state)
|
||||||
while (!procs[nprocs].waiter_ready)
|
while (!chld_procs[chld_procs.count ()].waiter_ready)
|
||||||
yield ();
|
yield ();
|
||||||
procs[ci] = procs[nprocs];
|
chld_procs[ci] = chld_procs[chld_procs.count ()];
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user