newlib/winsup/cygwin/sync.cc
Christopher Faylor e431827c7c Rename _threadinfo to _cygtls, throughout.
* cygtls.h (_cygtls::call_signal_handler): Rename from call_signal_handler_now.
(_cygtls::push): Make second argument mandatory.
(_cygtls::fixup_after_fork): Declare new function.
(_cygtls::lock): Ditto.
* cygtls.cc (_cygtls::fixup_after_fork): Define new function.
* dcrt0.cc (cygwin_finished_initializing): Define as bool.
(alloc_stack): Use _tlstop rather than arbitrary variable in probably vain
attempt to avoid strange fork problem on CTRL-C.
(dll_crt0_0): Remove obsolete winpids::init call.
* dll_init.cc (dll_dllcrt0): Detect forkee condition as equivalent to
initializing.
* winsup.h (cygwin_finished_initializing): Declare as bool.
* exceptions.cc (handle_exceptions): Rely on cygwin_finished_initializing to
determine how to handle exception during process startup.
(_cygtls::call_signal_handler): Rename from call_signal_handler_now.
(_cygtls::interrupt_now): Fill in second argument to push.
(signal_fixup_after_fork): Eliminate.
(setup_handler): Initialize locked to avoid potential inappropriate unlock.
Resume thread if it has acquired the stack lock.
(ctrl_c_handler): Just exit if ctrl-c is hit before cygiwn has finished
initializing.
* fork.cc (sync_with_child): Don't call abort since it can cause exit
deadlocks.
(sync_with_child): Change debugging output slightly.
(fork_child): Set cygwin_finished_initializing here.  Call _cygtls fork fixup
and explicitly call sigproc_init.
(fork_parent): Release malloc lock on fork failure.
(vfork): Call signal handler via _my_tls.
* sigproc.cc (sig_send): Ditto.
* syscalls.cc (readv): Ditto.
* termios.cc (tcsetattr): Ditto.
* wait.cc (wait4): Ditto.
* signal.cc (nanosleep): Ditto.
(abort): Ditto.
(kill_pgrp): Avoid killing self if exiting.
* sync.cc (muto::acquire): Remove (temporarily?) ill-advised exiting_thread
check.
* gendef (_sigfe): Be more agressive in protecting stack pointer from other
access by signal thread.
(_cygtls::locked): Define new function.
(_sigbe): Ditto.
(_cygtls::pop): Protect edx.
(_cygtls::lock): Use guaranteed method to set eax to 1.
(longjmp): Aggressively protect signal stack.
* miscfuncs.cc (low_priority_sleep): Reduce "sleep time" for secs == 0.
* pinfo.cc (winpids::set): Counterintuitively use malloc's lock to protect
simultaneous access to the pids list since there are pathological conditions
which can cause malloc to call winpid.
(winpids::init): Eliminate.
* pinfo.h (winpids::cs): Eliminate declaration.
* pinfo.h (winpids::init): Eliminate definition.
2004-02-12 03:01:58 +00:00

158 lines
4.2 KiB
C++

/* sync.cc: Synchronization functions for cygwin.
This file implements the methods for controlling the "muto" class
which is intended to operate similarly to a mutex but attempts to
avoid making expensive calls to the kernel.
Copyright 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
Written by Christopher Faylor <cgf@cygnus.com>
This file is part of Cygwin.
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 <stdlib.h>
#include <time.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h>
#include "sync.h"
#include "security.h"
#undef WaitForSingleObject
DWORD NO_COPY muto::exiting_thread;
/* Constructor */
muto *
muto::init (const char *s)
{
waiters = -1;
/* Create event which is used in the fallback case when blocking is necessary */
if (!(bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL)))
{
DWORD oerr = GetLastError ();
SetLastError (oerr);
return NULL;
}
name = s;
return this;
}
#if 0 /* FIXME: Do we need this? mutos aren't destroyed until process exit */
/* Destructor (racy?) */
muto::~muto ()
{
while (visits)
release ();
HANDLE h = bruteforce;
bruteforce = NULL;
/* Just need to close the event handle */
if (h)
CloseHandle (h);
}
#endif
/* Acquire the lock. Argument is the number of milliseconds to wait for
the lock. Multiple visits from the same thread are allowed and should
be handled correctly.
Note: The goal here is to minimize, as much as possible, calls to the
OS. Hence the use of InterlockedIncrement, etc., rather than (much) more
expensive OS mutexes. */
int
muto::acquire (DWORD ms)
{
DWORD this_tid = GetCurrentThreadId ();
#if 0
if (exiting_thread)
return this_tid == exiting_thread;
#endif
if (tid != this_tid)
{
/* Increment the waiters part of the class. Need to do this first to
avoid potential races. */
LONG was_waiting = InterlockedIncrement (&waiters);
/* This is deceptively simple. Basically, it allows multiple attempts to
lock the same muto to succeed without attempting to manipulate sync.
If the muto is already locked then this thread will wait for ms until
it is signalled by muto::release. Then it will attempt to grab the
sync field. If it succeeds, then this thread owns the muto.
There is a pathological condition where a thread times out waiting for
bruteforce but the release code triggers the bruteforce event. In this
case, it is possible for a thread which is going to wait for bruteforce
to wake up immediately. It will then attempt to grab sync but will fail
and go back to waiting. */
if (tid != this_tid && (was_waiting || InterlockedExchange (&sync, 1) != 0))
{
switch (WaitForSingleObject (bruteforce, ms))
{
case WAIT_OBJECT_0:
goto gotit;
break;
default:
InterlockedDecrement (&waiters);
return 0; /* failed. */
}
}
}
gotit:
tid = this_tid; /* register this thread. */
return ++visits; /* Increment visit count. */
}
/* Return the muto lock. Needs to be called once per every acquire. */
int
muto::release ()
{
DWORD this_tid = GetCurrentThreadId ();
if (tid != this_tid || !visits)
{
SetLastError (ERROR_NOT_OWNER); /* Didn't have the lock. */
return 0; /* failed. */
}
/* FIXME: Need to check that other thread has not exited, too. */
if (!--visits)
{
tid = 0; /* We were the last unlocker. */
(void) InterlockedExchange (&sync, 0); /* Reset trigger. */
/* This thread had incremented waiters but had never decremented it.
Decrement it now. If it is >= 0 then there are possibly other
threads waiting for the lock, so trigger bruteforce. */
if (InterlockedDecrement (&waiters) >= 0)
(void) SetEvent (bruteforce); /* Wake up one of the waiting threads */
}
return 1; /* success. */
}
bool
muto::acquired ()
{
return tid == GetCurrentThreadId ();
}
/* Call only when we're exiting. This is not thread safe. */
void
muto::reset ()
{
visits = sync = tid = 0;
InterlockedExchange (&waiters, -1);
if (bruteforce)
{
CloseHandle (bruteforce);
bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, name);
}
}