From f6de4c67d98be2827e4adf6bbff47852b70894a3 Mon Sep 17 00:00:00 2001 From: Dick Porter Date: Fri, 24 Mar 2006 17:52:33 +0000 Subject: [PATCH] Bring io-layer up to date on this branch too. svn path=/branches/mono-1-1-8/mono/; revision=58462 --- mono/handles/ChangeLog | 32 ++ mono/handles/Makefile.am | 10 +- mono/handles/hps.c | 73 +-- mono/handles/semdel.c | 34 ++ mono/io-layer/ChangeLog | 307 ++++++++++++ mono/io-layer/Makefile.am | 2 - mono/io-layer/atomic.h | 77 ++- mono/io-layer/collection.c | 51 +- mono/io-layer/collection.h | 12 - mono/io-layer/error.c | 9 +- mono/io-layer/event-private.h | 8 + mono/io-layer/events.c | 600 +++++++++++++++++++++--- mono/io-layer/events.h | 12 +- mono/io-layer/handles-private.h | 79 +++- mono/io-layer/handles.c | 755 ++++++++++++++++-------------- mono/io-layer/io-layer.h | 2 +- mono/io-layer/io.c | 79 +++- mono/io-layer/mono-mutex.c | 15 +- mono/io-layer/mutex-private.h | 3 +- mono/io-layer/mutexes.c | 321 ++++++++++--- mono/io-layer/mutexes.h | 8 +- mono/io-layer/process-private.h | 6 +- mono/io-layer/processes.c | 383 +++++++++++---- mono/io-layer/processes.h | 64 ++- mono/io-layer/semaphore-private.h | 8 + mono/io-layer/semaphores.c | 436 ++++++++++++++--- mono/io-layer/semaphores.h | 2 + mono/io-layer/shared.c | 215 ++++++++- mono/io-layer/shared.h | 3 +- mono/io-layer/sockets.c | 97 ++-- mono/io-layer/thread-private.h | 40 +- mono/io-layer/threads.c | 622 +++++++++++++----------- mono/io-layer/threads.h | 12 +- mono/io-layer/uglify.h | 5 + mono/io-layer/wait.c | 41 +- mono/io-layer/wapi-private.h | 54 ++- 36 files changed, 3282 insertions(+), 1195 deletions(-) create mode 100644 mono/handles/semdel.c diff --git a/mono/handles/ChangeLog b/mono/handles/ChangeLog index 26d78784838cb..889c16db3af20 100644 --- a/mono/handles/ChangeLog +++ b/mono/handles/ChangeLog @@ -1,3 +1,35 @@ +2005-12-23 Dick Porter + + * hps.c: Add named event support + +2005-12-06 Dick Porter + + * hps.c: Add named semaphore support + +2005-11-11 Dick Porter + + * hps.c: Thread handles are shared again + +2005-10-26 Zoltan Varga + + * hps.c (main): Applied patch from Robert Jordan (robertj@gmx.net). Fix + compilation with gcc 2.95. + +2005-07-04 Dick Porter + + * semdel.c: Fixed compilation on macos, put an error message back + at Hari's suggestion. + +2005-07-01 Dick Porter + + * semdel.c: Removed output, and always exit (0), to avoid giving + make trouble + +2005-06-30 Dick Porter + + * semdel.c: Added semdel, for deleting the current shared + semaphore + 2005-04-13 Dick Porter * hps.c: Update to new daemon-less code diff --git a/mono/handles/Makefile.am b/mono/handles/Makefile.am index 4d18f83b90cc8..07a2fbba56686 100644 --- a/mono/handles/Makefile.am +++ b/mono/handles/Makefile.am @@ -1,5 +1,5 @@ -noinst_PROGRAMS = hps +noinst_PROGRAMS = hps semdel INCLUDES = \ -I$(top_srcdir) \ @@ -14,5 +14,13 @@ hps_LDADD = \ $(GMODULE_LIBS) \ -lm +semdel_LDADD = \ + ../io-layer/libwapi.la \ + ../utils/libmonoutils.la \ + $(LIBGC_LIBS) \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + -lm + EXTRA_DIST = ChangeLog diff --git a/mono/handles/hps.c b/mono/handles/hps.c index 3914d48aadfe2..506a8812a74c1 100644 --- a/mono/handles/hps.c +++ b/mono/handles/hps.c @@ -11,10 +11,10 @@ static const guchar *unused_details (struct _WapiHandleShared *handle); static const guchar *unshared_details (struct _WapiHandleShared *handle); -#if 0 static const guchar *thread_details (struct _WapiHandleShared *handle); -#endif static const guchar *namedmutex_details (struct _WapiHandleShared *handle); +static const guchar *namedsem_details (struct _WapiHandleShared *handle); +static const guchar *namedevent_details (struct _WapiHandleShared *handle); static const guchar *process_details (struct _WapiHandleShared *handle); /* This depends on the ordering of the enum WapiHandleType in @@ -25,11 +25,7 @@ static const guchar * (*details[])(struct _WapiHandleShared *)= unused_details, unshared_details, /* file */ unshared_details, /* console */ -#if 0 thread_details, -#else - unshared_details, /* thread */ -#endif unshared_details, /* sem */ unshared_details, /* mutex */ unshared_details, /* event */ @@ -38,21 +34,24 @@ static const guchar * (*details[])(struct _WapiHandleShared *)= process_details, unshared_details, /* pipe */ namedmutex_details, + namedsem_details, + namedevent_details, unused_details, }; int main (int argc, char **argv) { guint32 i; - + guint32 now; + _wapi_shared_layout = _wapi_shm_attach(WAPI_SHM_DATA); - if (_wapi_shared_layout == FALSE) { + if (_wapi_shared_layout == NULL) { g_error ("Failed to attach shared memory!"); exit (-1); } _wapi_fileshare_layout = _wapi_shm_attach(WAPI_SHM_FILESHARE); - if (_wapi_fileshare_layout == FALSE) { + if (_wapi_fileshare_layout == NULL) { g_error ("Failed to attach fileshare shared memory!"); exit (-1); } @@ -63,32 +62,21 @@ int main (int argc, char **argv) _wapi_handle_collect (); } - g_print ("collection: %d signals: %d sem: 0x%x\n", + g_print ("collection: %d sem: 0x%x\n", _wapi_shared_layout->collection_count, - _wapi_shared_layout->signal_count, _wapi_shared_layout->sem_key); + now = (guint32)(time(NULL) & 0xFFFFFFFF); for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { struct _WapiHandleShared *shared; - struct _WapiHandleSharedMetadata *meta; - guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF); - - meta = &_wapi_shared_layout->metadata[i]; shared = &_wapi_shared_layout->handles[i]; - if (shared->stale == TRUE) { - g_print (" (%3x) [%7s] *STALE* (%s)\n", i, - _wapi_handle_typename[shared->type], - details[shared->type](shared)); - } - - shared = &_wapi_shared_layout->handles[meta->offset]; if (shared->type != WAPI_HANDLE_UNUSED) { - g_print ("%3x (%3x) [%7s] %4u %s (%s)\n", - i, meta->offset, + g_print ("%3x (%3d) [%7s] %4u %s (%s)\n", + i, shared->handle_refs, _wapi_handle_typename[shared->type], - now - meta->timestamp, - meta->signalled?"Sg":"Un", + now - shared->timestamp, + shared->signalled?"Sg":"Un", details[shared->type](shared)); } } @@ -117,7 +105,6 @@ static const guchar *unshared_details (struct _WapiHandleShared *handle) return("unshared details"); } -#if 0 static const guchar *thread_details (struct _WapiHandleShared *handle) { static guchar buf[80]; @@ -130,7 +117,6 @@ static const guchar *thread_details (struct _WapiHandleShared *handle) return(buf); } -#endif static const guchar *namedmutex_details (struct _WapiHandleShared *handle) { @@ -147,6 +133,35 @@ static const guchar *namedmutex_details (struct _WapiHandleShared *handle) return(buf); } +static const guchar *namedsem_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + gchar *name; + struct _WapiHandle_namedsem *sem = &handle->u.namedsem; + + name = sem->sharedns.name; + + g_snprintf (buf, sizeof(buf), "[%15s] val: %5u, max: %5d", + name == NULL?(gchar *)"":name, sem->val, sem->max); + + return(buf); +} + +static const guchar *namedevent_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + gchar *name; + struct _WapiHandle_namedevent *event = &handle->u.namedevent; + + name = event->sharedns.name; + + g_snprintf (buf, sizeof(buf), "[%15s] %s count: %5u", + name == NULL?(gchar *)"":name, + event->manual?"Manual":"Auto", event->set_count); + + return(buf); +} + static const guchar *process_details (struct _WapiHandleShared *handle) { static guchar buf[80]; @@ -155,7 +170,7 @@ static const guchar *process_details (struct _WapiHandleShared *handle) name = proc->proc_name; - g_snprintf (buf, sizeof(buf), "[%15s] pid: %5u exit: %u", + g_snprintf (buf, sizeof(buf), "[%25.25s] pid: %5u exit: %u", name==NULL?(gchar *)"":name, proc->id, proc->exitstatus); return(buf); diff --git a/mono/handles/semdel.c b/mono/handles/semdel.c new file mode 100644 index 0000000000000..1a8b873f4d54b --- /dev/null +++ b/mono/handles/semdel.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +/* We're digging into handle internals here... */ +#include +#include +#include + +int main (int argc, char **argv) +{ + int sem_id, ret; + + _wapi_shared_layout = _wapi_shm_attach(WAPI_SHM_DATA); + if (_wapi_shared_layout == FALSE) { + exit (0); + } + + sem_id = semget (_wapi_shared_layout->sem_key, _WAPI_SHARED_SEM_COUNT, 0600); + if (sem_id != -1) { + ret = semctl (sem_id, IPC_RMID, 0); + if (ret == -1) { + g_message ("Error deleting semaphore: %s", + g_strerror (errno)); + } + } + + exit (0); +} diff --git a/mono/io-layer/ChangeLog b/mono/io-layer/ChangeLog index 43f7d05a51e5f..3a8de9d05de82 100644 --- a/mono/io-layer/ChangeLog +++ b/mono/io-layer/ChangeLog @@ -1,3 +1,310 @@ +2006-03-22 Dick Porter + + * handles.c: + * wapi-private.h: + * shared.h: + * shared.c: Delete the semaphores and shared files when the last + process has finished with them + +2006-03-15 Dick Porter + + * events.c: + * io.c: + * mutexes.c: + * processes.c: + * semaphores.c: + * sockets.c: + * threads.c: Explicitly initialise the handle ops struct + +2006-03-14 Dick Porter + + * handles-private.h: + * wapi-private.h: + * wait.c: + * handles.c: Add a 'prewait' stage to the handle waiting, to give + waiting threads a chance to check for bogus states before + blocking. + + * mutexes.c (namedmutex_prewait): Use the prewait stage to check + for named mutexes that have been abandoned in a locked state (eg + if a process exited abnormally while owning the mutex) and + override it if needed. + +2006-03-08 Gonzalo Paniagua Javier + + * wait.c: speed up WaitFor* when the timeout is 0. + +2006-03-03 Dick Porter + + * processes.c (ShellExecuteEx): Cope when some of the parameters + are NULL. + +2006-02-27 Dick Porter + + * mutexes.c: + * mono-mutex.c: + * threads.c: Comparing pthread_t with == is not portable, so use + pthread_equal(). + +Thu Feb 23 18:47:20 GMT 2006 Paolo Molaro + + * *.h, *.h: patch from Dick to make Linuxthreads systems work again. + +2006-02-22 Dick Porter + + * thread-private.h: + * threads.c: There's no need to use the timed-thread support code + any more, as we aren't using the timed join. This greatly + simplifies thread setup, and fixes a memory leak (bug 77521). + + Don't bother to store thread IDs in a hash table, use TLS instead. + The one case that needs to look up the handle of a different + thread (OpenThread()) can do the extra work itself. Fixes bug + 77536. + + * handles.c (_wapi_search_handle): Add a reference when returning + a private handle (shared handles are already reffed.) + +2006-02-17 Dick Porter + + * handles-private.h: + * handles.c: Check handle values passed to array lookups. Fixes + bug 77572. + +2006-02-09 Dick Porter + + * threads.c: Cope with the problems caused by attaching already + existing threads - they don't get the infrastructure to clean up + after themselves, especially the ID to handle hash. This fixes + bug 77468. + +2006-02-06 Gonzalo Paniagua Javier + + * shared.c: use MONO_SHARED_HOSTNAME as a substitute for gethostname() + when creating the shared files in the .wapi directory. Fixes bug #77371. + +2006-02-02 Martin Baulig + + * threads.c: Removed the `WITH_INCLUDED_LIBGC' section; it has + never been used so far. + + * timed-thread.h (TimedThread): Removed the `stack_ptr' field; it + has only been used by the removed code. + +2006-01-03 Neale Ferguson + + * atomic.h: Correct s390x definitions and eliminate compiler warnings. + +2006-01-03 Sebastien Pouliot + + * io.c: Patch _wapi_stat_to_file_attributes against bug #76966, where + sockets could be considered as directory. + +2005-12-23 Dick Porter + + + * semaphores.h: + * semaphores.c: Implement OpenSemaphore + + * mutexes.c: + * mutexes.h: Implement OpenMutex + + * wapi-private.h: + * handles.c: + * events.c: + * events.h: + * event-private.h: Implement named events. Implement OpenEvent. + +2005-12-15 Dick Porter + + * processes.c (CreateProcess): The pipe-based cross-process + exclusion technique was trying to write NULL buffers when appname + was NULL (eg when coming from ShellExecuteEx,) which usually + succeeded even though EFAULT was returned - but sometimes it + failed. This meant that the child process could block forever on + the pipe read. Replace it with the simpler shared handle + semaphore protection used everywhere else. This showed up with + the test case in bug 76684. + +Tue Dec 13 11:41:49 GMT 2005 Paolo Molaro + + * shared.c: fallback to private mmap when shared mmap doesn't work + (like on jffs). + +2005-12-06 Dick Porter + + * wapi-private.h: + * handles.c: + * semaphore-private.h: + * semaphores.c: Implement named semaphores + +2005-11-24 Dick Porter + + * processes.c (GetProcessId): Implement GetProcessId() + +2005-11-17 Dick Porter + + * processes.h: + * processes.c: Implement ShellExecuteEx as a wrapper around + CreateProcess. + +2005-11-11 Dick Porter + + * threads.c: Give mutex abandoning its own exported function, so + it can be called when the runtime is cleaning up. + + * handles.c (_wapi_search_handle_namespace): Do a handle + collection befre starting to check namespace strings, so that any + stale cruft gets removed. + +2005-11-11 Dick Porter + + * threads.c: Move thread handles back into the shared space. + + * handles.c (_wapi_handle_unref): Add support for shared handles + with close() handlers + +2005-11-04 Dick Porter + + * sockets.c (ioctlsocket): Use select instead of if to avoid a + type promotion problem on 64bit freebsd. Based on patch by Lou + Kamenov , fixes bug 76447. + +2005-11-04 Dick Porter + + * io.c (file_write): Only do the file locking if + MONO_STRICT_IO_EMULATION is set. + +2005-10-21 Dick Porter + + * processes.c: + * handles.c (_wapi_lookup_handle): Cope when the shared part of a + handle has been deleted. + (_wapi_handle_unref): And when the deleted shared part is pointed + to as a handle is deleted + +2005-10-20 Dick Porter + + * processes.c (process_set_current): If the expected process + handle slot doesn't contain the correct pid, create a new handle. + + * handles.c (_wapi_search_handle): When the search doesn't find + anything, return failure instead of the last handle we looked at. + (_wapi_handle_ref, _wapi_handle_unref): Don't try to ref or unref + unused handles (makes tracking refcounting bugs easier.) + +2005-10-19 Dick Porter + + * handles.c: + * handles-private.h: + * wapi-private.h: + * processes.c: + * mutexes.c: + * collection.c (_wapi_handle_collect): Remove the shared handle + indirection layer, and use locking instead. Delete other + complexity that is no longer needed. Refcount shared handles and + delete them when needed, but keep the timestamps so that orphaned + handles will be cleaned up eventually. + + * shared.c (_wapi_shm_file): Add processor, OS and struct size + info to the shared file names, to cope with dual-boot and 32/64bit + size issues. Fixes bug 75839. + + * wait.c (WaitForMultipleObjectsEx): No need to distinguish + between shared handles and private, as the wait functions cope + with both together now. + +2005-10-11 Gonzalo Paniagua Javier + + * io.c: removed NO_SIGPIPE macro. + * sockets.c: no need for MSG_NOSIGNAL or ignoring SIGPIPE. + +2005-10-11 Dick Porter + + * sockets.c (_wapi_getsockopt): Translate SO_ERROR results into + w32 error codes. + + * error.c (errno_to_WSA): Don't return a bogus error if someone + asks to translate errno 0. + +2005-10-11 Dick Porter + + * handles.c: Fix several race conditions + +2005-10-11 Miguel de Icaza + + * io.c: no need to block SIGPIPE anymore, as it is ignored. + +2005-10-05 Dick Porter + + * threads.c (_wapi_thread_own_mutex, _wapi_thread_disown_mutex): + Keep a reference to mutexes owned by threads, so they won't be + destroyed prematurely. Fixes dotmsnclient crash. + +2005-09-26 Sebastien Pouliot + + * io.c: Apply patch from #76192 (Can't write files past 2gb on AMD64 + (x86_64)) for Brion on IRC (Dick approved it). + +2005-09-23 Dick Porter + + * processes.c: Don't wait for processes that have already been + signalled; also fix typo. + + * handles.c: Improve locking inside new handle and handle + searching functions. + +2005-09-20 Dick Porter + + * collection.c: + * processes.c: Periodically waitpid for known process IDs. Fixes + bug 74870. + +2005-09-15 Gonzalo Paniagua Javier + + * io.c: + (CreateFile): if the file is a named pipe, treat the handle as a pipe, + not as a file. Fixes bug #76075. + +2005-08-27 Zoltan Varga + + * atomic.h: Add support for intel icc. + +2005-08-25 Zoltan Varga + + * io-layer.h: Include winbase.h not WinBase.h. + +2005-08-19 Dick Porter + + * threads.c, threads.h, thread-private.h: Use a gsize to store the + thread ID, so it can hold a 64 bit value if needed. + +2005-07-26 Gonzalo Paniagua Javier + + * error.c: map ENOENT to WSAECONNREFUSED. It might happen when + connecting to unix sockets. Closes bug #75632. + +2005-07-05 Dick Porter + + * io.c: Make sure SIGPIPE is ignored when calling write(2). + Prevents the runtime exiting when writing to a closed pipe, + fixing bug 75468. + +2005-06-30 Dick Porter + + * shared.c (_wapi_shm_semaphores_init): Add some helpful error + messages when semget () fails due to lack of resources. + +2005-06-21 Dick Porter + + * mutex-private.h: + * thread-private.h: + * mutexes.c: + * threads.c: Keep a list of owned mutexes in each thread handle, + so that it is easier to abandon them when the thread exits. + Removes a bottleneck when multiple threads are finishing in + parallel. + 2005-06-12 Gonzalo Paniagua Javier * io.c: (GetLogicalDrives) when a bogus line is read, don't leak memory. diff --git a/mono/io-layer/Makefile.am b/mono/io-layer/Makefile.am index 2f3b11e33e97e..5200050e003af 100644 --- a/mono/io-layer/Makefile.am +++ b/mono/io-layer/Makefile.am @@ -91,8 +91,6 @@ OTHER_SRC = \ timefuncs.c \ timefuncs.h \ timefuncs-private.h \ - timed-thread.c \ - timed-thread.h \ types.h \ uglify.h \ versioninfo.h \ diff --git a/mono/io-layer/atomic.h b/mono/io-layer/atomic.h index dc2040ab52bcf..72067efd30676 100644 --- a/mono/io-layer/atomic.h +++ b/mono/io-layer/atomic.h @@ -292,14 +292,31 @@ InterlockedCompareExchange(volatile gint32 *dest, "\tCS\t%1,%2,0(1)\n" "\tJNZ\t0b\n" "1:\n" - : "+m" (*dest), "+r" (old) + : "+m" (*dest), "=r" (old) : "r" (exch), "r" (comp) : "1", "cc"); return(old); } #ifndef __s390x__ -# define InterlockedCompareExchangePointer InterlockedCompareExchange +static inline gpointer +InterlockedCompareExchangePointer(volatile gpointer *dest, + gpointer exch, gpointer comp) +{ + gpointer old; + + __asm__ __volatile__ ("\tLA\t1,%0\n" + "0:\tL\t%1,%0\n" + "\tCR\t%1,%3\n" + "\tJNE\t1f\n" + "\tCS\t%1,%2,0(1)\n" + "\tJNZ\t0b\n" + "1:\n" + : "+m" (*dest), "=r" (old) + : "r" (exch), "r" (comp) + : "1", "cc"); + return(old); +} # else static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, @@ -315,7 +332,7 @@ InterlockedCompareExchangePointer(volatile gpointer *dest, "\tCSG\t%1,%2,0(1)\n" "\tJNZ\t0b\n" "1:\n" - : "+m" (*dest), "+r" (old) + : "+m" (*dest), "=r" (old) : "r" (exch), "r" (comp) : "1", "cc"); @@ -370,7 +387,7 @@ InterlockedExchange(volatile gint32 *val, gint32 new_val) "0:\tL\t%1,%0\n" "\tCS\t%1,%2,0(1)\n" "\tJNZ\t0b" - : "+m" (*val), "+r" (ret) + : "+m" (*val), "=r" (ret) : "r" (new_val) : "1", "cc"); @@ -378,18 +395,32 @@ InterlockedExchange(volatile gint32 *val, gint32 new_val) } # ifndef __s390x__ -# define InterlockedExchangePointer InterlockedExchange +static inline gpointer +InterlockedExchangePointer(volatile gpointer *val, gpointer new_val) +{ + gpointer ret; + + __asm__ __volatile__ ("\tLA\t1,%0\n" + "0:\tL\t%1,%0\n" + "\tCS\t%1,%2,0(1)\n" + "\tJNZ\t0b" + : "+m" (*val), "=r" (ret) + : "r" (new_val) + : "1", "cc"); + + return(ret); +} # else static inline gpointer InterlockedExchangePointer(volatile gpointer *val, gpointer new_val) { gpointer ret; - __asm__ __volatile__ ("\tLA\t1,%1\n" + __asm__ __volatile__ ("\tLA\t1,%0\n" "0:\tLG\t%1,%0\n" "\tCSG\t%1,%2,0(1)\n" "\tJNZ\t0b" - : "+m" (*val), "+r" (ret) + : "+m" (*val), "=r" (ret) : "r" (new_val) : "1", "cc"); @@ -610,14 +641,22 @@ static inline gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add) #elif defined(__ia64__) #define WAPI_ATOMIC_ASM +#ifdef __INTEL_COMPILER +#include +#endif + static inline gint32 InterlockedCompareExchange(gint32 volatile *dest, gint32 exch, gint32 comp) { gint32 old; +#ifdef __INTEL_COMPILER + old = _InterlockedCompareExchange (dest, exch, comp); +#else asm volatile ("mov ar.ccv = %2 ;;\n\t" "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t" : "=r" (old) : "r" (dest), "r" (comp), "r" (exch)); +#endif return(old); } @@ -627,15 +666,22 @@ static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest { gpointer old; +#ifdef __INTEL_COMPILER + old = _InterlockedCompareExchangePointer (dest, exch, comp); +#else asm volatile ("mov ar.ccv = %2 ;;\n\t" "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t" : "=r" (old) : "r" (dest), "r" (comp), "r" (exch)); +#endif return(old); } static inline gint32 InterlockedIncrement(gint32 volatile *val) { +#ifdef __INTEL_COMPILER + return _InterlockedIncrement (val); +#else gint32 old; do { @@ -643,10 +689,14 @@ static inline gint32 InterlockedIncrement(gint32 volatile *val) } while (InterlockedCompareExchange (val, old + 1, old) != old); return old + 1; +#endif } static inline gint32 InterlockedDecrement(gint32 volatile *val) { +#ifdef __INTEL_COMPILER + return _InterlockedDecrement (val); +#else gint32 old; do { @@ -654,10 +704,14 @@ static inline gint32 InterlockedDecrement(gint32 volatile *val) } while (InterlockedCompareExchange (val, old - 1, old) != old); return old - 1; +#endif } static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val) { +#ifdef __INTEL_COMPILER + return _InterlockedExchange (dest, new_val); +#else gint32 res; do { @@ -665,10 +719,14 @@ static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val) } while (InterlockedCompareExchange (dest, new_val, res) != res); return res; +#endif } static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val) { +#ifdef __INTEL_COMPILER + return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val); +#else gpointer res; do { @@ -676,17 +734,22 @@ static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpoin } while (InterlockedCompareExchangePointer (dest, new_val, res) != res); return res; +#endif } static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add) { gint32 old; +#ifdef __INTEL_COMPILER + old = _InterlockedExchangeAdd (val, add); +#else do { old = *val; } while (InterlockedCompareExchange (val, old + add, old) != old); return old; +#endif } #else diff --git a/mono/io-layer/collection.c b/mono/io-layer/collection.c index c6d65f0c89a61..eb9e543ebe83b 100644 --- a/mono/io-layer/collection.c +++ b/mono/io-layer/collection.c @@ -4,7 +4,7 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2004 Novell, Inc. + * (C) 2004-2006 Novell, Inc. */ #include @@ -33,6 +33,13 @@ static gpointer collection_thread (gpointer unused G_GNUC_UNUSED) while (1) { //_wapi_handle_dump (); _wapi_handle_update_refs (); + + /* This is an abuse of the collection thread, but it's + * better than creating a new thread just for one more + * function. + */ + _wapi_process_reap (); + nanosleep (&sleepytime, NULL); } @@ -72,51 +79,39 @@ void _wapi_collection_init (void) void _wapi_handle_collect (void) { guint32 count = _wapi_shared_layout->collection_count; - int i; + int i, thr_ret; #ifdef DEBUG - g_message ("%s: (%d) Starting a collection", __func__, getpid ()); + g_message ("%s: (%d) Starting a collection", __func__, + _wapi_getpid ()); #endif /* Become the collection master */ - _WAPI_HANDLE_COLLECTION_UNSAFE; + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); #ifdef DEBUG - g_message ("%s: (%d) Master set", __func__, getpid ()); + g_message ("%s: (%d) Master set", __func__, _wapi_getpid ()); #endif /* If count has changed, someone else jumped in as master */ if (count == _wapi_shared_layout->collection_count) { + guint32 too_old = (guint32)(time(NULL) & 0xFFFFFFFF) - _WAPI_HANDLE_COLLECTION_EXPIRED_INTERVAL; + for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { - struct _WapiHandleShared *shared; - struct _WapiHandleSharedMetadata *meta; - guint32 too_old = (guint32)(time(NULL) & 0xFFFFFFFF) - _WAPI_HANDLE_COLLECTION_EXPIRED_INTERVAL; + struct _WapiHandleShared *data; - meta = &_wapi_shared_layout->metadata[i]; - if (meta->timestamp < too_old && meta->offset != 0) { -#ifdef DEBUG - g_message ("%s: (%d) Deleting metadata slot 0x%x handle 0x%x", __func__, getpid (), i, meta->offset); -#endif - memset (&_wapi_shared_layout->handles[meta->offset], '\0', sizeof(struct _WapiHandleShared)); - memset (&_wapi_shared_layout->metadata[i], '\0', sizeof(struct _WapiHandleSharedMetadata)); - } - - /* Need to blank any handles data that is no - * longer pointed to by a metadata entry too - */ - shared = &_wapi_shared_layout->handles[i]; - if (shared->stale == TRUE) { + data = &_wapi_shared_layout->handles[i]; + if (data->timestamp < too_old) { #ifdef DEBUG - g_message ("%s: (%d) Deleting stale handle 0x%x", __func__, getpid (), i); + g_message ("%s: (%d) Deleting handle 0x%x", __func__, _wapi_getpid (), i); #endif - memset (&_wapi_shared_layout->handles[i], '\0', - sizeof(struct _WapiHandleShared)); + memset (&_wapi_shared_layout->handles[i], '\0', sizeof(struct _WapiHandleShared)); } } for (i = 0; i < _wapi_fileshare_layout->hwm; i++) { struct _WapiFileShare *file_share = &_wapi_fileshare_layout->share_info[i]; - guint32 too_old = (guint32)(time(NULL) & 0xFFFFFFFF) - _WAPI_HANDLE_COLLECTION_EXPIRED_INTERVAL; if (file_share->timestamp < too_old) { memset (file_share, '\0', @@ -127,9 +122,9 @@ void _wapi_handle_collect (void) InterlockedIncrement (&_wapi_shared_layout->collection_count); } - _WAPI_HANDLE_COLLECTION_SAFE; + _wapi_handle_unlock_shared_handles (); #ifdef DEBUG - g_message ("%s: (%d) Collection done", __func__, getpid ()); + g_message ("%s: (%d) Collection done", __func__, _wapi_getpid ()); #endif } diff --git a/mono/io-layer/collection.h b/mono/io-layer/collection.h index 60013ebf3ff35..95831d3f60ce4 100644 --- a/mono/io-layer/collection.h +++ b/mono/io-layer/collection.h @@ -19,18 +19,6 @@ G_BEGIN_DECLS #include -#define _WAPI_HANDLE_COLLECTION_UNSAFE \ - { \ - int _wapi_thr_ret; \ - \ - _wapi_thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_COLLECTION); \ - g_assert(_wapi_thr_ret == 0); - -#define _WAPI_HANDLE_COLLECTION_SAFE \ - _wapi_thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_COLLECTION); \ - g_assert (_wapi_thr_ret == 0); \ - } - extern void _wapi_collection_init (void); extern void _wapi_handle_collect (void); diff --git a/mono/io-layer/error.c b/mono/io-layer/error.c index d1da41ade57db..7b41f3568bb3c 100644 --- a/mono/io-layer/error.c +++ b/mono/io-layer/error.c @@ -70,6 +70,7 @@ errno_to_WSA (guint32 code, const gchar *function_name) gchar *msg; switch (code) { + case 0: result = ERROR_SUCCESS; break; case EACCES: result = WSAEACCES; break; case EADDRINUSE: result = WSAEADDRINUSE; break; case EAFNOSUPPORT: result = WSAEAFNOSUPPORT; break; @@ -116,6 +117,8 @@ errno_to_WSA (guint32 code, const gchar *function_name) case ETIMEDOUT: result = WSAENETDOWN; break; case EWOULDBLOCK: result = WSAEWOULDBLOCK; break; case EADDRNOTAVAIL: result = WSAEADDRNOTAVAIL; break; + /* This might happen with unix sockets */ + case ENOENT: result = WSAECONNREFUSED; break; default: sys_error = strerror (code); msg = g_locale_to_utf8 (sys_error, strlen (sys_error), NULL, NULL, NULL); @@ -207,8 +210,12 @@ _wapi_get_win32_file_error (gint err) ret = ERROR_IO_PENDING; /* best match I could find */ break; + case EPIPE: + ret = ERROR_WRITE_FAULT; + break; + default: - g_message ("Unknown errno: %s\n", strerror (err)); + g_message ("Unknown errno: %s\n", g_strerror (err)); ret = ERROR_GEN_FAILURE; break; } diff --git a/mono/io-layer/event-private.h b/mono/io-layer/event-private.h index 30ceb3cbae24e..aa61229e46799 100644 --- a/mono/io-layer/event-private.h +++ b/mono/io-layer/event-private.h @@ -17,6 +17,7 @@ #include extern struct _WapiHandleOps _wapi_event_ops; +extern struct _WapiHandleOps _wapi_namedevent_ops; extern void _wapi_event_details (gpointer handle_info); @@ -26,4 +27,11 @@ struct _WapiHandle_event guint32 set_count; }; +struct _WapiHandle_namedevent +{ + WapiSharedNamespace sharedns; + gboolean manual; + guint32 set_count; +}; + #endif /* _WAPI_EVENT_PRIVATE_H_ */ diff --git a/mono/io-layer/events.c b/mono/io-layer/events.c index f87421208b771..40687cbcd4eeb 100644 --- a/mono/io-layer/events.c +++ b/mono/io-layer/events.c @@ -26,11 +26,53 @@ static void event_signal(gpointer handle); static gboolean event_own (gpointer handle); +static void namedevent_signal (gpointer handle); +static gboolean namedevent_own (gpointer handle); + struct _WapiHandleOps _wapi_event_ops = { NULL, /* close */ event_signal, /* signal */ event_own, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ +}; + +struct _WapiHandleOps _wapi_namedevent_ops = { + NULL, /* close */ + namedevent_signal, /* signal */ + namedevent_own, /* own */ + NULL, /* is_owned */ +}; + +static gboolean event_pulse (gpointer handle); +static gboolean event_reset (gpointer handle); +static gboolean event_set (gpointer handle); + +static gboolean namedevent_pulse (gpointer handle); +static gboolean namedevent_reset (gpointer handle); +static gboolean namedevent_set (gpointer handle); + +static struct +{ + gboolean (*pulse)(gpointer handle); + gboolean (*reset)(gpointer handle); + gboolean (*set)(gpointer handle); +} event_ops[WAPI_HANDLE_COUNT] = { + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {event_pulse, event_reset, event_set}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {namedevent_pulse, namedevent_reset, namedevent_set}, }; void _wapi_event_details (gpointer handle_info) @@ -47,6 +89,9 @@ static void event_ops_init (void) _wapi_handle_register_capabilities (WAPI_HANDLE_EVENT, WAPI_HANDLE_CAP_WAIT | WAPI_HANDLE_CAP_SIGNAL); + _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDEVENT, + WAPI_HANDLE_CAP_WAIT | + WAPI_HANDLE_CAP_SIGNAL); } static void event_signal(gpointer handle) @@ -82,34 +127,55 @@ static gboolean event_own (gpointer handle) return(TRUE); } -/** - * CreateEvent: - * @security: Ignored for now. - * @manual: Specifies whether the new event handle has manual or auto - * reset behaviour. - * @initial: Specifies whether the new event handle is initially - * signalled or not. - * @name:Pointer to a string specifying the name of this name, or - * %NULL. Currently ignored. - * - * Creates a new event handle. - * - * An event handle is signalled with SetEvent(). If the new handle is - * a manual reset event handle, it remains signalled until it is reset - * with ResetEvent(). An auto reset event remains signalled until a - * single thread has waited for it, at which time the event handle is - * automatically reset to unsignalled. - * - * Return value: A new handle, or %NULL on error. - */ -gpointer CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual, - gboolean initial, const gunichar2 *name G_GNUC_UNUSED) +static void namedevent_signal (gpointer handle) +{ + ResetEvent (handle); +} + +/* NB, always called with the shared handle lock held */ +static gboolean namedevent_own (gpointer handle) +{ + struct _WapiHandle_namedevent *namedevent_handle; + gboolean ok; + +#ifdef DEBUG + g_message ("%s: owning named event handle %p", __func__, handle); +#endif + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT, + (gpointer *)&namedevent_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up named event handle %p", + __func__, handle); + return(FALSE); + } + + if (namedevent_handle->manual == FALSE) { + g_assert (namedevent_handle->set_count > 0); + + if (--namedevent_handle->set_count == 0) { + _wapi_shared_handle_set_signal_state (handle, FALSE); + } + } + + return (TRUE); +} +static gpointer event_create (WapiSecurityAttributes *security G_GNUC_UNUSED, + gboolean manual, gboolean initial) { struct _WapiHandle_event event_handle = {0}; gpointer handle; int thr_ret; - mono_once (&event_ops_once, event_ops_init); + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if an event + * was freshly created + */ + SetLastError (ERROR_SUCCESS); + +#ifdef DEBUG + g_message ("%s: Creating unnamed event", __func__); +#endif event_handle.manual = manual; event_handle.set_count = 0; @@ -147,30 +213,161 @@ gpointer CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean ma return(handle); } +static gpointer namedevent_create (WapiSecurityAttributes *security G_GNUC_UNUSED, + gboolean manual, gboolean initial, + const gunichar2 *name G_GNUC_UNUSED) +{ + struct _WapiHandle_namedevent namedevent_handle = {{{0}}, 0}; + gpointer handle; + gchar *utf8_name; + int thr_ret; + gpointer ret = NULL; + guint32 namelen; + gint32 offset; + + /* w32 seems to guarantee that opening named objects can't + * race each other + */ + thr_ret = _wapi_namespace_lock (); + g_assert (thr_ret == 0); + + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if an event + * was freshly created + */ + SetLastError (ERROR_SUCCESS); + + utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); + +#ifdef DEBUG + g_message ("%s: Creating named event [%s]", __func__, utf8_name); +#endif + + offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDEVENT, + utf8_name); + if (offset == -1) { + /* The name has already been used for a different + * object. + */ + SetLastError (ERROR_INVALID_HANDLE); + goto cleanup; + } else if (offset != 0) { + /* Not an error, but this is how the caller is + * informed that the event wasn't freshly created + */ + SetLastError (ERROR_ALREADY_EXISTS); + } + /* Fall through to create the event handle. */ + + if (offset == 0) { + /* A new named event, so create both the private and + * shared parts + */ + + if (strlen (utf8_name) < MAX_PATH) { + namelen = strlen (utf8_name); + } else { + namelen = MAX_PATH; + } + + memcpy (&namedevent_handle.sharedns.name, utf8_name, namelen); + + namedevent_handle.manual = manual; + namedevent_handle.set_count = 0; + + if (initial == TRUE) { + if (manual == FALSE) { + namedevent_handle.set_count = 1; + } + } + + handle = _wapi_handle_new (WAPI_HANDLE_NAMEDEVENT, + &namedevent_handle); + } else { + /* A new reference to an existing named event, so just + * create the private part + */ + handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDEVENT, + offset, TRUE); + } + + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error creating event handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + goto cleanup; + } + ret = handle; + + if (offset == 0) { + /* Set the initial state, as this is a completely new + * handle + */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + if (initial == TRUE) { + _wapi_shared_handle_set_signal_state (handle, TRUE); + } + + _wapi_handle_unlock_shared_handles (); + } + +#ifdef DEBUG + g_message ("%s: returning event handle %p", __func__, handle); +#endif + +cleanup: + g_free (utf8_name); + + _wapi_namespace_unlock (NULL); + + return(ret); + +} + + /** - * PulseEvent: - * @handle: The event handle. + * CreateEvent: + * @security: Ignored for now. + * @manual: Specifies whether the new event handle has manual or auto + * reset behaviour. + * @initial: Specifies whether the new event handle is initially + * signalled or not. + * @name:Pointer to a string specifying the name of this name, or + * %NULL. Currently ignored. * - * Sets the event handle @handle to the signalled state, and then - * resets it to unsignalled after informing any waiting threads. + * Creates a new event handle. * - * If @handle is a manual reset event, all waiting threads that can be - * released immediately are released. @handle is then reset. If - * @handle is an auto reset event, one waiting thread is released even - * if multiple threads are waiting. + * An event handle is signalled with SetEvent(). If the new handle is + * a manual reset event handle, it remains signalled until it is reset + * with ResetEvent(). An auto reset event remains signalled until a + * single thread has waited for it, at which time the event handle is + * automatically reset to unsignalled. * - * Return value: %TRUE on success, %FALSE otherwise. (Currently only - * ever returns %TRUE). + * Return value: A new handle, or %NULL on error. */ -gboolean PulseEvent(gpointer handle) +gpointer CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, + gboolean manual, gboolean initial, + const gunichar2 *name G_GNUC_UNUSED) +{ + mono_once (&event_ops_once, event_ops_init); + + if (name == NULL) { + return(event_create (security, manual, initial)); + } else { + return(namedevent_create (security, manual, initial, name)); + } +} + +static gboolean event_pulse (gpointer handle) { struct _WapiHandle_event *event_handle; gboolean ok; int thr_ret; - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, - (gpointer *)&event_handle); - if(ok==FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle); + if (ok == FALSE) { g_warning ("%s: error looking up event handle %p", __func__, handle); return(FALSE); @@ -182,10 +379,10 @@ gboolean PulseEvent(gpointer handle) g_assert (thr_ret == 0); #ifdef DEBUG - g_message("%s: Pulsing event handle %p", __func__, handle); + g_message ("%s: Pulsing event handle %p", __func__, handle); #endif - if(event_handle->manual==TRUE) { + if (event_handle->manual == TRUE) { _wapi_handle_set_signal_state (handle, TRUE, TRUE); } else { event_handle->set_count = 1; @@ -197,7 +394,7 @@ gboolean PulseEvent(gpointer handle) pthread_cleanup_pop (0); - if(event_handle->manual==TRUE) { + if (event_handle->manual == TRUE) { /* For a manual-reset event, we're about to try and * get the handle lock again, so give other threads a * chance @@ -211,8 +408,8 @@ gboolean PulseEvent(gpointer handle) * a condition. */ #ifdef DEBUG - g_message("%s: Obtained write lock on event handle %p", - __func__, handle); + g_message ("%s: Obtained write lock on event handle %p", + __func__, handle); #endif pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, handle); @@ -229,31 +426,115 @@ gboolean PulseEvent(gpointer handle) return(TRUE); } +static gboolean namedevent_pulse (gpointer handle) +{ + struct _WapiHandle_namedevent *namedevent_handle; + gboolean ok; + int thr_ret; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT, + (gpointer *)&namedevent_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up named event handle %p", + __func__, handle); + return(FALSE); + } + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + +#ifdef DEBUG + g_message ("%s: Pulsing named event handle %p", __func__, handle); +#endif + + if (namedevent_handle->manual == TRUE) { + _wapi_shared_handle_set_signal_state (handle, TRUE); + } else { + namedevent_handle->set_count = 1; + _wapi_shared_handle_set_signal_state (handle, TRUE); + } + + _wapi_handle_unlock_shared_handles (); + + if (namedevent_handle->manual == TRUE) { + /* For a manual-reset event, we're about to try and + * get the handle lock again, so give other processes + * a chance + */ + _wapi_handle_spin (200); + + /* Reset the handle signal state */ + /* I'm not sure whether or not we need a barrier here + * to make sure that all threads waiting on the event + * have proceeded. Currently we rely on waiting for + * twice the shared handle poll interval. + */ +#ifdef DEBUG + g_message ("%s: Obtained write lock on event handle %p", + __func__, handle); +#endif + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + _wapi_shared_handle_set_signal_state (handle, FALSE); + + _wapi_handle_unlock_shared_handles (); + } + + return(TRUE); +} + /** - * ResetEvent: + * PulseEvent: * @handle: The event handle. * - * Resets the event handle @handle to the unsignalled state. + * Sets the event handle @handle to the signalled state, and then + * resets it to unsignalled after informing any waiting threads. + * + * If @handle is a manual reset event, all waiting threads that can be + * released immediately are released. @handle is then reset. If + * @handle is an auto reset event, one waiting thread is released even + * if multiple threads are waiting. * * Return value: %TRUE on success, %FALSE otherwise. (Currently only * ever returns %TRUE). */ -gboolean ResetEvent(gpointer handle) +gboolean PulseEvent(gpointer handle) +{ + WapiHandleType type; + + if (handle == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + type = _wapi_handle_type (handle); + + if (event_ops[type].pulse == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(event_ops[type].pulse (handle)); +} + +static gboolean event_reset (gpointer handle) { struct _WapiHandle_event *event_handle; gboolean ok; int thr_ret; - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, - (gpointer *)&event_handle); - if(ok==FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle); + if (ok == FALSE) { g_warning ("%s: error looking up event handle %p", __func__, handle); return(FALSE); } #ifdef DEBUG - g_message("%s: Resetting event handle %p", __func__, handle); + g_message ("%s: Resetting event handle %p", __func__, handle); #endif pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, @@ -261,15 +542,15 @@ gboolean ResetEvent(gpointer handle) thr_ret = _wapi_handle_lock_handle (handle); g_assert (thr_ret == 0); - if(_wapi_handle_issignalled (handle)==FALSE) { + if (_wapi_handle_issignalled (handle) == FALSE) { #ifdef DEBUG - g_message("%s: No need to reset event handle %p", __func__, - handle); + g_message ("%s: No need to reset event handle %p", __func__, + handle); #endif } else { #ifdef DEBUG - g_message("%s: Obtained write lock on event handle %p", - __func__, handle); + g_message ("%s: Obtained write lock on event handle %p", + __func__, handle); #endif _wapi_handle_set_signal_state (handle, FALSE, FALSE); @@ -285,29 +566,85 @@ gboolean ResetEvent(gpointer handle) return(TRUE); } +static gboolean namedevent_reset (gpointer handle) +{ + struct _WapiHandle_namedevent *namedevent_handle; + gboolean ok; + int thr_ret; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT, + (gpointer *)&namedevent_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up named event handle %p", + __func__, handle); + return(FALSE); + } + +#ifdef DEBUG + g_message ("%s: Resetting named event handle %p", __func__, handle); +#endif + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + if (_wapi_handle_issignalled (handle) == FALSE) { +#ifdef DEBUG + g_message ("%s: No need to reset named event handle %p", + __func__, handle); +#endif + } else { +#ifdef DEBUG + g_message ("%s: Obtained write lock on named event handle %p", + __func__, handle); +#endif + + _wapi_shared_handle_set_signal_state (handle, FALSE); + } + + namedevent_handle->set_count = 0; + + _wapi_handle_unlock_shared_handles (); + + return(TRUE); +} + /** - * SetEvent: - * @handle: The event handle - * - * Sets the event handle @handle to the signalled state. + * ResetEvent: + * @handle: The event handle. * - * If @handle is a manual reset event, it remains signalled until it - * is reset with ResetEvent(). An auto reset event remains signalled - * until a single thread has waited for it, at which time @handle is - * automatically reset to unsignalled. + * Resets the event handle @handle to the unsignalled state. * * Return value: %TRUE on success, %FALSE otherwise. (Currently only * ever returns %TRUE). */ -gboolean SetEvent(gpointer handle) +gboolean ResetEvent(gpointer handle) +{ + WapiHandleType type; + + if (handle == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + type = _wapi_handle_type (handle); + + if (event_ops[type].reset == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(event_ops[type].reset (handle)); +} + +static gboolean event_set (gpointer handle) { struct _WapiHandle_event *event_handle; gboolean ok; int thr_ret; - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, - (gpointer *)&event_handle); - if(ok==FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle); + if (ok == FALSE) { g_warning ("%s: error looking up event handle %p", __func__, handle); return(FALSE); @@ -319,10 +656,10 @@ gboolean SetEvent(gpointer handle) g_assert (thr_ret == 0); #ifdef DEBUG - g_message("%s: Setting event handle %p", __func__, handle); + g_message ("%s: Setting event handle %p", __func__, handle); #endif - if(event_handle->manual==TRUE) { + if (event_handle->manual == TRUE) { _wapi_handle_set_signal_state (handle, TRUE, TRUE); } else { event_handle->set_count = 1; @@ -337,3 +674,130 @@ gboolean SetEvent(gpointer handle) return(TRUE); } +static gboolean namedevent_set (gpointer handle) +{ + struct _WapiHandle_namedevent *namedevent_handle; + gboolean ok; + int thr_ret; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT, + (gpointer *)&namedevent_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up named event handle %p", + __func__, handle); + return(FALSE); + } + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + +#ifdef DEBUG + g_message ("%s: Setting named event handle %p", __func__, handle); +#endif + + if (namedevent_handle->manual == TRUE) { + _wapi_shared_handle_set_signal_state (handle, TRUE); + } else { + namedevent_handle->set_count = 1; + _wapi_shared_handle_set_signal_state (handle, TRUE); + } + + _wapi_handle_unlock_shared_handles (); + + return(TRUE); +} + +/** + * SetEvent: + * @handle: The event handle + * + * Sets the event handle @handle to the signalled state. + * + * If @handle is a manual reset event, it remains signalled until it + * is reset with ResetEvent(). An auto reset event remains signalled + * until a single thread has waited for it, at which time @handle is + * automatically reset to unsignalled. + * + * Return value: %TRUE on success, %FALSE otherwise. (Currently only + * ever returns %TRUE). + */ +gboolean SetEvent(gpointer handle) +{ + WapiHandleType type; + + if (handle == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + type = _wapi_handle_type (handle); + + if (event_ops[type].set == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(event_ops[type].set (handle)); +} + +gpointer OpenEvent (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, const gunichar2 *name) +{ + gpointer handle; + gchar *utf8_name; + int thr_ret; + gpointer ret = NULL; + gint32 offset; + + mono_once (&event_ops_once, event_ops_init); + + /* w32 seems to guarantee that opening named objects can't + * race each other + */ + thr_ret = _wapi_namespace_lock (); + g_assert (thr_ret == 0); + + utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); + +#ifdef DEBUG + g_message ("%s: Opening named event [%s]", __func__, utf8_name); +#endif + + offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDEVENT, + utf8_name); + if (offset == -1) { + /* The name has already been used for a different + * object. + */ + SetLastError (ERROR_INVALID_HANDLE); + goto cleanup; + } else if (offset == 0) { + /* This name doesn't exist */ + SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */ + goto cleanup; + } + + /* A new reference to an existing named event, so just create + * the private part + */ + handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDEVENT, offset, + TRUE); + + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error opening named event handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + goto cleanup; + } + ret = handle; + +#ifdef DEBUG + g_message ("%s: returning named event handle %p", __func__, handle); +#endif + +cleanup: + g_free (utf8_name); + + _wapi_namespace_unlock (NULL); + + return(ret); + +} diff --git a/mono/io-layer/events.h b/mono/io-layer/events.h index dea6849d65da4..577f57b648354 100644 --- a/mono/io-layer/events.h +++ b/mono/io-layer/events.h @@ -14,11 +14,13 @@ G_BEGIN_DECLS -extern gpointer CreateEvent(WapiSecurityAttributes *security, gboolean manual, - gboolean initial, const gunichar2 *name); -extern gboolean PulseEvent(gpointer handle); -extern gboolean ResetEvent(gpointer handle); -extern gboolean SetEvent(gpointer handle); +extern gpointer CreateEvent (WapiSecurityAttributes *security, gboolean manual, + gboolean initial, const gunichar2 *name); +extern gboolean PulseEvent (gpointer handle); +extern gboolean ResetEvent (gpointer handle); +extern gboolean SetEvent (gpointer handle); +extern gpointer OpenEvent (guint32 access, gboolean inherit, + const gunichar2 *name); G_END_DECLS diff --git a/mono/io-layer/handles-private.h b/mono/io-layer/handles-private.h index c39674ad697cf..ceebb3e1386f5 100644 --- a/mono/io-layer/handles-private.h +++ b/mono/io-layer/handles-private.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,8 @@ #define _WAPI_PRIVATE_HANDLES(x) (_wapi_private_handles [x / _WAPI_HANDLE_INITIAL_COUNT][x % _WAPI_HANDLE_INITIAL_COUNT]) #define _WAPI_PRIVATE_HAVE_SLOT(x) ((GPOINTER_TO_UINT (x) / _WAPI_PRIVATE_MAX_SLOTS) < _WAPI_PRIVATE_MAX_SLOTS && \ _wapi_private_handles [GPOINTER_TO_UINT (x) / _WAPI_HANDLE_INITIAL_COUNT] != NULL) +#define _WAPI_PRIVATE_VALID_SLOT(x) (((x) / _WAPI_HANDLE_INITIAL_COUNT) < _WAPI_PRIVATE_MAX_SLOTS) + #undef DEBUG extern struct _WapiHandleUnshared *_wapi_private_handles []; @@ -36,18 +39,16 @@ extern mono_mutex_t _wapi_global_signal_mutex; extern pthread_cond_t _wapi_global_signal_cond; extern int _wapi_sem_id; +extern pid_t _wapi_getpid (void); extern gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific); extern gpointer _wapi_handle_new_fd (WapiHandleType type, int fd, gpointer handle_specific); extern gpointer _wapi_handle_new_from_offset (WapiHandleType type, - guint32 offset); + guint32 offset, + gboolean timestamp); extern gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, gpointer *handle_specific); -extern gboolean _wapi_copy_handle (gpointer handle, WapiHandleType type, - struct _WapiHandleShared *handle_specific); -extern gboolean _wapi_replace_handle (gpointer handle, WapiHandleType type, - struct _WapiHandleShared *handle_specific); extern gpointer _wapi_search_handle (WapiHandleType type, gboolean (*check)(gpointer, gpointer), gpointer user_data, @@ -66,6 +67,7 @@ extern gboolean _wapi_handle_ops_own (gpointer handle); extern gboolean _wapi_handle_ops_isowned (gpointer handle); extern guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout); +extern void _wapi_handle_ops_prewait (gpointer handle); extern gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, gpointer *handles, @@ -76,8 +78,6 @@ extern void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles); extern int _wapi_handle_wait_signal (void); extern int _wapi_handle_timedwait_signal (struct timespec *timeout); -extern int _wapi_handle_wait_signal_poll_share (void); -extern int _wapi_handle_timedwait_signal_poll_share (struct timespec *timeout); extern int _wapi_handle_wait_signal_handle (gpointer handle); extern int _wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout); @@ -98,19 +98,18 @@ extern void _wapi_handle_foreach (WapiHandleType type, /* This is OK to use for atomic writes of individual struct members, as they * are independent */ -#define WAPI_SHARED_HANDLE_METADATA(handle) _wapi_shared_layout->metadata[_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT((handle))).u.shared.offset] +#define WAPI_SHARED_HANDLE_DATA(handle) _wapi_shared_layout->handles[_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT((handle))).u.shared.offset] -/* Use this for read-only accesses to shared handle structs, so that - * if a handle is updated we always look at the latest copy. For - * write access, use handle_copy/handle_replace so that all the - * handle-type-specific data is updated atomically. - */ -#define WAPI_SHARED_HANDLE_TYPED_DATA(handle, type) _wapi_shared_layout->handles[_wapi_shared_layout->metadata[_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT((handle))).u.shared.offset].offset].u.type +#define WAPI_SHARED_HANDLE_TYPED_DATA(handle, type) _wapi_shared_layout->handles[_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT((handle))).u.shared.offset].u.type static inline WapiHandleType _wapi_handle_type (gpointer handle) { guint32 idx = GPOINTER_TO_UINT(handle); + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(WAPI_HANDLE_COUNT); /* An impossible type */ + } + return(_WAPI_PRIVATE_HANDLES(idx).type); } @@ -122,6 +121,10 @@ static inline void _wapi_handle_set_signal_state (gpointer handle, struct _WapiHandleUnshared *handle_data; int thr_ret; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } + g_assert (!_WAPI_SHARED_HANDLE(_wapi_handle_type (handle))); handle_data = &_WAPI_PRIVATE_HANDLES(idx); @@ -171,30 +174,36 @@ static inline void _wapi_shared_handle_set_signal_state (gpointer handle, guint32 idx = GPOINTER_TO_UINT(handle); struct _WapiHandleUnshared *handle_data; struct _WapiHandle_shared_ref *ref; - struct _WapiHandleSharedMetadata *shared_meta; + struct _WapiHandleShared *shared_data; + + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } g_assert (_WAPI_SHARED_HANDLE(_wapi_handle_type (handle))); handle_data = &_WAPI_PRIVATE_HANDLES(idx); ref = &handle_data->u.shared; - shared_meta = &_wapi_shared_layout->metadata[ref->offset]; - shared_meta->signalled = state; + shared_data = &_wapi_shared_layout->handles[ref->offset]; + shared_data->signalled = state; #ifdef DEBUG g_message ("%s: signalled shared handle offset 0x%x", __func__, ref->offset); #endif - - InterlockedIncrement (&_wapi_shared_layout->signal_count); } static inline gboolean _wapi_handle_issignalled (gpointer handle) { guint32 idx = GPOINTER_TO_UINT(handle); + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(FALSE); + } + if (_WAPI_SHARED_HANDLE(_wapi_handle_type (handle))) { - return(WAPI_SHARED_HANDLE_METADATA(handle).signalled); + return(WAPI_SHARED_HANDLE_DATA(handle).signalled); } else { return(_WAPI_PRIVATE_HANDLES(idx).signalled); } @@ -227,6 +236,10 @@ static inline int _wapi_handle_lock_handle (gpointer handle) g_message ("%s: locking handle %p", __func__, handle); #endif + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(0); + } + _wapi_handle_ref (handle); if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { @@ -239,18 +252,28 @@ static inline int _wapi_handle_lock_handle (gpointer handle) static inline int _wapi_handle_trylock_handle (gpointer handle) { guint32 idx = GPOINTER_TO_UINT(handle); + int ret; #ifdef DEBUG g_message ("%s: locking handle %p", __func__, handle); #endif + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(0); + } + _wapi_handle_ref (handle); if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { return(0); } - return(mono_mutex_trylock (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex)); + ret = mono_mutex_trylock (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex); + if (ret != 0) { + _wapi_handle_unref (handle); + } + + return(ret); } static inline int _wapi_handle_unlock_handle (gpointer handle) @@ -262,6 +285,10 @@ static inline int _wapi_handle_unlock_handle (gpointer handle) g_message ("%s: unlocking handle %p", __func__, handle); #endif + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(0); + } + if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { _wapi_handle_unref (handle); return(0); @@ -288,17 +315,17 @@ static inline void _wapi_handle_spin (guint32 ms) static inline int _wapi_handle_lock_shared_handles (void) { - return(_wapi_shm_sem_lock (_WAPI_SHARED_SEM_HANDLE)); + return(_wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARED_HANDLES)); } static inline int _wapi_handle_trylock_shared_handles (void) { - return(_wapi_shm_sem_trylock (_WAPI_SHARED_SEM_HANDLE)); + return(_wapi_shm_sem_trylock (_WAPI_SHARED_SEM_SHARED_HANDLES)); } static inline int _wapi_handle_unlock_shared_handles (void) { - return(_wapi_shm_sem_unlock (_WAPI_SHARED_SEM_HANDLE)); + return(_wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARED_HANDLES)); } static inline int _wapi_namespace_lock (void) @@ -319,14 +346,14 @@ static inline void _wapi_handle_share_release (struct _WapiFileShare *info) g_assert (info->handle_refs > 0); /* Prevent new entries racing with us */ - thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE); g_assert(thr_ret == 0); if (InterlockedDecrement (&info->handle_refs) == 0) { memset (info, '\0', sizeof(struct _WapiFileShare)); } - thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE); } #endif /* _WAPI_HANDLES_PRIVATE_H_ */ diff --git a/mono/io-layer/handles.c b/mono/io-layer/handles.c index 4478bcbd731ee..cab0dec8b5fed 100644 --- a/mono/io-layer/handles.c +++ b/mono/io-layer/handles.c @@ -4,7 +4,7 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. + * (C) 2002-2006 Novell, Inc. */ #include @@ -49,6 +49,8 @@ static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={ &_wapi_process_ops, &_wapi_pipe_ops, &_wapi_namedmutex_ops, + &_wapi_namedsem_ops, + &_wapi_namedevent_ops, }; static void _wapi_shared_details (gpointer handle_info); @@ -66,6 +68,8 @@ static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = { _wapi_shared_details, /* process */ _wapi_pipe_details, _wapi_shared_details, /* namedmutex */ + _wapi_shared_details, /* namedsem */ + _wapi_shared_details, /* namedevent */ }; const char *_wapi_handle_typename[] = { @@ -81,6 +85,8 @@ const char *_wapi_handle_typename[] = { "Process", "Pipe", "N.Mutex", + "N.Sem", + "N.Event", "Error!!" }; @@ -104,14 +110,42 @@ pthread_cond_t _wapi_global_signal_cond; int _wapi_sem_id; +/* Use this instead of getpid(), to cope with linuxthreads. It's a + * function rather than a variable lookup because we need to get at + * this before share_init() might have been called. + */ +static pid_t _wapi_pid; +static mono_once_t pid_init_once = MONO_ONCE_INIT; + +static void pid_init (void) +{ + _wapi_pid = getpid (); +} + +pid_t _wapi_getpid (void) +{ + mono_once (&pid_init_once, pid_init); + + return(_wapi_pid); +} + + static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER; +static void handle_cleanup (void) +{ + _wapi_shm_semaphores_remove (); +} + static mono_once_t shared_init_once = MONO_ONCE_INIT; static void shared_init (void) { int thr_ret; int idx = 0; + g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0])) + == WAPI_HANDLE_COUNT); + _wapi_fd_reserve = getdtablesize(); do { @@ -120,11 +154,11 @@ static void shared_init (void) _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT; } while(_wapi_fd_reserve > _wapi_private_handle_count); + + _wapi_shm_semaphores_init (); _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA); g_assert (_wapi_shared_layout != NULL); - - _wapi_shm_semaphores_init (); _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE); g_assert (_wapi_fileshare_layout != NULL); @@ -136,12 +170,13 @@ static void shared_init (void) thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL); g_assert (thr_ret == 0); -} -static void _wapi_handle_init_shared_metadata (struct _WapiHandleSharedMetadata *meta) -{ - meta->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF); - meta->signalled = FALSE; + /* Using g_atexit here instead of an explicit function call in + * a cleanup routine lets us cope when a third-party library + * calls exit (eg if an X client loses the connection to its + * server.) + */ + g_atexit (handle_cleanup); } static void _wapi_handle_init_shared (struct _WapiHandleShared *handle, @@ -149,7 +184,9 @@ static void _wapi_handle_init_shared (struct _WapiHandleShared *handle, gpointer handle_specific) { handle->type = type; - handle->stale = FALSE; + handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF); + handle->signalled = FALSE; + handle->handle_refs = 1; if (handle_specific != NULL) { memcpy (&handle->u, handle_specific, sizeof (handle->u)); @@ -179,81 +216,39 @@ static void _wapi_handle_init (struct _WapiHandleUnshared *handle, } } -static guint32 _wapi_handle_new_shared_offset (guint32 offset) -{ - guint32 i; - static guint32 last = 1; - -again: - /* FIXME: expandable array */ - /* leave a few slots at the end so that there's always space - * to move a handle. (We leave the space in the offset table - * too, so we don't have to keep track of inter-segment - * offsets.) - */ - for(i = last; i <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM; i++) { - struct _WapiHandleSharedMetadata *meta = &_wapi_shared_layout->metadata[i]; - - if(meta->offset == 0) { - if (InterlockedCompareExchange (&meta->offset, offset, - 0) == 0) { - last = i + 1; - - _wapi_handle_init_shared_metadata (meta); - return(i); - } else { - /* Someone else beat us to it, just - * continue looking - */ - } - } - } - - if(last > 1) { - /* Try again from the beginning */ - last = 1; - goto again; - } - - /* Will need to expand the array. The caller will sort it out */ - - return(0); -} - static guint32 _wapi_handle_new_shared (WapiHandleType type, gpointer handle_specific) { guint32 offset; static guint32 last = 1; + int thr_ret; - /* The shared memory holds an offset to the real data, so we - * can update the handle RCU-style without taking a lock. - * This function just allocates the next available data slot, - * use _wapi_handle_new_shared_offset to get the offset entry. - */ - /* Leave the first slot empty as a guard */ again: /* FIXME: expandable array */ - /* Leave a few slots at the end so that there's always space - * to move a handle - */ - for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM; - offset++) { + for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) { struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset]; if(handle->type == WAPI_HANDLE_UNUSED) { + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) { last = offset + 1; _wapi_handle_init_shared (handle, type, handle_specific); + + _wapi_handle_unlock_shared_handles (); + return(offset); } else { /* Someone else beat us to it, just * continue looking */ } + + _wapi_handle_unlock_shared_handles (); } } @@ -345,15 +340,25 @@ gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific) while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) { /* Try and expand the array, and have another go */ int idx = SLOT_INDEX (_wapi_private_handle_count); + if (idx >= _WAPI_PRIVATE_MAX_SLOTS) { + break; + } + _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared, _WAPI_HANDLE_INITIAL_COUNT); _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT; } - + thr_ret = mono_mutex_unlock (&scan_mutex); g_assert (thr_ret == 0); pthread_cleanup_pop (0); + + if (handle_idx == 0) { + /* We ran out of slots */ + handle = _WAPI_HANDLE_INVALID; + goto done; + } /* Make sure we left the space for fd mappings */ g_assert (handle_idx >= _wapi_fd_reserve); @@ -366,27 +371,16 @@ gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific) if (_WAPI_SHARED_HANDLE(type)) { /* Add the shared section too */ - guint32 offset, ref; + guint32 ref; - offset = _wapi_handle_new_shared (type, handle_specific); - if (offset == 0) { - _wapi_handle_collect (); - offset = _wapi_handle_new_shared (type, - handle_specific); - if (offset == 0) { - /* FIXME: grow the arrays */ - return (_WAPI_HANDLE_INVALID); - } - } - - ref = _wapi_handle_new_shared_offset (offset); + ref = _wapi_handle_new_shared (type, handle_specific); if (ref == 0) { _wapi_handle_collect (); - ref = _wapi_handle_new_shared_offset (offset); - + ref = _wapi_handle_new_shared (type, handle_specific); if (ref == 0) { /* FIXME: grow the arrays */ - return (_WAPI_HANDLE_INVALID); + handle = _WAPI_HANDLE_INVALID; + goto done; } } @@ -396,18 +390,22 @@ gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific) ref); #endif } - + +done: return(handle); } -gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset) +gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset, + gboolean timestamp) { guint32 handle_idx = 0; - gpointer handle; + gpointer handle = INVALID_HANDLE_VALUE; int thr_ret, i, k; + struct _WapiHandleShared *shared; + guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); mono_once (&shared_init_once, shared_init); - + #ifdef DEBUG g_message ("%s: Creating new handle of type %s to offset %d", __func__, _wapi_handle_typename[type], offset); @@ -416,7 +414,18 @@ gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset) g_assert(!_WAPI_FD_HANDLE(type)); g_assert(_WAPI_SHARED_HANDLE(type)); g_assert(offset != 0); + + shared = &_wapi_shared_layout->handles[offset]; + if (timestamp) { + /* Bump up the timestamp for this offset */ + InterlockedExchange (&shared->timestamp, now); + } + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + g_assert (thr_ret == 0); + for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k]; @@ -424,16 +433,35 @@ gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset) if (handle_data->type == type && handle_data->u.shared.offset == offset) { handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k); - _wapi_handle_ref (handle); + goto first_pass_done; + } + } + } + +first_pass_done: + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + if (handle != INVALID_HANDLE_VALUE) { + _wapi_handle_ref (handle); #ifdef DEBUG - g_message ("%s: Returning old handle %p referencing 0x%x", __func__, handle, offset); + g_message ("%s: Returning old handle %p referencing 0x%x", + __func__, handle, offset); #endif - return (handle); - } - } + return (handle); } + /* Prevent entries expiring under us as we search */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + if (shared->type == WAPI_HANDLE_UNUSED) { + /* Someone deleted this handle while we were working */ + goto done; + } + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex); thr_ret = mono_mutex_lock (&scan_mutex); @@ -458,11 +486,15 @@ gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset) handle = GUINT_TO_POINTER (handle_idx); _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset; - + InterlockedIncrement (&shared->handle_refs); + #ifdef DEBUG g_message ("%s: Allocated new handle %p referencing 0x%x", __func__, handle, offset); #endif + +done: + _wapi_handle_unlock_shared_handles (); return(handle); } @@ -471,6 +503,7 @@ gpointer _wapi_handle_new_fd (WapiHandleType type, int fd, gpointer handle_specific) { struct _WapiHandleUnshared *handle; + int thr_ret; mono_once (&shared_init_once, shared_init); @@ -505,8 +538,16 @@ gpointer _wapi_handle_new_fd (WapiHandleType type, int fd, g_message ("%s: Assigning new fd handle %d", __func__, fd); #endif + /* Prevent file share entries racing with us, when the file + * handle is only half initialised + */ + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE); + g_assert(thr_ret == 0); + _wapi_handle_init (handle, type, handle_specific); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE); + return(GUINT_TO_POINTER(fd)); } @@ -516,6 +557,10 @@ gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, struct _WapiHandleUnshared *handle_data; guint32 handle_idx = GPOINTER_TO_UINT(handle); + if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) { + return(FALSE); + } + handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx); if (handle_data->type != type) { @@ -529,26 +574,17 @@ gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, if (_WAPI_SHARED_HANDLE(type)) { struct _WapiHandle_shared_ref *ref; struct _WapiHandleShared *shared_handle_data; - struct _WapiHandleSharedMetadata *shared_meta; - guint32 offset; - /* Unsafe, because we don't want the handle to vanish - * while we're checking it - */ - _WAPI_HANDLE_COLLECTION_UNSAFE; + ref = &handle_data->u.shared; + shared_handle_data = &_wapi_shared_layout->handles[ref->offset]; - do { - ref = &handle_data->u.shared; - shared_meta = &_wapi_shared_layout->metadata[ref->offset]; - offset = shared_meta->offset; - shared_handle_data = &_wapi_shared_layout->handles[offset]; + if (shared_handle_data->type != type) { + /* The handle must have been deleted on us + */ + return (FALSE); + } - g_assert(shared_handle_data->type == type); - - *handle_specific = &shared_handle_data->u; - } while (offset != shared_meta->offset); - - _WAPI_HANDLE_COLLECTION_SAFE; + *handle_specific = &shared_handle_data->u; } else { *handle_specific = &handle_data->u; } @@ -556,115 +592,6 @@ gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, return(TRUE); } -gboolean _wapi_copy_handle (gpointer handle, WapiHandleType type, - struct _WapiHandleShared *handle_specific) -{ - struct _WapiHandleUnshared *handle_data; - guint32 handle_idx = GPOINTER_TO_UINT(handle); - struct _WapiHandle_shared_ref *ref; - struct _WapiHandleShared *shared_handle_data; - struct _WapiHandleSharedMetadata *shared_meta; - guint32 offset; - - g_assert(_WAPI_SHARED_HANDLE(type)); - -#ifdef DEBUG - g_message ("%s: copying handle %p type %s", __func__, handle, - _wapi_handle_typename[type]); -#endif - - handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx); - - if(handle_data->type != type) { -#ifdef DEBUG - g_message ("%s: incorrect type, %p has type %s", __func__, - handle, _wapi_handle_typename[handle_data->type]); -#endif - - return(FALSE); - } - - if(handle_specific == NULL) { -#ifdef DEBUG - g_message ("%s: Nowhere to store data", __func__); -#endif - - return(FALSE); - } - - do { - ref = &handle_data->u.shared; - shared_meta = &_wapi_shared_layout->metadata[ref->offset]; - offset = shared_meta->offset; - shared_handle_data = &_wapi_shared_layout->handles[offset]; - - g_assert(shared_handle_data->type == type); - - memcpy(handle_specific, shared_handle_data, - sizeof(struct _WapiHandleShared)); - } while (offset != shared_meta->offset); - -#ifdef DEBUG - g_message ("%s: OK", __func__); -#endif - - return(TRUE); -} - -gboolean _wapi_replace_handle (gpointer handle, WapiHandleType type, - struct _WapiHandleShared *handle_specific) -{ - struct _WapiHandleShared *shared_handle_data; - struct _WapiHandleSharedMetadata *shared_meta; - guint32 handle_idx = GPOINTER_TO_UINT(handle); - guint32 old_off, new_off, ref; - -#ifdef DEBUG - g_message ("%s: Replacing handle %p of type %s", __func__, handle, - _wapi_handle_typename[type]); -#endif - - g_assert(_WAPI_SHARED_HANDLE(type)); - g_assert(_WAPI_PRIVATE_HANDLES(handle_idx).type == type); - - ref = _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset; - shared_meta = &_wapi_shared_layout->metadata[ref]; - - do { - old_off = shared_meta->offset; - new_off = _wapi_handle_new_shared (type, handle_specific); - if (new_off == 0) { - _wapi_handle_collect (); - new_off = _wapi_handle_new_shared (type, - handle_specific); - - if (new_off == 0) { - /* FIXME: grow the arrays */ - return (FALSE); - } - } - - shared_handle_data = &_wapi_shared_layout->handles[new_off]; - - memcpy (shared_handle_data, handle_specific, - sizeof(struct _WapiHandleShared)); - - /* An entry can't become fresh again (its going to be - * collected eventually), so no need for atomic ops - * here. - */ - _wapi_shared_layout->handles[old_off].stale = TRUE; - } while(InterlockedCompareExchange (&shared_meta->offset, new_off, - old_off) != old_off); - -#ifdef DEBUG - g_message ("%s: handle at 0x%x is now found at 0x%x", __func__, ref, - new_off); -#endif - - return (TRUE); -} - void _wapi_handle_foreach (WapiHandleType type, gboolean (*on_each)(gpointer test, gpointer user), @@ -710,11 +637,17 @@ gpointer _wapi_search_handle (WapiHandleType type, gpointer *handle_specific) { struct _WapiHandleUnshared *handle_data = NULL; + struct _WapiHandleShared *shared; gpointer ret = NULL; guint32 i, k; gboolean found = FALSE; + int thr_ret; - + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + g_assert (thr_ret == 0); + for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) { for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { handle_data = &_wapi_private_handles [i][k]; @@ -722,6 +655,7 @@ gpointer _wapi_search_handle (WapiHandleType type, if (handle_data->type == type) { ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k); if (check (ret, user_data) == TRUE) { + _wapi_handle_ref (ret); found = TRUE; break; } @@ -729,37 +663,71 @@ gpointer _wapi_search_handle (WapiHandleType type, } } - if (!found) { + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + if (!found && _WAPI_SHARED_HANDLE (type)) { /* Not found yet, so search the shared memory too */ #ifdef DEBUG g_message ("%s: Looking at other shared handles...", __func__); #endif for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { - struct _WapiHandleShared *shared; - struct _WapiHandleSharedMetadata *meta; - WapiHandleType shared_type; - - _WAPI_HANDLE_COLLECTION_UNSAFE; - - meta = &_wapi_shared_layout->metadata[i]; - shared = &_wapi_shared_layout->handles[meta->offset]; - shared_type = shared->type; - - _WAPI_HANDLE_COLLECTION_SAFE; + shared = &_wapi_shared_layout->handles[i]; - if (shared_type == type) { - ret = _wapi_handle_new_from_offset (type, i); - + if (shared->type == type) { + /* Tell new_from_offset to not + * timestamp this handle, because + * otherwise it will ping every handle + * in the list and they will never + * expire + */ + ret = _wapi_handle_new_from_offset (type, i, + FALSE); + if (ret == INVALID_HANDLE_VALUE) { + /* This handle was deleted + * while we were looking at it + */ + continue; + } + #ifdef DEBUG - g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], meta->offset); + g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i); #endif + /* It's possible that the shared part + * of this handle has now been blown + * away (after new_from_offset + * successfully opened it,) if its + * timestamp is too old. The check + * function needs to be aware of this, + * and cope if the handle has + * vanished. + */ if (check (ret, user_data) == TRUE) { - found = TRUE; - handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret)); + /* Timestamp this handle, but make + * sure it still exists first + */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); - break; + if (shared->type == type) { + guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); + InterlockedExchange (&shared->timestamp, now); + + found = TRUE; + handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret)); + + _wapi_handle_unlock_shared_handles (); + break; + } else { + /* It's been deleted, + * so just keep + * looking + */ + _wapi_handle_unlock_shared_handles (); + } } /* This isn't the handle we're looking @@ -772,39 +740,15 @@ gpointer _wapi_search_handle (WapiHandleType type, } if (!found) { + ret = NULL; goto done; } if(handle_specific != NULL) { if (_WAPI_SHARED_HANDLE(type)) { - struct _WapiHandle_shared_ref *ref ; - struct _WapiHandleShared *shared_handle_data; - struct _WapiHandleSharedMetadata *shared_meta; - guint32 offset, now; + g_assert(shared->type == type); - /* Unsafe, because we don't want the handle to - * vanish while we're checking it - */ - _WAPI_HANDLE_COLLECTION_UNSAFE; - - do { - ref = &handle_data->u.shared; - shared_meta = &_wapi_shared_layout->metadata[ref->offset]; - offset = shared_meta->offset; - shared_handle_data = &_wapi_shared_layout->handles[offset]; - - g_assert(shared_handle_data->type == type); - - *handle_specific = &shared_handle_data->u; - } while (offset != shared_meta->offset); - - /* Make sure this handle doesn't vanish in the - * next collection - */ - now = (guint32)(time (NULL) & 0xFFFFFFFF); - InterlockedExchange (&shared_meta->timestamp, now); - - _WAPI_HANDLE_COLLECTION_SAFE; + *handle_specific = &shared->u; } else { *handle_specific = &handle_data->u; } @@ -821,9 +765,9 @@ gint32 _wapi_search_handle_namespace (WapiHandleType type, gchar *utf8_name) { struct _WapiHandleShared *shared_handle_data; - struct _WapiHandleSharedMetadata *shared_meta; guint32 i; gint32 ret = 0; + int thr_ret; g_assert(_WAPI_SHARED_HANDLE(type)); @@ -832,16 +776,22 @@ gint32 _wapi_search_handle_namespace (WapiHandleType type, utf8_name, _wapi_handle_typename[type]); #endif - _WAPI_HANDLE_COLLECTION_UNSAFE; + /* Do a handle collection before starting to look, so that any + * stale cruft gets removed + */ + _wapi_handle_collect (); + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { WapiSharedNamespace *sharedns; - shared_meta = &_wapi_shared_layout->metadata[i]; - shared_handle_data = &_wapi_shared_layout->handles[shared_meta->offset]; + shared_handle_data = &_wapi_shared_layout->handles[i]; - /* Check mutex, event, semaphore, timer, job and file-mapping - * object names. So far only mutex is implemented. + /* Check mutex, event, semaphore, timer, job and + * file-mapping object names. So far only mutex, + * semaphore and event are implemented. */ if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) { continue; @@ -876,7 +826,7 @@ gint32 _wapi_search_handle_namespace (WapiHandleType type, } done: - _WAPI_HANDLE_COLLECTION_SAFE; + _wapi_handle_unlock_shared_handles (); return(ret); } @@ -885,7 +835,19 @@ void _wapi_handle_ref (gpointer handle) { guint32 idx = GPOINTER_TO_UINT(handle); guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); - struct _WapiHandleUnshared *handle_data = &_WAPI_PRIVATE_HANDLES(idx); + struct _WapiHandleUnshared *handle_data; + + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } + + if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) { + g_warning ("%s: Attempting to ref unused handle %p", __func__, + handle); + return; + } + + handle_data = &_WAPI_PRIVATE_HANDLES(idx); InterlockedIncrement (&handle_data->ref); @@ -895,13 +857,15 @@ void _wapi_handle_ref (gpointer handle) * to make sure. */ if (_WAPI_SHARED_HANDLE(handle_data->type)) { - struct _WapiHandleSharedMetadata *shared_meta = &_wapi_shared_layout->metadata[handle_data->u.shared.offset]; + struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset]; - InterlockedExchange (&shared_meta->timestamp, now); + InterlockedExchange (&shared_data->timestamp, now); } #ifdef DEBUG_REFS - g_message ("%s: handle %p ref now %d", __func__, handle, + g_message ("%s: %s handle %p ref now %d", __func__, + _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type], + handle, _WAPI_PRIVATE_HANDLES(idx).ref); #endif } @@ -913,6 +877,16 @@ void _wapi_handle_unref (gpointer handle) gboolean destroy = FALSE; int thr_ret; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } + + if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) { + g_warning ("%s: Attempting to unref unused handle %p", + __func__, handle); + return; + } + /* Possible race condition here if another thread refs the * handle between here and setting the type to UNUSED. I * could lock a mutex, but I'm not sure that allowing a handle @@ -921,7 +895,9 @@ void _wapi_handle_unref (gpointer handle) destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0); #ifdef DEBUG_REFS - g_message ("%s: handle %p ref now %d (destroy %s)", __func__, handle, + g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__, + _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type], + handle, _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE"); #endif @@ -933,8 +909,22 @@ void _wapi_handle_unref (gpointer handle) * same fd racing the memset()) */ struct _WapiHandleUnshared handle_data; + struct _WapiHandleShared shared_handle_data; WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type; void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type); + gboolean is_shared = _WAPI_SHARED_HANDLE(type); + + if (is_shared) { + /* If this is a shared handle we need to take + * the shared lock outside of the scan_mutex + * lock to avoid deadlocks + */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + } + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); #ifdef DEBUG g_message ("%s: Destroying handle %p", __func__, handle); @@ -948,7 +938,7 @@ void _wapi_handle_unref (gpointer handle) _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED; - if (!_WAPI_SHARED_HANDLE(type)) { + if (!is_shared) { /* Destroy the mutex and cond var. We hope nobody * tried to grab them between the handle unlock and * now, but pthreads doesn't have a @@ -959,14 +949,37 @@ void _wapi_handle_unref (gpointer handle) thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond); g_assert (thr_ret == 0); + } else { + struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset]; + + memcpy (&shared_handle_data, shared, + sizeof (struct _WapiHandleShared)); + + /* It's possible that this handle is already + * pointing at a deleted shared section + */ + if (shared->handle_refs > 0) { + shared->handle_refs--; + if (shared->handle_refs == 0) { + memset (shared, '\0', sizeof (struct _WapiHandleShared)); + } + } } - /* The garbage collector will take care of shared data - * if this is a shared handle - */ + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + if (is_shared) { + _wapi_handle_unlock_shared_handles (); + } if (close_func != NULL) { - close_func (handle, &handle_data.u); + if (is_shared) { + close_func (handle, &shared_handle_data.u); + } else { + close_func (handle, &handle_data.u); + } } } } @@ -983,6 +996,10 @@ gboolean _wapi_handle_test_capabilities (gpointer handle, guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(FALSE); + } + type = _WAPI_PRIVATE_HANDLES(idx).type; #ifdef DEBUG @@ -1008,6 +1025,10 @@ void _wapi_handle_ops_close (gpointer handle, gpointer data) guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } + type = _WAPI_PRIVATE_HANDLES(idx).type; if (handle_ops[type] != NULL && @@ -1021,6 +1042,10 @@ void _wapi_handle_ops_signal (gpointer handle) guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } + type = _WAPI_PRIVATE_HANDLES(idx).type; if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) { @@ -1033,6 +1058,10 @@ gboolean _wapi_handle_ops_own (gpointer handle) guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(FALSE); + } + type = _WAPI_PRIVATE_HANDLES(idx).type; if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) { @@ -1047,6 +1076,10 @@ gboolean _wapi_handle_ops_isowned (gpointer handle) guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(FALSE); + } + type = _WAPI_PRIVATE_HANDLES(idx).type; if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) { @@ -1061,6 +1094,10 @@ guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout) guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return(WAIT_FAILED); + } + type = _WAPI_PRIVATE_HANDLES(idx).type; if (handle_ops[type] != NULL && @@ -1071,6 +1108,23 @@ guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout) } } +void _wapi_handle_ops_prewait (gpointer handle) +{ + guint32 idx = GPOINTER_TO_UINT (handle); + WapiHandleType type; + + if (!_WAPI_PRIVATE_VALID_SLOT (idx)) { + return; + } + + type = _WAPI_PRIVATE_HANDLES (idx).type; + + if (handle_ops[type] != NULL && + handle_ops[type]->prewait != NULL) { + handle_ops[type]->prewait (handle); + } +} + /** * CloseHandle: @@ -1180,7 +1234,7 @@ gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) && (_wapi_handle_ops_isowned (handle) == TRUE)) || (_WAPI_SHARED_HANDLE(type) && - WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) || + WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) || (!_WAPI_SHARED_HANDLE(type) && _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) { count++; @@ -1271,24 +1325,6 @@ int _wapi_handle_timedwait_signal (struct timespec *timeout) return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout); } -int _wapi_handle_wait_signal_poll_share (void) -{ -#ifdef DEBUG - g_message ("%s: poll private and shared handles", __func__); -#endif - - return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL); -} - -int _wapi_handle_timedwait_signal_poll_share (struct timespec *timeout) -{ -#ifdef DEBUG - g_message ("%s: poll private and shared handles", __func__); -#endif - - return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout); -} - int _wapi_handle_wait_signal_handle (gpointer handle) { #ifdef DEBUG @@ -1307,7 +1343,7 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle, #endif if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { + if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) { return (0); } if (timeout != NULL) { @@ -1323,7 +1359,7 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle, */ _wapi_handle_spin (100); - if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { + if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) { return (0); } else { return (ETIMEDOUT); @@ -1350,14 +1386,14 @@ gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF); int thr_ret, i, first_unused = -1; gboolean exists = FALSE; - - /* Marking this as COLLECTION_UNSAFE prevents entries from - * expiring under us as we search + + /* Prevents entries from expiring under us as we search */ - _WAPI_HANDLE_COLLECTION_UNSAFE; + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); /* Prevent new entries racing with us */ - thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE); g_assert (thr_ret == 0); /* If a linear scan gets too slow we'll have to fit a hash @@ -1410,7 +1446,7 @@ gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, file_share->device = device; file_share->inode = inode; - file_share->opened_by_pid = getpid (); + file_share->opened_by_pid = _wapi_getpid (); file_share->sharemode = new_sharemode; file_share->access = new_access; file_share->handle_refs = 1; @@ -1422,9 +1458,9 @@ gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, InterlockedExchange (&(*share_info)->timestamp, now); } - thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE); - _WAPI_HANDLE_COLLECTION_SAFE; + _wapi_handle_unlock_shared_handles (); return(exists); } @@ -1460,32 +1496,53 @@ static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info) void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd) { gboolean found = FALSE, proc_fds = FALSE; - pid_t self = getpid(); + pid_t self = _wapi_getpid (); int pid; int thr_ret, i; + /* Prevents entries from expiring under us if we remove this + * one + */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + /* Prevent new entries racing with us */ + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE); + g_assert (thr_ret == 0); + /* If there is no /proc, there's nothing more we can do here */ if (access ("/proc", F_OK) == -1) { _wapi_handle_check_share_by_pid (share_info); - return; + goto done; } - - /* Marking this as COLLECTION_UNSAFE prevents entries from - * expiring under us if we remove this one + + /* If there's another handle that thinks it owns this fd, then even + * if the fd has been closed behind our back consider it still owned. + * See bugs 75764 and 75891 */ - _WAPI_HANDLE_COLLECTION_UNSAFE; - - /* Prevent new entries racing with us */ - thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); - g_assert (thr_ret == 0); + for (i = 0; i < _wapi_fd_reserve; i++) { + struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i); + + if (i != fd && + handle->type == WAPI_HANDLE_FILE) { + struct _WapiHandle_file *file_handle = &handle->u.file; + + if (file_handle->share_info == share_info) { +#ifdef DEBUG + g_message ("%s: handle 0x%x has this file open!", + __func__, i); +#endif + + goto done; + } + } + } for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { struct _WapiHandleShared *shared; - struct _WapiHandleSharedMetadata *meta; struct _WapiHandle_process *process_handle; - meta = &_wapi_shared_layout->metadata[i]; - shared = &_wapi_shared_layout->handles[meta->offset]; + shared = &_wapi_shared_layout->handles[i]; if (shared->type == WAPI_HANDLE_PROCESS) { DIR *fd_dir; @@ -1555,16 +1612,23 @@ void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd) memset (share_info, '\0', sizeof(struct _WapiFileShare)); } - thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); +done: + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE); - _WAPI_HANDLE_COLLECTION_SAFE; + _wapi_handle_unlock_shared_handles (); } void _wapi_handle_dump (void) { struct _WapiHandleUnshared *handle_data; guint32 i, k; - + int thr_ret; + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + g_assert (thr_ret == 0); + for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { handle_data = &_wapi_private_handles [i][k]; @@ -1582,6 +1646,10 @@ void _wapi_handle_dump (void) g_print ("\n"); } } + + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); } static void _wapi_shared_details (gpointer handle_info) @@ -1595,57 +1663,62 @@ void _wapi_handle_update_refs (void) { guint32 i, k; int thr_ret; + guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); - _WAPI_HANDLE_COLLECTION_UNSAFE; + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); /* Prevent file share entries racing with us */ - thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE); g_assert(thr_ret == 0); + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k]; - guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); if (_WAPI_SHARED_HANDLE(handle->type)) { - struct _WapiHandleSharedMetadata *shared_meta; + struct _WapiHandleShared *shared_data; #ifdef DEBUG - g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, - getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]); + g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]); #endif - shared_meta = &_wapi_shared_layout->metadata[handle->u.shared.offset]; + shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset]; #ifdef DEBUG - g_message ("%s: (%d) Updating timestamp of handle 0x%x", - __func__, getpid(), - handle->u.shared.offset); + g_message ("%s: (%d) Updating timestamp of handle 0x%x", __func__, _wapi_getpid (), handle->u.shared.offset); #endif - InterlockedExchange (&shared_meta->timestamp, now); + InterlockedExchange (&shared_data->timestamp, + now); } else if (handle->type == WAPI_HANDLE_FILE) { struct _WapiHandle_file *file_handle = &handle->u.file; #ifdef DEBUG - g_message ("%s: (%d) handle 0x%x is FILE", __func__, - getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k); + g_message ("%s: (%d) handle 0x%x is FILE", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k); #endif g_assert (file_handle->share_info != NULL); #ifdef DEBUG - g_message ("%s: (%d) Inc refs on fileshare 0x%x", - __func__, getpid(), - (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare)); + g_message ("%s: (%d) Inc refs on fileshare 0x%x", __func__, _wapi_getpid (), (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare)); #endif InterlockedExchange (&file_handle->share_info->timestamp, now); } } } + + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); - thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE); - _WAPI_HANDLE_COLLECTION_SAFE; + _wapi_handle_unlock_shared_handles (); } + diff --git a/mono/io-layer/io-layer.h b/mono/io-layer/io-layer.h index f8315fe9984bb..72d24f45c2bb3 100644 --- a/mono/io-layer/io-layer.h +++ b/mono/io-layer/io-layer.h @@ -18,7 +18,7 @@ #define __USE_W32_SOCKETS #include #include -#include +#include #include #include #include diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c index 8552f5d0ce6f7..25062980a05c2 100644 --- a/mono/io-layer/io.c +++ b/mono/io-layer/io.c @@ -60,6 +60,8 @@ struct _WapiHandleOps _wapi_file_ops = { NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; void _wapi_file_details (gpointer handle_info) @@ -94,6 +96,8 @@ struct _WapiHandleOps _wapi_console_ops = { NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; void _wapi_console_details (gpointer handle_info) @@ -108,6 +112,8 @@ struct _WapiHandleOps _wapi_find_ops = { NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; static void pipe_close (gpointer handle, gpointer data); @@ -125,6 +131,8 @@ struct _WapiHandleOps _wapi_pipe_ops = { NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; void _wapi_pipe_details (gpointer handle_info) @@ -193,8 +201,8 @@ static const struct { NULL, NULL, NULL, NULL, NULL, NULL}, }; - static mono_once_t io_ops_once=MONO_ONCE_INIT; +static gboolean lock_while_writing = FALSE; static void io_ops_init (void) { @@ -202,6 +210,10 @@ static void io_ops_init (void) /* WAPI_HANDLE_CAP_WAIT); */ /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */ /* WAPI_HANDLE_CAP_WAIT); */ + + if (g_getenv ("MONO_STRICT_IO_EMULATION") != NULL) { + lock_while_writing = TRUE; + } } /* Some utility functions. @@ -213,6 +225,10 @@ static guint32 _wapi_stat_to_file_attributes (struct stat *buf) /* FIXME: this could definitely be better */ + /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */ + if (S_ISSOCK (buf->st_mode)) + buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */ + if (S_ISDIR (buf->st_mode)) attrs |= FILE_ATTRIBUTE_DIRECTORY; else @@ -342,30 +358,36 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, return(FALSE); } - /* Need to lock the region we're about to write to, because we - * only do advisory locking on POSIX systems - */ - current_pos = lseek (fd, (off_t)0, SEEK_CUR); - if (current_pos == -1) { + if (lock_while_writing) { + /* Need to lock the region we're about to write to, + * because we only do advisory locking on POSIX + * systems + */ + current_pos = lseek (fd, (off_t)0, SEEK_CUR); + if (current_pos == -1) { #ifdef DEBUG - g_message ("%s: handle %p lseek failed: %s", __func__, handle, - strerror (errno)); + g_message ("%s: handle %p lseek failed: %s", __func__, + handle, strerror (errno)); #endif - _wapi_set_last_error_from_errno (); - return(FALSE); - } + _wapi_set_last_error_from_errno (); + return(FALSE); + } - if (_wapi_lock_file_region (fd, current_pos, numbytes) == FALSE) { - /* The error has already been set */ - return(FALSE); + if (_wapi_lock_file_region (fd, current_pos, + numbytes) == FALSE) { + /* The error has already been set */ + return(FALSE); + } } do { ret = write (fd, buffer, numbytes); } while (ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending()); - - _wapi_unlock_file_region (fd, current_pos, numbytes); + + if (lock_while_writing) { + _wapi_unlock_file_region (fd, current_pos, numbytes); + } if (ret == -1) { if (errno == EINTR) { @@ -484,7 +506,7 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, offset, movedistance); #endif } else { - offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance; + offset=((gint64) *highmovedistance << 32) | (guint32)movedistance; #ifdef DEBUG g_message("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance); @@ -610,8 +632,8 @@ static gboolean file_setendoffile(gpointer handle) * drop this write. */ do { - ret=write(fd, "", 1); - } while (ret==-1 && errno==EINTR && + ret = write (fd, "", 1); + } while (ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending()); if(ret==-1) { @@ -1017,8 +1039,9 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, } do { - ret=write(fd, buffer, numbytes); - } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); + ret = write(fd, buffer, numbytes); + } while (ret == -1 && errno == EINTR && + !_wapi_thread_cur_apc_pending()); if (ret == -1) { if (errno == EINTR) { @@ -1163,8 +1186,9 @@ static gboolean pipe_write(gpointer handle, gconstpointer buffer, #endif do { - ret=write(fd, buffer, numbytes); - } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); + ret = write (fd, buffer, numbytes); + } while (ret == -1 && errno == EINTR && + !_wapi_thread_cur_apc_pending()); if (ret == -1) { if (errno == EINTR) { @@ -1413,6 +1437,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, mode_t perms=0644; gchar *filename; int fd, ret; + int handle_type; struct stat statbuf; mono_once (&io_ops_once, io_ops_init); @@ -1525,8 +1550,12 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, file_handle.fileaccess=fileaccess; file_handle.sharemode=sharemode; file_handle.attrs=attrs; - - handle = _wapi_handle_new_fd (WAPI_HANDLE_FILE, fd, &file_handle); + +#ifndef S_ISFIFO +#define S_ISFIFO(m) ((m & S_IFIFO) != 0) +#endif + handle_type = (S_ISFIFO (statbuf.st_mode)) ? WAPI_HANDLE_PIPE : WAPI_HANDLE_FILE; + handle = _wapi_handle_new_fd (handle_type, fd, &file_handle); if (handle == _WAPI_HANDLE_INVALID) { g_warning ("%s: error creating file handle", __func__); g_free (filename); diff --git a/mono/io-layer/mono-mutex.c b/mono/io-layer/mono-mutex.c index 104c36b46fccf..a1ea18fdbdce5 100644 --- a/mono/io-layer/mono-mutex.c +++ b/mono/io-layer/mono-mutex.c @@ -199,11 +199,11 @@ mono_mutex_lock (mono_mutex_t *mutex) return EINVAL; while (1) { - if (mutex->owner == MONO_THREAD_NONE) { + if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) { mutex->owner = id; mutex->depth = 1; break; - } else if (mutex->owner == id) { + } else if (pthread_equal (mutex->owner, id)) { mutex->depth++; break; } else { @@ -234,13 +234,14 @@ mono_mutex_trylock (mono_mutex_t *mutex) if (pthread_mutex_lock (&mutex->mutex) != 0) return EINVAL; - if (mutex->owner != MONO_THREAD_NONE && mutex->owner != id) { + if (!pthread_equal (mutex->owner, MONO_THREAD_NONE) && + !pthread_equal (mutex->owner, id)) { pthread_mutex_unlock (&mutex->mutex); return EBUSY; } while (1) { - if (mutex->owner == MONO_THREAD_NONE) { + if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) { mutex->owner = id; mutex->depth = 1; break; @@ -271,11 +272,11 @@ mono_mutex_timedlock (mono_mutex_t *mutex, const struct timespec *timeout) return ETIMEDOUT; while (1) { - if (mutex->owner == MONO_THREAD_NONE) { + if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) { mutex->owner = id; mutex->depth = 1; break; - } else if (mutex->owner == id) { + } else if (pthread_equal (mutex->owner, id)) { mutex->depth++; break; } else { @@ -304,7 +305,7 @@ mono_mutex_unlock (mono_mutex_t *mutex) if (pthread_mutex_lock (&mutex->mutex) != 0) return EINVAL; - if (mutex->owner != pthread_self()) { + if (pthread_equal (mutex->owner, pthread_self())) { /* Not owned by this thread */ pthread_mutex_unlock (&mutex->mutex); return EPERM; diff --git a/mono/io-layer/mutex-private.h b/mono/io-layer/mutex-private.h index ac6558a4985a0..4faa3b720233c 100644 --- a/mono/io-layer/mutex-private.h +++ b/mono/io-layer/mutex-private.h @@ -30,12 +30,11 @@ struct _WapiHandle_mutex struct _WapiHandle_namedmutex { WapiSharedNamespace sharedns; - guint32 is_owned; pid_t pid; pthread_t tid; guint32 recursion; }; -extern void _wapi_mutex_check_abandoned (pid_t pid, pthread_t tid); +extern void _wapi_mutex_abandon (gpointer data, pid_t pid, pthread_t tid); #endif /* _WAPI_MUTEX_PRIVATE_H_ */ diff --git a/mono/io-layer/mutexes.c b/mono/io-layer/mutexes.c index dccb486ead125..e4f51eb6280fe 100644 --- a/mono/io-layer/mutexes.c +++ b/mono/io-layer/mutexes.c @@ -4,7 +4,7 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. + * (C) 2002-2006 Ximian, Inc. */ #include @@ -29,12 +29,15 @@ static gboolean mutex_is_owned (gpointer handle); static void namedmutex_signal (gpointer handle); static gboolean namedmutex_own (gpointer handle); static gboolean namedmutex_is_owned (gpointer handle); +static void namedmutex_prewait (gpointer handle); struct _WapiHandleOps _wapi_mutex_ops = { NULL, /* close */ mutex_signal, /* signal */ mutex_own, /* own */ mutex_is_owned, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; void _wapi_mutex_details (gpointer handle_info) @@ -55,6 +58,8 @@ struct _WapiHandleOps _wapi_namedmutex_ops = { namedmutex_signal, /* signal */ namedmutex_own, /* own */ namedmutex_is_owned, /* is_owned */ + NULL, /* special_wait */ + namedmutex_prewait /* prewait */ }; static gboolean mutex_release (gpointer handle); @@ -102,13 +107,15 @@ static gboolean mutex_own (gpointer handle) struct _WapiHandle_mutex *mutex_handle; gboolean ok; - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, - (gpointer *)&mutex_handle); - if(ok==FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle); + if (ok == FALSE) { g_warning ("%s: error looking up mutex handle %p", __func__, handle); return(FALSE); } + + _wapi_thread_own_mutex (handle); #ifdef DEBUG g_message("%s: owning mutex handle %p", __func__, handle); @@ -116,7 +123,7 @@ static gboolean mutex_own (gpointer handle) _wapi_handle_set_signal_state (handle, FALSE, FALSE); - mutex_handle->pid = getpid (); + mutex_handle->pid = _wapi_getpid (); mutex_handle->tid = pthread_self (); mutex_handle->recursion++; @@ -147,17 +154,17 @@ static gboolean mutex_is_owned (gpointer handle) #endif if (mutex_handle->recursion > 0 && - mutex_handle->pid == getpid () && - mutex_handle->tid == pthread_self ()) { + mutex_handle->pid == _wapi_getpid () && + pthread_equal (mutex_handle->tid, pthread_self ())) { #ifdef DEBUG g_message ("%s: mutex handle %p owned by %d:%ld", __func__, - handle, getpid (), pthread_self ()); + handle, _wapi_getpid (), pthread_self ()); #endif return(TRUE); } else { #ifdef DEBUG - g_message ("%s: mutex handle %p not owned by %d:%ld, but locked %d times by %d:%ld", __func__, handle, getpid (), pthread_self (), mutex_handle->recursion, mutex_handle->pid, mutex_handle->tid); + g_message ("%s: mutex handle %p not owned by %d:%ld, but locked %d times by %d:%ld", __func__, handle, _wapi_getpid (), pthread_self (), mutex_handle->recursion, mutex_handle->pid, mutex_handle->tid); #endif return(FALSE); @@ -169,9 +176,9 @@ static void namedmutex_signal (gpointer handle) ReleaseMutex(handle); } +/* NB, always called with the shared handle lock held */ static gboolean namedmutex_own (gpointer handle) { - struct _WapiHandleShared shared_handle; struct _WapiHandle_namedmutex *namedmutex_handle; gboolean ok; @@ -179,26 +186,20 @@ static gboolean namedmutex_own (gpointer handle) g_message ("%s: owning named mutex handle %p", __func__, handle); #endif - ok = _wapi_copy_handle (handle, WAPI_HANDLE_NAMEDMUTEX, - &shared_handle); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDMUTEX, + (gpointer *)&namedmutex_handle); if (ok == FALSE) { g_warning ("%s: error looking up named mutex handle %p", __func__, handle); return(FALSE); } - namedmutex_handle = &shared_handle.u.namedmutex; - namedmutex_handle->pid = getpid (); + _wapi_thread_own_mutex (handle); + + namedmutex_handle->pid = _wapi_getpid (); namedmutex_handle->tid = pthread_self (); namedmutex_handle->recursion++; - ok = _wapi_replace_handle (handle, WAPI_HANDLE_NAMEDMUTEX, - &shared_handle); - if (ok == FALSE) { - SetLastError (ERROR_OUTOFMEMORY); - return (FALSE); - } - _wapi_shared_handle_set_signal_state (handle, FALSE); #ifdef DEBUG @@ -228,34 +229,134 @@ static gboolean namedmutex_is_owned (gpointer handle) #endif if (namedmutex_handle->recursion > 0 && - namedmutex_handle->pid == getpid () && - namedmutex_handle->tid == pthread_self ()) { + namedmutex_handle->pid == _wapi_getpid () && + pthread_equal (namedmutex_handle->tid, pthread_self ())) { #ifdef DEBUG g_message ("%s: mutex handle %p owned by %d:%ld", __func__, - handle, getpid (), pthread_self ()); + handle, _wapi_getpid (), pthread_self ()); #endif return(TRUE); } else { #ifdef DEBUG - g_message ("%s: mutex handle %p not owned by %d:%ld, but locked %d times by %d:%ld", __func__, handle, getpid (), pthread_self (), namedmutex_handle->recursion, namedmutex_handle->pid, namedmutex_handle->tid); + g_message ("%s: mutex handle %p not owned by %d:%ld, but locked %d times by %d:%ld", __func__, handle, _wapi_getpid (), pthread_self (), namedmutex_handle->recursion, namedmutex_handle->pid, namedmutex_handle->tid); #endif return(FALSE); } } -struct mutex_check_data +/* The shared state is not locked when prewait methods are called */ +static void namedmutex_prewait (gpointer handle) { - pid_t pid; - pthread_t tid; -}; + /* If the mutex is not currently owned, do nothing and let the + * usual wait carry on. If it is owned, check that the owner + * is still alive; if it isn't we override the previous owner + * and assume that process exited abnormally and failed to + * clean up. + */ + struct _WapiHandle_namedmutex *namedmutex_handle; + gboolean ok; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDMUTEX, + (gpointer *)&namedmutex_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up named mutex handle %p", + __func__, handle); + return; + } + +#ifdef DEBUG + g_message ("%s: Checking ownership of named mutex handle %p", __func__, + handle); +#endif + + if (namedmutex_handle->recursion == 0) { +#ifdef DEBUG + g_message ("%s: Named mutex handle %p not owned", __func__, + handle); +#endif + } else if (namedmutex_handle->pid == _wapi_getpid ()) { +#ifdef DEBUG + g_message ("%s: Named mutex handle %p owned by this process", + __func__, handle); +#endif + } else { + guint32 *pids = g_new0 (guint32, 32); + guint32 count = 32, needed_bytes, i; + gboolean ret; + int thr_ret; + +#ifdef DEBUG + g_message ("%s: Named mutex handle %p owned by another process", __func__, handle); +#endif + + ret = EnumProcesses (pids, count * sizeof(guint32), + &needed_bytes); + if (ret == FALSE) { + do { + count = needed_bytes / sizeof(guint32); +#ifdef DEBUG + g_message ("%s: Retrying pid lookup with %d slots", __func__, count); +#endif + pids = g_renew (guint32, pids, count); + ret = EnumProcesses (pids, needed_bytes, + &needed_bytes); + } while (ret == FALSE); + } + + count = needed_bytes / sizeof(guint32); + +#ifdef DEBUG + g_message ("%s: Need to look at %d pids for named mutex handle %p", __func__, count, handle); +#endif + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + for (i = 0; i < count; i++) { +#ifdef DEBUG + g_message ("%s: Checking pid %d for named mutex handle %p", __func__, pids[i], handle); +#endif + + if (pids[i] == namedmutex_handle->pid) { + /* Must be still alive, because + * EnumProcesses() checks for us + */ +#ifdef DEBUG + g_message ("%s: Found active pid %d for named mutex handle %p", __func__, pids[i], handle); +#endif + + break; + } + } + + g_free (pids); + + if (i == count) { + /* Didn't find the process that this handle + * was owned by, overriding it + */ + +#ifdef DEBUG + g_message ("%s: overriding old owner of named mutex handle %p", __func__, handle); +#endif + + namedmutex_handle->pid = 0; + namedmutex_handle->tid = 0; + namedmutex_handle->recursion = 0; -static gboolean mutex_check (gpointer handle, gpointer user_data) + _wapi_shared_handle_set_signal_state (handle, TRUE); + } + + _wapi_handle_unlock_shared_handles (); + } +} + +static void mutex_abandon (gpointer handle, pid_t pid, pthread_t tid) { struct _WapiHandle_mutex *mutex_handle; gboolean ok; - struct mutex_check_data *data = (struct mutex_check_data *)user_data; int thr_ret; ok = _wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, @@ -263,7 +364,7 @@ static gboolean mutex_check (gpointer handle, gpointer user_data) if (ok == FALSE) { g_warning ("%s: error looking up mutex handle %p", __func__, handle); - return(FALSE); + return; } pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, @@ -271,8 +372,8 @@ static gboolean mutex_check (gpointer handle, gpointer user_data) thr_ret = _wapi_handle_lock_handle (handle); g_assert (thr_ret == 0); - if (mutex_handle->pid == data->pid && - mutex_handle->tid == data->tid) { + if (mutex_handle->pid == pid && + pthread_equal (mutex_handle->tid, tid)) { #ifdef DEBUG g_message ("%s: Mutex handle %p abandoned!", __func__, handle); #endif @@ -287,16 +388,12 @@ static gboolean mutex_check (gpointer handle, gpointer user_data) thr_ret = _wapi_handle_unlock_handle (handle); g_assert (thr_ret == 0); pthread_cleanup_pop (0); - - /* Return false to keep searching */ - return(FALSE); } -static gboolean namedmutex_check (gpointer handle, gpointer user_data) +static void namedmutex_abandon (gpointer handle, pid_t pid, pthread_t tid) { struct _WapiHandle_namedmutex *mutex_handle; gboolean ok; - struct mutex_check_data *data = (struct mutex_check_data *)user_data; int thr_ret; ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDMUTEX, @@ -304,14 +401,14 @@ static gboolean namedmutex_check (gpointer handle, gpointer user_data) if (ok == FALSE) { g_warning ("%s: error looking up named mutex handle %p", __func__, handle); - return(FALSE); + return; } thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); - if (mutex_handle->pid == data->pid && - mutex_handle->tid == data->tid) { + if (mutex_handle->pid == pid && + pthread_equal (mutex_handle->tid, tid)) { #ifdef DEBUG g_message ("%s: Mutex handle %p abandoned!", __func__, handle); #endif @@ -324,22 +421,23 @@ static gboolean namedmutex_check (gpointer handle, gpointer user_data) } _wapi_handle_unlock_shared_handles (); - - /* Return false to keep searching */ - return(FALSE); } -/* When a thread exits, any mutexes it still holds need to be signalled */ -void _wapi_mutex_check_abandoned (pid_t pid, pthread_t tid) +/* When a thread exits, any mutexes it still holds need to be + * signalled. This function must not be called with the shared handle + * lock held, as namedmutex_abandon () will try to acquire it + */ +void _wapi_mutex_abandon (gpointer data, pid_t pid, pthread_t tid) { - struct mutex_check_data data; + WapiHandleType type = _wapi_handle_type (data); - data.pid = pid; - data.tid = tid; - - _wapi_search_handle (WAPI_HANDLE_MUTEX, mutex_check, &data, NULL); - _wapi_search_handle (WAPI_HANDLE_NAMEDMUTEX, namedmutex_check, &data, - NULL); + if (type == WAPI_HANDLE_MUTEX) { + mutex_abandon (data, pid, tid); + } else if (type == WAPI_HANDLE_NAMEDMUTEX) { + namedmutex_abandon (data, pid, tid); + } else { + g_assert_not_reached (); + } } static gpointer mutex_create (WapiSecurityAttributes *security G_GNUC_UNUSED, @@ -399,10 +497,9 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE guint32 namelen; gint32 offset; - /* w32 seems to guarantee that opening named mutexes can't + /* w32 seems to guarantee that opening named objects can't * race each other */ - pthread_cleanup_push ((void(*)(void *))_wapi_namespace_unlock, NULL); thr_ret = _wapi_namespace_lock (); g_assert (thr_ret == 0); @@ -433,19 +530,20 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE SetLastError (ERROR_ALREADY_EXISTS); } /* Fall through to create the mutex handle. */ - - if (strlen (utf8_name) < MAX_PATH) { - namelen = strlen (utf8_name); - } else { - namelen = MAX_PATH; - } - - memcpy (&namedmutex_handle.sharedns.name, utf8_name, namelen); if (offset == 0) { /* A new named mutex, so create both the private and * shared parts */ + + if (strlen (utf8_name) < MAX_PATH) { + namelen = strlen (utf8_name); + } else { + namelen = MAX_PATH; + } + + memcpy (&namedmutex_handle.sharedns.name, utf8_name, namelen); + handle = _wapi_handle_new (WAPI_HANDLE_NAMEDMUTEX, &namedmutex_handle); } else { @@ -453,7 +551,7 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE * create the private part */ handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDMUTEX, - offset); + offset, TRUE); } if (handle == _WAPI_HANDLE_INVALID) { @@ -485,8 +583,8 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE cleanup: g_free (utf8_name); - /* Releases the timestamp */ - pthread_cleanup_pop (1); + + _wapi_namespace_unlock (NULL); return(ret); } @@ -526,14 +624,14 @@ static gboolean mutex_release (gpointer handle) { struct _WapiHandle_mutex *mutex_handle; gboolean ok; - pthread_t tid=pthread_self(); - pid_t pid=getpid (); + pthread_t tid = pthread_self (); + pid_t pid = _wapi_getpid (); int thr_ret; gboolean ret = FALSE; - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, - (gpointer *)&mutex_handle); - if(ok==FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle); + if (ok == FALSE) { g_warning ("%s: error looking up mutex handle %p", __func__, handle); return(FALSE); @@ -548,9 +646,10 @@ static gboolean mutex_release (gpointer handle) g_message("%s: Releasing mutex handle %p", __func__, handle); #endif - if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) { + if (!pthread_equal (mutex_handle->tid, tid) || + mutex_handle->pid != pid) { #ifdef DEBUG - g_message("%s: We don't own mutex handle %p (owned by %d:%ld, me %d:%ld)", __func__, handle, mutex_handle->pid, mutex_handle->tid, pid, tid); + g_message("%s: We don't own mutex handle %p (owned by %d:%ld, me %d:%ld)", __func__, handle, mutex_handle->pid, mutex_handle->tid, _wapi_getpid (), tid); #endif goto cleanup; @@ -561,6 +660,8 @@ static gboolean mutex_release (gpointer handle) mutex_handle->recursion--; if(mutex_handle->recursion==0) { + _wapi_thread_disown_mutex (handle); + #ifdef DEBUG g_message("%s: Unlocking mutex handle %p", __func__, handle); #endif @@ -582,8 +683,8 @@ static gboolean namedmutex_release (gpointer handle) { struct _WapiHandle_namedmutex *mutex_handle; gboolean ok; - pthread_t tid=pthread_self(); - pid_t pid=getpid (); + pthread_t tid = pthread_self (); + pid_t pid = _wapi_getpid (); int thr_ret; gboolean ret = FALSE; @@ -602,9 +703,10 @@ static gboolean namedmutex_release (gpointer handle) g_message("%s: Releasing mutex handle %p", __func__, handle); #endif - if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) { + if (!pthread_equal (mutex_handle->tid, tid) || + mutex_handle->pid != pid) { #ifdef DEBUG - g_message("%s: We don't own mutex handle %p (owned by %d:%ld, me %d:%ld)", __func__, handle, mutex_handle->pid, mutex_handle->tid, pid, tid); + g_message("%s: We don't own mutex handle %p (owned by %d:%ld, me %d:%ld)", __func__, handle, mutex_handle->pid, mutex_handle->tid, _wapi_getpid (), tid); #endif goto cleanup; @@ -615,6 +717,8 @@ static gboolean namedmutex_release (gpointer handle) mutex_handle->recursion--; if(mutex_handle->recursion==0) { + _wapi_thread_disown_mutex (handle); + #ifdef DEBUG g_message("%s: Unlocking mutex handle %p", __func__, handle); #endif @@ -657,3 +761,64 @@ gboolean ReleaseMutex(gpointer handle) return(mutex_ops[type].release (handle)); } + +gpointer OpenMutex (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, const gunichar2 *name) +{ + gpointer handle; + gchar *utf8_name; + int thr_ret; + gpointer ret = NULL; + gint32 offset; + + mono_once (&mutex_ops_once, mutex_ops_init); + + /* w32 seems to guarantee that opening named objects can't + * race each other + */ + thr_ret = _wapi_namespace_lock (); + g_assert (thr_ret == 0); + + utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); + +#ifdef DEBUG + g_message ("%s: Opening named mutex [%s]", __func__, utf8_name); +#endif + + offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDMUTEX, + utf8_name); + if (offset == -1) { + /* The name has already been used for a different + * object. + */ + SetLastError (ERROR_INVALID_HANDLE); + goto cleanup; + } else if (offset == 0) { + /* This name doesn't exist */ + SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */ + goto cleanup; + } + + /* A new reference to an existing named mutex, so just create + * the private part + */ + handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDMUTEX, offset, + TRUE); + + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error opening named mutex handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + goto cleanup; + } + ret = handle; + +#ifdef DEBUG + g_message ("%s: returning named mutex handle %p", __func__, handle); +#endif + +cleanup: + g_free (utf8_name); + + _wapi_namespace_unlock (NULL); + + return(ret); +} diff --git a/mono/io-layer/mutexes.h b/mono/io-layer/mutexes.h index e57feae8b88c2..ef9e46d9e9435 100644 --- a/mono/io-layer/mutexes.h +++ b/mono/io-layer/mutexes.h @@ -14,9 +14,11 @@ G_BEGIN_DECLS -extern gpointer CreateMutex(WapiSecurityAttributes *security, gboolean owned, - const gunichar2 *name); -extern gboolean ReleaseMutex(gpointer handle); +extern gpointer CreateMutex (WapiSecurityAttributes *security, gboolean owned, + const gunichar2 *name); +extern gboolean ReleaseMutex (gpointer handle); +extern gpointer OpenMutex (guint32 access, gboolean inherit, + const gunichar2 *name); G_END_DECLS diff --git a/mono/io-layer/process-private.h b/mono/io-layer/process-private.h index 0a676e5f3ce28..bccba2448e11c 100644 --- a/mono/io-layer/process-private.h +++ b/mono/io-layer/process-private.h @@ -15,6 +15,8 @@ extern struct _WapiHandleOps _wapi_process_ops; +#define _WAPI_PROC_NAME_MAX_LEN _POSIX_PATH_MAX + struct _WapiHandle_process { pid_t id; @@ -22,9 +24,11 @@ struct _WapiHandle_process gpointer main_thread; WapiFileTime create_time; WapiFileTime exit_time; - gchar proc_name[_POSIX_PATH_MAX]; + gchar proc_name[_WAPI_PROC_NAME_MAX_LEN]; size_t min_working_set; size_t max_working_set; }; +extern void _wapi_process_reap (void); + #endif /* _WAPI_PROCESS_PRIVATE_H_ */ diff --git a/mono/io-layer/processes.c b/mono/io-layer/processes.c index d51f7bcc3783f..8c183e86c5526 100644 --- a/mono/io-layer/processes.c +++ b/mono/io-layer/processes.c @@ -41,7 +41,8 @@ struct _WapiHandleOps _wapi_process_ops = { NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ - process_wait /* special_wait */ + process_wait, /* special_wait */ + NULL /* prewait */ }; static mono_once_t process_current_once=MONO_ONCE_INIT; @@ -56,13 +57,99 @@ static void process_ops_init (void) WAPI_HANDLE_CAP_SPECIAL_WAIT); } +static gboolean process_set_termination_details (gpointer handle, int status) +{ + struct _WapiHandle_process *process_handle; + gboolean ok; + int thr_ret; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, + (gpointer *)&process_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up process handle %p", + __func__, handle); + return(FALSE); + } + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + if (WIFSIGNALED(status)) { + process_handle->exitstatus = 128 + WTERMSIG(status); + } else { + process_handle->exitstatus = WEXITSTATUS(status); + } + _wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time); + + _wapi_shared_handle_set_signal_state (handle, TRUE); + + _wapi_handle_unlock_shared_handles (); + + return (ok); +} + +/* See if any child processes have terminated and wait() for them, + * updating process handle info. This function is called from the + * collection thread every few seconds. + */ +static gboolean waitfor_pid (gpointer test, gpointer user_data G_GNUC_UNUSED) +{ + struct _WapiHandle_process *process; + gboolean ok; + int status; + pid_t ret; + + if (_wapi_handle_issignalled (test)) { + /* We've already done this one */ + return (FALSE); + } + + ok = _wapi_lookup_handle (test, WAPI_HANDLE_PROCESS, + (gpointer *)&process); + if (ok == FALSE) { + /* The handle must have been too old and was reaped */ + return (FALSE); + } + + do { + ret = waitpid (process->id, &status, WNOHANG); + } while (errno == EINTR); + + if (ret <= 0) { + /* Process not ready for wait */ +#ifdef DEBUG + g_message ("%s: Process %d not ready for waiting for: %s", + __func__, process->id, g_strerror (errno)); +#endif + + return (FALSE); + } + +#ifdef DEBUG + g_message ("%s: Process %d finished", __func__, ret); +#endif + + process_set_termination_details (test, status); + + /* return FALSE to keep searching */ + return (FALSE); +} + +void _wapi_process_reap (void) +{ +#ifdef DEBUG + g_message ("%s: Reaping child processes", __func__); +#endif + + _wapi_search_handle (WAPI_HANDLE_PROCESS, waitfor_pid, NULL, NULL); +} + /* Limitations: This can only wait for processes that are our own * children. Fixing this means resurrecting a daemon helper to manage * processes. */ static guint32 process_wait (gpointer handle, guint32 timeout) { - struct _WapiHandleShared shared_handle; struct _WapiHandle_process *process_handle; gboolean ok; pid_t pid, ret; @@ -122,30 +209,11 @@ static guint32 process_wait (gpointer handle, guint32 timeout) g_message ("%s: Wait done", __func__); #endif - ok = _wapi_copy_handle (handle, WAPI_HANDLE_PROCESS, &shared_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up process handle %p", - __func__, handle); - return(WAIT_FAILED); - } - - process_handle = &shared_handle.u.process; - - if (WIFSIGNALED(status)) { - process_handle->exitstatus = 128 + WTERMSIG(status); - } else { - process_handle->exitstatus = WEXITSTATUS(status); - } - _wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time); - - ok = _wapi_replace_handle (handle, WAPI_HANDLE_PROCESS, - &shared_handle); + ok = process_set_termination_details (handle, status); if (ok == FALSE) { SetLastError (ERROR_OUTOFMEMORY); return (WAIT_FAILED); } - - _wapi_shared_handle_set_signal_state (handle, TRUE); return(WAIT_OBJECT_0); } @@ -159,7 +227,87 @@ static void process_set_defaults (struct _WapiHandle_process *process_handle) _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time); } -gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, +/* Implemented as just a wrapper around CreateProcess () */ +gboolean ShellExecuteEx (WapiShellExecuteInfo *sei) +{ + gboolean ret; + WapiProcessInformation process_info; + gunichar2 *args; + gchar *u8file, *u8params, *u8args; + + if (sei == NULL) { + /* w2k just segfaults here, but we can do better than + * that + */ + SetLastError (ERROR_INVALID_PARAMETER); + return (FALSE); + } + + if (sei->lpFile == NULL) { + /* w2k returns TRUE for this, for some reason. */ + return (TRUE); + } + + /* Put both executable and parameters into the second argument + * to CreateProcess (), so it searches $PATH. The conversion + * into and back out of utf8 is because there is no + * g_strdup_printf () equivalent for gunichar2 :-( + */ + u8file = g_utf16_to_utf8 (sei->lpFile, -1, NULL, NULL, NULL); + if (u8file == NULL) { + SetLastError (ERROR_INVALID_DATA); + return (FALSE); + } + + if (sei->lpParameters != NULL) { + u8params = g_utf16_to_utf8 (sei->lpParameters, -1, NULL, NULL, NULL); + if (u8params == NULL) { + SetLastError (ERROR_INVALID_DATA); + g_free (u8file); + return (FALSE); + } + + u8args = g_strdup_printf ("%s %s", u8file, u8params); + if (u8args == NULL) { + SetLastError (ERROR_INVALID_DATA); + g_free (u8params); + g_free (u8file); + return (FALSE); + } + + args = g_utf8_to_utf16 (u8args, -1, NULL, NULL, NULL); + + g_free (u8file); + g_free (u8params); + g_free (u8args); + } else { + args = g_utf8_to_utf16 (u8file, -1, NULL, NULL, NULL); + } + + if (args == NULL) { + SetLastError (ERROR_INVALID_DATA); + return (FALSE); + } + + ret = CreateProcess (NULL, args, NULL, NULL, TRUE, + CREATE_UNICODE_ENVIRONMENT, NULL, + sei->lpDirectory, NULL, &process_info); + g_free (args); + + if (!ret) { + return (FALSE); + } + + if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) { + sei->hProcess = process_info.hProcess; + } else { + CloseHandle (process_info.hProcess); + } + + return (ret); +} + +gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline, WapiSecurityAttributes *process_attrs G_GNUC_UNUSED, WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED, gboolean inherit_handles, guint32 create_flags, @@ -171,11 +319,11 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, guint32 i, env_count = 0; gboolean ret = FALSE; gpointer handle; - struct _WapiHandleShared shared_handle; - struct _WapiHandle_process process_handle = {0}; + struct _WapiHandle_process process_handle = {0}, *process_handle_data; GError *gerr = NULL; int in_fd, out_fd, err_fd; pid_t pid; + int thr_ret; mono_once (&process_ops_once, process_ops_init); @@ -296,6 +444,17 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, prog = g_strdup_printf ("%s/%s", curdir, unquoted); g_free (unquoted); g_free (curdir); + + /* And make sure it's executable */ + if (access (prog, X_OK) != 0) { +#ifdef DEBUG + g_message ("%s: Couldn't find executable %s", + __func__, prog); +#endif + g_free (prog); + SetLastError (ERROR_FILE_NOT_FOUND); + goto cleanup; + } } args_after_prog = args; @@ -460,7 +619,8 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE)); } - g_strlcpy (process_handle.proc_name, prog, _POSIX_PATH_MAX-1); + g_strlcpy (process_handle.proc_name, prog, + _WAPI_PROC_NAME_MAX_LEN - 1); process_set_defaults (&process_handle); @@ -542,13 +702,29 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, env_strings[env_count] = g_strdup_printf ("_WAPI_PROCESS_HANDLE_OFFSET=%d", ref->offset); } + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); pid = fork (); if (pid == -1) { /* Error */ + SetLastError (ERROR_OUTOFMEMORY); + _wapi_handle_unref (handle); + goto cleanup; } else if (pid == 0) { /* Child */ - + + /* Wait for the parent to finish setting up the + * handle. The semaphore lock is safe because the + * sem_undo structures of a semaphore aren't inherited + * across a fork () + */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + _wapi_handle_unlock_shared_handles (); + /* should we detach from the process group? */ /* Connect stdin, stdout and stderr */ @@ -590,24 +766,17 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, exit (-1); } /* parent */ - - ret = _wapi_copy_handle (handle, WAPI_HANDLE_PROCESS, &shared_handle); + + ret = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, + (gpointer *)&process_handle_data); if (ret == FALSE) { - g_warning ("%s: error copying process handle %p", __func__, + g_warning ("%s: error looking up process handle %p", __func__, handle); _wapi_handle_unref (handle); goto cleanup; } - shared_handle.u.process.id = pid; - - ret = _wapi_replace_handle (handle, WAPI_HANDLE_PROCESS, - &shared_handle); - if (ret == FALSE) { - SetLastError (ERROR_OUTOFMEMORY); - _wapi_handle_unref (handle); - goto cleanup; - } + process_handle_data->id = pid; if (process_info != NULL) { process_info->hProcess = handle; @@ -621,6 +790,8 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, } cleanup: + _wapi_handle_unlock_shared_handles (); + if (cmd != NULL) { g_free (cmd); } @@ -655,10 +826,10 @@ static void process_set_name (struct _WapiHandle_process *process_handle) slash=strrchr (utf8_progname, '/'); if(slash!=NULL) { g_strlcpy (process_handle->proc_name, slash+1, - _POSIX_PATH_MAX-1); + _WAPI_PROC_NAME_MAX_LEN - 1); } else { g_strlcpy (process_handle->proc_name, utf8_progname, - _POSIX_PATH_MAX-1); + _WAPI_PROC_NAME_MAX_LEN - 1); } g_free (utf8_progname); @@ -669,41 +840,17 @@ extern void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime); static void process_set_current (void) { - pid_t pid = getpid (); + pid_t pid = _wapi_getpid (); char *handle_env; + struct _WapiHandle_process process_handle = {0}; handle_env = getenv ("_WAPI_PROCESS_HANDLE_OFFSET"); - if (handle_env == NULL) { - struct _WapiHandle_process process_handle = {0}; - -#ifdef DEBUG - g_message ("%s: Need to create my own process handle", - __func__); -#endif - - process_handle.id = pid; - - process_set_defaults (&process_handle); - process_set_name (&process_handle); - - current_process = _wapi_handle_new (WAPI_HANDLE_PROCESS, - &process_handle); - if (current_process == _WAPI_HANDLE_INVALID) { - g_warning ("%s: error creating process handle", - __func__); - return; - } - - /* Make sure the new handle has a reference so it wont go away - * until this process exits - */ - _wapi_handle_ref (current_process); - } else { - struct _WapiHandle_process *process_handle; + if (handle_env != NULL) { + struct _WapiHandle_process *process_handlep; guchar *procname = NULL; gboolean ok; - current_process = _wapi_handle_new_from_offset (WAPI_HANDLE_PROCESS, atoi (handle_env)); + current_process = _wapi_handle_new_from_offset (WAPI_HANDLE_PROCESS, atoi (handle_env), TRUE); #ifdef DEBUG g_message ("%s: Found my process handle: %p (offset %d)", @@ -711,29 +858,63 @@ static void process_set_current (void) #endif ok = _wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS, - (gpointer *)&process_handle); + (gpointer *)&process_handlep); if (ok == FALSE) { g_warning ("%s: error looking up process handle %p", __func__, current_process); return; } - procname = process_handle->proc_name; - if (!strcmp (procname, "mono")) { - /* Set a better process name */ + /* This test will probably break on linuxthreads, but + * that should be ancient history on all distros we + * care about by now + */ + if (process_handlep->id == pid) { + procname = process_handlep->proc_name; + if (!strcmp (procname, "mono")) { + /* Set a better process name */ #ifdef DEBUG - g_message ("%s: Setting better process name", - __func__); + g_message ("%s: Setting better process name", + __func__); #endif - process_set_name (process_handle); - } else { + process_set_name (process_handlep); + } else { #ifdef DEBUG - g_message ("%s: Leaving process name: %s", __func__, - procname); + g_message ("%s: Leaving process name: %s", + __func__, procname); #endif + } + + return; } + + /* Wrong pid, so drop this handle and fall through to + * create a new one + */ + _wapi_handle_unref (current_process); + } + +#ifdef DEBUG + g_message ("%s: Need to create my own process handle", __func__); +#endif + + process_handle.id = pid; + + process_set_defaults (&process_handle); + process_set_name (&process_handle); + + current_process = _wapi_handle_new (WAPI_HANDLE_PROCESS, + &process_handle); + if (current_process == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error creating process handle", __func__); + return; } + + /* Make sure the new handle has a reference so it wont go away + * until this process exits + */ + _wapi_handle_ref (current_process); } /* Returns a pseudo handle that doesn't need to be closed afterwards */ @@ -744,26 +925,26 @@ gpointer GetCurrentProcess (void) return((gpointer)-1); } -guint32 GetCurrentProcessId (void) +guint32 GetProcessId (gpointer handle) { - struct _WapiHandle_process *current_process_handle; + struct _WapiHandle_process *process_handle; gboolean ok; - mono_once (&process_current_once, process_set_current); - - ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS, - (gpointer *)¤t_process_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up current process handle %p", - __func__, current_process); - /* No failure return is defined. PID 0 is invalid. - * This should only be reached when something else has - * gone badly wrong anyway. - */ - return(0); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, + (gpointer *)&process_handle); + if (ok == FALSE) { + SetLastError (ERROR_INVALID_HANDLE); + return (0); } - return(current_process_handle->id); + return (process_handle->id); +} + +guint32 GetCurrentProcessId (void) +{ + mono_once (&process_current_once, process_set_current); + + return (GetProcessId (current_process)); } /* Returns the process id as a convenience to the functions that call this */ @@ -778,8 +959,12 @@ static pid_t signal_process_if_gone (gpointer handle) ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle); if (ok == FALSE) { - g_warning ("%s: error looking up process handle %p", - __func__, handle); + /* It's possible that the handle has vanished during + * the _wapi_search_handle before it gets here, so + * don't spam the console with warnings. + */ +/* g_warning ("%s: error looking up process handle %p", + __func__, handle);*/ return (0); } @@ -876,6 +1061,10 @@ static gboolean process_open_compare (gpointer handle, gpointer user_data) */ if (checking_pid == wanted_pid && _wapi_handle_issignalled (handle) == FALSE) { + /* If the handle is blown away in the window between + * returning TRUE here and _wapi_search_handle pinging + * the timestamp, the search will continue + */ return(TRUE); } else { return(FALSE); diff --git a/mono/io-layer/processes.h b/mono/io-layer/processes.h index 46d566cbca07a..bd636e878ffdb 100644 --- a/mono/io-layer/processes.h +++ b/mono/io-layer/processes.h @@ -64,6 +64,65 @@ struct _WapiProcessInformation guint32 dwThreadId; }; +typedef enum { + SEE_MASK_CLASSNAME = 0x01, + SEE_MASK_CLASSKEY = 0x03, + SEE_MASK_IDLIST = 0x04, + SEE_MASK_INVOKEIDLIST = 0x0c, + SEE_MASK_ICON = 0x10, + SEE_MASK_HOTKEY = 0x20, + SEE_MASK_NOCLOSEPROCESS = 0x40, + SEE_MASK_CONNECTNETDRV = 0x80, + SEE_MASK_FLAG_DDEWAIT = 0x100, + SEE_MASK_DOENVSUBST = 0x200, + SEE_MASK_FLAG_NO_UI = 0x400, + SEE_MASK_NO_CONSOLE = 0x8000, + SEE_MASK_UNICODE = 0x10000, + SEE_MASK_HMONITOR = 0x200000, + /*SEE_MASK_FLAG_LOG_USAGE,*/ + /*SEE_MASK_NOZONECHECKS,*/ +} WapiShellExecuteInfoFlags; + +typedef enum { + SW_HIDE = 0, + SW_SHOWNORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_MAXIMIZE = 3, + SW_SHOWMAXIMIZED = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_SHOWDEFAULT = 10, +} WapiShellExecuteShowFlags; + +typedef struct _WapiShellExecuteInfo WapiShellExecuteInfo; + +struct _WapiShellExecuteInfo +{ + guint32 cbSize; + WapiShellExecuteInfoFlags fMask; + gpointer hwnd; + const gunichar2 *lpVerb; + const gunichar2 *lpFile; + const gunichar2 *lpParameters; + const gunichar2 *lpDirectory; + WapiShellExecuteShowFlags nShow; + gpointer hInstApp; + gpointer lpIDList; + const gunichar2 *lpClass; + gpointer hkeyClass; + guint32 dwHotKey; + union + { + gpointer hIcon; + gpointer hMonitor; + } u; + gpointer hProcess; +}; + #define DEBUG_PROCESS 0x00000001 #define DEBUG_ONLY_THIS_PROCESS 0x00000002 @@ -103,7 +162,9 @@ struct _WapiProcessInformation #define PROCESS_QUERY_INFORMATION 0x0400 #define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff) -extern gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, +extern gboolean ShellExecuteEx (WapiShellExecuteInfo *sei); +extern gboolean CreateProcess (const gunichar2 *appname, + const gunichar2 *cmdline, WapiSecurityAttributes *process_attrs, WapiSecurityAttributes *thread_attrs, gboolean inherit_handles, guint32 create_flags, @@ -111,6 +172,7 @@ extern gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, WapiStartupInfo *startup, WapiProcessInformation *process_info); extern gpointer GetCurrentProcess (void); +extern guint32 GetProcessId (gpointer handle); extern guint32 GetCurrentProcessId (void); extern gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed); extern gpointer OpenProcess (guint32 access, gboolean inherit, guint32 pid); diff --git a/mono/io-layer/semaphore-private.h b/mono/io-layer/semaphore-private.h index 43fbe1b052594..8414f4e920526 100644 --- a/mono/io-layer/semaphore-private.h +++ b/mono/io-layer/semaphore-private.h @@ -14,6 +14,7 @@ #include extern struct _WapiHandleOps _wapi_sem_ops; +extern struct _WapiHandleOps _wapi_namedsem_ops; extern void _wapi_sem_details (gpointer handle_info); @@ -24,4 +25,11 @@ struct _WapiHandle_sem gint32 max; }; +struct _WapiHandle_namedsem +{ + WapiSharedNamespace sharedns; + guint32 val; + gint32 max; +}; + #endif /* _WAPI_SEMAPHORE_PRIVATE_H_ */ diff --git a/mono/io-layer/semaphores.c b/mono/io-layer/semaphores.c index c6bd093980723..73fda73552db2 100644 --- a/mono/io-layer/semaphores.c +++ b/mono/io-layer/semaphores.c @@ -29,11 +29,16 @@ static void sema_signal(gpointer handle); static gboolean sema_own (gpointer handle); +static void namedsema_signal (gpointer handle); +static gboolean namedsema_own (gpointer handle); + struct _WapiHandleOps _wapi_sem_ops = { NULL, /* close */ sema_signal, /* signal */ sema_own, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; void _wapi_sem_details (gpointer handle_info) @@ -43,6 +48,37 @@ void _wapi_sem_details (gpointer handle_info) g_print ("val: %5u, max: %5d", sem->val, sem->max); } +struct _WapiHandleOps _wapi_namedsem_ops = { + NULL, /* close */ + namedsema_signal, /* signal */ + namedsema_own, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ +}; + +static gboolean sem_release (gpointer handle, gint32 count, gint32 *prev); +static gboolean namedsem_release (gpointer handle, gint32 count, gint32 *prev); + +static struct +{ + gboolean (*release)(gpointer handle, gint32 count, gint32 *prev); +} sem_ops[WAPI_HANDLE_COUNT] = { + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {sem_release}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {NULL}, + {namedsem_release}, +}; + static mono_once_t sem_ops_once=MONO_ONCE_INIT; static void sem_ops_init (void) @@ -50,6 +86,9 @@ static void sem_ops_init (void) _wapi_handle_register_capabilities (WAPI_HANDLE_SEM, WAPI_HANDLE_CAP_WAIT | WAPI_HANDLE_CAP_SIGNAL); + _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDSEM, + WAPI_HANDLE_CAP_WAIT | + WAPI_HANDLE_CAP_SIGNAL); } static void sema_signal(gpointer handle) @@ -87,51 +126,55 @@ static gboolean sema_own (gpointer handle) return(TRUE); } +static void namedsema_signal (gpointer handle) +{ + ReleaseSemaphore (handle, 1, NULL); +} -/** - * CreateSemaphore: - * @security: Ignored for now. - * @initial: The initial count for the semaphore. The value must be - * greater than or equal to zero, and less than or equal to @max. - * @max: The maximum count for this semaphore. The value must be - * greater than zero. - * @name: Pointer to a string specifying the name of this semaphore, - * or %NULL. Currently ignored. - * - * Creates a new semaphore handle. A semaphore is signalled when its - * count is greater than zero, and unsignalled otherwise. The count - * is decreased by one whenever a wait function releases a thread that - * was waiting for the semaphore. The count is increased by calling - * ReleaseSemaphore(). - * - * Return value: a new handle, or NULL - */ -gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED) +/* NB, always called with the shared handle lock held */ +static gboolean namedsema_own (gpointer handle) { - struct _WapiHandle_sem sem_handle = {0}; - gpointer handle; - int thr_ret; - - mono_once (&sem_ops_once, sem_ops_init); + struct _WapiHandle_namedsem *namedsem_handle; + gboolean ok; - if(max<=0) { #ifdef DEBUG - g_message("%s: max <= 0", __func__); + g_message ("%s: owning named sem handle %p", __func__, handle); #endif - SetLastError (ERROR_INVALID_PARAMETER); - return(NULL); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM, + (gpointer *)&namedsem_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up named sem handle %p", + __func__, handle); + return (FALSE); } - if(initial>max || initial<0) { + namedsem_handle->val--; + #ifdef DEBUG - g_message("%s: initial>max or < 0", __func__); + g_message ("%s: named sem %p val now %d", __func__, handle, + namedsem_handle->val); #endif - SetLastError (ERROR_INVALID_PARAMETER); - return(NULL); + if (namedsem_handle->val == 0) { + _wapi_shared_handle_set_signal_state (handle, FALSE); } + return (TRUE); +} +static gpointer sem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, + gint32 initial, gint32 max) +{ + struct _WapiHandle_sem sem_handle = {0}; + gpointer handle; + int thr_ret; + + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if a + * semaphore was freshly created + */ + SetLastError (ERROR_SUCCESS); + sem_handle.val = initial; sem_handle.max = max; @@ -163,28 +206,166 @@ gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 return(handle); } +static gpointer namedsem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED) +{ + struct _WapiHandle_namedsem namedsem_handle = {{{0}}, 0}; + gpointer handle; + gchar *utf8_name; + int thr_ret; + gpointer ret = NULL; + guint32 namelen; + gint32 offset; + + /* w32 seems to guarantee that opening named objects can't + * race each other + */ + thr_ret = _wapi_namespace_lock (); + g_assert (thr_ret == 0); + + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if a + * semaphore was freshly created + */ + SetLastError (ERROR_SUCCESS); + + utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); + +#ifdef DEBUG + g_message ("%s: Creating named sem [%s]", __func__, utf8_name); +#endif + + offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM, + utf8_name); + if (offset == -1) { + /* The name has already been used for a different + * object. + */ + SetLastError (ERROR_INVALID_HANDLE); + goto cleanup; + } else if (offset != 0) { + /* Not an error, but this is how the caller is + * informed that the semaphore wasn't freshly created + */ + SetLastError (ERROR_ALREADY_EXISTS); + } + /* Fall through to create the semaphore handle */ + + if (offset == 0) { + /* A new named semaphore, so create both the private + * and shared parts + */ + if (strlen (utf8_name) < MAX_PATH) { + namelen = strlen (utf8_name); + } else { + namelen = MAX_PATH; + } + + memcpy (&namedsem_handle.sharedns.name, utf8_name, namelen); + + namedsem_handle.val = initial; + namedsem_handle.max = max; + + handle = _wapi_handle_new (WAPI_HANDLE_NAMEDSEM, + &namedsem_handle); + } else { + /* A new reference to an existing named semaphore, so + * just create the private part + */ + handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, + offset, TRUE); + } + + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error creating named sem handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + goto cleanup; + } + ret = handle; + + if (offset == 0) { + /* Set the initial state, as this is a completely new + * handle + */ + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + if (initial != 0) { + _wapi_shared_handle_set_signal_state (handle, TRUE); + } + + _wapi_handle_unlock_shared_handles (); + } + +#ifdef DEBUG + g_message ("%s: returning named sem handle %p", __func__, handle); +#endif + +cleanup: + g_free (utf8_name); + + _wapi_namespace_unlock (NULL); + + return (ret); +} + + /** - * ReleaseSemaphore: - * @handle: The semaphore handle to release. - * @count: The amount by which the semaphore's count should be - * increased. - * @prevcount: Pointer to a location to store the previous count of - * the semaphore, or %NULL. + * CreateSemaphore: + * @security: Ignored for now. + * @initial: The initial count for the semaphore. The value must be + * greater than or equal to zero, and less than or equal to @max. + * @max: The maximum count for this semaphore. The value must be + * greater than zero. + * @name: Pointer to a string specifying the name of this semaphore, + * or %NULL. Currently ignored. * - * Increases the count of semaphore @handle by @count. + * Creates a new semaphore handle. A semaphore is signalled when its + * count is greater than zero, and unsignalled otherwise. The count + * is decreased by one whenever a wait function releases a thread that + * was waiting for the semaphore. The count is increased by calling + * ReleaseSemaphore(). * - * Return value: %TRUE on success, %FALSE otherwise. + * Return value: a new handle, or NULL */ -gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount) +gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name) +{ + mono_once (&sem_ops_once, sem_ops_init); + + if (max <= 0) { +#ifdef DEBUG + g_message ("%s: max <= 0", __func__); +#endif + + SetLastError (ERROR_INVALID_PARAMETER); + return(NULL); + } + + if (initial > max || initial < 0) { +#ifdef DEBUG + g_message ("%s: initial>max or < 0", __func__); +#endif + + SetLastError (ERROR_INVALID_PARAMETER); + return(NULL); + } + + if (name == NULL) { + return (sem_create (security, initial, max)); + } else { + return (namedsem_create (security, initial, max, name)); + } +} + +static gboolean sem_release (gpointer handle, gint32 count, gint32 *prevcount) { struct _WapiHandle_sem *sem_handle; gboolean ok; gboolean ret=FALSE; int thr_ret; - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM, - (gpointer *)&sem_handle); - if(ok==FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SEM, + (gpointer *)&sem_handle); + if (ok == FALSE) { g_warning ("%s: error looking up sem handle %p", __func__, handle); return(FALSE); @@ -196,33 +377,33 @@ gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount) g_assert (thr_ret == 0); #ifdef DEBUG - g_message("%s: sem %p val %d count %d", __func__, handle, - sem_handle->val, count); + g_message ("%s: sem %p val %d count %d", __func__, handle, + sem_handle->val, count); #endif /* Do this before checking for count overflow, because overflowing max * is a listed technique for finding the current value */ - if(prevcount!=NULL) { - *prevcount=sem_handle->val; + if (prevcount != NULL) { + *prevcount = sem_handle->val; } /* No idea why max is signed, but thats the spec :-( */ - if(sem_handle->val+count > (guint32)sem_handle->max) { + if (sem_handle->val + count > (guint32)sem_handle->max) { #ifdef DEBUG - g_message("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count); + g_message ("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count); #endif goto end; } - sem_handle->val+=count; + sem_handle->val += count; _wapi_handle_set_signal_state (handle, TRUE, TRUE); - ret=TRUE; + ret = TRUE; #ifdef DEBUG - g_message("%s: sem %p val now %d", __func__, handle, sem_handle->val); + g_message ("%s: sem %p val now %d", __func__, handle, sem_handle->val); #endif end: @@ -232,3 +413,152 @@ gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount) return(ret); } + +static gboolean namedsem_release (gpointer handle, gint32 count, + gint32 *prevcount) +{ + struct _WapiHandle_namedsem *sem_handle; + gboolean ok; + gboolean ret=FALSE; + int thr_ret; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM, + (gpointer *)&sem_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up sem handle %p", __func__, + handle); + return(FALSE); + } + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + +#ifdef DEBUG + g_message("%s: named sem %p val %d count %d", __func__, handle, + sem_handle->val, count); +#endif + + /* Do this before checking for count overflow, because overflowing max + * is a listed technique for finding the current value + */ + if (prevcount != NULL) { + *prevcount = sem_handle->val; + } + + /* No idea why max is signed, but thats the spec :-( */ + if (sem_handle->val + count > (guint32)sem_handle->max) { +#ifdef DEBUG + g_message ("%s: named sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count); +#endif + + goto end; + } + + sem_handle->val += count; + _wapi_shared_handle_set_signal_state (handle, TRUE); + + ret = TRUE; + +#ifdef DEBUG + g_message("%s: named sem %p val now %d", __func__, handle, + sem_handle->val); +#endif + +end: + _wapi_handle_unlock_shared_handles (); + + return(ret); +} + +/** + * ReleaseSemaphore: + * @handle: The semaphore handle to release. + * @count: The amount by which the semaphore's count should be + * increased. + * @prevcount: Pointer to a location to store the previous count of + * the semaphore, or %NULL. + * + * Increases the count of semaphore @handle by @count. + * + * Return value: %TRUE on success, %FALSE otherwise. + */ +gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount) +{ + WapiHandleType type; + + if (handle == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return (FALSE); + } + + type = _wapi_handle_type (handle); + + if (sem_ops[type].release == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return (FALSE); + } + + return (sem_ops[type].release (handle, count, prevcount)); +} + +gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, + const gunichar2 *name) +{ + gpointer handle; + gchar *utf8_name; + int thr_ret; + gpointer ret = NULL; + gint32 offset; + + mono_once (&sem_ops_once, sem_ops_init); + + /* w32 seems to guarantee that opening named objects can't + * race each other + */ + thr_ret = _wapi_namespace_lock (); + g_assert (thr_ret == 0); + + utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); + +#ifdef DEBUG + g_message ("%s: Opening named sem [%s]", __func__, utf8_name); +#endif + + offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM, + utf8_name); + if (offset == -1) { + /* The name has already been used for a different + * object. + */ + SetLastError (ERROR_INVALID_HANDLE); + goto cleanup; + } else if (offset == 0) { + /* This name doesn't exist */ + SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */ + goto cleanup; + } + + /* A new reference to an existing named semaphore, so just + * create the private part + */ + handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, offset, + TRUE); + + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error opening named sem handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + goto cleanup; + } + ret = handle; + +#ifdef DEBUG + g_message ("%s: returning named sem handle %p", __func__, handle); +#endif + +cleanup: + g_free (utf8_name); + + _wapi_namespace_unlock (NULL); + + return (ret); +} diff --git a/mono/io-layer/semaphores.h b/mono/io-layer/semaphores.h index 029c3d1020163..1cdaee02bfb2f 100644 --- a/mono/io-layer/semaphores.h +++ b/mono/io-layer/semaphores.h @@ -19,6 +19,8 @@ extern gpointer CreateSemaphore(WapiSecurityAttributes *security, const gunichar2 *name); extern gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount); +extern gpointer OpenSemaphore (guint32 access, gboolean inherit, + const gunichar2 *name); G_END_DECLS #endif /* _WAPI_SEMAPHORES_H_ */ diff --git a/mono/io-layer/shared.c b/mono/io-layer/shared.c index ee9375c7e2984..3e92c3436e00d 100644 --- a/mono/io-layer/shared.c +++ b/mono/io-layer/shared.c @@ -4,7 +4,7 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. + * (C) 2002-2006 Novell, Inc. */ @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -33,19 +34,42 @@ static guchar *_wapi_shm_file (_wapi_shm_t type) static guchar file[_POSIX_PATH_MAX]; guchar *name = NULL, *filename, *dir, *wapi_dir; gchar machine_name[256]; + const gchar *fake_name; + struct utsname ubuf; + int ret; + int len; + + ret = uname (&ubuf); + if (ret == -1) { + ubuf.machine[0] = '\0'; + ubuf.sysname[0] = '\0'; + } - if (gethostname(machine_name, sizeof(machine_name)) != 0) - machine_name[0] = '\0'; + fake_name = g_getenv ("MONO_SHARED_HOSTNAME"); + if (fake_name == NULL) { + if (gethostname(machine_name, sizeof(machine_name)) != 0) + machine_name[0] = '\0'; + } else { + len = MIN (strlen (fake_name), sizeof (machine_name) - 1); + strncpy (machine_name, fake_name, len); + machine_name [len] = '\0'; + } switch (type) { case WAPI_SHM_DATA: - name = g_strdup_printf ("shared_data-%s-%d-%d", - machine_name, _WAPI_HANDLE_VERSION, 0); + name = g_strdup_printf ("shared_data-%s-%s-%s-%d-%d-%d", + machine_name, ubuf.sysname, + ubuf.machine, + (int) sizeof(struct _WapiHandleShared), + _WAPI_HANDLE_VERSION, 0); break; case WAPI_SHM_FILESHARE: - name = g_strdup_printf ("shared_fileshare-%s-%d-%d", - machine_name, _WAPI_HANDLE_VERSION, 0); + name = g_strdup_printf ("shared_fileshare-%s-%s-%s-%d-%d-%d", + machine_name, ubuf.sysname, + ubuf.machine, + (int) sizeof(struct _WapiFileShare), + _WAPI_HANDLE_VERSION, 0); break; } @@ -220,10 +244,13 @@ gpointer _wapi_shm_attach (_wapi_shm_t type) shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (shm_seg == MAP_FAILED) { - g_critical ("%s: mmap error: %s", __func__, - g_strerror (errno)); - close (fd); - return(NULL); + shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + if (shm_seg == MAP_FAILED) { + g_critical ("%s: mmap error: %s", __func__, g_strerror (errno)); + close (fd); + return(NULL); + } } close (fd); @@ -232,9 +259,11 @@ gpointer _wapi_shm_attach (_wapi_shm_t type) void _wapi_shm_semaphores_init () { - key_t key = ftok (_wapi_shm_file (WAPI_SHM_DATA), 'M'); + key_t key; key_t oldkey; - + int thr_ret; + struct _WapiHandleSharedLayout *tmp_shared; + /* Yet more barmy API - this union is a well-defined parameter * in a syscall, yet I still have to define it here as it * doesn't appear in a header @@ -251,11 +280,31 @@ void _wapi_shm_semaphores_init () for (i = 0; i < _WAPI_SHARED_SEM_COUNT; i++) { def_vals[i] = 1; } +#ifdef NEXT_VERSION_INC + /* Process count must start at '0' - the 1 for all the others + * sets the semaphore to "unlocked" + */ + def_vals[_WAPI_SHARED_SEM_PROCESS_COUNT] = 0; +#endif + defs.array = def_vals; + /* Temporarily attach the shared data so we can read the + * semaphore key. We release this mapping and attach again + * after getting the semaphores to avoid a race condition + * where a terminating process can delete the shared files + * between a new process attaching the file and getting access + * to the semaphores (which increments the process count, + * preventing destruction of the shared data...) + */ + tmp_shared = _wapi_shm_attach (WAPI_SHM_DATA); + g_assert (tmp_shared != NULL); + + key = ftok (_wapi_shm_file (WAPI_SHM_DATA), 'M'); + again: retries++; - oldkey = _wapi_shared_layout->sem_key; + oldkey = tmp_shared->sem_key; if (oldkey == 0) { #ifdef DEBUG @@ -269,7 +318,12 @@ void _wapi_shm_semaphores_init () */ while ((_wapi_sem_id = semget (key, _WAPI_SHARED_SEM_COUNT, IPC_CREAT | IPC_EXCL | 0600)) == -1) { - if (errno != EEXIST) { + if (errno == ENOMEM) { + g_critical ("%s: semget error: %s", __func__, + g_strerror (errno)); + } else if (errno == ENOSPC) { + g_critical ("%s: semget error: %s. Try deleting some semaphores with ipcs and ipcrm", __func__, g_strerror (errno)); + } else if (errno != EEXIST) { if (retries > 3) g_warning ("%s: semget error: %s key 0x%x - trying again", __func__, g_strerror (errno), key); @@ -296,7 +350,7 @@ void _wapi_shm_semaphores_init () goto again; } - if (InterlockedCompareExchange (&_wapi_shared_layout->sem_key, + if (InterlockedCompareExchange (&tmp_shared->sem_key, key, 0) != 0) { /* Someone else created one and installed the * key while we were working, so delete the @@ -304,12 +358,12 @@ void _wapi_shm_semaphores_init () * 'key already known' case. */ semctl (_wapi_sem_id, 0, IPC_RMID); - oldkey = _wapi_shared_layout->sem_key; + oldkey = tmp_shared->sem_key; } else { /* We've installed this semaphore set's key into * the shared memory */ - return; + goto done; } } @@ -326,11 +380,80 @@ void _wapi_shm_semaphores_init () /* Someone must have deleted the semaphore set, so * blow away the bad key and try again */ - InterlockedCompareExchange (&_wapi_shared_layout->sem_key, 0, - oldkey); + InterlockedCompareExchange (&tmp_shared->sem_key, 0, oldkey); goto again; } + + done: + /* Increment the usage count of this semaphore set */ + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK); + g_assert (thr_ret == 0); + +#ifdef DEBUG + g_message ("%s: Incrementing the process count", __func__); +#endif + + /* We only ever _unlock_ this semaphore, letting the kernel + * restore (ie decrement) this unlock when this process exits. + * We lock another semaphore around it so we can serialise + * access when we're testing the value of this semaphore when + * we exit cleanly, so we can delete the whole semaphore set. + */ + _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT); + +#ifdef DEBUG + g_message ("%s: Process count is now %d", __func__, semctl (_wapi_sem_id, _WAPI_SHARED_SEM_PROCESS_COUNT, GETVAL)); +#endif + + _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK); + + munmap (tmp_shared, sizeof(struct _WapiHandleSharedLayout)); +} + +void _wapi_shm_semaphores_remove (void) +{ + int thr_ret; + int proc_count; + +#ifdef DEBUG + g_message ("%s: Checking process count", __func__); +#endif + + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK); + g_assert (thr_ret == 0); + + proc_count = semctl (_wapi_sem_id, _WAPI_SHARED_SEM_PROCESS_COUNT, + GETVAL); +#ifdef NEXT_VERSION_INC + g_assert (proc_count > 0); + if (proc_count == 1) { +#else + /* Compatibility - the semaphore was initialised to '1' (which + * normally means 'unlocked'. Instead of fixing that right + * now, which would mean a shared file version increment, just + * cope with the value starting too high for now. Fix this + * next time I have to change the file version. + */ + g_assert (proc_count > 1); + if (proc_count == 2) { +#endif + /* Just us, so blow away the semaphores and the shared + * files + */ +#ifdef DEBUG + g_message ("%s: Removing semaphores!", __func__); +#endif + + semctl (_wapi_sem_id, IPC_RMID, 0); + unlink (_wapi_shm_file (WAPI_SHM_DATA)); + unlink (_wapi_shm_file (WAPI_SHM_FILESHARE)); + } else { + /* "else" clause, because there's no point unlocking + * the semaphore if we've just blown it away... + */ + _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK); + } } int _wapi_shm_sem_lock (int sem) @@ -346,11 +469,28 @@ int _wapi_shm_sem_lock (int sem) ops.sem_op = -1; ops.sem_flg = SEM_UNDO; + retry: do { ret = semop (_wapi_sem_id, &ops, 1); } while (ret == -1 && errno == EINTR); if (ret == -1) { + /* EINVAL covers the case when the semaphore was + * deleted before we started the semop + */ + if (errno == EIDRM || errno == EINVAL) { + /* Someone blew away this semaphore set, so + * get a new one and try again + */ +#ifdef DEBUG + g_message ("%s: Reinitialising the semaphores!", + __func__); +#endif + + _wapi_shm_semaphores_init (); + goto retry; + } + /* Turn this into a pthreads-style return value */ ret = errno; } @@ -375,11 +515,28 @@ int _wapi_shm_sem_trylock (int sem) ops.sem_op = -1; ops.sem_flg = IPC_NOWAIT | SEM_UNDO; + retry: do { ret = semop (_wapi_sem_id, &ops, 1); } while (ret == -1 && errno == EINTR); if (ret == -1) { + /* EINVAL covers the case when the semaphore was + * deleted before we started the semop + */ + if (errno == EIDRM || errno == EINVAL) { + /* Someone blew away this semaphore set, so + * get a new one and try again + */ +#ifdef DEBUG + g_message ("%s: Reinitialising the semaphores!", + __func__); +#endif + + _wapi_shm_semaphores_init (); + goto retry; + } + /* Turn this into a pthreads-style return value */ ret = errno; } @@ -409,11 +566,29 @@ int _wapi_shm_sem_unlock (int sem) ops.sem_op = 1; ops.sem_flg = SEM_UNDO; + retry: do { ret = semop (_wapi_sem_id, &ops, 1); } while (ret == -1 && errno == EINTR); if (ret == -1) { + /* EINVAL covers the case when the semaphore was + * deleted before we started the semop + */ + if (errno == EIDRM || errno == EINVAL) { + /* Someone blew away this semaphore set, so + * get a new one and try again (we can't just + * assume that the semaphore is now unlocked) + */ +#ifdef DEBUG + g_message ("%s: Reinitialising the semaphores!", + __func__); +#endif + + _wapi_shm_semaphores_init (); + goto retry; + } + /* Turn this into a pthreads-style return value */ ret = errno; } diff --git a/mono/io-layer/shared.h b/mono/io-layer/shared.h index af31fe1d9085d..be256f23fc6a3 100644 --- a/mono/io-layer/shared.h +++ b/mono/io-layer/shared.h @@ -4,7 +4,7 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. + * (C) 2002-2006 Novell, Inc. */ #ifndef _WAPI_SHARED_H_ @@ -19,6 +19,7 @@ typedef enum { extern gpointer _wapi_shm_attach (_wapi_shm_t type); extern void _wapi_shm_semaphores_init (void); +extern void _wapi_shm_semaphores_remove (void); extern int _wapi_shm_sem_lock (int sem); extern int _wapi_shm_sem_trylock (int sem); extern int _wapi_shm_sem_unlock (int sem); diff --git a/mono/io-layer/sockets.c b/mono/io-layer/sockets.c index b2cf55faf211c..51592e164177b 100644 --- a/mono/io-layer/sockets.c +++ b/mono/io-layer/sockets.c @@ -47,6 +47,8 @@ struct _WapiHandleOps _wapi_socket_ops = { NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; static mono_once_t socket_ops_once=MONO_ONCE_INIT; @@ -429,6 +431,13 @@ int _wapi_getsockopt(guint32 fd, int level, int optname, void *optval, *((int *) optval) = tv.tv_sec * 1000 + tv.tv_usec; *optlen = sizeof (int); } + + if (optname == SO_ERROR) { + if (*((int *)optval) != 0) { + *((int *) optval) = errno_to_WSA (*((int *)optval), + __func__); + } + } return(ret); } @@ -472,9 +481,6 @@ int _wapi_recv(guint32 fd, void *buf, size_t len, int recv_flags) int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags, struct sockaddr *from, socklen_t *fromlen) { -#ifndef HAVE_MSG_NOSIGNAL - void (*old_sigpipe)(int); // old SIGPIPE handler -#endif gpointer handle = GUINT_TO_POINTER (fd); int ret; @@ -488,20 +494,10 @@ int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags, return(SOCKET_ERROR); } -#ifdef HAVE_MSG_NOSIGNAL - do { - ret = recvfrom (fd, buf, len, recv_flags | MSG_NOSIGNAL, from, - fromlen); - } while (ret == -1 && errno == EINTR && - !_wapi_thread_cur_apc_pending ()); -#else - old_sigpipe = signal (SIGPIPE, SIG_IGN); do { ret = recvfrom (fd, buf, len, recv_flags, from, fromlen); } while (ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ()); - signal (SIGPIPE, old_sigpipe); -#endif if (ret == -1) { gint errnum = errno; @@ -519,9 +515,6 @@ int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags, int _wapi_send(guint32 fd, const void *msg, size_t len, int send_flags) { -#ifndef HAVE_MSG_NOSIGNAL - void (*old_sigpipe)(int); // old SIGPIPE handler -#endif gpointer handle = GUINT_TO_POINTER (fd); int ret; @@ -535,19 +528,11 @@ int _wapi_send(guint32 fd, const void *msg, size_t len, int send_flags) return(SOCKET_ERROR); } -#ifdef HAVE_MSG_NOSIGNAL - do { - ret = send (fd, msg, len, send_flags | MSG_NOSIGNAL); - } while (ret == -1 && errno == EINTR && - !_wapi_thread_cur_apc_pending ()); -#else - old_sigpipe = signal (SIGPIPE, SIG_IGN); do { ret = send (fd, msg, len, send_flags); } while (ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ()); - signal (SIGPIPE, old_sigpipe); -#endif + if (ret == -1) { gint errnum = errno; #ifdef DEBUG @@ -565,9 +550,6 @@ int _wapi_send(guint32 fd, const void *msg, size_t len, int send_flags) int _wapi_sendto(guint32 fd, const void *msg, size_t len, int send_flags, const struct sockaddr *to, socklen_t tolen) { -#ifndef HAVE_MSG_NOSIGNAL - void (*old_sigpipe)(int); // old SIGPIPE handler -#endif gpointer handle = GUINT_TO_POINTER (fd); int ret; @@ -581,20 +563,11 @@ int _wapi_sendto(guint32 fd, const void *msg, size_t len, int send_flags, return(SOCKET_ERROR); } -#ifdef HAVE_MSG_NOSIGNAL - do { - ret = sendto (fd, msg, len, send_flags | MSG_NOSIGNAL, to, - tolen); - } while (ret == -1 && errno == EINTR && - !_wapi_thread_cur_apc_pending ()); -#else - old_sigpipe = signal (SIGPIPE, SIG_IGN); do { ret = sendto (fd, msg, len, send_flags, to, tolen); } while (ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ()); - signal (SIGPIPE, old_sigpipe); -#endif + if (ret == -1) { gint errnum = errno; #ifdef DEBUG @@ -846,35 +819,33 @@ int ioctlsocket(guint32 fd, gint32 command, gpointer arg) return(SOCKET_ERROR); } - if (command != FIONBIO && - command != FIONREAD && - command != SIOCATMARK) { - /* Not listed in the MSDN specs, but ioctl(2) returns - * this if command is invalid - */ - WSASetLastError (WSAEINVAL); - return(SOCKET_ERROR); - } - + switch(command){ + case FIONBIO: #ifdef O_NONBLOCK - /* This works better than ioctl(...FIONBIO...) on Linux (it causes - * connect to return EINPROGRESS, but the ioctl doesn't seem to) - */ - if (command == FIONBIO) { - ret = fcntl (fd, F_GETFL, 0); - if (ret != -1) { - if (*(gboolean *)arg) { - ret |= O_NONBLOCK; - } else { - ret &= ~O_NONBLOCK; + /* This works better than ioctl(...FIONBIO...) + * on Linux (it causes connect to return + * EINPROGRESS, but the ioctl doesn't seem to) + */ + ret = fcntl(fd, F_GETFL, 0); + if (ret != -1) { + if (*(gboolean *)arg) { + ret |= O_NONBLOCK; + } else { + ret &= ~O_NONBLOCK; + } + ret = fcntl(fd, F_SETFL, ret); } - ret = fcntl (fd, F_SETFL, ret); - } - } else + break; #endif /* O_NONBLOCK */ - { - ret = ioctl (fd, command, arg); + case FIONREAD: + case SIOCATMARK: + ret = ioctl (fd, command, arg); + break; + default: + WSASetLastError (WSAEINVAL); + return(SOCKET_ERROR); } + if (ret == -1) { gint errnum = errno; #ifdef DEBUG diff --git a/mono/io-layer/thread-private.h b/mono/io-layer/thread-private.h index bae837eadce0a..c97c9f425cfbc 100644 --- a/mono/io-layer/thread-private.h +++ b/mono/io-layer/thread-private.h @@ -12,8 +12,26 @@ #include #include - -#include +#include +#ifdef HAVE_SEMAPHORE_H +#include +#endif +#ifdef USE_MACH_SEMA +#include +#include +#include +typedef semaphore_t MonoSemType; +#define MONO_SEM_INIT(addr,value) semaphore_create (current_task (), (addr), SYNC_POLICY_FIFO, (value)) +#define MONO_SEM_WAIT(sem) semaphore_wait (*(sem)) +#define MONO_SEM_POST(sem) semaphore_signal (*(sem)) +#define MONO_SEM_DESTROY(sem) semaphore_destroy (current_task (), *(sem)) +#else +typedef sem_t MonoSemType; +#define MONO_SEM_INIT(addr,value) sem_init ((addr), 0, (value)) +#define MONO_SEM_WAIT(sem) sem_wait ((sem)) +#define MONO_SEM_POST(sem) sem_post ((sem)) +#define MONO_SEM_DESTROY(sem) sem_destroy ((sem)) +#endif extern struct _WapiHandleOps _wapi_thread_ops; @@ -27,13 +45,29 @@ struct _WapiHandle_thread WapiThreadState state; guint32 exitstatus; pid_t owner_pid; - TimedThread *thread; gboolean joined; + guint32 create_flags; + /* Fields below this point are only valid for the owning process */ + pthread_t id; + GPtrArray *owned_mutexes; + gpointer handle; + MonoSemType suspend_sem; + guint32 (*start_routine)(gpointer arg); + gpointer start_arg; + GSList *apc_queue; }; +typedef struct +{ + guint32 (*callback)(gpointer arg); + gpointer param; +} ApcInfo; + extern gboolean _wapi_thread_apc_pending (gpointer handle); extern gboolean _wapi_thread_cur_apc_pending (void); extern gboolean _wapi_thread_dispatch_apc_queue (gpointer handle); +extern void _wapi_thread_own_mutex (gpointer mutex); +extern void _wapi_thread_disown_mutex (gpointer mutex); #endif /* _WAPI_THREAD_PRIVATE_H_ */ diff --git a/mono/io-layer/threads.c b/mono/io-layer/threads.c index 74b7150788aa1..71ae4b73c2432 100644 --- a/mono/io-layer/threads.c +++ b/mono/io-layer/threads.c @@ -4,13 +4,14 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. + * (C) 2002-2006 Ximian, Inc. */ #include #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include #include -#include #include #include #include @@ -41,68 +41,68 @@ * would have to set the data in the new thread, which is more hassle */ static mono_once_t thread_hash_once = MONO_ONCE_INIT; -static mono_mutex_t thread_hash_mutex = MONO_MUTEX_INITIALIZER; -static GHashTable *thread_hash=NULL; - -static gboolean thread_own (gpointer handle); +static pthread_key_t thread_hash_key; struct _WapiHandleOps _wapi_thread_ops = { - NULL, /* close_shared */ + NULL, /* close */ NULL, /* signal */ - thread_own, /* own */ + NULL, /* own */ NULL, /* is_owned */ + NULL, /* special_wait */ + NULL /* prewait */ }; static mono_once_t thread_ops_once=MONO_ONCE_INIT; -#ifdef WITH_INCLUDED_LIBGC -static void gc_init (void); -#endif - static void thread_ops_init (void) { _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD, WAPI_HANDLE_CAP_WAIT); - -#ifdef WITH_INCLUDED_LIBGC - gc_init (); -#endif } -static gboolean thread_own (gpointer handle) +/* Called by thread_exit(), but maybe by mono_thread_manage() too */ +void _wapi_thread_abandon_mutexes (gpointer handle) { struct _WapiHandle_thread *thread_handle; gboolean ok; + int i; + pid_t pid = _wapi_getpid (); + pthread_t tid = pthread_self (); -#ifdef DEBUG - g_message ("%s: owning thread handle %p", __func__, handle); -#endif - ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle); if (ok == FALSE) { g_warning ("%s: error looking up thread handle %p", __func__, handle); - return(FALSE); + return; } - if (thread_handle->joined == FALSE) { - _wapi_timed_thread_join (thread_handle->thread, NULL, NULL); - thread_handle->joined = TRUE; + if (thread_handle->owner_pid != pid || + !pthread_equal (thread_handle->id, tid)) { + return; + } + + for (i = 0; i < thread_handle->owned_mutexes->len; i++) { + gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i); + + _wapi_mutex_abandon (mutex, pid, tid); + _wapi_thread_disown_mutex (mutex); } - - return(TRUE); } -static void thread_exit(guint32 exitstatus, gpointer handle) +/* Called by the thread creation code as a thread is finishing up, and + * by ExitThread() +*/ +static void thread_exit (guint32 exitstatus, gpointer handle) G_GNUC_NORETURN; +static void thread_exit (guint32 exitstatus, gpointer handle) { struct _WapiHandle_thread *thread_handle; gboolean ok; int thr_ret; - pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, - handle); - thr_ret = _wapi_handle_lock_handle (handle); + _wapi_thread_abandon_mutexes (handle); + + thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, @@ -110,10 +110,13 @@ static void thread_exit(guint32 exitstatus, gpointer handle) if (ok == FALSE) { g_warning ("%s: error looking up thread handle %p", __func__, handle); - return; + /* exit the calling thread anyway, even though we + * can't record the exit status and clean up the + * private bits of handle data + */ + _wapi_handle_unlock_shared_handles (); + pthread_exit (NULL); } - - _wapi_mutex_check_abandoned (getpid (), thread_handle->thread->id); #ifdef DEBUG g_message ("%s: Recording thread handle %p exit status", __func__, @@ -122,37 +125,81 @@ static void thread_exit(guint32 exitstatus, gpointer handle) thread_handle->exitstatus = exitstatus; thread_handle->state = THREAD_STATE_EXITED; + MONO_SEM_DESTROY (&thread_handle->suspend_sem); + g_ptr_array_free (thread_handle->owned_mutexes, TRUE); - _wapi_handle_set_signal_state (handle, TRUE, TRUE); + _wapi_shared_handle_set_signal_state (handle, TRUE); - thr_ret = _wapi_handle_unlock_handle (handle); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); + _wapi_handle_unlock_shared_handles (); #ifdef DEBUG g_message("%s: Recording thread handle %p id %ld status as %d", - __func__, handle, thread_handle->thread->id, exitstatus); + __func__, handle, thread_handle->id, exitstatus); #endif - /* Remove this thread from the hash */ - pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, - (void *)&thread_hash_mutex); - thr_ret = mono_mutex_lock(&thread_hash_mutex); - g_assert (thr_ret == 0); - - g_hash_table_remove (thread_hash, GUINT_TO_POINTER (thread_handle->thread->id)); - - thr_ret = mono_mutex_unlock(&thread_hash_mutex); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); - /* The thread is no longer active, so unref it */ _wapi_handle_unref (handle); + + /* Call pthread_exit() to call destructors and really exit the + * thread + */ + pthread_exit (NULL); } static void thread_hash_init(void) { - thread_hash = g_hash_table_new (NULL, NULL); + int thr_ret; + + thr_ret = pthread_key_create (&thread_hash_key, NULL); + g_assert (thr_ret == 0); +} + +static void _wapi_thread_suspend (struct _WapiHandle_thread *thread) +{ + g_assert (thread->owner_pid == _wapi_getpid ()); + g_assert (pthread_equal (thread->id, pthread_self ())); + + while (MONO_SEM_WAIT (&thread->suspend_sem) != 0 && + errno == EINTR); +} + +static void _wapi_thread_resume (struct _WapiHandle_thread *thread) +{ + if (thread->owner_pid != _wapi_getpid ()) { + return; + } + + MONO_SEM_POST (&thread->suspend_sem); +} + +static void *thread_start_routine (gpointer args) G_GNUC_NORETURN; +static void *thread_start_routine (gpointer args) +{ + struct _WapiHandle_thread *thread = (struct _WapiHandle_thread *)args; + int thr_ret; + + thr_ret = pthread_detach (pthread_self ()); + g_assert (thr_ret == 0); + + thr_ret = pthread_setspecific (thread_hash_key, + (void *)thread->handle); + g_assert (thr_ret == 0); + + thread->id = pthread_self(); + + if (thread->create_flags & CREATE_SUSPENDED) { + _wapi_thread_suspend (thread); + } + + thread_exit (thread->start_routine (thread->start_arg), + thread->handle); + +#ifndef __GNUC__ + /* Even though we tell gcc that this function doesn't return, + * other compilers won't see that. + */ + return(NULL); +#endif } /** @@ -165,7 +212,9 @@ static void thread_hash_init(void) * @create: If 0, the new thread is ready to run immediately. If * %CREATE_SUSPENDED, the new thread will be in the suspended state, * requiring a ResumeThread() call to continue running. - * @tid: If non-NULL, the ID of the new thread is stored here. + * @tid: If non-NULL, the ID of the new thread is stored here. NB + * this is defined as a DWORD (ie 32bit) in the MS API, but we need to + * cope with 64 bit IDs for s390x and amd64. * * Creates a new threading handle. * @@ -173,7 +222,7 @@ static void thread_hash_init(void) */ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize, WapiThreadStart start, gpointer param, guint32 create, - guint32 *tid) + gsize *tid) { struct _WapiHandle_thread thread_handle = {0}, *thread_handle_p; pthread_attr_t attr; @@ -192,7 +241,11 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st } thread_handle.state = THREAD_STATE_START; - thread_handle.owner_pid = getpid(); + thread_handle.owner_pid = _wapi_getpid (); + thread_handle.owned_mutexes = g_ptr_array_new (); + thread_handle.create_flags = create; + thread_handle.start_routine = start; + thread_handle.start_arg = param; handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle); if (handle == _WAPI_HANDLE_INVALID) { @@ -202,9 +255,7 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st return (NULL); } - pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, - handle); - thr_ret = _wapi_handle_lock_handle (handle); + thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, @@ -222,14 +273,6 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st */ _wapi_handle_ref (handle); - /* Lock around the thread create, so that the new thread cant - * race us to look up the thread handle in GetCurrentThread() - */ - pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, - (void *)&thread_hash_mutex); - thr_ret = mono_mutex_lock(&thread_hash_mutex); - g_assert (thr_ret == 0); - /* Set a 2M stack size. This is the default on Linux, but BSD * needs it. (The original bug report from Martin Dvorak * set the size to 2M-4k. I don't know why it's short by 4k, so @@ -260,9 +303,11 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st g_assert (thr_ret == 0); #endif - ret = _wapi_timed_thread_create (&thread_handle_p->thread, &attr, - create, start, thread_exit, param, - handle); + MONO_SEM_INIT (&thread_handle_p->suspend_sem, 0); + thread_handle_p->handle = handle; + + ret = pthread_create (&thread_handle_p->id, &attr, + thread_start_routine, (void *)thread_handle_p); if (ret != 0) { #ifdef DEBUG g_message ("%s: Thread create error: %s", __func__, @@ -272,40 +317,32 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st /* Two, because of the reference we took above */ unrefs = 2; - goto thread_hash_cleanup; + goto cleanup; } ct_ret = handle; - g_hash_table_insert (thread_hash, - GUINT_TO_POINTER (thread_handle_p->thread->id), - handle); - #ifdef DEBUG - g_message("%s: Started thread handle %p thread %p ID %ld", __func__, - handle, thread_handle_p->thread, - thread_handle_p->thread->id); + g_message("%s: Started thread handle %p ID %ld", __func__, handle, + thread_handle_p->id); #endif if (tid != NULL) { #ifdef PTHREAD_POINTER_ID - *tid = GPOINTER_TO_UINT (thread_handle_p->thread->id); + /* Don't use GPOINTER_TO_UINT here, it can't cope with + * sizeof(void *) > sizeof(uint) when a cast to uint + * would overflow + */ + *tid = (gsize)(thread_handle_p->id); #else - *tid = thread_handle_p->thread->id; + *tid = thread_handle_p->id; #endif } -thread_hash_cleanup: - thr_ret = mono_mutex_unlock (&thread_hash_mutex); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); - cleanup: - thr_ret = _wapi_handle_unlock_handle (handle); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); + _wapi_handle_unlock_shared_handles (); - /* Must not call _wapi_handle_unref() with the handle already - * locked + /* Must not call _wapi_handle_unref() with the shared handles + * already locked */ for (i = 0; i < unrefs; i++) { _wapi_handle_unref (handle); @@ -314,30 +351,108 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st return(ct_ret); } -gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 tid) +/* The only time this function is called when tid != pthread_self () + * is from OpenThread (), so we can fast-path most cases by just + * looking up the handle in TLS. OpenThread () must cope with a NULL + * return and do a handle search in that case. + */ +static gpointer _wapi_thread_handle_from_id (pthread_t tid) { - gpointer ret=NULL; - int thr_ret; + gpointer ret; + + if (pthread_equal (tid, pthread_self ()) && + (ret = pthread_getspecific (thread_hash_key)) != NULL) { + /* We know the handle */ + +#ifdef DEBUG + g_message ("%s: Returning %p for self thread %ld from TLS", + __func__, ret, tid); +#endif + + return(ret); + } - mono_once (&thread_hash_once, thread_hash_init); - mono_once (&thread_ops_once, thread_ops_init); +#ifdef DEBUG + g_message ("%s: Returning NULL for unknown or non-self thread %ld", + __func__, tid); +#endif + + + return(NULL); +} + +static gboolean find_thread_by_id (gpointer handle, gpointer user_data) +{ + pthread_t tid = (pthread_t)user_data; + struct _WapiHandle_thread *thread_handle; + gboolean ok; + /* Ignore threads that have already exited (ie they are signalled) */ + if (_wapi_handle_issignalled (handle) == FALSE) { + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + /* It's possible that the handle has vanished + * during the _wapi_search_handle before it + * gets here, so don't spam the console with + * warnings. + */ + return(FALSE); + } + #ifdef DEBUG - g_message ("%s: looking up thread %d", __func__, tid); + g_message ("%s: looking at thread %ld from process %d", __func__, thread_handle->id, thread_handle->owner_pid); #endif - pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, - (void *)&thread_hash_mutex); - thr_ret = mono_mutex_lock(&thread_hash_mutex); - g_assert (thr_ret == 0); + if (thread_handle->owner_pid != _wapi_getpid ()) { + /* Not sure if ms has this limitation with + * OpenThread(), but pthreads IDs are not + * unique across processes + */ +#ifdef DEBUG + g_message ("%s: not this process", __func__); +#endif + return(FALSE); + } + + if (pthread_equal (thread_handle->id, tid)) { +#ifdef DEBUG + g_message ("%s: found the thread we are looking for", + __func__); +#endif + return(TRUE); + } + } - ret = g_hash_table_lookup (thread_hash, GUINT_TO_POINTER (tid)); +#ifdef DEBUG + g_message ("%s: not found %ld, returning FALSE", __func__, tid); +#endif + + return(FALSE); +} - thr_ret = mono_mutex_unlock(&thread_hash_mutex); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); +/* NB tid is 32bit in MS API, but we need 64bit on amd64 and s390x + * (and probably others) + */ +gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, gsize tid) +{ + gpointer ret=NULL; + + mono_once (&thread_hash_once, thread_hash_init); + mono_once (&thread_ops_once, thread_ops_init); - if(ret!=NULL) { +#ifdef DEBUG + g_message ("%s: looking up thread %"G_GSIZE_FORMAT, __func__, tid); +#endif + + ret = _wapi_thread_handle_from_id ((pthread_t)tid); + if (ret == NULL) { + /* We need to search for this thread */ + ret = _wapi_search_handle (WAPI_HANDLE_THREAD, find_thread_by_id, (gpointer)tid, NULL); + } else { + /* if _wapi_search_handle() returns a found handle, it + * refs it itself + */ _wapi_handle_ref (ret); } @@ -359,7 +474,7 @@ gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSE */ void ExitThread(guint32 exitcode) { - _wapi_timed_thread_exit(exitcode); + thread_exit(exitcode, GetCurrentThread ()); } /** @@ -387,7 +502,7 @@ gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode) #ifdef DEBUG g_message ("%s: Finding exit status for thread handle %p id %ld", - __func__, handle, thread_handle->thread->id); + __func__, handle, thread_handle->id); #endif if (exitcode == NULL) { @@ -418,34 +533,39 @@ gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode) * Looks up the thread ID of the current thread. This ID can be * passed to OpenThread() to create a new handle on this thread. * - * Return value: the thread ID. + * Return value: the thread ID. NB this is defined as DWORD (ie 32 + * bit) in the MS API, but we need to cope with 64 bit IDs for s390x + * and amd64. This doesn't really break the API, it just embraces and + * extends it on 64bit platforms :) */ -guint32 GetCurrentThreadId(void) +gsize GetCurrentThreadId(void) { pthread_t tid = pthread_self(); #ifdef PTHREAD_POINTER_ID - return(GPOINTER_TO_UINT(tid)); + /* Don't use GPOINTER_TO_UINT here, it can't cope with + * sizeof(void *) > sizeof(uint) when a cast to uint would + * overflow + */ + return((gsize)tid); #else return(tid); #endif } -static gpointer thread_attach(guint32 *tid) +static gpointer thread_attach(gsize *tid) { struct _WapiHandle_thread thread_handle = {0}, *thread_handle_p; gpointer handle; gboolean ok; - int ret; int thr_ret; - int i, unrefs = 0; - gpointer ta_ret = NULL; mono_once (&thread_hash_once, thread_hash_init); mono_once (&thread_ops_once, thread_ops_init); thread_handle.state = THREAD_STATE_START; - thread_handle.owner_pid = getpid(); + thread_handle.owner_pid = _wapi_getpid (); + thread_handle.owned_mutexes = g_ptr_array_new (); handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle); if (handle == _WAPI_HANDLE_INVALID) { @@ -455,9 +575,7 @@ static gpointer thread_attach(guint32 *tid) return (NULL); } - pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, - handle); - thr_ret = _wapi_handle_lock_handle (handle); + thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, @@ -474,66 +592,38 @@ static gpointer thread_attach(guint32 *tid) * the handle to store thread exit information */ _wapi_handle_ref (handle); - - /* Lock around the thread create, so that the new thread cant - * race us to look up the thread handle in GetCurrentThread() - */ - pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, - (void *)&thread_hash_mutex); - thr_ret = mono_mutex_lock(&thread_hash_mutex); - g_assert (thr_ret == 0); - ret = _wapi_timed_thread_attach (&thread_handle_p->thread, thread_exit, - handle); - if (ret != 0) { -#ifdef DEBUG - g_message ("%s: Thread attach error: %s", __func__, - strerror(ret)); -#endif + /* suspend_sem is not used for attached threads, but + * thread_exit() might try to destroy it + */ + MONO_SEM_INIT (&thread_handle_p->suspend_sem, 0); + thread_handle_p->handle = handle; + thread_handle_p->id = pthread_self (); - /* Two, because of the reference we took above */ - unrefs = 2; - - goto thread_hash_cleanup; - } - ta_ret = handle; + thr_ret = pthread_setspecific (thread_hash_key, (void *)handle); + g_assert (thr_ret == 0); - g_hash_table_insert (thread_hash, - GUINT_TO_POINTER (thread_handle_p->thread->id), - handle); - #ifdef DEBUG - g_message("%s: Attached thread handle %p thread %p ID %ld", __func__, - handle, thread_handle_p->thread, - thread_handle_p->thread->id); + g_message("%s: Attached thread handle %p ID %ld", __func__, handle, + thread_handle_p->id); #endif if (tid != NULL) { #ifdef PTHREAD_POINTER_ID - *tid = GPOINTER_TO_UINT(thread_handle_p->thread->id); + /* Don't use GPOINTER_TO_UINT here, it can't cope with + * sizeof(void *) > sizeof(uint) when a cast to uint + * would overflow + */ + *tid = (gsize)(thread_handle_p->id); #else - *tid = thread_handle_p->thread->id; + *tid = thread_handle_p->id; #endif } -thread_hash_cleanup: - thr_ret = mono_mutex_unlock (&thread_hash_mutex); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); - cleanup: - thr_ret = _wapi_handle_unlock_handle (handle); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); - - /* Must not call _wapi_handle_unref() with the handle already - * locked - */ - for (i = 0; i < unrefs; i++) { - _wapi_handle_unref (handle); - } + _wapi_handle_unlock_shared_handles (); - return(ta_ret); + return(handle); } /** @@ -550,25 +640,11 @@ static gpointer thread_attach(guint32 *tid) gpointer GetCurrentThread(void) { gpointer ret=NULL; - guint32 tid; - int thr_ret; mono_once(&thread_hash_once, thread_hash_init); mono_once (&thread_ops_once, thread_ops_init); - tid=GetCurrentThreadId(); - - pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, - (void *)&thread_hash_mutex); - thr_ret = mono_mutex_lock(&thread_hash_mutex); - g_assert (thr_ret == 0); - - ret = g_hash_table_lookup (thread_hash, GUINT_TO_POINTER (tid)); - - thr_ret = mono_mutex_unlock(&thread_hash_mutex); - g_assert (thr_ret == 0); - pthread_cleanup_pop (0); - + ret = _wapi_thread_handle_from_id (pthread_self ()); if (!ret) { ret = thread_attach (NULL); } @@ -598,24 +674,13 @@ guint32 ResumeThread(gpointer handle) return (0xFFFFFFFF); } - - if (thread_handle->thread == NULL) { - return(0xFFFFFFFF); - } -#ifdef WITH_INCLUDED_LIBGC - if (thread_handle->thread->suspend_count <= 1) - _wapi_timed_thread_resume (thread_handle->thread); - - return (--thread_handle->thread->suspend_count)); -#else /* This is still a kludge that only copes with starting a * thread that was suspended on create, so don't bother with * the suspend count crap yet */ - _wapi_timed_thread_resume (thread_handle->thread); + _wapi_thread_resume (thread_handle); return(0xFFFFFFFF); -#endif } /** @@ -629,41 +694,7 @@ guint32 ResumeThread(gpointer handle) */ guint32 SuspendThread(gpointer handle) { -#ifdef WITH_INCLUDED_LIBGC - struct _WapiHandle_thread *thread_handle; - gpointer current; - gboolean ok; - - current = GetCurrentThread (); - ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, - (gpointer *)&thread_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up thread handle %p", __func__, - handle); - return (0xFFFFFFFF); - } - - if (thread_handle->thread == NULL) { - return(0xFFFFFFFF); - } - - if (!thread_handle->thread->suspend_count) { - if (handle == current) - _wapi_timed_thread_suspend (thread_handle->thread); - else { - pthread_kill (thread_handle->thread->id, SIGPWR); - while (MONO_SEM_WAIT (&thread_handle->thread->suspended_sem) != 0) { - if (errno != EINTR) { - return(0xFFFFFFFF); - } - } - } - } - - return (thread_handle->thread->suspend_count++); -#else return(0xFFFFFFFF); -#endif } /* @@ -905,47 +936,95 @@ void Sleep(guint32 ms) SleepEx(ms, FALSE); } -guint32 QueueUserAPC (WapiApcProc apc_callback, gpointer handle, - gpointer param) +gboolean _wapi_thread_cur_apc_pending (void) { - struct _WapiHandle_thread *thread_handle; + return(_wapi_thread_apc_pending (GetCurrentThread ())); +} + +static void _wapi_thread_queue_apc (struct _WapiHandle_thread *thread, + guint32 (*apc_callback)(gpointer), + gpointer param) +{ + ApcInfo *apc; + int thr_ret; + + if (thread->owner_pid != _wapi_getpid ()) { + return; + } + + apc = (ApcInfo *)g_new (ApcInfo, 1); + apc->callback = apc_callback; + apc->param = param; + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + thread->apc_queue = g_slist_append (thread->apc_queue, apc); + + _wapi_handle_unlock_shared_handles (); +} + +gboolean _wapi_thread_apc_pending (gpointer handle) +{ + struct _WapiHandle_thread *thread; gboolean ok; ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, - (gpointer *)&thread_handle); + (gpointer *)&thread); if (ok == FALSE) { g_warning ("%s: error looking up thread handle %p", __func__, handle); - return (0); + return (FALSE); } - _wapi_timed_thread_queue_apc (thread_handle->thread, apc_callback, - param); - return(1); -} - -gboolean _wapi_thread_cur_apc_pending (void) -{ - return(_wapi_thread_apc_pending (GetCurrentThread ())); + if (thread->owner_pid != _wapi_getpid ()) { + return(FALSE); + } + + return(thread->apc_queue != NULL); } -gboolean _wapi_thread_apc_pending (gpointer handle) +gboolean _wapi_thread_dispatch_apc_queue (gpointer handle) { - struct _WapiHandle_thread *thread_handle; + struct _WapiHandle_thread *thread; gboolean ok; + ApcInfo *apc; + GSList *list; + int thr_ret; ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, - (gpointer *)&thread_handle); + (gpointer *)&thread); if (ok == FALSE) { g_warning ("%s: error looking up thread handle %p", __func__, handle); return (FALSE); } - return(_wapi_timed_thread_apc_pending (thread_handle->thread)); + if (thread->owner_pid != _wapi_getpid ()) { + return(FALSE); + } + + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + + list = thread->apc_queue; + thread->apc_queue = NULL; + + _wapi_handle_unlock_shared_handles (); + + while (list != NULL) { + apc = (ApcInfo *)list->data; + apc->callback (apc->param); + g_free (apc); + list = g_slist_next (list); + } + g_slist_free (list); + + return(TRUE); } -gboolean _wapi_thread_dispatch_apc_queue (gpointer handle) +guint32 QueueUserAPC (WapiApcProc apc_callback, gpointer handle, + gpointer param) { struct _WapiHandle_thread *thread_handle; gboolean ok; @@ -958,59 +1037,56 @@ gboolean _wapi_thread_dispatch_apc_queue (gpointer handle) return (0); } - _wapi_timed_thread_dispatch_apc_queue (thread_handle->thread); + _wapi_thread_queue_apc (thread_handle, apc_callback, param); return(1); } - - -#ifdef WITH_INCLUDED_LIBGC - -static void GC_suspend_handler (int sig) +void _wapi_thread_own_mutex (gpointer mutex) { struct _WapiHandle_thread *thread_handle; - gpointer handle; gboolean ok; + gpointer thread; + + thread = _wapi_thread_handle_from_id (pthread_self ()); + if (thread == NULL) { + g_warning ("%s: error looking up thread by ID", __func__); + return; + } - handle = GetCurrentThread (); - ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + ok = _wapi_lookup_handle (thread, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle); if (ok == FALSE) { g_warning ("%s: error looking up thread handle %p", __func__, - handle); + thread); return; } - - thread_handle->thread->stack_ptr = &ok; - MONO_SEM_POST (&thread_handle->thread->suspended_sem); - - _wapi_timed_thread_suspend (thread_handle->thread); - thread_handle->thread->stack_ptr = NULL; -} - -static void gc_init (void) -{ - struct sigaction act; - - act.sa_handler = GC_suspend_handler; - g_assert (sigaction (SIGPWR, &act, NULL) == 0); + _wapi_handle_ref (mutex); + + g_ptr_array_add (thread_handle->owned_mutexes, mutex); } -void mono_wapi_push_thread_stack (gpointer handle, gpointer stack_ptr) +void _wapi_thread_disown_mutex (gpointer mutex) { struct _WapiHandle_thread *thread_handle; gboolean ok; - - ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + gpointer thread; + + thread = _wapi_thread_handle_from_id (pthread_self ()); + if (thread == NULL) { + g_warning ("%s: error looking up thread by ID", __func__); + return; + } + + ok = _wapi_lookup_handle (thread, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle); if (ok == FALSE) { g_warning ("%s: error looking up thread handle %p", __func__, - handle); + thread); return; } + + _wapi_handle_unref (mutex); - GC_push_all_stack (thread_handle->thread->stack_ptr, stack_ptr); + g_ptr_array_remove (thread_handle->owned_mutexes, mutex); } - -#endif /* WITH_INCLUDED_LIBGC */ diff --git a/mono/io-layer/threads.h b/mono/io-layer/threads.h index 56965eb064f08..ecba0d45e0b60 100644 --- a/mono/io-layer/threads.h +++ b/mono/io-layer/threads.h @@ -42,11 +42,11 @@ typedef guint32 (*WapiApcProc)(gpointer); extern gpointer CreateThread(WapiSecurityAttributes *security, guint32 stacksize, WapiThreadStart start, - gpointer param, guint32 create, guint32 *tid); -extern gpointer OpenThread (guint32 access, gboolean inherit, guint32 tid); + gpointer param, guint32 create, gsize *tid); /* NB tid is 32bit in MS API */ +extern gpointer OpenThread (guint32 access, gboolean inherit, gsize tid); /* NB tid is 32bit in MS API */ extern void ExitThread(guint32 exitcode) G_GNUC_NORETURN; extern gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode); -extern guint32 GetCurrentThreadId(void); +extern gsize GetCurrentThreadId(void); /* NB return is 32bit in MS API */ extern gpointer GetCurrentThread(void); extern guint32 ResumeThread(gpointer handle); extern guint32 SuspendThread(gpointer handle); @@ -59,5 +59,11 @@ extern void Sleep(guint32 ms); extern guint32 SleepEx(guint32 ms, gboolean alertable); extern guint32 QueueUserAPC (WapiApcProc apc_callback, gpointer thread_handle, gpointer param); + +/* Kludge alert! Making this visible outside io-layer is broken, but I + * can't find any w32 call that will let me do this. + */ +extern void _wapi_thread_abandon_mutexes (gpointer handle); + G_END_DECLS #endif /* _WAPI_THREADS_H_ */ diff --git a/mono/io-layer/uglify.h b/mono/io-layer/uglify.h index f7055de04d0d1..a136ef1354aed 100644 --- a/mono/io-layer/uglify.h +++ b/mono/io-layer/uglify.h @@ -37,6 +37,9 @@ typedef gpointer HANDLE; typedef gpointer *LPHANDLE; typedef guint32 SOCKET; typedef gpointer HMODULE; +typedef gpointer HINSTANCE; +typedef gpointer HWND; +typedef gpointer HKEY; typedef WapiSecurityAttributes SECURITY_ATTRIBUTES; typedef WapiSecurityAttributes *LPSECURITY_ATTRIBUTES; @@ -68,6 +71,8 @@ typedef WapiStartupInfo *LPSTARTUPINFO; typedef WapiProcessInformation PROCESS_INFORMATION; typedef WapiFixedFileInfo VS_FIXEDFILEINFO; typedef WapiApcProc PAPCFUNC; +typedef WapiShellExecuteInfo SHELLEXECUTEINFO; +typedef WapiShellExecuteInfo *LPSHELLEXECUTEINFO; #define CONST const #define VOID void diff --git a/mono/io-layer/wait.c b/mono/io-layer/wait.c index 485a14e5b6440..97dfb76b945aa 100644 --- a/mono/io-layer/wait.c +++ b/mono/io-layer/wait.c @@ -104,6 +104,8 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout, return(WAIT_FAILED); } + _wapi_handle_ops_prewait (handle); + if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) { #ifdef DEBUG g_message ("%s: handle %p has special wait", __func__, handle); @@ -157,6 +159,10 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout, goto done; } + if (timeout == 0) { + ret = WAIT_TIMEOUT; + goto done; + } /* Have to wait for it */ if (timeout != INFINITE) { _wapi_calc_timeout (&abstime, timeout); @@ -165,6 +171,8 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout, do { /* Check before waiting on the condition, just in case */ + _wapi_handle_ops_prewait (handle); + if (own_if_signalled (handle)) { #ifdef DEBUG g_message ("%s: handle %p signalled", __func__, @@ -290,6 +298,8 @@ guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait, return(WAIT_FAILED); } + _wapi_handle_ops_prewait (wait); + if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) { g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait); @@ -342,6 +352,8 @@ guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait, do { /* Check before waiting on the condition, just in case */ + _wapi_handle_ops_prewait (wait); + if (own_if_signalled (wait)) { #ifdef DEBUG g_message ("%s: handle %p signalled", __func__, wait); @@ -494,7 +506,6 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles, { GHashTable *dups; gboolean duplicate = FALSE, bogustype = FALSE, done; - gboolean shared_wait = FALSE; guint32 count, lowest; struct timespec abstime; guint i; @@ -537,11 +548,8 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles, bogustype = TRUE; } - if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i]))) { - shared_wait = TRUE; - } - g_hash_table_insert (dups, handles[i], handles[i]); + _wapi_handle_ops_prewait (handles[i]); } g_hash_table_destroy (dups); @@ -566,6 +574,9 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles, return(WAIT_OBJECT_0+lowest); } + if (timeout == 0) { + return WAIT_TIMEOUT; + } /* Have to wait for some or all handles to become signalled */ @@ -579,10 +590,12 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles, } while(1) { - /* Prod all special-wait handles that aren't already - * signalled + /* Prod all handles with prewait methods and + * special-wait handles that aren't already signalled */ for (i = 0; i < numobjects; i++) { + _wapi_handle_ops_prewait (handles[i]); + if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) { _wapi_handle_ops_special_wait (handles[i], 0); } @@ -604,18 +617,10 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles, thr_ret = _wapi_handle_lock_signal_mutex (); g_assert (thr_ret == 0); - if (shared_wait == TRUE) { - if (timeout == INFINITE) { - ret = _wapi_handle_wait_signal_poll_share (); - } else { - ret = _wapi_handle_timedwait_signal_poll_share (&abstime); - } + if (timeout == INFINITE) { + ret = _wapi_handle_wait_signal (); } else { - if (timeout == INFINITE) { - ret = _wapi_handle_wait_signal (); - } else { - ret = _wapi_handle_timedwait_signal (&abstime); - } + ret = _wapi_handle_timedwait_signal (&abstime); } #ifdef DEBUG diff --git a/mono/io-layer/wapi-private.h b/mono/io-layer/wapi-private.h index 33f7c8f8e4dbe..918c1d4ee312b 100644 --- a/mono/io-layer/wapi-private.h +++ b/mono/io-layer/wapi-private.h @@ -4,7 +4,7 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. + * (C) 2002-2006 Novell, Inc. */ #ifndef _WAPI_PRIVATE_H_ @@ -24,7 +24,8 @@ /* Increment this whenever an incompatible change is made to the * shared handle structure. */ -#define _WAPI_HANDLE_VERSION 6 +/* Next time I change this, remember to fix the process count in shared.c */ +#define _WAPI_HANDLE_VERSION 10 typedef enum { WAPI_HANDLE_UNUSED=0, @@ -39,20 +40,27 @@ typedef enum { WAPI_HANDLE_PROCESS, WAPI_HANDLE_PIPE, WAPI_HANDLE_NAMEDMUTEX, + WAPI_HANDLE_NAMEDSEM, + WAPI_HANDLE_NAMEDEVENT, WAPI_HANDLE_COUNT } WapiHandleType; extern const char *_wapi_handle_typename[]; #define _WAPI_SHARED_HANDLE(type) (type == WAPI_HANDLE_PROCESS || \ - type == WAPI_HANDLE_NAMEDMUTEX) + type == WAPI_HANDLE_THREAD || \ + type == WAPI_HANDLE_NAMEDMUTEX || \ + type == WAPI_HANDLE_NAMEDSEM || \ + type == WAPI_HANDLE_NAMEDEVENT) #define _WAPI_FD_HANDLE(type) (type == WAPI_HANDLE_FILE || \ type == WAPI_HANDLE_CONSOLE || \ type == WAPI_HANDLE_SOCKET || \ type == WAPI_HANDLE_PIPE) -#define _WAPI_SHARED_NAMESPACE(type) (type == WAPI_HANDLE_NAMEDMUTEX) +#define _WAPI_SHARED_NAMESPACE(type) (type == WAPI_HANDLE_NAMEDMUTEX || \ + type == WAPI_HANDLE_NAMEDSEM || \ + type == WAPI_HANDLE_NAMEDEVENT) typedef struct { @@ -91,6 +99,12 @@ struct _WapiHandleOps * Returns the WaitForSingleObject return code. */ guint32 (*special_wait)(gpointer handle, guint32 timeout); + + /* Called by WaitForSingleObject and WaitForMultipleObjects, + * if the handle in question needs some preprocessing before the + * signal wait. + */ + void (*prewait)(gpointer handle); }; #include @@ -110,7 +124,6 @@ struct _WapiHandle_shared_ref }; #define _WAPI_HANDLE_INITIAL_COUNT 4096 -#define _WAPI_HEADROOM 16 struct _WapiHandleUnshared { @@ -129,50 +142,39 @@ struct _WapiHandleUnshared struct _WapiHandle_sem sem; struct _WapiHandle_socket sock; struct _WapiHandle_shared_ref shared; - - /* Move thread data into the private set, while - * problems with cleaning up shared handles are fixed - */ - struct _WapiHandle_thread thread; } u; }; -struct _WapiHandleSharedMetadata -{ - volatile guint32 offset; - guint32 timestamp; - volatile gboolean signalled; -}; - struct _WapiHandleShared { WapiHandleType type; - gboolean stale; + guint32 timestamp; + guint32 handle_refs; + volatile gboolean signalled; union { - /* Leave this one while the thread is in the private - * set, so the shared space doesn't change size - */ struct _WapiHandle_thread thread; struct _WapiHandle_process process; struct _WapiHandle_namedmutex namedmutex; + struct _WapiHandle_namedsem namedsem; + struct _WapiHandle_namedevent namedevent; } u; }; #define _WAPI_SHARED_SEM_NAMESPACE 0 -#define _WAPI_SHARED_SEM_COLLECTION 1 -#define _WAPI_SHARED_SEM_SHARE 2 -#define _WAPI_SHARED_SEM_HANDLE 3 +/*#define _WAPI_SHARED_SEM_COLLECTION 1*/ +#define _WAPI_SHARED_SEM_FILESHARE 2 +#define _WAPI_SHARED_SEM_SHARED_HANDLES 3 +#define _WAPI_SHARED_SEM_PROCESS_COUNT_LOCK 6 +#define _WAPI_SHARED_SEM_PROCESS_COUNT 7 #define _WAPI_SHARED_SEM_COUNT 8 /* Leave some future expansion space */ struct _WapiHandleSharedLayout { - volatile guint32 signal_count; volatile guint32 collection_count; volatile key_t sem_key; - struct _WapiHandleSharedMetadata metadata[_WAPI_HANDLE_INITIAL_COUNT]; struct _WapiHandleShared handles[_WAPI_HANDLE_INITIAL_COUNT]; };