* cygwin.din (sem_unlink): Export.

* posix_ipc.cc: Include thread.h and semaphore.h.  Remove TODO
	comment.
	(ipc_names): Add max_len member.  Set to maximum length of the path
	before tacking on the prefix path.  Set prefix path for named semaphors
	to /dev/shm, as on Linux.
	(enum ipc_type_t): Change sem to semaphore to avoid name conflicts.
	(check_path): Detect empty paths.  Use ipc_names's max_len member.
	Use __small_sprintf to create full object path name.  Special case
	semaphores.
	(ipc_cond_init): Drop superfluous strcpy.
	(class ipc_flock): New class to simplify file locking in subsequent
	code.
	(struct mq_hdr): Raise size of mqh_uname to allow adding a unique
	LUID to the name.
	(mq_open): Fix formatting.  Create unique synchronization object names
	using AllocateLocallyUniqueId.
	(struct sem_finfo): New structure defining named semaphore file content.
	(sem_open): Move here.  Rework implementation to allow kernel
	persistent implementation of POSIX named semaphores.
	(_sem_close): Implement sem_close.
	(sem_close): Move here.  Just call _sem_close with do_close parameter
	set to true.
	(sem_unlink): New function.
	* pthread.cc (mangle_sem_name): Remove.
	(sem_open): Move to posix_ipc.cc.
	(sem_close): Ditto.
	* syscalls.cc (close_all_files): Call semaphore::terminate here.
	* thread.cc: Fix formatting.  Rearrange semaphore functions so that
	they are close together.
	(semaphore::semaphore): Rework to play nicely with new named semaphore
	implementation.
	(semaphore::_terminate): Call _sem_close if semaphore is a named
	semaphore.
	(semaphore::destroy): Don't destroy named semaphores.  Return EINVAL
	instead.
	(semaphore::close): Only destroy named semaphores.  Return EINVAL
	otherwise.
	(semaphore::open): Rework to play nicely with new named semaphore
	implementation.  Loop through existing semaphores to be able to
	return same sem_t pointer as a former call on the same named semaphore.
	(semaphore::getinternal): New function called from _sem_close.
	* thread.h (class List): Make mx and head public.
	(class semaphore): Fix formatting.  Align method declarations with
	implementation in thread.cc.  Add members used for named semaphores.
	(semaphore::terminate): New static method.
	* include/semaphore.h: Redefine SEM_FAILED.  Fix formatting.
	(sem_unlink): Add declaration.
	* include/cygwin/version.h: Bump API minor number.
This commit is contained in:
Corinna Vinschen 2007-02-20 15:48:04 +00:00
parent d0cf179299
commit 8fbd574ef0
9 changed files with 555 additions and 292 deletions

View File

@ -1,3 +1,55 @@
2007-02-20 Corinna Vinschen <corinna@vinschen.de>
* cygwin.din (sem_unlink): Export.
* posix_ipc.cc: Include thread.h and semaphore.h. Remove TODO
comment.
(ipc_names): Add max_len member. Set to maximum length of the path
before tacking on the prefix path. Set prefix path for named semaphors
to /dev/shm, as on Linux.
(enum ipc_type_t): Change sem to semaphore to avoid name conflicts.
(check_path): Detect empty paths. Use ipc_names's max_len member.
Use __small_sprintf to create full object path name. Special case
semaphores.
(ipc_cond_init): Drop superfluous strcpy.
(class ipc_flock): New class to simplify file locking in subsequent
code.
(struct mq_hdr): Raise size of mqh_uname to allow adding a unique
LUID to the name.
(mq_open): Fix formatting. Create unique synchronization object names
using AllocateLocallyUniqueId.
(struct sem_finfo): New structure defining named semaphore file content.
(sem_open): Move here. Rework implementation to allow kernel
persistent implementation of POSIX named semaphores.
(_sem_close): Implement sem_close.
(sem_close): Move here. Just call _sem_close with do_close parameter
set to true.
(sem_unlink): New function.
* pthread.cc (mangle_sem_name): Remove.
(sem_open): Move to posix_ipc.cc.
(sem_close): Ditto.
* syscalls.cc (close_all_files): Call semaphore::terminate here.
* thread.cc: Fix formatting. Rearrange semaphore functions so that
they are close together.
(semaphore::semaphore): Rework to play nicely with new named semaphore
implementation.
(semaphore::_terminate): Call _sem_close if semaphore is a named
semaphore.
(semaphore::destroy): Don't destroy named semaphores. Return EINVAL
instead.
(semaphore::close): Only destroy named semaphores. Return EINVAL
otherwise.
(semaphore::open): Rework to play nicely with new named semaphore
implementation. Loop through existing semaphores to be able to
return same sem_t pointer as a former call on the same named semaphore.
(semaphore::getinternal): New function called from _sem_close.
* thread.h (class List): Make mx and head public.
(class semaphore): Fix formatting. Align method declarations with
implementation in thread.cc. Add members used for named semaphores.
(semaphore::terminate): New static method.
* include/semaphore.h: Redefine SEM_FAILED. Fix formatting.
(sem_unlink): Add declaration.
* include/cygwin/version.h: Bump API minor number.
2007-02-20 Christopher Faylor <me@cgf.cx>
* exceptions.cc (_cygtls::signal_exit): Only call myself.exit when when

View File

@ -1235,6 +1235,7 @@ sem_open SIGFE
sem_post SIGFE
sem_timedwait SIGFE
sem_trywait SIGFE
sem_unlink SIGFE
sem_wait SIGFE
semctl SIGFE
semget SIGFE

View File

@ -305,12 +305,13 @@ details. */
164: Export shm_open, shm_unlink.
165: Export mq_close, mq_getattr, mq_notify, mq_open, mq_receive,
mq_send, mq_setattr, mq_timedreceive, mq_timedsend, mq_unlink.
166: Export sem_unlink.
*/
/* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
#define CYGWIN_VERSION_API_MAJOR 0
#define CYGWIN_VERSION_API_MINOR 165
#define CYGWIN_VERSION_API_MINOR 166
/* There is also a compatibity version number associated with the
shared memory regions. It is incremented when incompatible

View File

@ -24,18 +24,19 @@ extern "C"
typedef struct __sem_t {char __dummy;} *sem_t;
#endif
#define SEM_FAILED 0
#define SEM_FAILED ((sem_t *) 0)
/* Semaphores */
int sem_init (sem_t * sem, int pshared, unsigned int value);
int sem_destroy (sem_t * sem);
int sem_init (sem_t *sem, int pshared, unsigned int value);
int sem_destroy (sem_t *sem);
sem_t *sem_open (const char *name, int oflag, ...);
int sem_close (sem_t *sem);
int sem_wait (sem_t * sem);
int sem_trywait (sem_t * sem);
int sem_timedwait (sem_t * sem, const struct timespec *abstime);
int sem_post (sem_t * sem);
int sem_getvalue (sem_t * sem, int *sval);
int sem_unlink (const char *name);
int sem_wait (sem_t *sem);
int sem_trywait (sem_t *sem);
int sem_timedwait (sem_t *sem, const struct timespec *abstime);
int sem_post (sem_t *sem);
int sem_getvalue (sem_t *sem, int *sval);
#ifdef __cplusplus
}

View File

@ -8,12 +8,8 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* TODO: POSIX semaphores are implemented in thread.cc right now. The
implementation in thread.cc disallows implementing kernel
persistent semaphores, so in the long run we should move the
implementation here, using file based shared memory instead. */
#include "winsup.h"
#include "thread.h"
#include "path.h"
#include "cygerrno.h"
#include "cygtls.h"
@ -29,22 +25,24 @@ details. */
#include <unistd.h>
#include <stdarg.h>
#include <mqueue.h>
#include <semaphore.h>
struct
{
const char *prefix;
const size_t max_len;
const char *description;
} ipc_names[] = {
{ "/dev/shm", "POSIX shared memory object" },
{ "/dev/mqueue", "POSIX message queue" },
{ "/dev/sem", "POSIX semaphore" }
{ "/dev/shm", CYG_MAX_PATH - 10, "POSIX shared memory object" },
{ "/dev/mqueue", CYG_MAX_PATH - 13, "POSIX message queue" },
{ "/dev/shm", CYG_MAX_PATH - 14, "POSIX semaphore" }
};
enum ipc_type_t
{
shmem,
mqueue,
sem
semaphore
};
static bool
@ -69,20 +67,21 @@ check_path (char *res_name, ipc_type_t type, const char *name)
return false;
}
/* Name must start with a single slash. */
if (!name || name[0] != '/' || name[1] == '/')
if (!name || name[0] != '/' || name[1] == '/' || !name[1])
{
debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
set_errno (EINVAL);
return false;
}
if (strlen (name) > CYG_MAX_PATH - sizeof (ipc_names[type].prefix))
if (strlen (name) > ipc_names[type].max_len)
{
debug_printf ("%s name '%s' too long", ipc_names[type].description, name);
set_errno (ENAMETOOLONG);
return false;
}
strcpy (res_name, ipc_names[type].prefix);
strcat (res_name, name);
__small_sprintf (res_name, "%s/%s%s", ipc_names[type].prefix,
type == semaphore ? "sem." : "",
name + 1);
return true;
}
@ -133,7 +132,6 @@ static int
ipc_cond_init (HANDLE *pevt, const char *name)
{
char buf[CYG_MAX_PATH];
strcpy (buf, wincap.has_terminal_services () ? "Global\\" : "");
__small_sprintf (buf, "%scyg_pevt/%s",
wincap.has_terminal_services () ? "Global\\" : "", name);
*pevt = CreateEvent (&sec_all, TRUE, FALSE, buf);
@ -195,6 +193,30 @@ ipc_cond_close (HANDLE evt)
return CloseHandle (evt) ? 0 : geterrno_from_win_error ();
}
class ipc_flock
{
struct __flock64 fl;
public:
ipc_flock () { memset (&fl, 0, sizeof fl); }
int lock (int fd, size_t size)
{
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = size;
return fcntl (fd, F_SETLKW, &fl);
}
int unlock (int fd)
{
if (!fl.l_len)
return 0;
fl.l_type = F_UNLCK;
return fcntl (fd, F_SETLKW, &fl);
}
};
/* POSIX shared memory object implementation. */
extern "C" int
@ -242,7 +264,7 @@ struct mq_hdr
long mqh_free; /* index of first free message */
long mqh_nwait; /* #threads blocked in mq_receive() */
pid_t mqh_pid; /* nonzero PID if mqh_event set */
char mqh_uname[20]; /* unique name used to identify synchronization
char mqh_uname[36]; /* unique name used to identify synchronization
objects connected to this queue */
struct sigevent mqh_event; /* for mq_notify() */
};
@ -288,6 +310,7 @@ mq_open (const char *name, int oflag, ...)
struct msg_hdr *msghdr;
struct mq_attr *attr;
struct mq_info *mqinfo;
LUID luid;
char mqname[CYG_MAX_PATH];
if (!check_path (mqname, mqueue, name))
@ -297,6 +320,7 @@ mq_open (const char *name, int oflag, ...)
if (efault.faulted (EFAULT))
return (mqd_t) -1;
oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
created = 0;
nonblock = oflag & O_NONBLOCK;
oflag &= ~O_NONBLOCK;
@ -358,7 +382,14 @@ again:
mqhdr->mqh_attr.mq_curmsgs = 0;
mqhdr->mqh_nwait = 0;
mqhdr->mqh_pid = 0;
__small_sprintf (mqhdr->mqh_uname, "cyg%016X", hash_path_name (0,mqname));
if (!AllocateLocallyUniqueId (&luid))
{
__seterrno ();
goto err;
}
__small_sprintf (mqhdr->mqh_uname, "cyg%016X%08x%08x",
hash_path_name (0,mqname),
luid.HighPart, luid.LowPart);
mqhdr->mqh_head = 0;
index = sizeof (struct mq_hdr);
mqhdr->mqh_free = index;
@ -402,7 +433,7 @@ exists:
{
if (errno == ENOENT && (oflag & O_CREAT))
{
close(fd);
close (fd);
goto again;
}
goto err;
@ -847,3 +878,173 @@ mq_unlink (const char *name)
return 0;
}
/* POSIX named semaphore implementation. Loosely based on Richard W. STEPHENS
implementation as far as sem_open is concerned, but under the hood using
the already existing semaphore class in thread.cc. Using a file backed
solution allows to implement kernel persistent named semaphores. */
struct sem_finfo
{
unsigned int value;
unsigned long long hash;
LUID luid;
};
extern "C" sem_t *
sem_open (const char *name, int oflag, ...)
{
int i, fd, created;
va_list ap;
mode_t mode = 0;
unsigned int value = 0;
struct __stat64 statbuff;
sem_t *sem = SEM_FAILED;
sem_finfo sf;
char semname[CYG_MAX_PATH];
bool wasopen = false;
ipc_flock file;
if (!check_path (semname, semaphore, name))
return SEM_FAILED;
myfault efault;
if (efault.faulted (EFAULT))
return SEM_FAILED;
created = 0;
oflag &= (O_CREAT | O_EXCL);
again:
if (oflag & O_CREAT)
{
va_start (ap, oflag); /* init ap to final named argument */
mode = va_arg (ap, mode_t) & ~S_IXUSR;
value = va_arg (ap, unsigned int);
va_end (ap);
/* Open and specify O_EXCL and user-execute */
fd = open (semname, oflag | O_EXCL | O_RDWR, mode | S_IXUSR);
if (fd < 0)
{
if (errno == EEXIST && (oflag & O_EXCL) == 0)
goto exists; /* already exists, OK */
return SEM_FAILED;
}
created = 1;
/* First one to create the file initializes it. */
if (!AllocateLocallyUniqueId (&sf.luid))
{
__seterrno ();
goto err;
}
sf.value = value;
sf.hash = hash_path_name (0, semname);
if (write (fd, &sf, sizeof sf) != sizeof sf)
goto err;
sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, wasopen);
if (sem == SEM_FAILED)
goto err;
/* Initialization complete, turn off user-execute bit */
if (fchmod (fd, mode) == -1)
goto err;
/* Don't close (fd); */
return sem;
}
exists:
/* Open the file and fetch the semaphore name. */
if ((fd = open (semname, O_RDWR)) < 0)
{
if (errno == ENOENT && (oflag & O_CREAT))
goto again;
goto err;
}
/* Make certain initialization is complete */
for (i = 0; i < MAX_TRIES; i++)
{
if (stat64 (semname, &statbuff) == -1)
{
if (errno == ENOENT && (oflag & O_CREAT))
{
close (fd);
goto again;
}
goto err;
}
if ((statbuff.st_mode & S_IXUSR) == 0)
break;
sleep (1);
}
if (i == MAX_TRIES)
{
set_errno (ETIMEDOUT);
goto err;
}
if (file.lock (fd, sizeof sf))
goto err;
if (read (fd, &sf, sizeof sf) != sizeof sf)
goto err;
sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value, wasopen);
file.unlock (fd);
if (sem == SEM_FAILED)
goto err;
/* If wasopen is set, the semaphore was already opened and we already have
an open file descriptor pointing to the file. This means, we have to
close the file descriptor created in this call. It won't be stored
anywhere anyway. */
if (wasopen)
close (fd);
return sem;
err:
/* Don't let following function calls change errno */
save_errno save;
file.unlock (fd);
if (created)
unlink (semname);
if (sem != SEM_FAILED)
semaphore::close (sem);
close (fd);
return SEM_FAILED;
}
int
_sem_close (sem_t *sem, bool do_close)
{
sem_finfo sf;
int fd, ret = -1;
ipc_flock file;
if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
return -1;
if (!file.lock (fd, sizeof sf)
&& lseek64 (fd, 0LL, SEEK_SET) != (_off64_t) -1
&& write (fd, &sf, sizeof sf) == sizeof sf)
ret = do_close ? semaphore::close (sem) : 0;
/* Don't let following function calls change errno */
save_errno save;
file.unlock (fd);
close (fd);
return ret;
}
extern "C" int
sem_close (sem_t *sem)
{
return _sem_close (sem, true);
}
extern "C" int
sem_unlink (const char *name)
{
char semname[CYG_MAX_PATH];
if (!check_path (semname, semaphore, name))
return -1;
if (unlink (semname) == -1)
return -1;
return 0;
}

View File

@ -1,6 +1,6 @@
/* pthread.cc: posix pthread interface for Cygwin
Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2007 Red Hat, Inc.
Originally written by Marco Fuykschot <marco@ddi.nl>
@ -164,60 +164,6 @@ sem_destroy (sem_t * sem)
return semaphore::destroy (sem);
}
/* Mangle semaphore name to follow windows naming rules. Prepend "Global\"
if running on terminal service aware machine. Substitute invalid backslash
by forward slash characters, hoping not to collide. */
static bool
mangle_sem_name (char *mangled, const char *name)
{
myfault efault;
if (efault.faulted (EFAULT))
return false;
if (!*name)
{
set_errno (ENOENT);
return false;
}
size_t len = strlen (cygheap->shared_prefix);
if (strlen (name) >= CYG_MAX_PATH - len)
{
set_errno (EINVAL);
return false;
}
strcpy (mangled, cygheap->shared_prefix);
char *d = mangled + len;
const char *s = name;
while (*s)
*d++ = (*s == '\\') ? '/' : *s++;
*d = '\0';
return true;
}
sem_t *
sem_open (const char *name, int oflag, ...)
{
mode_t mode = 0;
unsigned int value = 0;
if (oflag & O_CREAT)
{
va_list ap;
va_start (ap, oflag);
mode = va_arg (ap, mode_t);
value = va_arg (ap, unsigned int);
va_end (ap);
}
char mangled_name[CYG_MAX_PATH];
if (!mangle_sem_name (mangled_name, name))
return NULL;
return semaphore::open (mangled_name, oflag, mode, value);
}
int
sem_close (sem_t * sem)
{
return semaphore::destroy (sem);
}
int
sem_wait (sem_t * sem)
{

View File

@ -109,6 +109,8 @@ close_all_files (bool norelease)
{
cygheap->fdtab.lock ();
semaphore::terminate ();
fhandler_base *fh;
for (int i = 0; i < (int) cygheap->fdtab.size; i++)
if ((fh = cygheap->fdtab[i]) != NULL)

View File

@ -1,6 +1,6 @@
/* thread.cc: Locking and threading module functions
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Red Hat, Inc.
Originally written by Marco Fuykschot <marco@ddi.nl>
Substantialy enhanced by Robert Collins <rbtcollins@hotmail.com>
@ -784,7 +784,8 @@ pthread::static_cancel_self ()
}
DWORD
cancelable_wait (HANDLE object, DWORD timeout, const cw_cancel_action cancel_action,
cancelable_wait (HANDLE object, DWORD timeout,
const cw_cancel_action cancel_action,
const enum cw_sig_wait sig_wait)
{
DWORD res;
@ -1734,197 +1735,6 @@ pthread_mutexattr::~pthread_mutexattr ()
{
}
List<semaphore> semaphore::semaphores;
semaphore::semaphore (int pshared, unsigned int value)
: verifyable_object (SEM_MAGIC),
shared (pshared),
currentvalue (value),
name (NULL)
{
SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE)
? sec_all : sec_none_nih;
this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, NULL);
if (!this->win32_obj_id)
magic = 0;
semaphores.insert (this);
}
semaphore::semaphore (const char *sem_name, int oflag, mode_t mode,
unsigned int value)
: verifyable_object (SEM_MAGIC),
shared (PTHREAD_PROCESS_SHARED),
currentvalue (value), /* Unused for named semaphores. */
name (NULL)
{
if (oflag & O_CREAT)
{
SECURITY_ATTRIBUTES sa = sec_all;
security_descriptor sd;
if (allow_ntsec)
set_security_attribute (mode, &sa, sd);
this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, sem_name);
if (!this->win32_obj_id)
magic = 0;
if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL))
{
__seterrno ();
CloseHandle (this->win32_obj_id);
magic = 0;
}
}
else
{
this->win32_obj_id = ::OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
sem_name);
if (!this->win32_obj_id)
{
__seterrno ();
magic = 0;
}
}
if (magic)
{
name = new char [strlen (sem_name + 1)];
if (!name)
{
set_errno (ENOSPC);
CloseHandle (this->win32_obj_id);
magic = 0;
}
else
strcpy (name, sem_name);
}
semaphores.insert (this);
}
semaphore::~semaphore ()
{
if (win32_obj_id)
CloseHandle (win32_obj_id);
delete [] name;
semaphores.remove (this);
}
void
semaphore::_post ()
{
if (ReleaseSemaphore (win32_obj_id, 1, &currentvalue))
currentvalue++;
}
int
semaphore::_getvalue (int *sval)
{
long val;
switch (WaitForSingleObject (win32_obj_id, 0))
{
case WAIT_OBJECT_0:
ReleaseSemaphore (win32_obj_id, 1, &val);
*sval = val + 1;
break;
case WAIT_TIMEOUT:
*sval = 0;
break;
default:
set_errno (EAGAIN);
return -1;
}
return 0;
}
int
semaphore::_trywait ()
{
/* FIXME: signals should be able to interrupt semaphores...
We probably need WaitForMultipleObjects here. */
if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
{
set_errno (EAGAIN);
return -1;
}
currentvalue--;
return 0;
}
int
semaphore::_timedwait (const struct timespec *abstime)
{
struct timeval tv;
long waitlength;
myfault efault;
if (efault.faulted ())
{
/* According to SUSv3, abstime need not be checked for validity,
if the semaphore can be locked immediately. */
if (!_trywait ())
return 0;
set_errno (EINVAL);
return -1;
}
gettimeofday (&tv, NULL);
waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000);
waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (waitlength < 0)
waitlength = 0;
switch (cancelable_wait (win32_obj_id, waitlength, cw_cancel_self, cw_sig_eintr))
{
case WAIT_OBJECT_0:
currentvalue--;
break;
case WAIT_SIGNALED:
set_errno (EINTR);
return -1;
case WAIT_TIMEOUT:
set_errno (ETIMEDOUT);
return -1;
default:
debug_printf ("cancelable_wait failed. %E");
__seterrno ();
return -1;
}
return 0;
}
int
semaphore::_wait ()
{
switch (cancelable_wait (win32_obj_id, INFINITE, cw_cancel_self, cw_sig_eintr))
{
case WAIT_OBJECT_0:
currentvalue--;
break;
case WAIT_SIGNALED:
set_errno (EINTR);
return -1;
default:
debug_printf ("cancelable_wait failed. %E");
break;
}
return 0;
}
void
semaphore::_fixup_after_fork ()
{
if (shared == PTHREAD_PROCESS_PRIVATE)
{
debug_printf ("sem %x in _fixup_after_fork", this);
/* FIXME: duplicate code here and in the constructor. */
this->win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue,
LONG_MAX, NULL);
if (!win32_obj_id)
api_fatal ("failed to create new win32 semaphore, error %d");
}
}
verifyable_object::verifyable_object (long verifyer):
magic (verifyer)
{
@ -3112,6 +2922,185 @@ pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type)
/* Semaphores */
List<semaphore> semaphore::semaphores;
semaphore::semaphore (int pshared, unsigned int value)
: verifyable_object (SEM_MAGIC),
shared (pshared),
currentvalue (value),
fd (-1),
hash (0ULL),
sem (NULL)
{
SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE)
? sec_all : sec_none_nih;
this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, NULL);
if (!this->win32_obj_id)
magic = 0;
semaphores.insert (this);
}
semaphore::semaphore (unsigned long long shash, LUID sluid, int sfd,
sem_t *ssem, int oflag, mode_t mode, unsigned int value)
: verifyable_object (SEM_MAGIC),
shared (PTHREAD_PROCESS_SHARED),
currentvalue (value), /* Unused for named semaphores. */
fd (sfd),
hash (shash),
luid (sluid),
sem (ssem)
{
char name[CYG_MAX_PATH];
__small_sprintf (name, "%scyg_psem/cyg%016X%08x%08x",
wincap.has_terminal_services () ? "Global\\" : "",
hash, luid.HighPart, luid.LowPart);
this->win32_obj_id = ::CreateSemaphore (&sec_all, value, LONG_MAX, name);
if (!this->win32_obj_id)
magic = 0;
if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL))
{
__seterrno ();
CloseHandle (this->win32_obj_id);
magic = 0;
}
semaphores.insert (this);
}
semaphore::~semaphore ()
{
if (win32_obj_id)
CloseHandle (win32_obj_id);
semaphores.remove (this);
}
void
semaphore::_post ()
{
if (ReleaseSemaphore (win32_obj_id, 1, &currentvalue))
currentvalue++;
}
int
semaphore::_getvalue (int *sval)
{
long val;
switch (WaitForSingleObject (win32_obj_id, 0))
{
case WAIT_OBJECT_0:
ReleaseSemaphore (win32_obj_id, 1, &val);
*sval = val + 1;
break;
case WAIT_TIMEOUT:
*sval = 0;
break;
default:
set_errno (EAGAIN);
return -1;
}
return 0;
}
int
semaphore::_trywait ()
{
/* FIXME: signals should be able to interrupt semaphores...
We probably need WaitForMultipleObjects here. */
if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
{
set_errno (EAGAIN);
return -1;
}
currentvalue--;
return 0;
}
int
semaphore::_timedwait (const struct timespec *abstime)
{
struct timeval tv;
long waitlength;
myfault efault;
if (efault.faulted ())
{
/* According to SUSv3, abstime need not be checked for validity,
if the semaphore can be locked immediately. */
if (!_trywait ())
return 0;
set_errno (EINVAL);
return -1;
}
gettimeofday (&tv, NULL);
waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000);
waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (waitlength < 0)
waitlength = 0;
switch (cancelable_wait (win32_obj_id, waitlength, cw_cancel_self, cw_sig_eintr))
{
case WAIT_OBJECT_0:
currentvalue--;
break;
case WAIT_SIGNALED:
set_errno (EINTR);
return -1;
case WAIT_TIMEOUT:
set_errno (ETIMEDOUT);
return -1;
default:
debug_printf ("cancelable_wait failed. %E");
__seterrno ();
return -1;
}
return 0;
}
int
semaphore::_wait ()
{
switch (cancelable_wait (win32_obj_id, INFINITE, cw_cancel_self, cw_sig_eintr))
{
case WAIT_OBJECT_0:
currentvalue--;
break;
case WAIT_SIGNALED:
set_errno (EINTR);
return -1;
default:
debug_printf ("cancelable_wait failed. %E");
break;
}
return 0;
}
void
semaphore::_fixup_after_fork ()
{
if (shared == PTHREAD_PROCESS_PRIVATE)
{
debug_printf ("sem %x in _fixup_after_fork", this);
/* FIXME: duplicate code here and in the constructor. */
this->win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue,
LONG_MAX, NULL);
if (!win32_obj_id)
api_fatal ("failed to create new win32 semaphore, error %d");
}
}
void
semaphore::_terminate ()
{
int _sem_close (sem_t *, bool);
if (sem)
_sem_close (sem, false);
}
/* static members */
int
@ -3141,6 +3130,10 @@ semaphore::destroy (sem_t *sem)
if (!is_good_object (sem))
return EINVAL;
/* It's invalid to destroy a semaphore not opened with sem_init. */
if ((*sem)->fd != -1)
return EINVAL;
/* FIXME - new feature - test for busy against threads... */
delete (*sem);
@ -3148,8 +3141,24 @@ semaphore::destroy (sem_t *sem)
return 0;
}
int
semaphore::close (sem_t *sem)
{
if (!is_good_object (sem))
return EINVAL;
/* It's invalid to close a semaphore not opened with sem_open. */
if ((*sem)->fd == -1)
return EINVAL;
delete (*sem);
delete sem;
return 0;
}
sem_t *
semaphore::open (const char *name, int oflag, mode_t mode, unsigned int value)
semaphore::open (unsigned long long hash, LUID luid, int fd, int oflag,
mode_t mode, unsigned int value, bool &wasopen)
{
if (value > SEM_VALUE_MAX)
{
@ -3157,6 +3166,22 @@ semaphore::open (const char *name, int oflag, mode_t mode, unsigned int value)
return NULL;
}
/* sem_open is supposed to return the same pointer, if the same named
semaphore is opened multiple times in the same process, as long as
the semaphore hasn't been closed or unlinked in the meantime. */
semaphores.mx.lock ();
for (semaphore *sema = semaphores.head; sema; sema = sema->next)
if (sema->fd >= 0 && sema->hash == hash
&& sema->luid.HighPart == luid.HighPart
&& sema->luid.LowPart == sema->luid.LowPart)
{
wasopen = true;
semaphores.mx.unlock ();
return sema->sem;
}
semaphores.mx.unlock ();
wasopen = false;
sem_t *sem = new sem_t;
if (!sem)
{
@ -3164,7 +3189,7 @@ semaphore::open (const char *name, int oflag, mode_t mode, unsigned int value)
return NULL;
}
*sem = new semaphore (name, oflag, mode, value);
*sem = new semaphore (hash, luid, fd, sem, oflag, mode, value);
if (!is_good_object (sem))
{
@ -3239,6 +3264,28 @@ semaphore::getvalue (sem_t *sem, int *sval)
return (*sem)->_getvalue (sval);
}
int
semaphore::getinternal (sem_t *sem, int *sfd, unsigned long long *shash,
LUID *sluid, unsigned int *sval)
{
myfault efault;
if (efault.faulted () || !is_good_object (sem))
{
set_errno (EINVAL);
return -1;
}
if ((*sfd = (*sem)->fd) < 0)
{
set_errno (EINVAL);
return -1;
}
*shash = (*sem)->hash;
*sluid = (*sem)->luid;
/* POSIX defines the value in calls to sem_init/sem_open as unsigned, but
the sem_getvalue gets a pointer to int to return the value. Go figure! */
return (*sem)->_getvalue ((int *)sval);
}
/* pthread_null */
pthread *
pthread_null::get_null_pthread ()

View File

@ -1,6 +1,6 @@
/* thread.h: Locking and threading module definitions
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007 Red Hat, Inc.
Written by Marco Fuykschot <marco@ddi.nl>
Major update 2001 Robert Collins <rbtcollins@hotmail.com>
@ -208,15 +208,15 @@ template <class list_node> class List
mx.unlock ();
}
fast_mutex mx;
list_node *head;
protected:
void mx_init ()
{
if (!mx.init ())
api_fatal ("Could not create mutex for list synchronisation.");
}
fast_mutex mx;
list_node *head;
};
class pthread_key: public verifyable_object
@ -633,23 +633,30 @@ class semaphore: public verifyable_object
public:
static bool is_good_object(sem_t const *);
/* API calls */
static int init (sem_t * sem, int pshared, unsigned int value);
static int destroy (sem_t * sem);
static sem_t *open (const char *name, int oflag, mode_t mode,
unsigned int value);
static int wait (sem_t * sem);
static int post (sem_t * sem);
static int getvalue (sem_t * sem, int *sval);
static int trywait (sem_t * sem);
static int timedwait (sem_t * sem, const struct timespec *abstime);
static int init (sem_t *sem, int pshared, unsigned int value);
static int destroy (sem_t *sem);
static sem_t *open (unsigned long long hash, LUID luid, int fd, int oflag,
mode_t mode, unsigned int value, bool &wasopen);
static int close (sem_t *sem);
static int wait (sem_t *sem);
static int post (sem_t *sem);
static int getvalue (sem_t *sem, int *sval);
static int trywait (sem_t *sem);
static int timedwait (sem_t *sem, const struct timespec *abstime);
static int getinternal (sem_t *sem, int *sfd, unsigned long long *shash,
LUID *sluid, unsigned int *sval);
HANDLE win32_obj_id;
int shared;
long currentvalue;
char *name;
int fd;
unsigned long long hash;
LUID luid;
sem_t *sem;
semaphore (int, unsigned int);
semaphore (const char *name, int oflag, mode_t mode, unsigned int value);
semaphore (unsigned long long, LUID, int, sem_t *, int, mode_t, unsigned int);
~semaphore ();
class semaphore * next;
@ -658,6 +665,10 @@ public:
semaphores.fixup_after_fork ();
semaphores.for_each (&semaphore::_fixup_after_fork);
}
static void terminate ()
{
semaphores.for_each (&semaphore::_terminate);
}
private:
int _wait ();
@ -667,6 +678,7 @@ private:
int _timedwait (const struct timespec *abstime);
void _fixup_after_fork ();
void _terminate ();
static List<semaphore> semaphores;
};