4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-15 02:09:19 +08:00
newlib-cygwin/winsup/cygwin/cygthread.cc
Christopher Faylor 8cb359d947 * child_info.h (child_info_spawn::hexec_proc): Eliminate.
* dcrt0.cc (dll_crt0_0): Remove hexec_proc stuff.
* fork.cc (fork_child): Remove call to pinfo_fixup_after_fork.
* pinfo.cc (set_myself): Close and zero pid_handle if set.
(pinfo_fixup_after_fork): Delete.
(proc_waiter): Don't close vchild.hProcess here.  Do that when we are remove
the vchild from procs.  Save hProcess as pid_handle only on first reparent
operation.
(pinfo::wait): Don't set pid_handle here.
(pinfo::alert_parent): Always try to send signal.  If unsuccessful then close
and zero wr_proc_pipe.
* pinfo.h (pinfo::pinfo): Make sure that appropriate parts of the class are
zeroed on construction.
(pinfo::alert_parent): Take char argument.
(pinfo_fixup_after_fork): Delete declaration.
(hexec_proc): Ditto.
* sigproc.cc (remove_proc): Close pid_handle and hProcess if appropriate.
* spawn.cc (spawn_guts): Set cygheap->pid_handle on first exec.
* cygheap.h (init_cygheap::pid_handle): New element.
* pinfo.cc (set_myself): Clear previously existing cygheap->pid_handle when a
new process has been started.
(pinfo::wait): Make sure that a handle to the newly forked/spawned process is
kept around so that the pid will not be reused.
* pinfo.h (_pinfo::pid_handle): Move.
(pinfo::pid_handle): to here.
* spawn.cc (spawn_guts): Create a pid_handle in cygheap prior to spawning to
ensure that the pid does not get reused during the lifetime of the "cygwin
pid".
* pinfo.h (pinfo::alert_parent): New function.
* exceptions.cc (sig_handle_tty_stop): Use alert_parent to send "signals" to
parent.
* fork.cc (fork_parent): Don't close pi.hProcess.  Let the waiter thread do
that.
* pinfo.cc (proc_waiter): Detect case where process exits without setting the
exit code and use value from GetExitCodeProcess.  Reluctantly implement
__SIGREPARENT.
(pinfo::alert_parent): Define.
* sigproc.h (__SIGREPARENT): New enum.
* spawn.cc (spawn_guts): Send reparent signal to parent on exec.  Always create
process in suspended state to avoid races.  Remove cygthread.h in favor of
cygtls.h throughout since cygtls now includes cygthread.h.  Eliminate
ppid_handle usage throughout.
* child_info.h: Regenerate magic number
(child_info): Remove pppid_handle.
* cygthread.h (cygthread::release): New method.  Frees thread without waiting.
* cygthread.cc (cygthread::stub): Set _ctinfo in _mytls to point to information
for executing thread.  Don't call SetEvent if thread is no longer in use.
(cygthread::simplestub): Ditto.
* cygtls.h (_cygtls::_ctinfo): New element contains pointer to information
about executing cygthread, if any.
* dcrt0.cc: Remove last vestiges of per_thread stuff.
(dll_crt0_0): Ditto.  Remove accommodation for ppid_handle.
(do_exit): Remove obsolete reparenting test.
(_exit): Exit with a more SUSv3-like exit value.
* dtable.cc (dtable::stdio_init): Check for myself->cygstarted rather than
myself->ppid_handle to see if we were started by a cygwin process.
* exceptions.cc (open_stackdumpfile): Ditto.
(handle_exceptions): Ditto.
(ctrl_c_handler): Ditto.
(sig_handle_tty_stop): Ditto.  Let parent send signal to itself on STOP.
(sigpacket::process): Comment out vfork test.
(signal_exit): Use more SUSv3-like exit value on signal.
* external.cc (fillout_pinfo): Don't set hProcess.
* fork.cc: Remove VFORK cruft.
(per_thread::set): Delete.
(fork_child): Remove perthread stuff.
(fork_parent): Remove obsolete subproc_init.  Accommodate new method for
tracking subprocesses.
* pinfo.cc (set_myself): Accommodate new pinfo/_pinfo layout.  Set some things
here that used to be set in wait_sig.
(_pinfo::exit): Set exitcode here.  Close process pipe.
(_pinfo::commune_send): Accommodeate new pinfo/_pinfo layout.
(proc_waiter): New function.  Waits, in a thread for subprocess to go away.
(pinfo::wait): New function.  Initialization for proc_waiter.
* pinfo.h (_pinfo::exitcode): New element.
(_pinfo::cygstarted): Ditto.
(_pinfo::wr_proc_pipe): Ditto.
(_pinfo::ppid_handle): Delete.
(_pinfo::hProcess): Delete.
(_pinfo::lock): Delete.
(pinfo::hProcess): New element.
(pinfo::lock): Ditto.
(pinfo::wait): Declare new function.
(pinfo::preserve): Define new function.
* sigproc.cc: Remove old stuff from wait_subproc thread based method.
(zombies): Remove.
(procs): New.
(my_parent_is_alive): Just check that the parent pid exists.
(mychild): Just use pinfo methods to determine if child is mine.
(proc_subproc): Revamp PROC_ADDCHILD to use pinfo::wait.  Remove
PROC_CHILDTERMINATED logic.  Use different method to remove processes from list
when SIGCHLD == SIG_IGN.
(proc_terminate): Gut.
(subproc_init): Delete.
(init_child_info): Remove setting of pppid_handle.
(checkstate): Revamp to only scan procs array.
(remove_proc): Rename from remove_zombie.  Don't close hProcess or pid_handle.
Don't release memory if it's myself.
(stopped_or_terminated): Change logic to handle new consolidated proc/zombie
array.
(wait_subproc): Delete.
* sigproc.h: Remove obsolete EXIT_* defines.
(subproc_init): Remove declaration.
* spawn.cc (spawn_guts): Remove reparenting stuff.  Use standard wait logic to
wait for child if started from a non-cygwin process.
* tlsoffsets.h: Regenerate.
* tty.cc (tty_init): Check for myself->cygstarted rather than
myself->ppid_handle to see if we were started by a cygwin process.
* include/sys/signal.h (external_pinfo::exitcode): Replace hProcess.
* include/sys/wait.h (WCOREDUMP): Define.
* fhandler_tty.cc (fhandler_tty_slave::read): Add debugging output for timeout
case.
* signal.cc (abort): Flag that we are exiting with the ABORT signal.
2004-11-26 04:15:10 +00:00

316 lines
6.9 KiB
C++

/* cygthread.cc
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <windows.h>
#include <stdlib.h>
#include "exceptions.h"
#include "security.h"
#include "sync.h"
#include "cygerrno.h"
#include "sigproc.h"
#include "thread.h"
#include "cygtls.h"
#undef CloseHandle
static cygthread NO_COPY threads[32];
#define NTHREADS (sizeof (threads) / sizeof (threads[0]))
DWORD NO_COPY cygthread::main_thread_id;
bool NO_COPY cygthread::exiting;
/* Initial stub called by cygthread constructor. Performs initial
per-thread initialization and loops waiting for new thread functions
to execute. */
DWORD WINAPI
cygthread::stub (VOID *arg)
{
cygthread *info = (cygthread *) arg;
_my_tls._ctinfo = info;
if (info->arg == cygself)
{
if (info->ev)
{
CloseHandle (info->ev);
CloseHandle (info->thread_sync);
}
info->ev = info->thread_sync = info->stack_ptr = NULL;
}
else
{
info->stack_ptr = &arg;
if (!info->ev)
{
info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
}
}
while (1)
{
if (!info->__name)
system_printf ("erroneous thread activation");
else
{
if (!info->func || exiting)
return 0;
/* Cygwin threads should not call ExitThread directly */
info->func (info->arg == cygself ? info : info->arg);
/* ...so the above should always return */
#ifdef DEBUGGING
info->func = NULL; // catch erroneous activation
#endif
info->__name = NULL;
if (info->inuse)
SetEvent (info->ev);
}
switch (WaitForSingleObject (info->thread_sync, INFINITE))
{
case WAIT_OBJECT_0:
continue;
default:
api_fatal ("WFSO failed, %E");
break;
}
}
}
/* Overflow stub called by cygthread constructor. Calls specified function
and then exits the thread. */
DWORD WINAPI
cygthread::simplestub (VOID *arg)
{
cygthread *info = (cygthread *) arg;
_my_tls._ctinfo = info;
info->stack_ptr = &arg;
info->ev = info->h;
info->func (info->arg == cygself ? info : info->arg);
return 0;
}
/* Start things going. Called from dll_crt0_1. */
void
cygthread::init ()
{
main_thread_id = GetCurrentThreadId ();
}
cygthread *
cygthread::freerange ()
{
cygthread *self = (cygthread *) calloc (1, sizeof (*self));
self->is_freerange = true;
self->inuse = 1;
return self;
}
void * cygthread::operator
new (size_t)
{
cygthread *info;
/* Search the threads array for an empty slot to use */
for (info = threads; info < threads + NTHREADS; info++)
if (!InterlockedExchange (&info->inuse, 1))
{
/* available */
#ifdef DEBUGGING
if (info->__name)
api_fatal ("name not NULL? id %p, i %d", info->id, info - threads);
#endif
goto out;
}
#ifdef DEBUGGING
char buf[1024];
if (!GetEnvironmentVariable ("CYGWIN_FREERANGE_NOCHECK", buf, sizeof (buf)))
api_fatal ("Overflowed cygwin thread pool");
else
thread_printf ("Overflowed cygwin thread pool");
#endif
info = freerange (); /* exhausted thread pool */
out:
return info;
}
cygthread::cygthread (LPTHREAD_START_ROUTINE start, LPVOID param,
const char *name): __name (name),
func (start), arg (param)
{
thread_printf ("name %s, id %p", name, id);
if (h)
{
while (!thread_sync)
low_priority_sleep (0);
SetEvent (thread_sync);
thread_printf ("activated thread_sync %p", thread_sync);
}
else
{
stack_ptr = NULL;
h = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
this, 0, &id);
if (!h)
api_fatal ("thread handle not set - %p<%p>, %E", h, id);
thread_printf ("created thread %p", h);
}
}
/* Return the symbolic name of the current thread for debugging.
*/
const char *
cygthread::name (DWORD tid)
{
const char *res = NULL;
if (!tid)
tid = GetCurrentThreadId ();
if (tid == main_thread_id)
return "main";
for (DWORD i = 0; i < NTHREADS; i++)
if (threads[i].id == tid)
{
res = threads[i].__name ?: "exiting thread";
break;
}
if (!res)
{
static char buf[30] NO_COPY = {0};
__small_sprintf (buf, "unknown (%p)", tid);
res = buf;
}
return res;
}
cygthread::operator
HANDLE ()
{
while (!ev)
low_priority_sleep (0);
return ev;
}
/* Should only be called when the process is exiting since it
leaves an open thread slot. */
void
cygthread::exit_thread ()
{
if (!is_freerange)
SetEvent (*this);
ExitThread (0);
}
/* Forcibly terminate a thread. */
void
cygthread::terminate_thread ()
{
if (!is_freerange)
{
ResetEvent (*this);
ResetEvent (thread_sync);
}
(void) TerminateThread (h, 0);
(void) WaitForSingleObject (h, INFINITE);
CloseHandle (h);
while (!stack_ptr)
low_priority_sleep (0);
MEMORY_BASIC_INFORMATION m;
memset (&m, 0, sizeof (m));
(void) VirtualQuery (stack_ptr, &m, sizeof m);
if (!m.RegionSize)
system_printf ("m.RegionSize 0? stack_ptr %p", stack_ptr);
else if (!VirtualFree (m.AllocationBase, 0, MEM_RELEASE))
debug_printf ("VirtualFree of allocation base %p<%p> failed, %E",
stack_ptr, m.AllocationBase);
if (is_freerange)
free (this);
else
{
h = NULL;
__name = NULL;
stack_ptr = NULL;
(void) InterlockedExchange (&inuse, 0); /* No longer in use */
}
}
/* Detach the cygthread from the current thread. Note that the
theory is that cygthreads are only associated with one thread.
So, there should be never be multiple threads doing waits
on the same cygthread. */
bool
cygthread::detach (HANDLE sigwait)
{
bool signalled = false;
if (!inuse)
system_printf ("called detach but inuse %d, thread %p?", inuse, id);
else
{
DWORD res;
if (!sigwait)
res = WaitForSingleObject (*this, INFINITE);
else
{
HANDLE w4[2];
w4[0] = *this;
w4[1] = signal_arrived;
res = WaitForSingleObject (sigwait, INFINITE);
if (res != WAIT_OBJECT_0)
system_printf ("WFSO sigwait %p failed, res %u, %E", sigwait, res);
res = WaitForMultipleObjects (2, w4, FALSE, INFINITE);
if (res == WAIT_OBJECT_0)
/* nothing */;
else if (WaitForSingleObject (sigwait, 0) == WAIT_OBJECT_0)
res = WaitForSingleObject (*this, INFINITE);
else if ((res = WaitForSingleObject (*this, 0)) != WAIT_OBJECT_0)
{
signalled = true;
terminate_thread ();
set_sig_errno (EINTR); /* caller should be dealing with return
values. */
}
}
thread_printf ("%s returns %d, id %p", sigwait ? "WFMO" : "WFSO",
res, id);
if (signalled)
/* already handled */;
else if (is_freerange)
{
CloseHandle (h);
free (this);
}
else
{
ResetEvent (*this);
/* Mark the thread as available by setting inuse to zero */
(void) InterlockedExchange (&inuse, 0);
}
}
return signalled;
}
void
cygthread::terminate ()
{
exiting = 1;
}