diff --git a/ChangeLog b/ChangeLog index 689c4b654a314..e2991d7411936 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2002-04-30 Dick Porter + + * acconfig.h: + * configure.in: Added option to disable using shared memory for + handles + 2002-04-28 Gonzalo Paniagua Javier * configure.in: fix for conditional "THREADS_PTHREAD" was never diff --git a/acconfig.h b/acconfig.h index 53815a7b6bfc3..4d3912b688f9a 100644 --- a/acconfig.h +++ b/acconfig.h @@ -13,3 +13,4 @@ #undef NAME_DEV_RANDOM #undef HAVE_CRYPT_RNG #undef HAVE_BOEHM_GC +#undef DISABLE_SHARED_HANDLES diff --git a/configure.in b/configure.in index 477e421c51d10..fa8f6d19d61e4 100644 --- a/configure.in +++ b/configure.in @@ -432,6 +432,14 @@ else fi fi +AC_MSG_CHECKING([if inter-process shared handles are requested]) +AC_ARG_ENABLE(shared-handles, [ --disable-shared-handles disable inter-process shared handles], try_shared_handles=$enableval, try_shared_handles=yes) +AC_MSG_RESULT($try_shared_handles) +if test "x$try_shared_handles" != "xyes"; then + AC_DEFINE(DISABLE_SHARED_HANDLES) + AC_SUBST(DISABLE_SHARED_HANDLES) +fi + TARGET="unknown" ACCESS_UNALIGNED="yes" @@ -485,6 +493,7 @@ mono/monoburg/Makefile mono/monograph/Makefile mono/jit/Makefile mono/io-layer/Makefile +mono/handles/Makefile runtime/Makefile scripts/Makefile man/Makefile diff --git a/mono/Makefile.am b/mono/Makefile.am index 589bffcb67e5e..f4924ea34ceae 100644 --- a/mono/Makefile.am +++ b/mono/Makefile.am @@ -1,4 +1,9 @@ +# the handles dir doesn't apply to windows +if PLATFORM_WIN32 SUBDIRS = utils io-layer monoburg metadata cil dis \ arch monograph interpreter jit tests benchmark - +else +SUBDIRS = utils io-layer monoburg metadata cil dis \ + arch monograph interpreter jit tests benchmark handles +endif diff --git a/mono/handles/.cvsignore b/mono/handles/.cvsignore new file mode 100644 index 0000000000000..4ad75e1e41e3b --- /dev/null +++ b/mono/handles/.cvsignore @@ -0,0 +1,7 @@ +.deps +.libs +Makefile +Makefile.in +hps +scratch +shmdel diff --git a/mono/handles/ChangeLog b/mono/handles/ChangeLog new file mode 100644 index 0000000000000..68548d5c14a2e --- /dev/null +++ b/mono/handles/ChangeLog @@ -0,0 +1,10 @@ +2002-04-30 Dick Porter + + * shmdel.c: + * scratch.c: + * hps.c: + * Makefile.am: Some tools to help debug shared memory handles: + 'hps' shows handle status, 'scratch' displays info about scratch + data (such as handle names), 'shmdel' deletes the shared memory + segment. + diff --git a/mono/handles/Makefile.am b/mono/handles/Makefile.am new file mode 100644 index 0000000000000..4b45f62a10047 --- /dev/null +++ b/mono/handles/Makefile.am @@ -0,0 +1,28 @@ + +noinst_PROGRAMS = hps scratch shmdel + +INCLUDES = \ + -I$(top_srcdir) \ + $(GMODULE_CFLAGS) \ + $(GLIB_CFLAGS) + +hps_LDADD = \ + ../io-layer/libwapi.a \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + -lm + +scratch_LDADD = \ + ../io-layer/libwapi.a \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + -lm + +shmdel_LDADD = \ + ../io-layer/libwapi.a \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + -lm + +EXTRA_DIST = ChangeLog + diff --git a/mono/handles/hps.c b/mono/handles/hps.c new file mode 100644 index 0000000000000..998556704c565 --- /dev/null +++ b/mono/handles/hps.c @@ -0,0 +1,196 @@ +#include +#include + +#include + +/* We're digging into handle internals here... */ +#include +#include + +static const guchar *unused_details (struct _WapiHandleShared *handle); +static const guchar *file_details (struct _WapiHandleShared *handle); +static const guchar *console_details (struct _WapiHandleShared *handle); +static const guchar *thread_details (struct _WapiHandleShared *handle); +static const guchar *sem_details (struct _WapiHandleShared *handle); +static const guchar *mutex_details (struct _WapiHandleShared *handle); +static const guchar *event_details (struct _WapiHandleShared *handle); +static const guchar *socket_details (struct _WapiHandleShared *handle); +static const guchar *find_details (struct _WapiHandleShared *handle); +static const guchar *process_details (struct _WapiHandleShared *handle); + +/* This depends on the ordering of the enum WapiHandleType in + * io-layer/wapi-private.h + */ +static const char *typename[]={ + "Unused", + "File", + "Console", + "Thread", + "Sem", + "Mutex", + "Event", + "Socket", + "Find", + "Process", + "Error!!" +}; + +/* So does this... */ +static const guchar * (*details[])(struct _WapiHandleShared *)= +{ + unused_details, + file_details, + console_details, + thread_details, + sem_details, + mutex_details, + event_details, + socket_details, + find_details, + process_details, + unused_details, +}; + +int main (int argc, char **argv) +{ + guint32 idx; + guint32 scratch_size; + + _wapi_shared_data=_wapi_shm_attach (&scratch_size); + + _wapi_handle_shared_lock (); + + /* Make sure index 0 is actually unused */ + for(idx=0; idx<_WAPI_MAX_HANDLES; idx++) { + struct _WapiHandleShared *shared=&_wapi_shared_data->handles[idx]; + + if(shared->type!=WAPI_HANDLE_UNUSED) { + g_print ("%4x [%7s] %4u %s (%s)\n", idx, + typename[shared->type], shared->ref, + shared->signalled?"Sg":"Un", + details[shared->type](shared)); + } + } + + _wapi_handle_shared_unlock (); + + exit (0); +} + +static const guchar *unused_details (struct _WapiHandleShared *handle) +{ + return("unused details"); +} + +static const guchar *file_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + guchar *name; + struct _WapiHandle_file *file=&handle->u.file; + + name=_wapi_handle_scratch_lookup_as_string (file->filename); + + g_snprintf (buf, sizeof(buf), + "[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u", + name==NULL?(guchar *)"":name, + file->fileaccess&GENERIC_READ?'R':'.', + file->fileaccess&GENERIC_WRITE?'W':'.', + file->fileaccess&GENERIC_EXECUTE?'X':'.', + file->sharemode&FILE_SHARE_READ?'R':'.', + file->sharemode&FILE_SHARE_WRITE?'W':'.', + file->sharemode&FILE_SHARE_DELETE?'D':'.', + file->attrs); + + if(name!=NULL) { + g_free (name); + } + + return(buf); +} + +static const guchar *console_details (struct _WapiHandleShared *handle) +{ + return(file_details (handle)); +} + +static const guchar *thread_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + struct _WapiHandle_thread *thr=&handle->u.thread; + + g_snprintf (buf, sizeof(buf), + "proc: %p, state: %d, exit: %u, join: %s", + thr->process_handle, thr->state, thr->exitstatus, + thr->joined?"TRUE":"FALSE"); + + return(buf); +} + +static const guchar *sem_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + struct _WapiHandle_sem *sem=&handle->u.sem; + + g_snprintf (buf, sizeof(buf), "val: %5u, max: %5d", + sem->val, sem->max); + + return(buf); +} + +static const guchar *mutex_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + guchar *name; + struct _WapiHandle_mutex *mut=&handle->u.mutex; + + name=_wapi_handle_scratch_lookup_as_string (mut->name); + + g_snprintf (buf, sizeof(buf), "[%20s] own: %5d:%5ld, count: %5u", + name==NULL?(guchar *)"":name, mut->pid, mut->tid, + mut->recursion); + + if(name!=NULL) { + g_free (name); + } + + return(buf); +} + +static const guchar *event_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + struct _WapiHandle_event *event=&handle->u.event; + + g_snprintf (buf, sizeof(buf), "manual: %s", + event->manual?"TRUE":"FALSE"); + + return(buf); +} + +static const guchar *socket_details (struct _WapiHandleShared *handle) +{ + /* Nothing to see here */ + return(""); +} + +static const guchar *find_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + struct _WapiHandle_find *find=&handle->u.find; + + g_snprintf (buf, sizeof(buf), "count: %5d", + find->count); + + return(buf); +} + +static const guchar *process_details (struct _WapiHandleShared *handle) +{ + static guchar buf[80]; + struct _WapiHandle_process *proc=&handle->u.process; + + g_snprintf (buf, sizeof(buf), "pid: %5u", + proc->id); + + return(buf); +} diff --git a/mono/handles/scratch.c b/mono/handles/scratch.c new file mode 100644 index 0000000000000..4bc669ddf3853 --- /dev/null +++ b/mono/handles/scratch.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include + +/* We're digging into handle internals here... */ +#include +#include + +#define HDRSIZE sizeof(struct _WapiScratchHeader) + +static guchar *printable (guchar *data, guint32 datalen) +{ + static guchar buf[32]; + guint32 i; + + if(datalen>=32) { + datalen=31; + } + + for(i=0; iscratch_base[0]; + if(hdr->flags==0 && hdr->length==0) { + g_print ("Scratch space unused\n"); + _wapi_handle_shared_unlock (); + exit (0); + } + + while(idxscratch_base[idx]; + if(hdr->flags & WAPI_SHM_SCRATCH_FREE) { + g_print ("Free block at %6d (index %6d), length %6d\n", + idx, idx+HDRSIZE, hdr->length); + } else { + guchar *data=&_wapi_shared_data->scratch_base[idx+HDRSIZE]; + + g_print ("Used block at %6d (index %6d), length %6d, [%s]\n", + idx, idx+HDRSIZE, hdr->length, + printable (data, hdr->length)); + } + + idx+=(hdr->length+HDRSIZE); + } + + _wapi_handle_shared_unlock (); + + exit (0); +} diff --git a/mono/handles/shmdel.c b/mono/handles/shmdel.c new file mode 100644 index 0000000000000..2eb6571d24126 --- /dev/null +++ b/mono/handles/shmdel.c @@ -0,0 +1,12 @@ +#include +#include + +/* We're digging into handle internals here... */ +#include + +int main (int argc, char **argv) +{ + _wapi_shm_destroy (); + + exit (0); +} diff --git a/mono/interpreter/ChangeLog b/mono/interpreter/ChangeLog index cfb9e92e67483..d092162adb0ae 100644 --- a/mono/interpreter/ChangeLog +++ b/mono/interpreter/ChangeLog @@ -1,3 +1,7 @@ +2002-04-30 Dick Porter + + * interp.c: Tell glib to not abort when g_log() etc print + recursively Mon Apr 22 16:52:03 CEST 2002 Paolo Molaro diff --git a/mono/interpreter/interp.c b/mono/interpreter/interp.c index abc35ad377eb1..0d94a025745ba 100644 --- a/mono/interpreter/interp.c +++ b/mono/interpreter/interp.c @@ -3923,6 +3923,9 @@ main (int argc, char *argv []) if (!file) usage (); + g_log_set_always_fatal (G_LOG_LEVEL_ERROR); + g_log_set_fatal_mask (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR); + mono_init_icall (); mono_add_internal_call ("System.Array::Set", ves_array_set); mono_add_internal_call ("System.Array::Get", ves_array_get); diff --git a/mono/io-layer/ChangeLog b/mono/io-layer/ChangeLog index 99212f2c73937..1526eea44f30e 100644 --- a/mono/io-layer/ChangeLog +++ b/mono/io-layer/ChangeLog @@ -1,3 +1,14 @@ +2002-04-30 Dick Porter + + * Completely rewrote the handle waiting code: removed the helper + thread and its attendant complexity. All handle waiting is now + abstracted into the WaitForSingleObject() and + WaitForMultipleObjects() functions. + + * Implemented inter-process sharing of handles using sysv shared + memory. This makes handles even more opaque, with a handle now + just an index into an array. + 2002-04-25 Dan Lewis * io.c: unitialized pointer in GetCurrentDirectory. diff --git a/mono/io-layer/Makefile.am b/mono/io-layer/Makefile.am index 3cd566d17fe3d..9c1550e7ec2fb 100644 --- a/mono/io-layer/Makefile.am +++ b/mono/io-layer/Makefile.am @@ -19,6 +19,7 @@ OTHER_H = \ io-layer.h \ macros.h \ mutexes.h \ + processes.h \ semaphores.h \ sockets.h \ status.h \ @@ -41,28 +42,39 @@ OTHER_SRC = \ error.h \ events.c \ events.h \ + event-private.h \ handles.c \ handles.h \ handles-private.h \ io.c \ io.h \ + io-private.h \ io-layer.h \ macros.h \ misc.c \ misc-private.h \ mutexes.c \ mutexes.h \ + mutex-private.h \ mono-mutex.c \ mono-mutex.h \ + processes.c \ + processes.h \ + process-private.h \ semaphores.c \ semaphores.h \ + semaphore-private.h \ + shared.c \ + shared.h \ sockets.c \ sockets.h \ + socket-private.h \ status.h \ system.c \ system.h \ threads.c \ threads.h \ + thread-private.h \ timefuncs.c \ timefuncs.h \ timed-thread.c \ diff --git a/mono/io-layer/context.c b/mono/io-layer/context.c index f87a9d3151279..4895be235209a 100644 --- a/mono/io-layer/context.c +++ b/mono/io-layer/context.c @@ -4,7 +4,7 @@ #include "mono/io-layer/wapi.h" -gboolean GetThreadContext(WapiHandle *handle G_GNUC_UNUSED, WapiContext *context G_GNUC_UNUSED) +gboolean GetThreadContext(gpointer handle G_GNUC_UNUSED, WapiContext *context G_GNUC_UNUSED) { return(FALSE); } diff --git a/mono/io-layer/context.h b/mono/io-layer/context.h index 456714d7dcbca..1f00e8e1a2612 100644 --- a/mono/io-layer/context.h +++ b/mono/io-layer/context.h @@ -72,6 +72,6 @@ typedef struct guint8 ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } WapiContext; -extern gboolean GetThreadContext(WapiHandle *handle, WapiContext *context); +extern gboolean GetThreadContext(gpointer handle, WapiContext *context); #endif /* _WAPI_COMPEX_H_ */ diff --git a/mono/io-layer/event-private.h b/mono/io-layer/event-private.h new file mode 100644 index 0000000000000..e7f17216aafac --- /dev/null +++ b/mono/io-layer/event-private.h @@ -0,0 +1,20 @@ +#ifndef _WAPI_EVENT_PRIVATE_H_ +#define _WAPI_EVENT_PRIVATE_H_ + +#include +#include +#include + +#include + +struct _WapiHandle_event +{ + gboolean manual; +}; + +struct _WapiHandlePrivate_event +{ + int dummy; +}; + +#endif /* _WAPI_EVENT_PRIVATE_H_ */ diff --git a/mono/io-layer/events.c b/mono/io-layer/events.c index 829f8b8e2433d..4f522e3d45203 100644 --- a/mono/io-layer/events.c +++ b/mono/io-layer/events.c @@ -3,42 +3,21 @@ #include #include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" -#include "wait-private.h" -#include "handles-private.h" -#include "misc-private.h" +#include +#include +#include +#include +#include -#include "mono-mutex.h" +#include -#undef DEBUG - -/* event_wait() uses the event-private condition to signal that the - * event has been set - * - * Hold mutex before setting the event, and before the final test - * Hold rwlock for reading while testing the event - * Hold rwlock for writing before resetting the event - */ -struct _WapiHandle_event -{ - WapiHandle handle; - mono_mutex_t mutex; - pthread_cond_t cond; - pthread_rwlock_t rwlock; - gboolean manual; -}; +#include -/* event_wait_multiple() uses the global condition to signal that an - * event has been set - */ -static mono_mutex_t event_signal_mutex = MONO_MUTEX_INITIALIZER; -static pthread_cond_t event_signal_cond = PTHREAD_COND_INITIALIZER; +#undef DEBUG -static void event_close(WapiHandle *handle); -static gboolean event_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms); -static guint32 event_wait_multiple(gpointer data); -static void event_signal(WapiHandle *handle); +static void event_close(gpointer handle); +static void event_signal(gpointer handle); +static void event_own (gpointer handle); static struct _WapiHandleOps event_ops = { event_close, /* close */ @@ -51,401 +30,64 @@ static struct _WapiHandleOps event_ops = { NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - event_wait, /* wait */ - event_wait_multiple, /* wait_multiple */ event_signal, /* signal */ + event_own, /* own */ + NULL, /* is_owned */ }; -static void event_close(WapiHandle *handle) -{ - struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": closing event handle %p", - event_handle); -#endif +static pthread_once_t event_ops_once=PTHREAD_ONCE_INIT; - mono_mutex_destroy(&event_handle->mutex); - pthread_cond_destroy(&event_handle->cond); - pthread_rwlock_destroy(&event_handle->rwlock); +static void event_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_EVENT, &event_ops); + _wapi_handle_register_capabilities (WAPI_HANDLE_EVENT, + WAPI_HANDLE_CAP_WAIT | + WAPI_HANDLE_CAP_SIGNAL); } -static gboolean event_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms) +static void event_close(gpointer handle) { - struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle; - struct timespec timeout; - int ret; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting on event handle %p for %d ms", handle, ms); -#endif - - mono_mutex_lock(&event_handle->mutex); - - /* Signal this handle after we have obtained the event lock */ - if(signal!=NULL) { - signal->ops->signal(signal); - } - - /* First check if the handle is already signalled */ - if(handle->signalled==TRUE) { - /* If this is an auto-reset event, reset the state to - * unsignalled - */ - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": event handle %p already signalled", handle); -#endif - - if(event_handle->manual==FALSE) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": resetting auto event handle %p", handle); -#endif - handle->signalled=FALSE; - } - mono_mutex_unlock(&event_handle->mutex); - - return(TRUE); - } - - /* We'll have to wait for it then */ - if(ms!=INFINITE) { - _wapi_calc_timeout(&timeout, ms); - } - -again: - /* Acquire a read lock so that the signal status can't be - * reset without us noticing. (PulseEvent and ResetEvent will - * gain a write lock before changing the status to - * unsignalled, which will block while one or more threads - * hold a read lock.) - */ - pthread_rwlock_rdlock(&event_handle->rwlock); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting for event handle %p to be signalled", handle); -#endif - - if(ms==INFINITE) { - ret=mono_cond_wait(&event_handle->cond, - &event_handle->mutex); - } else { - ret=mono_cond_timedwait(&event_handle->cond, - &event_handle->mutex, &timeout); - } - - if(ret==0) { - /* Condition was signalled, so hopefully event is - * signalled now. (It might not be if its an - * auto-reset event and someone else got in before - * us.) - */ - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": event handle %p signalled", - handle); -#endif - if(handle->signalled==TRUE) { - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": event handle %p still signalled", handle); -#endif - /* If this is an auto-reset event, reset the - * state to unsignalled - */ - if(event_handle->manual==FALSE) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": resetting auto event handle %p", - handle); -#endif - handle->signalled=FALSE; - } - pthread_rwlock_unlock(&event_handle->rwlock); - mono_mutex_unlock(&event_handle->mutex); - - return(TRUE); - } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": event handle %p no longer signalled", handle); -#endif - - /* Better luck next time */ - - /* Drop the rwlock briefly so that another thread has - * a chance to reset the event - */ - pthread_rwlock_unlock(&event_handle->rwlock); - goto again; + struct _WapiHandle_event *event_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up event handle %p", handle); + return; } - - /* Timeout or other error */ #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": wait on event handle %p error: %s", handle, - strerror(ret)); + g_message(G_GNUC_PRETTY_FUNCTION ": closing event handle %p", handle); #endif - - pthread_rwlock_unlock(&event_handle->rwlock); - mono_mutex_unlock(&event_handle->mutex); - - return(FALSE); } -static gboolean event_count_signalled(WaitQueueItem *item, guint32 numhandles, - gboolean waitall, guint32 *retcount) +static void event_signal(gpointer handle) { - guint32 count, i; - gboolean ret; - - /* Lock all the handles, with backoff */ -again: - for(i=0; ihandles[WAPI_HANDLE_EVENT], i); - - ret=mono_mutex_trylock(&event_handle->mutex); - if(ret!=0) { - /* Bummer */ - while(i--) { - event_handle=g_ptr_array_index( - item->handles[WAPI_HANDLE_EVENT], i); - mono_mutex_unlock(&event_handle->mutex); - } - - /* It's not possible for two threads calling - * WaitForMultipleObjects to both be calling - * this function simultaneously, because the - * global event_signal_mutex is held. - * Therefore any collision is with a single - * lock from one of the functions that deal - * with single event handles. It's just about - * theoretically possible for the other - * threads to keep locking an event in a tight - * loop but eventually we will get the lock. - */ - sched_yield(); - - goto again; - } - } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Locked all event handles"); -#endif - - count=_wapi_handle_count_signalled(item, WAPI_HANDLE_EVENT); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": %d event handles signalled", - count); -#endif - - if((waitall==TRUE && count==numhandles) || - (waitall==FALSE && count>0)) { - /* done */ - ret=TRUE; - } else { - ret=FALSE; - } - - for(i=0; ihandles[WAPI_HANDLE_EVENT], i); - - mono_mutex_unlock(&event_handle->mutex); - } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Returning %d", ret); -#endif - - *retcount=count; - return(ret); + ResetEvent(handle); } -static guint32 event_wait_multiple(gpointer data) +static void event_own (gpointer handle) { - WaitQueueItem *item=(WaitQueueItem *)data; - struct timespec timeout; - guint32 iterations; - guint32 numhandles, count, i; - gboolean done; - int ret; - - numhandles=item->handles[WAPI_HANDLE_EVENT]->len; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting on %d event handles for %d ms", numhandles, - item->timeout); -#endif - - /* First, check if any of the handles are already - * signalled. If waitall is specified we only return if all - * handles have been signalled. - */ - done=event_count_signalled(item, numhandles, item->waitall, &count); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Preliminary check found %d handles signalled", count); -#endif - - if(done==TRUE) { - item->waited[WAPI_HANDLE_EVENT]=TRUE; - item->waitcount[WAPI_HANDLE_EVENT]=count; - - return(count); + struct _WapiHandle_event *event_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up event handle %p", handle); + return; } - /* We'll have to wait then */ - - mono_mutex_lock(&event_signal_mutex); - - iterations=0; - do { - iterations++; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Wait iteration %d", - iterations); -#endif - - /* If the timeout isnt INFINITE but greater than 1s, - * split the timeout into 1s chunks. - * - * This is so that ResetEvent() wont block forever if - * another thread is waiting on multiple events, with - * some already signalled, and ResetEvent() wants to - * reset one of the signalled ones. (1s is a bit of a - * long wait too, this might need to be tuned.) - */ - - if((item->timeout!=INFINITE) && - (item->timeout < (iterations*1000))) { - _wapi_calc_timeout( - &timeout, item->timeout-((iterations-1)*1000)); - } else { - _wapi_calc_timeout(&timeout, 1000); - } - - /* Acquire a read lock on all handles so that the - * signal status can't be reset without us - * noticing. (PulseEvent and ResetEvent will gain a - * write lock before changing the status to - * unsignalled, which will block while one or more - * threads hold a read lock.) - */ - for(i=0; ihandles[WAPI_HANDLE_EVENT], i); - - pthread_rwlock_rdlock(&event_handle->rwlock); - } - - ret=mono_cond_timedwait(&event_signal_cond, - &event_signal_mutex, &timeout); - - if(ret==0) { - /* Condition was signalled, so hopefully an - * event is signalled now. (It might not be - * if it was an auto-reset event and someone - * else got in before us.) - */ - done=event_count_signalled(item, numhandles, - item->waitall, &count); - #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": signal check found %d handles signalled", - count); + g_message(G_GNUC_PRETTY_FUNCTION ": owning event handle %p", handle); #endif - - if(done==TRUE) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Returning wait success"); -#endif - - for(i=0; ihandles[WAPI_HANDLE_EVENT], i); - - pthread_rwlock_unlock(&event_handle->rwlock); - } - - item->waited[WAPI_HANDLE_EVENT]=TRUE; - item->waitcount[WAPI_HANDLE_EVENT]=count; - - return(count); - } - } else { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Wait error %s", - strerror(ret)); -#endif - } -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Still waiting for more event handles"); -#endif - /* Drop the rwlocks briefly so that another thread has - * a chance to reset any of the events - */ - for(i=0; ihandles[WAPI_HANDLE_EVENT], i); - - pthread_rwlock_unlock(&event_handle->rwlock); - } - } while((item->timeout==INFINITE) || - (item->timeout > (iterations * 1000))); - - /* Timeout or other error */ - - for(i=0; ihandles[WAPI_HANDLE_EVENT], i); - - pthread_rwlock_unlock(&event_handle->rwlock); + if(event_handle->manual==FALSE) { + _wapi_handle_set_signal_state (handle, FALSE, FALSE); } - - mono_mutex_unlock(&event_signal_mutex); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Returning wait failed"); -#endif - - item->waited[WAPI_HANDLE_EVENT]=TRUE; - item->waitcount[WAPI_HANDLE_MUTEX]=0; - - return(0); -} - -static void event_signal(WapiHandle *handle) -{ - ResetEvent(handle); } /** @@ -468,23 +110,37 @@ static void event_signal(WapiHandle *handle) * * Return value: A new handle, or %NULL on error. */ -WapiHandle *CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual, - gboolean initial, const guchar *name G_GNUC_UNUSED) +gpointer CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual, + gboolean initial, const guchar *name G_GNUC_UNUSED) { struct _WapiHandle_event *event_handle; - WapiHandle *handle; + gpointer handle; + gboolean ok; - event_handle=(struct _WapiHandle_event *)g_new0(struct _WapiHandle_event, 1); - handle=(WapiHandle *)event_handle; - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_EVENT, event_ops); + pthread_once (&event_ops_once, event_ops_init); + + handle=_wapi_handle_new (WAPI_HANDLE_EVENT); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating event handle"); + return(NULL); + } + + _wapi_handle_lock_handle (handle); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up event handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(NULL); + } - mono_mutex_init(&event_handle->mutex, NULL); - pthread_cond_init(&event_handle->cond, NULL); - pthread_rwlock_init(&event_handle->rwlock, NULL); event_handle->manual=manual; if(initial==TRUE) { - handle->signalled=TRUE; + _wapi_handle_set_signal_state (handle, TRUE, FALSE); } #ifdef DEBUG @@ -492,6 +148,8 @@ WapiHandle *CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean handle); #endif + _wapi_handle_unlock_handle (handle); + return(handle); } @@ -510,56 +168,55 @@ WapiHandle *CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean * Return value: %TRUE on success, %FALSE otherwise. (Currently only * ever returns %TRUE). */ -gboolean PulseEvent(WapiHandle *handle) +gboolean PulseEvent(gpointer handle) { - struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle; + struct _WapiHandle_event *event_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up event handle %p", handle); + return(FALSE); + } - mono_mutex_lock(&event_handle->mutex); + _wapi_handle_lock_handle (handle); #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Pulsing event handle %p", handle); #endif - handle->signalled=TRUE; - - /* Tell everyone blocking on WaitForSingleObject */ if(event_handle->manual==TRUE) { - pthread_cond_broadcast(&event_handle->cond); + _wapi_handle_set_signal_state (handle, TRUE, TRUE); } else { - pthread_cond_signal(&event_handle->cond); + _wapi_handle_set_signal_state (handle, TRUE, FALSE); } - mono_mutex_unlock(&event_handle->mutex); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Informed single waits for event handle %p", handle); -#endif - - /* Tell everyone blocking on WaitForMultipleObjects */ - mono_mutex_lock(&event_signal_mutex); - pthread_cond_broadcast(&event_signal_cond); - mono_mutex_unlock(&event_signal_mutex); -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Informed multiple waits for event handles"); -#endif + _wapi_handle_unlock_handle (handle); - /* Reset the handle signal state */ - - /* This rwlock blocks until no other thread holds a read lock. - * This ensures that we can't reset the event until every - * waiting thread has had a chance to examine it - */ - pthread_rwlock_wrlock(&event_handle->rwlock); + 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 + */ + sched_yield (); + /* 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 broadcasting + * a condition. + */ #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Obtained write lock on event handle %p", handle); + g_message(G_GNUC_PRETTY_FUNCTION + ": Obtained write lock on event handle %p", handle); #endif - handle->signalled=FALSE; - pthread_rwlock_unlock(&event_handle->rwlock); + _wapi_handle_lock_handle (handle); + _wapi_handle_set_signal_state (handle, FALSE, FALSE); + _wapi_handle_unlock_handle (handle); + } return(TRUE); } @@ -573,42 +230,44 @@ gboolean PulseEvent(WapiHandle *handle) * Return value: %TRUE on success, %FALSE otherwise. (Currently only * ever returns %TRUE). */ -gboolean ResetEvent(WapiHandle *handle) +gboolean ResetEvent(gpointer handle) { - struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle; + struct _WapiHandle_event *event_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up event handle %p", handle); + return(FALSE); + } #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Resetting event handle %p", handle); #endif - /* Test for the current state, because another thread might be - * waiting forever on an unsignalled event with the read lock - * held. Theres no point going for the write lock if we dont - * need it. - */ - mono_mutex_lock(&event_handle->mutex); - if(handle->signalled==FALSE) { + _wapi_handle_lock_handle (handle); + if(_wapi_handle_issignalled (handle)==FALSE) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": No need to reset event handle %p", handle); #endif - mono_mutex_unlock(&event_handle->mutex); + _wapi_handle_unlock_handle (handle); return(TRUE); } - mono_mutex_unlock(&event_handle->mutex); - pthread_rwlock_wrlock(&event_handle->rwlock); - #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Obtained write lock on event handle %p", handle); #endif - handle->signalled=FALSE; - pthread_rwlock_unlock(&event_handle->rwlock); + _wapi_handle_set_signal_state (handle, FALSE, FALSE); + + _wapi_handle_unlock_handle (handle); return(TRUE); } @@ -627,41 +286,33 @@ gboolean ResetEvent(WapiHandle *handle) * Return value: %TRUE on success, %FALSE otherwise. (Currently only * ever returns %TRUE). */ -gboolean SetEvent(WapiHandle *handle) +gboolean SetEvent(gpointer handle) { - struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle; + struct _WapiHandle_event *event_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT, + (gpointer *)&event_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up event handle %p", handle); + return(FALSE); + } - mono_mutex_lock(&event_handle->mutex); + _wapi_handle_lock_handle (handle); #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Setting event handle %p", handle); #endif - handle->signalled=TRUE; - - /* Tell everyone blocking on WaitForSingleObject */ if(event_handle->manual==TRUE) { - pthread_cond_broadcast(&event_handle->cond); + _wapi_handle_set_signal_state (handle, TRUE, TRUE); } else { - pthread_cond_signal(&event_handle->cond); + _wapi_handle_set_signal_state (handle, TRUE, FALSE); } - mono_mutex_unlock(&event_handle->mutex); -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Informed single waits for event handle %p", handle); -#endif - - /* Tell everyone blocking on WaitForMultipleObjects */ - mono_mutex_lock(&event_signal_mutex); - pthread_cond_broadcast(&event_signal_cond); - mono_mutex_unlock(&event_signal_mutex); + _wapi_handle_unlock_handle (handle); -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Informed multiple waits for event handles"); -#endif - return(TRUE); } diff --git a/mono/io-layer/events.h b/mono/io-layer/events.h index e2e27c05ff9e9..529ef83b8d453 100644 --- a/mono/io-layer/events.h +++ b/mono/io-layer/events.h @@ -3,9 +3,10 @@ #include -extern WapiHandle *CreateEvent(WapiSecurityAttributes *security, gboolean manual, gboolean initial, const guchar *name); -extern gboolean PulseEvent(WapiHandle *handle); -extern gboolean ResetEvent(WapiHandle *handle); -extern gboolean SetEvent(WapiHandle *handle); +extern gpointer CreateEvent(WapiSecurityAttributes *security, gboolean manual, + gboolean initial, const guchar *name); +extern gboolean PulseEvent(gpointer handle); +extern gboolean ResetEvent(gpointer handle); +extern gboolean SetEvent(gpointer handle); #endif /* _WAPI_EVENTS_H_ */ diff --git a/mono/io-layer/handles-private.h b/mono/io-layer/handles-private.h index fa7149ce2b8c8..e4a617be8872b 100644 --- a/mono/io-layer/handles-private.h +++ b/mono/io-layer/handles-private.h @@ -4,9 +4,216 @@ #include #include -#include "wait-private.h" +#include +#include -extern guint32 _wapi_handle_count_signalled(WaitQueueItem *item, WapiHandleType type); -extern void _wapi_handle_set_lowest(WaitQueueItem *item, guint32 idx); +#undef DEBUG + +extern struct _WapiHandleShared_list *_wapi_shared_data; +extern struct _WapiHandlePrivate_list *_wapi_private_data; +extern guint32 _wapi_open_handles[]; + +extern void _wapi_handle_init (void); +extern void _wapi_handle_cleanup (void); + +extern gpointer _wapi_handle_new (WapiHandleType type); +extern gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, + gpointer *shared, gpointer *private); +extern guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes); +extern gconstpointer _wapi_handle_scratch_lookup (guint32 idx); +extern guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx); +extern void _wapi_handle_scratch_delete (guint32 idx); +extern void _wapi_handle_register_capabilities (WapiHandleType type, + WapiHandleCapability caps); +extern gboolean _wapi_handle_test_capabilities (gpointer handle, + WapiHandleCapability caps); +extern void _wapi_handle_register_ops (WapiHandleType type, + struct _WapiHandleOps *ops); +extern void _wapi_handle_ops_close (gpointer handle); +extern WapiFileType _wapi_handle_ops_getfiletype (gpointer handle); +extern gboolean _wapi_handle_ops_readfile (gpointer handle, gpointer buffer, + guint32 numbytes, + guint32 *bytesread, + WapiOverlapped *overlapped); +extern gboolean _wapi_handle_ops_writefile (gpointer handle, + gconstpointer buffer, + guint32 numbytes, + guint32 *byteswritten, + WapiOverlapped *overlapped); +extern gboolean _wapi_handle_ops_flushfile (gpointer handle); +extern guint32 _wapi_handle_ops_seek (gpointer handle, gint32 movedistance, + gint32 *highmovedistance, + WapiSeekMethod method); +extern gboolean _wapi_handle_ops_setendoffile (gpointer handle); +extern guint32 _wapi_handle_ops_getfilesize (gpointer handle, + guint32 *highsize); +extern gboolean _wapi_handle_ops_getfiletime (gpointer handle, + WapiFileTime *create_time, + WapiFileTime *last_access, + WapiFileTime *last_write); +extern gboolean _wapi_handle_ops_setfiletime (gpointer handle, + const WapiFileTime *create_time, + const WapiFileTime *last_access, + const WapiFileTime *last_write); +extern void _wapi_handle_ops_signal (gpointer handle); +extern void _wapi_handle_ops_own (gpointer handle); +extern gboolean _wapi_handle_ops_isowned (gpointer handle); + +extern gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, + gpointer *handles, + gboolean waitall, + guint32 *retcount, + guint32 *lowest); +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_handle (gpointer handle); +extern int _wapi_handle_timedwait_signal_handle (gpointer handle, + struct timespec *timeout); + +static inline void _wapi_handle_shared_lock (void) +{ + /* Spin while waiting for the lock */ + while(InterlockedCompareExchange (&_wapi_shared_data->lock, 1, 0) != 0) { + sched_yield (); + } +} + +static inline void _wapi_handle_shared_unlock (void) +{ + InterlockedExchange (&_wapi_shared_data->lock, 0); +} + +static inline void _wapi_handle_ref (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + InterlockedIncrement (&_wapi_shared_data->handles[idx].ref); + InterlockedIncrement (&_wapi_open_handles[idx]); + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p ref now %d (%d this process)", handle, + _wapi_shared_data->handles[idx].ref, + _wapi_open_handles[idx]); +#endif +} + +static inline void _wapi_handle_unref (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + InterlockedDecrement (&_wapi_shared_data->handles[idx].ref); + InterlockedDecrement (&_wapi_open_handles[idx]); + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p ref now %d (%d this process)", handle, + _wapi_shared_data->handles[idx].ref, + _wapi_open_handles[idx]); +#endif + + if(_wapi_shared_data->handles[idx].ref==0) { + _wapi_handle_shared_lock (); + + if (_wapi_open_handles[idx]!=0) { + g_warning (G_GNUC_PRETTY_FUNCTION ": _wapi_open_handles mismatch, set to %d, should be 0", _wapi_open_handles[idx]); + } + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p", + handle); +#endif + + _wapi_handle_ops_close (handle); + + _wapi_shared_data->handles[idx].type=WAPI_HANDLE_UNUSED; + mono_mutex_destroy (&_wapi_shared_data->handles[idx].signal_mutex); + pthread_cond_destroy (&_wapi_shared_data->handles[idx].signal_cond); + memset (&_wapi_shared_data->handles[idx].u, '\0', sizeof(_wapi_shared_data->handles[idx].u)); + + _wapi_handle_shared_unlock (); + } +} + +static inline void _wapi_handle_set_signal_state (gpointer handle, + gboolean state, + gboolean broadcast) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + if(state==TRUE) { + /* Tell everyone blocking on a single handle */ + + /* This function _must_ be called with + * handle->signal_mutex locked + */ + _wapi_shared_data->handles[idx].signalled=state; + + if(broadcast==TRUE) { + pthread_cond_broadcast (&_wapi_shared_data->handles[idx].signal_cond); + } else { + pthread_cond_signal (&_wapi_shared_data->handles[idx].signal_cond); + } + + /* Tell everyone blocking on multiple handles that something + * was signalled + */ +#ifdef _POSIX_THREAD_PROCESS_SHARED + mono_mutex_lock (&_wapi_shared_data->signal_mutex); + pthread_cond_broadcast (&_wapi_shared_data->signal_cond); + mono_mutex_unlock (&_wapi_shared_data->signal_mutex); +#else + mono_mutex_lock (&_wapi_private_data->signal_mutex); + pthread_cond_broadcast (&_wapi_private_data->signal_cond); + mono_mutex_unlock (&_wapi_private_data->signal_mutex); +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + } else { + _wapi_shared_data->handles[idx].signalled=state; + } +} + +static inline gboolean _wapi_handle_issignalled (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + return(_wapi_shared_data->handles[idx].signalled); +} + +static inline int _wapi_handle_lock_signal_mutex (void) +{ +#ifdef _POSIX_THREAD_PROCESS_SHARED + return(mono_mutex_lock (&_wapi_shared_data->signal_mutex)); +#else + return(mono_mutex_lock (&_wapi_private_data->signal_mutex)); +#endif /* _POSIX_THREAD_PROCESS_SHARED */ +} + +static inline int _wapi_handle_unlock_signal_mutex (void) +{ +#ifdef _POSIX_THREAD_PROCESS_SHARED + return(mono_mutex_unlock (&_wapi_shared_data->signal_mutex)); +#else + return(mono_mutex_unlock (&_wapi_private_data->signal_mutex)); +#endif /* _POSIX_THREAD_PROCESS_SHARED */ +} + +static inline int _wapi_handle_lock_handle (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + return(mono_mutex_lock (&_wapi_shared_data->handles[idx].signal_mutex)); +} + +static inline int _wapi_handle_unlock_handle (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + return(mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex)); +} #endif /* _WAPI_HANDLES_PRIVATE_H_ */ diff --git a/mono/io-layer/handles.c b/mono/io-layer/handles.c index 4da579b1dd209..71f28d9bb12a4 100644 --- a/mono/io-layer/handles.c +++ b/mono/io-layer/handles.c @@ -1,65 +1,509 @@ #include #include #include +#include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" -#include "handles-private.h" +#if HAVE_BOEHM_GC +#include +#endif -#include "mono-mutex.h" +#include +#include +#include +#include +#include +#include #undef DEBUG -guint32 _wapi_handle_count_signalled(WaitQueueItem *item, WapiHandleType type) +static pthread_once_t shared_data_once=PTHREAD_ONCE_INIT; +static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0}; +static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={NULL}; +static guint32 scratch_size=0; + +struct _WapiHandleShared_list *_wapi_shared_data=NULL; +struct _WapiHandlePrivate_list *_wapi_private_data=NULL; + +/* All non-zero entries here need to be CloseHandle()d that many times + * on process exit + */ +guint32 _wapi_open_handles[_WAPI_MAX_HANDLES]={0}; + +static void shared_init (void) +{ + _wapi_shared_data=_wapi_shm_attach (&scratch_size); + _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1); + + /* This wont be called if the process exits due to a signal, + * any suggestions welcome (I tried {on_,at,g_at}exit and gcc + * destructor attributes.) + */ + atexit (_wapi_handle_cleanup); +} + +void _wapi_handle_init (void) { - guint32 i, ret=0; + /* Create our own process and main thread handles (assume this + * function is being called from the main thread) + */ + /* No need to make these variables visible outside this + * function, the handles will be cleaned up at process exit. + */ + gpointer process_handle; + gpointer thread_handle; + + process_handle=_wapi_handle_new (WAPI_HANDLE_PROCESS); + _wapi_handle_lock_handle (process_handle); + _wapi_handle_unlock_handle (process_handle); - /* Count how many of the interesting thread handles are signalled */ - for(i=0; ihandles[type]->len; i++) { - WapiHandle *handle; + thread_handle=_wapi_handle_new (WAPI_HANDLE_THREAD); + _wapi_handle_lock_handle (thread_handle); + _wapi_handle_unlock_handle (thread_handle); +} - handle=(WapiHandle *)g_ptr_array_index(item->handles[type], i); +void _wapi_handle_cleanup (void) +{ + guint32 i, j; + + /* Close all the handles listed in _wapi_open_handles */ + for(i=0; i<_WAPI_MAX_HANDLES; i++) { + for(j=0; j< _wapi_open_handles[i]; j++) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Checking handle %p", - handle); + g_message (G_GNUC_PRETTY_FUNCTION ": Closing %d", i); #endif + + CloseHandle ((gpointer)i); + } + } +} + +gpointer _wapi_handle_new (WapiHandleType type) +{ + guint32 idx; + gpointer handle; +#if HAVE_BOEHM_GC + gboolean tried_collect=FALSE; +#endif + + pthread_once (&shared_data_once, shared_init); + +again: + _wapi_handle_shared_lock (); + + /* A linear scan should be fast enough. Start from 1, leaving + * 0 (NULL) as a guard + */ + for(idx=1; idx<_WAPI_MAX_HANDLES; idx++) { + struct _WapiHandleShared *shared=&_wapi_shared_data->handles[idx]; - if(handle->signalled==TRUE) { - guint32 idx; + if(shared->type==WAPI_HANDLE_UNUSED) { + shared->type=type; + shared->signalled=FALSE; + mono_mutex_init (&_wapi_shared_data->handles[idx].signal_mutex, NULL); + pthread_cond_init (&_wapi_shared_data->handles[idx].signal_cond, NULL); -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Handle %p signalled", handle); + handle=GUINT_TO_POINTER (idx); + _wapi_handle_ref (handle); + + _wapi_handle_shared_unlock (); + + return(handle); + } + } + + _wapi_handle_shared_unlock (); + + g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!"); + +#if HAVE_BOEHM_GC + /* See if we can reclaim some handles by forcing a GC collection */ + if(tried_collect==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": Seeing if GC collection helps..."); + GC_gcollect (); + tried_collect=TRUE; + goto again; + } else { + g_warning (G_GNUC_PRETTY_FUNCTION + ": didn't help, returning error"); + } #endif + + return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID)); +} - idx=g_array_index(item->waitindex[type], guint32, i); - _wapi_handle_set_lowest(item, idx); - - ret++; +gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, + gpointer *shared, gpointer *private) +{ + struct _WapiHandleShared *shared_handle_data; + struct _WapiHandlePrivate *private_handle_data; + guint32 idx=GPOINTER_TO_UINT (handle); + + if(shared!=NULL) { + shared_handle_data=&_wapi_shared_data->handles[idx]; + if(shared_handle_data->type!=type) { + return(FALSE); } + + *shared=&shared_handle_data->u; + } + + if(private!=NULL) { + private_handle_data=&_wapi_private_data->handles[idx]; + + *private=&private_handle_data->u; + } + + return(TRUE); +} + +#define HDRSIZE sizeof(struct _WapiScratchHeader) + +guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes) +{ + guint32 idx=0, last_idx=0; + struct _WapiScratchHeader *hdr, *last_hdr; + gboolean last_was_free=FALSE; + guchar *storage=&_wapi_shared_data->scratch_base[0]; + + _wapi_handle_shared_lock (); + + hdr=(struct _WapiScratchHeader *)&storage[0]; + if(hdr->flags==0 && hdr->length==0) { + /* Need to initialise scratch data */ + hdr->flags |= WAPI_SHM_SCRATCH_FREE; + hdr->length = scratch_size - HDRSIZE; } + + while(idxflags & WAPI_SHM_SCRATCH_FREE && + hdr->length >= bytes + HDRSIZE) { + /* found space */ + guint32 old_length=hdr->length; +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length); +#endif + + hdr->flags &= ~WAPI_SHM_SCRATCH_FREE; + hdr->length=bytes; + idx += HDRSIZE; + memcpy (&storage[idx], data, bytes); #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": %d signalled handles", ret); + g_message (G_GNUC_PRETTY_FUNCTION ": copied %d bytes", + bytes); #endif - return(ret); + /* Put a new header in at the end of the used + * space + */ + hdr=(struct _WapiScratchHeader *)&storage[idx+bytes]; + hdr->flags |= WAPI_SHM_SCRATCH_FREE; + hdr->length = old_length-bytes-HDRSIZE; + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length); +#endif + + _wapi_handle_shared_unlock (); + + return(idx); + } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE && + last_was_free == FALSE) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length); +#endif + + /* save this point in case we can coalesce it with + * the next block, if that is free. + */ + last_was_free=TRUE; + last_idx=idx; + last_hdr=hdr; + idx+=(hdr->length+HDRSIZE); + } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE && + last_was_free == TRUE) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length); +#endif + + /* This block and the previous are both free, + * so coalesce them + */ + last_hdr->length += (hdr->length + HDRSIZE); + + /* If the new block is now big enough, use it + * (next time round the loop) + */ + if(last_hdr->length >= bytes + HDRSIZE) { + idx=last_idx; + } else { + /* leave the last free info as it is, + * in case the next block is also free + * and can be coalesced too + */ + idx=last_idx+last_hdr->length+HDRSIZE; + } + } else { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": found used block at %d, length %d", idx, + hdr->length); +#endif + + /* must be used, try next chunk */ + idx+=(hdr->length+HDRSIZE); + } + } + + _wapi_handle_shared_unlock (); + + return(0); +} + +guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx) +{ + struct _WapiScratchHeader *hdr; + guchar *str; + guchar *storage=&_wapi_shared_data->scratch_base[0]; + + if(idx < HDRSIZE || idx > scratch_size) { + return(NULL); + } + + hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE]; + str=g_malloc0 (hdr->length+1); + memcpy (str, &storage[idx], hdr->length); + + return(str); +} + +void _wapi_handle_scratch_delete (guint32 idx) +{ + struct _WapiScratchHeader *hdr; + guchar *storage=&_wapi_shared_data->scratch_base[0]; + + if(idx < HDRSIZE || idx > scratch_size) { + return; + } + + hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE]; + memset (&storage[idx], '\0', hdr->length); + hdr->flags |= WAPI_SHM_SCRATCH_FREE; + + /* We could coalesce forwards here if the next block is also + * free, but the _store() function will do that anyway. + */ +} + +void _wapi_handle_register_ops (WapiHandleType type, + struct _WapiHandleOps *ops) +{ + handle_ops[type]=ops; +} + +void _wapi_handle_register_capabilities (WapiHandleType type, + WapiHandleCapability caps) +{ + handle_caps[type]=caps; } -void _wapi_handle_set_lowest(WaitQueueItem *item, guint32 idx) +gboolean _wapi_handle_test_capabilities (gpointer handle, + WapiHandleCapability caps) { - mono_mutex_lock(&item->mutex); + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; - if(item->lowest_signal>idx) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Set %p lowest index to %d", - item, idx); + g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)", + handle_caps[type], caps, handle_caps[type] & caps); #endif - item->lowest_signal=idx; + return((handle_caps[type] & caps)!=0); +} + +void _wapi_handle_ops_close (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->close!=NULL) { + handle_ops[type]->close(handle); + } +} + +WapiFileType _wapi_handle_ops_getfiletype (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->getfiletype!=NULL) { + return(handle_ops[type]->getfiletype()); + } else { + return(FILE_TYPE_UNKNOWN); + } +} + +gboolean _wapi_handle_ops_readfile (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, WapiOverlapped *overlapped) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->readfile!=NULL) { + return(handle_ops[type]->readfile(handle, buffer, numbytes, bytesread, overlapped)); + } else { + return(FALSE); + } +} + +gboolean _wapi_handle_ops_writefile (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, WapiOverlapped *overlapped) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->writefile!=NULL) { + return(handle_ops[type]->writefile(handle, buffer, numbytes, byteswritten, overlapped)); + } else { + return(FALSE); + } +} + +gboolean _wapi_handle_ops_flushfile (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->flushfile!=NULL) { + return(handle_ops[type]->flushfile(handle)); + } else { + return(FALSE); + } +} + +guint32 _wapi_handle_ops_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, WapiSeekMethod method) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->seek!=NULL) { + return(handle_ops[type]->seek(handle, movedistance, highmovedistance, method)); + } else { + return(INVALID_SET_FILE_POINTER); + } +} + +gboolean _wapi_handle_ops_setendoffile (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->setendoffile!=NULL) { + return(handle_ops[type]->setendoffile(handle)); + } else { + return(FALSE); + } +} + +guint32 _wapi_handle_ops_getfilesize (gpointer handle, guint32 *highsize) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->getfilesize!=NULL) { + return(handle_ops[type]->getfilesize(handle, highsize)); + } else { + return(INVALID_FILE_SIZE); + } +} + +gboolean _wapi_handle_ops_getfiletime (gpointer handle, WapiFileTime *create_time, WapiFileTime *last_access, WapiFileTime *last_write) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->getfiletime!=NULL) { + return(handle_ops[type]->getfiletime(handle, create_time, last_access, last_write)); + } else { + return(FALSE); + } +} + +gboolean _wapi_handle_ops_setfiletime (gpointer handle, const WapiFileTime *create_time, const WapiFileTime *last_access, const WapiFileTime *last_write) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->setfiletime!=NULL) { + return(handle_ops[type]->setfiletime(handle, create_time, last_access, last_write)); + } else { + return(FALSE); + } +} + +void _wapi_handle_ops_signal (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) { + handle_ops[type]->signal (handle); + } +} + +void _wapi_handle_ops_own (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) { + handle_ops[type]->own_handle (handle); + } +} + +gboolean _wapi_handle_ops_isowned (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + WapiHandleType type; + + type=_wapi_shared_data->handles[idx].type; + + if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) { + return(handle_ops[type]->is_owned (handle)); + } else { + return(FALSE); } - - mono_mutex_unlock(&item->mutex); } /** @@ -73,94 +517,161 @@ void _wapi_handle_set_lowest(WaitQueueItem *item, guint32 idx) * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean CloseHandle(WapiHandle *handle) +gboolean CloseHandle(gpointer handle) { - g_return_val_if_fail(handle->ref>0, FALSE); - - handle->ref--; - if(handle->ref==0) { - if(handle->ops->close!=NULL) { - handle->ops->close(handle); - } - - g_free(handle); /* maybe this should be in - * ops, cuurently ops->close() - * is being used to free - * handle data - */ - } + _wapi_handle_unref (handle); return(TRUE); } -/** - * SignalObjectAndWait: - * @signal_handle: An object to signal - * @wait: An object to wait for - * @timeout: The maximum time in milliseconds to wait for - * @alertable: Specifies whether the function returnes when the system - * queues an I/O completion routine or an APC for the calling thread. - * - * Atomically signals @signal and waits for @wait to become signalled, - * or @timeout ms elapses. If @timeout is zero, the object's state is - * tested and the function returns immediately. If @timeout is - * %INFINITE, the function waits forever. - * - * @signal can be a semaphore, mutex or event object. - * - * If @alertable is %TRUE and the system queues an I/O completion - * routine or an APC for the calling thread, the function returns and - * the thread calls the completion routine or APC function. If - * %FALSE, the function does not return, and the thread does not call - * the completion routine or APC function. A completion routine is - * queued when the ReadFileEx() or WriteFileEx() function in which it - * was specified has completed. The calling thread is the thread that - * initiated the read or write operation. An APC is queued when - * QueueUserAPC() is called. Currently completion routines and APC - * functions are not supported. - * - * Return value: %WAIT_ABANDONED - @wait is a mutex that was not - * released by the owning thread when it exited. Ownershop of the - * mutex object is granted to the calling thread and the mutex is set - * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one - * or more user-mode asynchronous procedure calls queued to the - * thread. %WAIT_OBJECT_0 - The state of @wait is signalled. - * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is - * still not signalled. %WAIT_FAILED - an error occurred. - */ -guint32 SignalObjectAndWait(WapiHandle *signal_handle, WapiHandle *wait, - guint32 timeout, gboolean alertable) +gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, + gpointer *handles, + gboolean waitall, + guint32 *retcount, + guint32 *lowest) { - gboolean waited; - guint32 ret; + guint32 count, i, iter=0; + gboolean ret; - if(signal_handle->ops->signal==NULL) { - return(WAIT_FAILED); + /* Lock all the handles, with backoff */ +again: + for(i=0; ihandles[idx].signal_mutex); + if(ret!=0) { + /* Bummer */ + struct timespec sleepytime; + + while(i--) { + idx=GPOINTER_TO_UINT (handles[i]); + mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex); + } + + /* If iter ever reaches 100 the nanosleep will + * return EINVAL immediately, but we have a + * design flaw if that happens. + */ + iter++; + if(iter==100) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": iteration overflow!"); + iter=1; + } + + sleepytime.tv_sec=0; + sleepytime.tv_nsec=10000000 * iter; /* 10ms*iter */ + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": Backing off for %d ms", iter*10); +#endif + nanosleep (&sleepytime, NULL); + + goto again; + } } - if(wait->ops->wait==NULL) { - return(WAIT_FAILED); - } +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles"); +#endif - waited=wait->ops->wait(wait, signal_handle, timeout); - if(waited==TRUE) { - /* Object signalled before timeout expired */ + count=0; + *lowest=numhandles; + + for(i=0; ihandles[idx].signalled==TRUE)) { + count++; + #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Object %p wait timed out", - wait); + g_message (G_GNUC_PRETTY_FUNCTION + ": Handle %p signalled", handles[i]); #endif - ret=WAIT_TIMEOUT; + if(*lowest>i) { + *lowest=i; + } + } } + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled", + count); +#endif - if(alertable==TRUE) { - /* Deal with queued APC or IO completion routines */ + if((waitall==TRUE && count==numhandles) || + (waitall==FALSE && count>0)) { + ret=TRUE; + } else { + ret=FALSE; } +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret); +#endif + + *retcount=count; + return(ret); } + +void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles) +{ + guint32 i; + + for(i=0; ihandles[idx].signal_mutex); + } +} + +int _wapi_handle_wait_signal (void) +{ +#ifdef _POSIX_THREAD_PROCESS_SHARED + return(mono_cond_wait (&_wapi_shared_data->signal_cond, + &_wapi_shared_data->signal_mutex)); +#else + return(mono_cond_wait (&_wapi_private_data->signal_cond, + &_wapi_private_data->signal_mutex)); +#endif /* _POSIX_THREAD_PROCESS_SHARED */ +} + +int _wapi_handle_timedwait_signal (struct timespec *timeout) +{ +#ifdef _POSIX_THREAD_PROCESS_SHARED + return(mono_cond_timedwait (&_wapi_shared_data->signal_cond, + &_wapi_shared_data->signal_mutex, + timeout)); +#else + return(mono_cond_timedwait (&_wapi_private_data->signal_cond, + &_wapi_private_data->signal_mutex, + timeout)); +#endif /* _POSIX_THREAD_PROCESS_SHARED */ +} + +int _wapi_handle_wait_signal_handle (gpointer handle) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + return(mono_cond_wait (&_wapi_shared_data->handles[idx].signal_cond, + &_wapi_shared_data->handles[idx].signal_mutex)); +} + +int _wapi_handle_timedwait_signal_handle (gpointer handle, + struct timespec *timeout) +{ + guint32 idx=GPOINTER_TO_UINT (handle); + + return(mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond, + &_wapi_shared_data->handles[idx].signal_mutex, + timeout)); +} diff --git a/mono/io-layer/handles.h b/mono/io-layer/handles.h index ab2a3b88719dc..6d5d7c4b81d4c 100644 --- a/mono/io-layer/handles.h +++ b/mono/io-layer/handles.h @@ -1,12 +1,8 @@ #ifndef _WAPI_HANDLES_H_ #define _WAPI_HANDLES_H_ -#define INVALID_HANDLE_VALUE (WapiHandle *)-1 +#define INVALID_HANDLE_VALUE (gpointer)-1 -typedef struct _WapiHandle WapiHandle; - -extern gboolean CloseHandle(WapiHandle *handle); -extern guint32 SignalObjectAndWait(WapiHandle *signal_handle, WapiHandle *wait, - guint32 timeout, gboolean alertable); +extern gboolean CloseHandle(gpointer handle); #endif /* _WAPI_HANDLES_H_ */ diff --git a/mono/io-layer/io-private.h b/mono/io-layer/io-private.h new file mode 100644 index 0000000000000..18ba8c7496913 --- /dev/null +++ b/mono/io-layer/io-private.h @@ -0,0 +1,37 @@ +#ifndef _WAPI_IO_PRIVATE_H_ +#define _WAPI_IO_PRIVATE_H_ + +#include +#include +#include + +/* Currently used for both FILE and CONSOLE handle types. This may + * have to change in future. + */ +struct _WapiHandle_file +{ + guint32 filename; + guint32 security_attributes; + guint32 fileaccess; + guint32 sharemode; + guint32 attrs; +}; + +struct _WapiHandlePrivate_file +{ + int fd; +}; + +struct _WapiHandle_find +{ + glob_t glob; + size_t count; +}; + +struct _WapiHandlePrivate_find +{ + int dummy; +}; + + +#endif /* _WAPI_IO_PRIVATE_H_ */ diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c index ce9d03abc0d7d..bdba2d59b87b5 100644 --- a/mono/io-layer/io.c +++ b/mono/io-layer/io.c @@ -11,51 +11,32 @@ #include #include -#include "mono/io-layer/wapi.h" -#include "unicode.h" -#include "wapi-private.h" +#include +#include +#include +#include +#include #undef DEBUG #define ACTUALLY_DO_UNICODE -/* Currently used for both FILE and CONSOLE handle types. This may - * have to change in future. - */ -struct _WapiHandle_file -{ - WapiHandle handle; - int fd; - gchar *filename; - WapiSecurityAttributes *security_attributes; - guint32 fileaccess; - guint32 sharemode; - guint32 attrs; -}; - -struct _WapiHandle_find -{ - WapiHandle handle; - glob_t glob; - size_t count; -}; - -static void file_close(WapiHandle *handle); +static void file_close(gpointer handle); static WapiFileType file_getfiletype(void); -static gboolean file_read(WapiHandle *handle, gpointer buffer, +static gboolean file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, WapiOverlapped *overlapped); -static gboolean file_write(WapiHandle *handle, gconstpointer buffer, +static gboolean file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, WapiOverlapped *overlapped); -static gboolean file_flush(WapiHandle *handle); -static guint32 file_seek(WapiHandle *handle, gint32 movedistance, +static gboolean file_flush(gpointer handle); +static guint32 file_seek(gpointer handle, gint32 movedistance, gint32 *highmovedistance, WapiSeekMethod method); -static gboolean file_setendoffile(WapiHandle *handle); -static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize); -static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time, +static gboolean file_setendoffile(gpointer handle); +static guint32 file_getfilesize(gpointer handle, guint32 *highsize); +static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, WapiFileTime *last_access, WapiFileTime *last_write); -static gboolean file_setfiletime(WapiHandle *handle, +static gboolean file_setfiletime(gpointer handle, const WapiFileTime *create_time, const WapiFileTime *last_access, const WapiFileTime *last_write); @@ -72,30 +53,38 @@ static struct _WapiHandleOps file_ops = { file_getfilesize, /* getfilesize */ file_getfiletime, /* getfiletime */ file_setfiletime, /* setfiletime */ - NULL, /* wait */ - NULL, /* wait_multiple */ NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ }; +static void console_close(gpointer handle); static WapiFileType console_getfiletype(void); +static gboolean console_read(gpointer handle, gpointer buffer, + guint32 numbytes, guint32 *bytesread, + WapiOverlapped *overlapped); +static gboolean console_write(gpointer handle, gconstpointer buffer, + guint32 numbytes, guint32 *byteswritten, + WapiOverlapped *overlapped); +static gboolean console_flush(gpointer handle); /* Console is mostly the same as file, except it can block waiting for * input or output */ static struct _WapiHandleOps console_ops = { - file_close, /* close */ + console_close, /* close */ console_getfiletype, /* getfiletype */ - file_read, /* readfile */ - file_write, /* writefile */ - file_flush, /* flushfile */ + console_read, /* readfile */ + console_write, /* writefile */ + console_flush, /* flushfile */ NULL, /* seek */ NULL, /* setendoffile */ NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - NULL, /* FIXME: wait */ - NULL, /* FIXME: wait_multiple */ NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ }; /* Find handle has no ops. @@ -111,11 +100,24 @@ static struct _WapiHandleOps find_ops = { NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - NULL, /* FIXME: wait */ - NULL, /* FIXME: wait_multiple */ NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ }; +static pthread_once_t io_ops_once=PTHREAD_ONCE_INIT; + +static void io_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_FILE, &file_ops); +/* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */ +/* WAPI_HANDLE_CAP_WAIT); */ + _wapi_handle_register_ops (WAPI_HANDLE_CONSOLE, &console_ops); +/* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */ +/* WAPI_HANDLE_CAP_WAIT); */ + _wapi_handle_register_ops (WAPI_HANDLE_FIND, &find_ops); +} + /* Some utility functions. */ static void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime) @@ -206,19 +208,34 @@ static void _wapi_set_last_error_from_errno (void) /* Handle ops. */ -static void file_close(WapiHandle *handle) +static void file_close(gpointer handle) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return; + } #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d", - file_handle, file_handle->fd); + handle, file_private_handle->fd); #endif - close(file_handle->fd); - if(file_handle->filename!=NULL) { - g_free(file_handle->filename); - file_handle->filename=NULL; + close(file_private_handle->fd); + if(file_handle->filename!=0) { + _wapi_handle_scratch_delete (file_handle->filename); + file_handle->filename=0; + } + if(file_handle->security_attributes!=0) { + _wapi_handle_scratch_delete (file_handle->security_attributes); + file_handle->security_attributes=0; } } @@ -227,13 +244,24 @@ static WapiFileType file_getfiletype(void) return(FILE_TYPE_DISK); } -static gboolean file_read(WapiHandle *handle, gpointer buffer, +static gboolean file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, WapiOverlapped *overlapped G_GNUC_UNUSED) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; int ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } + if(bytesread!=NULL) { *bytesread=0; } @@ -241,18 +269,18 @@ static gboolean file_read(WapiHandle *handle, gpointer buffer, if(!(file_handle->fileaccess&GENERIC_READ) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(FALSE); } - ret=read(file_handle->fd, buffer, numbytes); + ret=read(file_private_handle->fd, buffer, numbytes); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": read of handle %p fd %d error: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -265,13 +293,24 @@ static gboolean file_read(WapiHandle *handle, gpointer buffer, return(TRUE); } -static gboolean file_write(WapiHandle *handle, gconstpointer buffer, +static gboolean file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, WapiOverlapped *overlapped G_GNUC_UNUSED) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; int ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } + if(byteswritten!=NULL) { *byteswritten=0; } @@ -279,18 +318,18 @@ static gboolean file_write(WapiHandle *handle, gconstpointer buffer, if(!(file_handle->fileaccess&GENERIC_WRITE) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(FALSE); } - ret=write(file_handle->fd, buffer, numbytes); + ret=write(file_private_handle->fd, buffer, numbytes); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": write of handle %p fd %d error: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -302,26 +341,37 @@ static gboolean file_write(WapiHandle *handle, gconstpointer buffer, return(TRUE); } -static gboolean file_flush(WapiHandle *handle) +static gboolean file_flush(gpointer handle) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; int ret; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } if(!(file_handle->fileaccess&GENERIC_WRITE) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(FALSE); } - ret=fsync(file_handle->fd); + ret=fsync(file_private_handle->fd); if (ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": write of handle %p fd %d error: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -330,19 +380,30 @@ static gboolean file_flush(WapiHandle *handle) return(TRUE); } -static guint32 file_seek(WapiHandle *handle, gint32 movedistance, +static guint32 file_seek(gpointer handle, gint32 movedistance, gint32 *highmovedistance, WapiSeekMethod method) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; off_t offset, newpos; int whence; guint32 ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(INVALID_SET_FILE_POINTER); + } + if(!(file_handle->fileaccess&GENERIC_READ) && !(file_handle->fileaccess&GENERIC_WRITE) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(INVALID_SET_FILE_POINTER); @@ -390,20 +451,20 @@ static guint32 file_seek(WapiHandle *handle, gint32 movedistance, #ifdef HAVE_LARGE_FILE_SUPPORT g_message(G_GNUC_PRETTY_FUNCTION ": moving handle %p fd %d by %lld bytes from %d", handle, - file_handle->fd, offset, whence); + file_private_handle->fd, offset, whence); #else g_message(G_GNUC_PRETTY_FUNCTION ": moving handle %p fd %d by %ld bytes from %d", handle, - file_handle->fd, offset, whence); + file_private_handle->fd, offset, whence); #endif #endif - newpos=lseek(file_handle->fd, offset, whence); + newpos=lseek(file_private_handle->fd, offset, whence); if(newpos==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": lseek on handle %p fd %d returned error %s", - handle, file_handle->fd, strerror(errno)); + handle, file_private_handle->fd, strerror(errno)); #endif return(INVALID_SET_FILE_POINTER); @@ -433,24 +494,35 @@ static guint32 file_seek(WapiHandle *handle, gint32 movedistance, #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": move of handle %p fd %d returning %d/%d", handle, - file_handle->fd, ret, + file_private_handle->fd, ret, highmovedistance==NULL?0:*highmovedistance); #endif return(ret); } -static gboolean file_setendoffile(WapiHandle *handle) +static gboolean file_setendoffile(gpointer handle) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; struct stat statbuf; off_t size, pos; int ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } + if(!(file_handle->fileaccess&GENERIC_WRITE) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(FALSE); @@ -462,24 +534,24 @@ static gboolean file_setendoffile(WapiHandle *handle) * than the length, truncate the file. */ - ret=fstat(file_handle->fd, &statbuf); + ret=fstat(file_private_handle->fd, &statbuf); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d fstat failed: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); } size=statbuf.st_size; - pos=lseek(file_handle->fd, (off_t)0, SEEK_CUR); + pos=lseek(file_private_handle->fd, (off_t)0, SEEK_CUR); if(pos==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -487,12 +559,13 @@ static gboolean file_setendoffile(WapiHandle *handle) if(pos>size) { /* extend */ - ret=write(file_handle->fd, "", 1); + ret=write(file_private_handle->fd, "", 1); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d extend write failed: %s", - handle, file_handle->fd, strerror(errno)); + handle, file_private_handle->fd, + strerror(errno)); #endif return(FALSE); @@ -502,12 +575,12 @@ static gboolean file_setendoffile(WapiHandle *handle) /* always truncate, because the extend write() adds an extra * byte to the end of the file */ - ret=ftruncate(file_handle->fd, pos); + ret=ftruncate(file_private_handle->fd, pos); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d ftruncate failed: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -516,29 +589,40 @@ static gboolean file_setendoffile(WapiHandle *handle) return(TRUE); } -static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize) +static guint32 file_getfilesize(gpointer handle, guint32 *highsize) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; struct stat statbuf; guint32 size; int ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(INVALID_FILE_SIZE); + } + if(!(file_handle->fileaccess&GENERIC_READ) && !(file_handle->fileaccess&GENERIC_WRITE) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(INVALID_FILE_SIZE); } - ret=fstat(file_handle->fd, &statbuf); + ret=fstat(file_private_handle->fd, &statbuf); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d fstat failed: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(INVALID_FILE_SIZE); @@ -565,30 +649,41 @@ static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize) return(size); } -static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time, +static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, WapiFileTime *last_access, WapiFileTime *last_write) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; struct stat statbuf; guint64 create_ticks, access_ticks, write_ticks; int ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } + if(!(file_handle->fileaccess&GENERIC_READ) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(FALSE); } - ret=fstat(file_handle->fd, &statbuf); + ret=fstat(file_private_handle->fd, &statbuf); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d fstat failed: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -642,31 +737,43 @@ static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time, return(TRUE); } -static gboolean file_setfiletime(WapiHandle *handle, +static gboolean file_setfiletime(gpointer handle, const WapiFileTime *create_time G_GNUC_UNUSED, const WapiFileTime *last_access, const WapiFileTime *last_write) { - struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle; + struct _WapiHandle_file *file_handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; + guchar *name; struct utimbuf utbuf; struct stat statbuf; guint64 access_ticks, write_ticks; int ret; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } + if(!(file_handle->fileaccess&GENERIC_WRITE) && !(file_handle->fileaccess&GENERIC_ALL)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess); + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess); #endif return(FALSE); } - if(file_handle->filename==NULL) { + if(file_handle->filename==0) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d unknown filename", handle, - file_handle->fd); + file_private_handle->fd); #endif return(FALSE); @@ -675,12 +782,12 @@ static gboolean file_setfiletime(WapiHandle *handle, /* Get the current times, so we can put the same times back in * the event that one of the FileTime structs is NULL */ - ret=fstat(file_handle->fd, &statbuf); + ret=fstat(file_private_handle->fd, &statbuf); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d fstat failed: %s", handle, - file_handle->fd, strerror(errno)); + file_private_handle->fd, strerror(errno)); #endif return(FALSE); @@ -708,26 +815,198 @@ static gboolean file_setfiletime(WapiHandle *handle, utbuf.actime, utbuf.modtime); #endif - ret=utime(file_handle->filename, &utbuf); + name=_wapi_handle_scratch_lookup_as_string (file_handle->filename); + + ret=utime(name, &utbuf); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": handle %p [%s] fd %d utime failed: %s", handle, - file_handle->filename, file_handle->fd, - strerror(errno)); -#endif + name, file_private_handle->fd, strerror(errno)); +#endif + g_free (name); return(FALSE); } + g_free (name); + return(TRUE); } +static void console_close(gpointer handle) +{ + struct _WapiHandle_file *console_handle; + struct _WapiHandlePrivate_file *console_private_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, + (gpointer *)&console_handle, + (gpointer *)&console_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up console handle %p", handle); + return; + } + +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION + ": closing console handle %p with fd %d", handle, + console_private_handle->fd); +#endif + + close(console_private_handle->fd); + if(console_handle->filename!=0) { + _wapi_handle_scratch_delete (console_handle->filename); + console_handle->filename=0; + } + if(console_handle->security_attributes!=0) { + _wapi_handle_scratch_delete (console_handle->security_attributes); + console_handle->security_attributes=0; + } +} + static WapiFileType console_getfiletype(void) { return(FILE_TYPE_CHAR); } +static gboolean console_read(gpointer handle, gpointer buffer, + guint32 numbytes, guint32 *bytesread, + WapiOverlapped *overlapped G_GNUC_UNUSED) +{ + struct _WapiHandle_file *console_handle; + struct _WapiHandlePrivate_file *console_private_handle; + gboolean ok; + int ret; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&console_handle, + (gpointer *)&console_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up console handle %p", handle); + return(FALSE); + } + + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(console_handle->fileaccess&GENERIC_READ) && + !(console_handle->fileaccess&GENERIC_ALL)) { +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd, console_handle->fileaccess); +#endif + + return(FALSE); + } + + ret=read(console_private_handle->fd, buffer, numbytes); + if(ret==-1) { +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION + ": read of handle %p fd %d error: %s", handle, + console_private_handle->fd, strerror(errno)); +#endif + + return(FALSE); + } + + if(bytesread!=NULL) { + *bytesread=ret; + } + + return(TRUE); +} + +static gboolean console_write(gpointer handle, gconstpointer buffer, + guint32 numbytes, guint32 *byteswritten, + WapiOverlapped *overlapped G_GNUC_UNUSED) +{ + struct _WapiHandle_file *console_handle; + struct _WapiHandlePrivate_file *console_private_handle; + gboolean ok; + int ret; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, + (gpointer *)&console_handle, + (gpointer *)&console_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up console handle %p", handle); + return(FALSE); + } + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(console_handle->fileaccess&GENERIC_WRITE) && + !(console_handle->fileaccess&GENERIC_ALL)) { +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd, console_handle->fileaccess); +#endif + + return(FALSE); + } + + ret=write(console_private_handle->fd, buffer, numbytes); + if(ret==-1) { +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION + ": write of handle %p fd %d error: %s", handle, + console_private_handle->fd, strerror(errno)); +#endif + + return(FALSE); + } + if(byteswritten!=NULL) { + *byteswritten=ret; + } + + return(TRUE); +} + +static gboolean console_flush(gpointer handle) +{ + struct _WapiHandle_file *console_handle; + struct _WapiHandlePrivate_file *console_private_handle; + gboolean ok; + int ret; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, + (gpointer *)&console_handle, + (gpointer *)&console_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up console handle %p", handle); + return(FALSE); + } + + if(!(console_handle->fileaccess&GENERIC_WRITE) && + !(console_handle->fileaccess&GENERIC_ALL)) { +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd, console_handle->fileaccess); +#endif + + return(FALSE); + } + + ret=fsync(console_private_handle->fd); + if (ret==-1) { +#ifdef DEBUG + g_message(G_GNUC_PRETTY_FUNCTION + ": write of handle %p fd %d error: %s", handle, + console_private_handle->fd, strerror(errno)); +#endif + + return(FALSE); + } + + return(TRUE); +} + static int convert_flags(guint32 fileaccess, guint32 createmode) { int flags=0; @@ -838,17 +1117,21 @@ static mode_t convert_perms(guint32 sharemode) * * Return value: the new handle, or %INVALID_HANDLE_VALUE on error. */ -WapiHandle *CreateFile(const gunichar2 *name, guint32 fileaccess, - guint32 sharemode, WapiSecurityAttributes *security, - guint32 createmode, guint32 attrs, - WapiHandle *template G_GNUC_UNUSED) +gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, + guint32 sharemode, WapiSecurityAttributes *security, + guint32 createmode, guint32 attrs, + gpointer template G_GNUC_UNUSED) { struct _WapiHandle_file *file_handle; - WapiHandle *handle; + struct _WapiHandlePrivate_file *file_private_handle; + gpointer handle; + gboolean ok; int flags=convert_flags(fileaccess, createmode); mode_t perms=convert_perms(sharemode); gchar *filename; int ret; + + pthread_once (&io_ops_once, io_ops_init); if(name==NULL) { #ifdef DEBUG @@ -889,18 +1172,37 @@ WapiHandle *CreateFile(const gunichar2 *name, guint32 fileaccess, return(INVALID_HANDLE_VALUE); } - file_handle=g_new0(struct _WapiHandle_file, 1); - handle=(WapiHandle *)file_handle; + handle=_wapi_handle_new (WAPI_HANDLE_FILE); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating file handle"); + return(INVALID_HANDLE_VALUE); + } + + _wapi_handle_lock_handle (handle); - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_FILE, file_ops); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(INVALID_HANDLE_VALUE); + } - file_handle->fd=ret; + file_private_handle->fd=ret; #ifdef ACTUALLY_DO_UNICODE - file_handle->filename=filename; + file_handle->filename=_wapi_handle_scratch_store (filename, + strlen (filename)); #else - file_handle->filename=g_strdup(name); + file_handle->filename=_wapi_handle_scratch_store (name, strlen (name)); #endif - file_handle->security_attributes=security; + if(security!=NULL) { + file_handle->security_attributes=_wapi_handle_scratch_store ( + security, sizeof(WapiSecurityAttributes)); + } + file_handle->fileaccess=fileaccess; file_handle->sharemode=sharemode; file_handle->attrs=attrs; @@ -908,9 +1210,11 @@ WapiHandle *CreateFile(const gunichar2 *name, guint32 fileaccess, #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p [%s] with fd %d", - handle, file_handle->filename, file_handle->fd); + handle, file_handle->filename, file_private_handle->fd); #endif + _wapi_handle_unlock_handle (handle); + return(handle); } @@ -1030,9 +1334,10 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name) * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists) +gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, + gboolean fail_if_exists) { - WapiHandle *src, *dest; + gpointer src, dest; guint32 attrs; char *buffer; int remain, n; @@ -1108,23 +1413,31 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean f * Return value: the handle, or %INVALID_HANDLE_VALUE on error */ -WapiHandle *GetStdHandle(WapiStdHandle stdhandle) +gpointer GetStdHandle(WapiStdHandle stdhandle) { struct _WapiHandle_file *file_handle; - WapiHandle *handle; + struct _WapiHandlePrivate_file *file_private_handle; + gboolean ok; + gpointer handle; + const guchar *name; int flags, fd; + + pthread_once (&io_ops_once, io_ops_init); switch(stdhandle) { case STD_INPUT_HANDLE: fd=0; + name=""; break; case STD_OUTPUT_HANDLE: fd=1; + name=""; break; case STD_ERROR_HANDLE: fd=2; + name=""; break; default: @@ -1150,26 +1463,40 @@ WapiHandle *GetStdHandle(WapiStdHandle stdhandle) return(INVALID_HANDLE_VALUE); } - file_handle=g_new0(struct _WapiHandle_file, 1); - handle=(WapiHandle *)file_handle; - - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_CONSOLE, console_ops); + handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating file handle"); + return(NULL); + } - file_handle->fd=fd; - /* We might want to set file_handle->filename to something - * like "" if we ever want to display handle internal - * details somehow - */ - file_handle->security_attributes=/*some default*/NULL; + _wapi_handle_lock_handle (handle); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, + (gpointer *)&file_handle, + (gpointer *)&file_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up console handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(NULL); + } + + file_private_handle->fd=fd; + file_handle->filename=_wapi_handle_scratch_store (name, strlen (name)); + /* some default security attributes might be needed */ + file_handle->security_attributes=0; file_handle->fileaccess=convert_from_flags(flags); file_handle->sharemode=0; file_handle->attrs=0; #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d", - handle, file_handle->fd); + handle, file_private_handle->fd); #endif + _wapi_handle_unlock_handle (handle); + return(handle); } @@ -1200,14 +1527,10 @@ WapiHandle *GetStdHandle(WapiStdHandle stdhandle) * read due to an attempt to read past the end of the file), %FALSE on * error. */ -gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes, +gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, WapiOverlapped *overlapped) { - if(handle->ops->readfile==NULL) { - return(FALSE); - } - - return(handle->ops->readfile(handle, buffer, numbytes, bytesread, + return(_wapi_handle_ops_readfile(handle, buffer, numbytes, bytesread, overlapped)); } @@ -1236,15 +1559,11 @@ gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes, * * Return value: %TRUE if the write succeeds, %FALSE on error. */ -gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, guint32 numbytes, +gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, WapiOverlapped *overlapped) { - if(handle->ops->writefile==NULL) { - return(FALSE); - } - - return(handle->ops->writefile(handle, buffer, numbytes, byteswritten, - overlapped)); + return(_wapi_handle_ops_writefile(handle, buffer, numbytes, + byteswritten, overlapped)); } /** @@ -1257,13 +1576,9 @@ gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, guint32 numbytes, * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean FlushFileBuffers(WapiHandle *handle) +gboolean FlushFileBuffers(gpointer handle) { - if(handle->ops->flushfile==NULL) { - return(FALSE); - } - - return(handle->ops->flushfile(handle)); + return(_wapi_handle_ops_flushfile(handle)); } /** @@ -1276,13 +1591,9 @@ gboolean FlushFileBuffers(WapiHandle *handle) * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean SetEndOfFile(WapiHandle *handle) +gboolean SetEndOfFile(gpointer handle) { - if(handle->ops->setendoffile==NULL) { - return(FALSE); - } - - return(handle->ops->setendoffile(handle)); + return(_wapi_handle_ops_setendoffile(handle)); } /** @@ -1314,15 +1625,11 @@ gboolean SetEndOfFile(WapiHandle *handle) * If @highmovedistance is not %NULL, the high 32 bits of the new file * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER. */ -guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance, +guint32 SetFilePointer(gpointer handle, gint32 movedistance, gint32 *highmovedistance, WapiSeekMethod method) { - if(handle->ops->seek==NULL) { - return(INVALID_SET_FILE_POINTER); - } - - return(handle->ops->seek(handle, movedistance, highmovedistance, - method)); + return(_wapi_handle_ops_seek(handle, movedistance, highmovedistance, + method)); } /** @@ -1336,13 +1643,9 @@ guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance, * %FILE_TYPE_CHAR - @handle is a character device, such as a console. * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe. */ -WapiFileType GetFileType(WapiHandle *handle) +WapiFileType GetFileType(gpointer handle) { - if(handle->ops->getfiletype==NULL) { - return(FILE_TYPE_UNKNOWN); - } - - return(handle->ops->getfiletype()); + return(_wapi_handle_ops_getfiletype(handle)); } /** @@ -1361,13 +1664,9 @@ WapiFileType GetFileType(WapiHandle *handle) * @highsize is non-%NULL then the high 32 bits of the file size are * stored here. On failure %INVALID_FILE_SIZE is returned. */ -guint32 GetFileSize(WapiHandle *handle, guint32 *highsize) +guint32 GetFileSize(gpointer handle, guint32 *highsize) { - if(handle->ops->getfilesize==NULL) { - return(INVALID_FILE_SIZE); - } - - return(handle->ops->getfilesize(handle, highsize)); + return(_wapi_handle_ops_getfilesize(handle, highsize)); } /** @@ -1395,15 +1694,11 @@ guint32 GetFileSize(WapiHandle *handle, guint32 *highsize) * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time, +gboolean GetFileTime(gpointer handle, WapiFileTime *create_time, WapiFileTime *last_access, WapiFileTime *last_write) { - if(handle->ops->getfiletime==NULL) { - return(FALSE); - } - - return(handle->ops->getfiletime(handle, create_time, last_access, - last_write)); + return(_wapi_handle_ops_getfiletime(handle, create_time, last_access, + last_write)); } /** @@ -1429,16 +1724,12 @@ gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time, * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean SetFileTime(WapiHandle *handle, const WapiFileTime *create_time, +gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time, const WapiFileTime *last_access, const WapiFileTime *last_write) { - if(handle->ops->setfiletime==NULL) { - return(FALSE); - } - - return(handle->ops->setfiletime(handle, create_time, last_access, - last_write)); + return(_wapi_handle_ops_setfiletime(handle, create_time, last_access, + last_write)); } /* A tick is a 100-nanosecond interval. File time epoch is Midnight, @@ -1601,9 +1892,11 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time, return(TRUE); } -WapiHandle *FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) +gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) { - struct _WapiHandle_find *handle; + struct _WapiHandle_find *find_handle; + gpointer handle; + gboolean ok; gchar *utf8_pattern = NULL; int result; @@ -1624,15 +1917,31 @@ WapiHandle *FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) return INVALID_HANDLE_VALUE; } - handle = g_new0 (struct _WapiHandle_find, 1); - _WAPI_HANDLE_INIT ((&handle->handle), WAPI_HANDLE_FIND, find_ops); + handle=_wapi_handle_new (WAPI_HANDLE_FIND); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating find handle"); + return(INVALID_HANDLE_VALUE); + } - result = glob (utf8_pattern, 0, NULL, &handle->glob); + _wapi_handle_lock_handle (handle); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, + (gpointer *)&find_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up find handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(INVALID_HANDLE_VALUE); + } + + result = glob (utf8_pattern, 0, NULL, &find_handle->glob); g_free (utf8_pattern); if (result != 0) { - globfree (&handle->glob); - g_free (handle); + globfree (&find_handle->glob); + _wapi_handle_unlock_handle (handle); + _wapi_handle_unref (handle); switch (result) { #ifdef GLOB_NOMATCH @@ -1652,19 +1961,22 @@ WapiHandle *FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) return INVALID_HANDLE_VALUE; } - handle->count = 0; - if (!FindNextFile ((WapiHandle *)handle, find_data)) { - FindClose ((WapiHandle *)handle); + find_handle->count = 0; + if (!FindNextFile (handle, find_data)) { + FindClose (handle); SetLastError (ERROR_NO_MORE_FILES); return INVALID_HANDLE_VALUE; } - return (WapiHandle *)handle; + _wapi_handle_unlock_handle (handle); + + return (handle); } -gboolean FindNextFile (WapiHandle *wapi_handle, WapiFindData *find_data) +gboolean FindNextFile (gpointer handle, WapiFindData *find_data) { - struct _WapiHandle_find *handle; + struct _WapiHandle_find *find_handle; + gboolean ok; struct stat buf; const gchar *filename; @@ -1672,21 +1984,24 @@ gboolean FindNextFile (WapiHandle *wapi_handle, WapiFindData *find_data) gunichar2 *utf16_basename; time_t create_time; int i; - - if (wapi_handle == INVALID_HANDLE_VALUE || wapi_handle->type != WAPI_HANDLE_FIND) { + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, + (gpointer *)&find_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up find handle %p", handle); SetLastError (ERROR_INVALID_HANDLE); - return FALSE; + return(FALSE); } - handle = (struct _WapiHandle_find *)wapi_handle; - if (handle->count >= handle->glob.gl_pathc) { + if (find_handle->count >= find_handle->glob.gl_pathc) { SetLastError (ERROR_NO_MORE_FILES); return FALSE; } /* stat next glob match */ - filename = handle->glob.gl_pathv [handle->count ++]; + filename = find_handle->glob.gl_pathv [find_handle->count ++]; if (stat (filename, &buf) != 0) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename); @@ -1746,18 +2061,22 @@ gboolean FindNextFile (WapiHandle *wapi_handle, WapiFindData *find_data) * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean FindClose (WapiHandle *wapi_handle) +gboolean FindClose (gpointer handle) { - struct _WapiHandle_find *handle; - - if (wapi_handle == INVALID_HANDLE_VALUE || wapi_handle->type != WAPI_HANDLE_FIND) { + struct _WapiHandle_find *find_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, + (gpointer *)&find_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up find handle %p", handle); SetLastError (ERROR_INVALID_HANDLE); - return FALSE; + return(FALSE); } - handle = (struct _WapiHandle_find *)wapi_handle; - globfree (&handle->glob); - g_free (handle); + globfree (&find_handle->glob); + _wapi_handle_unref (handle); return TRUE; } diff --git a/mono/io-layer/io.h b/mono/io-layer/io.h index 21b9a0ce1c523..da1ba44a9cf3b 100644 --- a/mono/io-layer/io.h +++ b/mono/io-layer/io.h @@ -22,7 +22,7 @@ struct _WapiOverlapped guint32 InternalHigh; guint32 Offset; guint32 OffsetHigh; - WapiHandle *hEvent; + gpointer hEvent; }; #define GENERIC_READ 0x80000000 @@ -137,36 +137,46 @@ typedef struct #define INVALID_SET_FILE_POINTER ((guint32)-1) #define INVALID_FILE_SIZE ((guint32)0xFFFFFFFF) -extern WapiHandle *CreateFile(const gunichar2 *name, guint32 fileaccess, - guint32 sharemode, - WapiSecurityAttributes *security, - guint32 createmode, - guint32 attrs, WapiHandle *tmplate); +extern gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, + guint32 sharemode, + WapiSecurityAttributes *security, + guint32 createmode, + guint32 attrs, gpointer tmplate); extern gboolean DeleteFile(const gunichar2 *name); -extern WapiHandle *GetStdHandle(WapiStdHandle stdhandle); -extern gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes, +extern gpointer GetStdHandle(WapiStdHandle stdhandle); +extern gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, WapiOverlapped *overlapped); -extern gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, +extern gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, WapiOverlapped *overlapped); -extern gboolean FlushFileBuffers(WapiHandle *handle); -extern gboolean SetEndOfFile(WapiHandle *handle); -extern guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance, +extern gboolean FlushFileBuffers(gpointer handle); +extern gboolean SetEndOfFile(gpointer handle); +extern guint32 SetFilePointer(gpointer handle, gint32 movedistance, gint32 *highmovedistance, WapiSeekMethod method); -extern WapiFileType GetFileType(WapiHandle *handle); -extern guint32 GetFileSize(WapiHandle *handle, guint32 *highsize); -extern gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time, WapiFileTime *last_access, WapiFileTime *last_write); -extern gboolean SetFileTime(WapiHandle *handle, const WapiFileTime *create_time, const WapiFileTime *last_access, const WapiFileTime *last_write); -extern gboolean FileTimeToSystemTime(const WapiFileTime *file_time, WapiSystemTime *system_time); -extern WapiHandle *FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data); -extern gboolean FindNextFile (WapiHandle *handle, WapiFindData *find_data); -extern gboolean FindClose (WapiHandle *handle); -extern gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security); +extern WapiFileType GetFileType(gpointer handle); +extern guint32 GetFileSize(gpointer handle, guint32 *highsize); +extern gboolean GetFileTime(gpointer handle, WapiFileTime *create_time, + WapiFileTime *last_access, + WapiFileTime *last_write); +extern gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time, + const WapiFileTime *last_access, + const WapiFileTime *last_write); +extern gboolean FileTimeToSystemTime(const WapiFileTime *file_time, + WapiSystemTime *system_time); +extern gpointer FindFirstFile (const gunichar2 *pattern, + WapiFindData *find_data); +extern gboolean FindNextFile (gpointer handle, WapiFindData *find_data); +extern gboolean FindClose (gpointer handle); +extern gboolean CreateDirectory (const gunichar2 *name, + WapiSecurityAttributes *security); extern gboolean RemoveDirectory (const gunichar2 *name); extern gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name); -extern gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists); +extern gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, + gboolean fail_if_exists); extern guint32 GetFileAttributes (const gunichar2 *name); -extern gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info); +extern gboolean GetFileAttributesEx (const gunichar2 *name, + WapiGetFileExInfoLevels level, + gpointer info); extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs); extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer); extern gboolean SetCurrentDirectory (const gunichar2 *path); diff --git a/mono/io-layer/mutex-private.h b/mono/io-layer/mutex-private.h new file mode 100644 index 0000000000000..ce5f7c40fcc1d --- /dev/null +++ b/mono/io-layer/mutex-private.h @@ -0,0 +1,22 @@ +#ifndef _WAPI_MUTEX_PRIVATE_H_ +#define _WAPI_MUTEX_PRIVATE_H_ + +#include +#include +#include +#include + +struct _WapiHandle_mutex +{ + guint32 name; + pid_t pid; + pthread_t tid; + guint32 recursion; +}; + +struct _WapiHandlePrivate_mutex +{ + int dummy; +}; + +#endif /* _WAPI_MUTEX_PRIVATE_H_ */ diff --git a/mono/io-layer/mutexes.c b/mono/io-layer/mutexes.c index 02d09aec5a1fe..3cb088e9385d3 100644 --- a/mono/io-layer/mutexes.c +++ b/mono/io-layer/mutexes.c @@ -2,29 +2,22 @@ #include #include #include +#include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" -#include "wait-private.h" -#include "misc-private.h" -#include "handles-private.h" - -#include "mono-mutex.h" +#include +#include +#include +#include +#include +#include +#include #undef DEBUG -struct _WapiHandle_mutex -{ - WapiHandle handle; - mono_mutex_t mutex; - pthread_t tid; - guint32 recursion; -}; - -static void mutex_close(WapiHandle *handle); -static gboolean mutex_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms); -static guint32 mutex_wait_multiple(gpointer data); -static void mutex_signal(WapiHandle *handle); +static void mutex_close(gpointer handle); +static void mutex_signal(gpointer handle); +static void mutex_own (gpointer handle); +static gboolean mutex_is_owned (gpointer handle); static struct _WapiHandleOps mutex_ops = { mutex_close, /* close */ @@ -37,253 +30,118 @@ static struct _WapiHandleOps mutex_ops = { NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - mutex_wait, /* wait */ - mutex_wait_multiple, /* wait_multiple */ mutex_signal, /* signal */ + mutex_own, /* own */ + mutex_is_owned, /* is_owned */ }; -static void mutex_close(WapiHandle *handle) -{ - struct _WapiHandle_mutex *mutex_handle=(struct _WapiHandle_mutex *)handle; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p", - mutex_handle); -#endif +static pthread_once_t mutex_ops_once=PTHREAD_ONCE_INIT; - mono_mutex_destroy(&mutex_handle->mutex); +static void mutex_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_MUTEX, &mutex_ops); + _wapi_handle_register_capabilities (WAPI_HANDLE_MUTEX, + WAPI_HANDLE_CAP_WAIT | + WAPI_HANDLE_CAP_SIGNAL | + WAPI_HANDLE_CAP_OWN); } -static gboolean mutex_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms) +static void mutex_close(gpointer handle) { - struct _WapiHandle_mutex *mutex_handle=(struct _WapiHandle_mutex *)handle; - pthread_t tid=pthread_self(); - int ret; + struct _WapiHandle_mutex *mutex_handle; + gboolean ok; -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": waiting for mutex handle %p", - mutex_handle); -#endif - - /* Signal this handle now. It really doesn't matter if some - * other thread grabs the mutex before we can - */ - if(signal!=NULL) { - signal->ops->signal(signal); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up mutex handle %p", handle); + return; } - - if(mutex_handle->tid==tid) { - /* We already own this mutex, so just increase the count and - * return TRUE - */ - - mutex_handle->recursion++; #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Already own mutex handle %p (recursion %d)", - mutex_handle, mutex_handle->recursion); + g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p", handle); #endif - return(TRUE); + if(mutex_handle->name!=0) { + _wapi_handle_scratch_delete (mutex_handle->name); + mutex_handle->name=0; } +} + +static void mutex_signal(gpointer handle) +{ + ReleaseMutex(handle); +} + +static void mutex_own (gpointer handle) +{ + struct _WapiHandle_mutex *mutex_handle; + gboolean ok; - if(ms==INFINITE) { - ret=mono_mutex_lock(&mutex_handle->mutex); - } else { - struct timespec timeout; - - _wapi_calc_timeout(&timeout, ms); - - ret=mono_mutex_timedlock(&mutex_handle->mutex, &timeout); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up mutex handle %p", handle); + return; } - - if(ret==0) { - /* Mutex locked */ #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Locking mutex handle %p", - mutex_handle); + g_message(G_GNUC_PRETTY_FUNCTION ": owning mutex handle %p", handle); #endif - mutex_handle->tid=tid; - mutex_handle->recursion=1; - - return(TRUE); - } else { - /* ret might be ETIMEDOUT for timeout, or other for error */ + _wapi_handle_set_signal_state (handle, FALSE, FALSE); + mutex_handle->pid=getpid (); + mutex_handle->tid=pthread_self (); + mutex_handle->recursion++; + #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Failed to lock mutex handle %p: %s", mutex_handle, - strerror(ret)); + g_message (G_GNUC_PRETTY_FUNCTION + ": mutex handle %p locked %d times by %d:%ld", handle, + mutex_handle->recursion, mutex_handle->pid, + mutex_handle->tid); #endif - return(FALSE); - } } -static guint32 mutex_wait_multiple(gpointer data G_GNUC_UNUSED) +static gboolean mutex_is_owned (gpointer handle) { - WaitQueueItem *item=(WaitQueueItem *)data; - GPtrArray *needed; - int ret; - guint32 numhandles; - struct timespec timeout; - pthread_t tid=pthread_self(); - guint32 i, iterations; - - numhandles=item->handles[WAPI_HANDLE_MUTEX]->len; + struct _WapiHandle_mutex *mutex_handle; + gboolean ok; -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting on %d mutex handles for %d ms", numhandles, - item->timeout); -#endif - - /* - * See which ones we need to lock - */ - needed=g_ptr_array_new(); - for(i=0; ihandles[WAPI_HANDLE_MUTEX], i); - - if(mutex_handle->tid!=tid) { - /* We don't have this one, so add it to the list */ - g_ptr_array_add(needed, mutex_handle); - } + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up mutex handle %p", handle); + return(FALSE); } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": need to lock %d mutex handles", - needed->len); -#endif - iterations=0; - do { - iterations++; - - /* If the timeout isnt INFINITE but greater than 1s, - * split the timeout into 1s chunks - */ - if((item->timeout!=INFINITE) && - (item->timeout < (iterations*1000))) { - _wapi_calc_timeout( - &timeout, item->timeout-((iterations-1)*1000)); - } else { - _wapi_calc_timeout(&timeout, 1000); - } - - /* Try and lock as many mutexes as we can until we run - * out of time, but to avoid deadlocks back off if we - * fail to lock one - */ - for(i=0; ilen; i++) { - struct _WapiHandle_mutex *mutex_handle; - - mutex_handle=g_ptr_array_index(needed, i); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Locking %d mutex %p (owner %ld, me %ld)", - i, mutex_handle, mutex_handle->tid, tid); -#endif - - ret=mono_mutex_timedlock(&mutex_handle->mutex, - &timeout); - #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": timedlock ret %s", - strerror(ret)); -#endif - - if(ret!=0) { - /* ETIMEDOUT is the most likely, but - * fail on other error too - */ - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Lock %d mutex failed: %s", i, - strerror(ret)); + g_message(G_GNUC_PRETTY_FUNCTION + ": testing ownership mutex handle %p", handle); #endif - while(i--) { + if(mutex_handle->recursion>0 && + mutex_handle->pid==getpid () && + mutex_handle->tid==pthread_self ()) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Releasing %d mutex", i); + g_message (G_GNUC_PRETTY_FUNCTION + ": mutex handle %p owned by %d:%ld", handle, + getpid (), pthread_self ()); #endif - mutex_handle=g_ptr_array_index(needed, - i); - mono_mutex_unlock(&mutex_handle->mutex); - } - break; - } - - /* OK, got that one. Don't record it as ours - * though until we get them all - */ - } - - if(i==needed->len) { - /* We've locked all the mutexes. Update the - * ones we already had, and record that the - * new ones belong to us - */ - for(i=0; ihandles[WAPI_HANDLE_MUTEX], i); - - idx=g_array_index( - item->waitindex[WAPI_HANDLE_MUTEX], - guint32, i); - _wapi_handle_set_lowest(item, idx); - + return(TRUE); + } else { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Updating mutex %p", mutex_handle); + g_message (G_GNUC_PRETTY_FUNCTION + ": mutex handle %p not owned by %d:%ld", handle, + getpid (), pthread_self ()); #endif - - if(mutex_handle->tid==tid) { - /* We already own this mutex, - * so just increase the count - */ - mutex_handle->recursion++; - } else { - mutex_handle->tid=tid; - mutex_handle->recursion=1; - } - } - - g_ptr_array_free(needed, FALSE); - - item->waited[WAPI_HANDLE_MUTEX]=TRUE; - item->waitcount[WAPI_HANDLE_MUTEX]=numhandles; - - return(numhandles); - } - } while((item->timeout==INFINITE) || - (item->timeout > (iterations * 1000))); - /* Didn't get all the locks, and timeout isn't INFINITE */ - - g_ptr_array_free(needed, FALSE); - - item->waited[WAPI_HANDLE_MUTEX]=TRUE; - item->waitcount[WAPI_HANDLE_MUTEX]=0; - - return(0); -} - -static void mutex_signal(WapiHandle *handle) -{ - ReleaseMutex(handle); + return(FALSE); + } } /** @@ -292,7 +150,7 @@ static void mutex_signal(WapiHandle *handle) * @owned: If %TRUE, the mutex is created with the calling thread * already owning the mutex. * @name:Pointer to a string specifying the name of this mutex, or - * %NULL. Currently ignored. + * %NULL. * * Creates a new mutex handle. A mutex is signalled when no thread * owns it. A thread acquires ownership of the mutex by waiting for @@ -305,26 +163,51 @@ static void mutex_signal(WapiHandle *handle) * * Return value: A new handle, or %NULL on error. */ -WapiHandle *CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned G_GNUC_UNUSED, - const guchar *name G_GNUC_UNUSED) +gpointer CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned, + const guchar *name) { struct _WapiHandle_mutex *mutex_handle; - WapiHandle *handle; + gpointer handle; + gboolean ok; - mutex_handle=(struct _WapiHandle_mutex *)g_new0(struct _WapiHandle_mutex, 1); - handle=(WapiHandle *)mutex_handle; - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_MUTEX, mutex_ops); + pthread_once (&mutex_ops_once, mutex_ops_init); + + handle=_wapi_handle_new (WAPI_HANDLE_MUTEX); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating mutex handle"); + return(NULL); + } - mono_mutex_init(&mutex_handle->mutex, NULL); + _wapi_handle_lock_handle (handle); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up mutex handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(NULL); + } + + if(name!=NULL) { + mutex_handle->name=_wapi_handle_scratch_store (name, + strlen (name)); + } + if(owned==TRUE) { - pthread_t tid=pthread_self(); - - mono_mutex_lock(&mutex_handle->mutex); - - mutex_handle->tid=tid; - mutex_handle->recursion=1; + mutex_own (handle); + } else { + _wapi_handle_set_signal_state (handle, TRUE, FALSE); } +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": returning mutex handle %p", + handle); +#endif + + _wapi_handle_unlock_handle (handle); + return(handle); } @@ -337,21 +220,34 @@ WapiHandle *CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean * Return value: %TRUE on success, %FALSE otherwise. This function * fails if the calling thread does not own the mutex @handle. */ -gboolean ReleaseMutex(WapiHandle *handle) +gboolean ReleaseMutex(gpointer handle) { - struct _WapiHandle_mutex *mutex_handle=(struct _WapiHandle_mutex *)handle; + struct _WapiHandle_mutex *mutex_handle; + gboolean ok; pthread_t tid=pthread_self(); + pid_t pid=getpid (); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX, + (gpointer *)&mutex_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up mutex handle %p", handle); + return(FALSE); + } + + _wapi_handle_lock_handle (handle); #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex handle %p", - mutex_handle); + handle); #endif - if(mutex_handle->tid!=tid) { + if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": We don't own mutex handle %p (owned by %ld, me %ld)", mutex_handle, mutex_handle->tid, tid); + g_message(G_GNUC_PRETTY_FUNCTION ": We don't own mutex handle %p (owned by %d:%ld, me %d:%ld)", handle, mutex_handle->pid, mutex_handle->tid, pid, tid); #endif + _wapi_handle_unlock_handle (handle); return(FALSE); } @@ -361,12 +257,15 @@ gboolean ReleaseMutex(WapiHandle *handle) if(mutex_handle->recursion==0) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p", - mutex_handle); + handle); #endif + mutex_handle->pid=0; mutex_handle->tid=0; - mono_mutex_unlock(&mutex_handle->mutex); + _wapi_handle_set_signal_state (handle, TRUE, FALSE); } + + _wapi_handle_unlock_handle (handle); return(TRUE); } diff --git a/mono/io-layer/mutexes.h b/mono/io-layer/mutexes.h index 04e579413bc88..aac2ac843ff55 100644 --- a/mono/io-layer/mutexes.h +++ b/mono/io-layer/mutexes.h @@ -3,7 +3,8 @@ #include -extern WapiHandle *CreateMutex(WapiSecurityAttributes *security, gboolean owned, const guchar *name); -extern gboolean ReleaseMutex(WapiHandle *handle); +extern gpointer CreateMutex(WapiSecurityAttributes *security, gboolean owned, + const guchar *name); +extern gboolean ReleaseMutex(gpointer handle); #endif /* _WAPI_MUTEXES_H_ */ diff --git a/mono/io-layer/process-private.h b/mono/io-layer/process-private.h new file mode 100644 index 0000000000000..95e605f350935 --- /dev/null +++ b/mono/io-layer/process-private.h @@ -0,0 +1,17 @@ +#ifndef _WAPI_PROCESS_PRIVATE_H_ +#define _WAPI_PROCESS_PRIVATE_H_ + +#include +#include + +struct _WapiHandle_process +{ + pid_t id; +}; + +struct _WapiHandlePrivate_process +{ + int dummy; +}; + +#endif /* _WAPI_PROCESS_PRIVATE_H_ */ diff --git a/mono/io-layer/processes.c b/mono/io-layer/processes.c new file mode 100644 index 0000000000000..e2d7a015148d2 --- /dev/null +++ b/mono/io-layer/processes.c @@ -0,0 +1,83 @@ +#include +#if HAVE_BOEHM_GC +#include +#include "mono/utils/mono-hash.h" +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +static void process_close (gpointer handle); + +static struct _WapiHandleOps process_ops = { + process_close, /* close */ + NULL, /* getfiletype */ + NULL, /* readfile */ + NULL, /* writefile */ + NULL, /* flushfile */ + NULL, /* seek */ + NULL, /* setendoffile */ + NULL, /* getfilesize */ + NULL, /* getfiletime */ + NULL, /* setfiletime */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ +}; + +static pthread_once_t process_ops_once=PTHREAD_ONCE_INIT; + +static void process_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_PROCESS, &process_ops); + _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS, + WAPI_HANDLE_CAP_WAIT); +} + +static void process_close (gpointer handle G_GNUC_UNUSED) +{ +#ifdef DEBUG + struct _WapiHandle_process *process_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, + (gpointer *)&process_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up process handle %p", handle); + return; + } +#endif + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": closing process handle %p with id %ld", handle, + process_handle->id); +#endif +} + +gboolean CreateProcess (const gunichar2 *appname G_GNUC_UNUSED, gunichar2 *cmdline G_GNUC_UNUSED, + WapiSecurityAttributes *process_attrs G_GNUC_UNUSED, + WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED, + gboolean inherit_handles G_GNUC_UNUSED, guint32 create_flags G_GNUC_UNUSED, + gpointer env G_GNUC_UNUSED, const gunichar2 *cwd G_GNUC_UNUSED, + WapiStartupInfo *startup G_GNUC_UNUSED, + WapiProcessInformation *process_info G_GNUC_UNUSED) +{ + pthread_once (&process_ops_once, process_ops_init); + + return(FALSE); +} diff --git a/mono/io-layer/processes.h b/mono/io-layer/processes.h new file mode 100644 index 0000000000000..91a195c42b3d2 --- /dev/null +++ b/mono/io-layer/processes.h @@ -0,0 +1,76 @@ +#ifndef _WAPI_PROCESSES_H_ +#define _WAPI_PROCESSES_H_ + +#include + +#include "mono/io-layer/handles.h" + +typedef struct _WapiStartupInfo WapiStartupInfo; + +struct _WapiStartupInfo +{ + guint32 cb; + guchar *lpReserved; + guchar *lpDesktop; + guchar *lpTitle; + guint32 dwX; + guint32 dwY; + guint32 dwXSize; + guint32 dwYSize; + guint32 dwXCountChars; + guint32 dwYCountChars; + guint32 dwFillAttribute; + guint32 dwFlags; + guint16 wShowWindow; + guint16 cbReserved2; + guint8 *lpReserved2; + gpointer hStdInput; + gpointer hStdOutput; + gpointer hStdError; +}; + +typedef struct _WapiProcessInformation WapiProcessInformation; + +struct _WapiProcessInformation +{ + gpointer hProcess; + gpointer hThread; + guint32 dwProcessId; + guint32 dwThreadId; +}; + + +#define DEBUG_PROCESS 0x00000001 +#define DEBUG_ONLY_THIS_PROCESS 0x00000002 +#define CREATE_SUSPENDED 0x00000004 +#define DETACHED_PROCESS 0x00000008 +#define CREATE_NEW_CONSOLE 0x00000010 +#define NORMAL_PRIORITY_CLASS 0x00000020 +#define IDLE_PRIORITY_CLASS 0x00000040 +#define HIGH_PRIORITY_CLASS 0x00000080 +#define REALTIME_PRIORITY_CLASS 0x00000100 +#define CREATE_NEW_PROCESS_GROUP 0x00000200 +#define CREATE_UNICODE_ENVIRONMENT 0x00000400 +#define CREATE_SEPARATE_WOW_VDM 0x00000800 +#define CREATE_SHARED_WOW_VDM 0x00001000 +#define CREATE_FORCEDOS 0x00002000 +#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 +#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 +#define CREATE_BREAKAWAY_FROM_JOB 0x01000000 +#define CREATE_WITH_USERPROFILE 0x02000000 +#define CREATE_DEFAULT_ERROR_MODE 0x04000000 +#define CREATE_NO_WINDOW 0x08000000 + +#ifdef NEW_STUFF +#define CREATE_PRESERVE_CODE_AUTHZ_LEVEL find out the value for this one... +#endif + +extern gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, + WapiSecurityAttributes *process_attrs, + WapiSecurityAttributes *thread_attrs, + gboolean inherit_handles, guint32 create_flags, + gpointer env, const gunichar2 *cwd, + WapiStartupInfo *startup, + WapiProcessInformation *process_info); + +#endif /* _WAPI_PROCESSES_H_ */ diff --git a/mono/io-layer/semaphore-private.h b/mono/io-layer/semaphore-private.h new file mode 100644 index 0000000000000..eb1159871acca --- /dev/null +++ b/mono/io-layer/semaphore-private.h @@ -0,0 +1,19 @@ +#ifndef _WAPI_SEMAPHORE_PRIVATE_H_ +#define _WAPI_SEMAPHORE_PRIVATE_H_ + +#include +#include + +/* emulate sem_t, so that we can prod the internal state more easily */ +struct _WapiHandle_sem +{ + guint32 val; + gint32 max; +}; + +struct _WapiHandlePrivate_sem +{ + int dummy; +}; + +#endif /* _WAPI_SEMAPHORE_PRIVATE_H_ */ diff --git a/mono/io-layer/semaphores.c b/mono/io-layer/semaphores.c index 1b7f3d1db4e8c..2e2905e50a9f0 100644 --- a/mono/io-layer/semaphores.c +++ b/mono/io-layer/semaphores.c @@ -6,37 +6,19 @@ #include #include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" -#include "wait-private.h" -#include "misc-private.h" -#include "handles-private.h" - -#include "mono-mutex.h" +#include +#include +#include +#include +#include +#include +#include #undef DEBUG -/* emulate sem_t, so that we can prod the internal state more easily */ -struct _WapiHandle_sem -{ - WapiHandle handle; - guint32 val; - gint32 max; -}; - -/* This mutex controls access to _all_ semaphores and should not be - * locked for long periods. - * - * This global mutex and cond is really for wait_multiple, so we dont - * have to try and lock multiple handle mutexes and conditions. - */ -static mono_mutex_t sem_mutex=MONO_MUTEX_INITIALIZER; -static pthread_cond_t sem_cond=PTHREAD_COND_INITIALIZER; - -static void sema_close(WapiHandle *handle); -static gboolean sema_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms); -static guint32 sema_wait_multiple(gpointer data); -static void sema_signal(WapiHandle *handle); +static void sema_close(gpointer handle); +static void sema_signal(gpointer handle); +static void sema_own (gpointer handle); static struct _WapiHandleOps sem_ops = { sema_close, /* close */ @@ -49,12 +31,22 @@ static struct _WapiHandleOps sem_ops = { NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - sema_wait, /* wait */ - sema_wait_multiple, /* wait_multiple */ sema_signal, /* signal */ + sema_own, /* own */ + NULL, /* is_owned */ }; -static void sema_close(WapiHandle *handle G_GNUC_UNUSED) +static pthread_once_t sem_ops_once=PTHREAD_ONCE_INIT; + +static void sem_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_SEM, &sem_ops); + _wapi_handle_register_capabilities (WAPI_HANDLE_SEM, + WAPI_HANDLE_CAP_WAIT | + WAPI_HANDLE_CAP_SIGNAL); +} + +static void sema_close(gpointer handle G_GNUC_UNUSED) { /* Not really much to do here */ #ifdef DEBUG @@ -62,268 +54,40 @@ static void sema_close(WapiHandle *handle G_GNUC_UNUSED) #endif } -static gboolean sema_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms) +static void sema_signal(gpointer handle) { - struct _WapiHandle_sem *sem_handle=(struct _WapiHandle_sem *)handle; - gboolean waited; - int ret; - - mono_mutex_lock(&sem_mutex); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Sem %p val %d ms %d", handle, - sem_handle->val, ms); -#endif - - /* Signal this handle after we have obtained the semaphore - * global lock - */ - if(signal!=NULL) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": signalling %p", signal); -#endif - signal->ops->signal(signal); - } - - /* Shortcut when ms==0 */ - if(ms==0) { - /* Just poll */ -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Polling"); -#endif - if(sem_handle->val>0) { - waited=TRUE; - } else { - waited=FALSE; - } - goto end; - } - - /* Check state first */ - if(sem_handle->val>0) { - waited=TRUE; - goto end; - } - - if(ms==INFINITE) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": wait for %p INFINITE", - sem_handle); -#endif - try_again_infinite: - ret=mono_cond_wait(&sem_cond, &sem_mutex); - if(ret==0) { - /* See if we were signalled (it might have been - * another semaphore) - */ - if(sem_handle->val>0) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": sem %p has been signalled", - sem_handle); -#endif - waited=TRUE; - } else { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": sem %p not signalled", - sem_handle); -#endif - goto try_again_infinite; - } - } - } else { - struct timespec timeout; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": wait for %p for %d ms", - sem_handle, ms); -#endif - - _wapi_calc_timeout(&timeout, ms); - - try_again_timed: - ret=mono_cond_timedwait(&sem_cond, &sem_mutex, &timeout); - if(ret==0) { - /* See if we were signalled (it might have been - * another semaphore) - */ - if(sem_handle->val>0) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": sem %p has been signalled", - sem_handle); -#endif - waited=TRUE; - } else { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": sem %p not signalled", - sem_handle); -#endif - goto try_again_timed; - } - } else { - /* ret might be ETIMEDOUT for timeout, or - * other for error */ - waited=FALSE; - } - } - -end: - if(waited==TRUE) { - sem_handle->val--; -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Waited TRUE, sem %p val now %d", sem_handle, - sem_handle->val); -#endif - } -#ifdef DEBUG - else { - g_message(G_GNUC_PRETTY_FUNCTION ": Waited FALSE, sem %p", - sem_handle); - } -#endif - - mono_mutex_unlock(&sem_mutex); - return(waited); + ReleaseSemaphore(handle, 1, NULL); } -static guint32 sema_wait_multiple(gpointer data G_GNUC_UNUSED) +static void sema_own (gpointer handle) { - WaitQueueItem *item=(WaitQueueItem *)data; - guint32 numhandles, count; - struct timespec timeout; - guint32 i; - int ret; - - numhandles=item->handles[WAPI_HANDLE_SEM]->len; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting on %d sem handles for %d ms", numhandles, - item->timeout); -#endif - - mono_mutex_lock(&sem_mutex); - - /* First, check if any of the handles are already signalled. - * If waitall is specified we only return if all handles have - * been signalled. - */ - for(count=0, i=0; ihandles[WAPI_HANDLE_SEM], - i); - if(sem_handle->val>0) { - count++; - } - } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Preliminary check found %d handles signalled", count); -#endif - - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - goto success; - } - - /* OK, we need to wait for some */ - if(item->timeout!=INFINITE) { - _wapi_calc_timeout(&timeout, item->timeout); - } + struct _WapiHandle_sem *sem_handle; + gboolean ok; - /* We can restart from here without resetting the timeout, - * because it is calculated from absolute time, not an offset. - */ -again: - if(item->timeout==INFINITE) { - ret=mono_cond_wait(&sem_cond, &sem_mutex); - } else { - ret=mono_cond_timedwait(&sem_cond, &sem_mutex, &timeout); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM, + (gpointer *)&sem_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up sem handle %p", handle); + return; } - if(ret==ETIMEDOUT) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out"); -#endif - - goto success; - } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Sem posted, checking status"); -#endif - - /* A semaphore was posted, so see if it was one we are - * interested in - */ - for(count=0, i=0; ihandles[WAPI_HANDLE_SEM], - i); - if(sem_handle->val>0) { - count++; - } - } - #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Check after sem post found %d handles signalled", count); + g_message(G_GNUC_PRETTY_FUNCTION ": owning sem handle %p", handle); #endif - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - goto success; - } + sem_handle->val--; - /* Either we have waitall set with more handles to wait for, or - * the sem that was posted wasn't interesting to us - */ #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Waiting a bit longer"); + g_message (G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", handle, + sem_handle->val); #endif - goto again; - -success: - item->waited[WAPI_HANDLE_SEM]=TRUE; - item->waitcount[WAPI_HANDLE_SEM]=count; - - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - /* Decrease all waited semaphores */ - for(i=0; ihandles[WAPI_HANDLE_SEM], i); - - idx=g_array_index(item->waitindex[WAPI_HANDLE_SEM], - guint32, i); - _wapi_handle_set_lowest(item, idx); - - if(sem_handle->val>0) { - sem_handle->val--; - } - } + if(sem_handle->val==0) { + _wapi_handle_set_signal_state (handle, FALSE, FALSE); } - - mono_mutex_unlock(&sem_mutex); - - return(count); } -static void sema_signal(WapiHandle *handle) -{ - ReleaseSemaphore(handle, 1, NULL); -} /** * CreateSemaphore: @@ -343,10 +107,13 @@ static void sema_signal(WapiHandle *handle) * * Return value: a new handle, or NULL */ -WapiHandle *CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const guchar *name G_GNUC_UNUSED) +gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const guchar *name G_GNUC_UNUSED) { struct _WapiHandle_sem *sem_handle; - WapiHandle *handle; + gpointer handle; + gboolean ok; + + pthread_once (&sem_ops_once, sem_ops_init); if(max<=0) { #ifdef DEBUG @@ -364,17 +131,37 @@ WapiHandle *CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint return(NULL); } - sem_handle=(struct _WapiHandle_sem *)g_new0(struct _WapiHandle_sem, 1); - handle=(WapiHandle *)sem_handle; - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_SEM, sem_ops); + handle=_wapi_handle_new (WAPI_HANDLE_SEM); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating semaphore handle"); + return(NULL); + } + + _wapi_handle_lock_handle (handle); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM, + (gpointer *)&sem_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error lookup up semaphore handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(NULL); + } sem_handle->val=initial; sem_handle->max=max; + if(initial!=0) { + _wapi_handle_set_signal_state (handle, TRUE, FALSE); + } + #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Created semaphore handle %p", handle); #endif + + _wapi_handle_unlock_handle (handle); return(handle); } @@ -391,17 +178,25 @@ WapiHandle *CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint * * Return value: %TRUE on success, %FALSE otherwise. */ -gboolean ReleaseSemaphore(WapiHandle *handle, gint32 count, gint32 *prevcount) +gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount) { - struct _WapiHandle_sem *sem_handle=(struct _WapiHandle_sem *)handle; + struct _WapiHandle_sem *sem_handle; + gboolean ok; gboolean ret=FALSE; - - mono_mutex_lock(&sem_mutex); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM, + (gpointer *)&sem_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up sem handle %p", handle); + return(FALSE); + } + + _wapi_handle_lock_handle (handle); #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val %d count %d", - sem_handle, sem_handle->val, count); + handle, sem_handle->val, count); #endif /* Do this before checking for count overflow, because overflowing max @@ -422,15 +217,17 @@ gboolean ReleaseSemaphore(WapiHandle *handle, gint32 count, gint32 *prevcount) } sem_handle->val+=count; - pthread_cond_broadcast(&sem_cond); + _wapi_handle_set_signal_state (handle, TRUE, TRUE); + ret=TRUE; #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", sem_handle, + g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", handle, sem_handle->val); #endif end: - mono_mutex_unlock(&sem_mutex); + _wapi_handle_unlock_handle (handle); + return(ret); } diff --git a/mono/io-layer/semaphores.h b/mono/io-layer/semaphores.h index 21a8bd0e535b1..ad801a786e946 100644 --- a/mono/io-layer/semaphores.h +++ b/mono/io-layer/semaphores.h @@ -3,7 +3,10 @@ #include -extern WapiHandle *CreateSemaphore(WapiSecurityAttributes *security, gint32 initial, gint32 max, const guchar *name); -extern gboolean ReleaseSemaphore(WapiHandle *handle, gint32 count, gint32 *prevcount); +extern gpointer CreateSemaphore(WapiSecurityAttributes *security, + gint32 initial, gint32 max, + const guchar *name); +extern gboolean ReleaseSemaphore(gpointer handle, gint32 count, + gint32 *prevcount); #endif /* _WAPI_SEMAPHORES_H_ */ diff --git a/mono/io-layer/shared.c b/mono/io-layer/shared.c new file mode 100644 index 0000000000000..c9d4e62576589 --- /dev/null +++ b/mono/io-layer/shared.c @@ -0,0 +1,132 @@ +/* + * Code to support inter-process sharing of handles. + * + * I thought of using an mmap()ed file for this. If linuxthreads + * supported PTHREAD_PROCESS_SHARED I would have done; however without + * that pthread support the only other inter-process IPC + * synchronisation option is a sysV semaphore, and if I'm going to use + * that I may as well take advantage of sysV shared memory too. + * Actually, semaphores seem to be buggy, or I was using them + * incorrectly :-). I've replaced the sysV semaphore with a shared + * integer controlled with Interlocked functions. + + * mmap() files have the advantage of avoiding namespace collisions, + * but have the disadvantage of needing cleaning up, and also msync(). + * sysV shared memory has a really stupid way of getting random key + * IDs, which can lead to collisions. + * + * I deliberately don't ever delete the shared memory: I'd like to + * have been able to set the shared memory segment to destroy itself + * on last close, but it doesn't support that. (Setting IPC_RMID on a + * segment causes subsequent shmat() with the same key to get a new + * segment :-( ). The function to delete the shared memory segment is + * only called from a debugging tool (mono/handles/shmdel). + * + * w32 processes do not have the POSIX parent-child relationship, so a + * process handle is available to any other process to find out exit + * status. Handles are destroyed when the last reference to them is + * closed. New handles can be created for long lasting items such as + * processes or threads, and also for named synchronisation objects so + * long as these haven't been deleted by having the last referencing + * handle closed. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#undef DEBUG + +static gboolean shared; + +gpointer _wapi_shm_attach (guint32 *scratch_size) +{ + gpointer shm_seg; + + *scratch_size=getpagesize ()*100; + +#ifndef DISABLE_SHARED_HANDLES + if(getenv ("MONO_DISABLE_SHM")) +#endif + { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": Using process-private handles"); +#endif + + shared=FALSE; + shm_seg=g_malloc0 (sizeof(struct _WapiHandleShared_list)+ + *scratch_size); +#ifndef DISABLE_SHARED_HANDLES + } else { + int shm_id; + key_t key; + + shared=TRUE; + + /* + * This is an attempt to get a unique key id. The + * first arg to ftok is a path, so when the config + * file support is done we should use that. + */ + key=ftok (g_get_home_dir (), _WAPI_HANDLE_VERSION); + + /* sysv shared mem is set to all zero when allocated, + * so we don't need to do any more initialisation here + */ + shm_id=shmget (key, sizeof(struct _WapiHandleShared_list)+ + *scratch_size, IPC_CREAT | 0600); + if(shm_id==-1) { + perror ("shmget"); + exit (-1); + } + + shm_seg=shmat (shm_id, NULL, 0); + if(shm_seg==(gpointer)-1) { + perror ("shmat"); + exit (-1); + } +#endif /* DISABLE_SHARED_HANDLES */ + } + + return(shm_seg); +} + +void _wapi_shm_destroy (void) +{ +#ifndef DISABLE_SHARED_HANDLES + int shm_id; + key_t key; + + /* + * This is an attempt to get a unique key id. The + * first arg to ftok is a path, so when the config + * file support is done we should use that. + */ + key=ftok (g_get_home_dir (), _WAPI_HANDLE_VERSION); + + /* sysv shared mem is set to all zero when allocated, + * so we don't need to do any more initialisation here + */ + shm_id=shmget (key, 0, 0600); + if(shm_id==-1 && errno==ENOENT) { + return; + } else if (shm_id==-1) { + perror ("shmget"); + exit (-1); + } + if(shmctl (shm_id, IPC_RMID, NULL)==-1) { + perror ("shmctl"); + exit (-1); + } +#endif /* DISABLE_SHARED_HANDLES */ +} diff --git a/mono/io-layer/shared.h b/mono/io-layer/shared.h new file mode 100644 index 0000000000000..270f7f533661a --- /dev/null +++ b/mono/io-layer/shared.h @@ -0,0 +1,24 @@ +#ifndef _WAPI_SHARED_H_ +#define _WAPI_SHARED_H_ + +struct _WapiScratchHeader +{ + /* These two can be merged */ + guint32 flags; + guint32 length; +}; + +enum { + WAPI_SHM_SCRATCH_FREE=0x1, +}; + +extern gpointer _wapi_shm_attach (guint32 *scratch_size); +extern void _wapi_shm_destroy (void); + +extern guint32 _wapi_shm_scratch_store (guchar *storage, gconstpointer data, + guint32 len); +extern guchar *_wapi_shm_scratch_lookup_as_string (guchar *storage, + guint32 idx); +extern void _wapi_shm_scratch_delete (guchar *storage, guint32 idx); + +#endif /* _WAPI_SHARED_H_ */ diff --git a/mono/io-layer/socket-private.h b/mono/io-layer/socket-private.h new file mode 100644 index 0000000000000..eadbf82618334 --- /dev/null +++ b/mono/io-layer/socket-private.h @@ -0,0 +1,17 @@ +#ifndef _WAPI_SOCKET_PRIVATE_H_ +#define _WAPI_SOCKET_PRIVATE_H_ + +#include +#include + +struct _WapiHandle_socket +{ + int dummy; +}; + +struct _WapiHandlePrivate_socket +{ + int fd; +}; + +#endif /* _WAPI_SOCKET_PRIVATE_H_ */ diff --git a/mono/io-layer/sockets.c b/mono/io-layer/sockets.c index 08ecb25e67d22..6782740aacf7a 100644 --- a/mono/io-layer/sockets.c +++ b/mono/io-layer/sockets.c @@ -14,23 +14,19 @@ #endif #include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" +#include +#include +#include +#include #undef DEBUG -struct _WapiHandle_socket -{ - WapiHandle handle; - int fd; -}; - static guint32 startup_count=0; static GPtrArray *sockets=NULL; static pthread_key_t error_key; static pthread_once_t error_key_once=PTHREAD_ONCE_INIT; -static void socket_close(WapiHandle *handle); +static void socket_close(gpointer handle); static struct _WapiHandleOps socket_ops = { socket_close, /* close */ @@ -43,18 +39,27 @@ static struct _WapiHandleOps socket_ops = { NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - NULL, /* wait */ - NULL, /* wait_multiple */ NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ }; -static void socket_close(WapiHandle *handle) +static pthread_once_t socket_ops_once=PTHREAD_ONCE_INIT; + +static void socket_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_SOCKET, &socket_ops); + /* No capabilities to register */ +} + +static void socket_close(gpointer handle) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": closing socket handle %p", handle); + g_message(G_GNUC_PRETTY_FUNCTION ": closing socket handle 0x%x", + handle); #endif - closesocket(handle); + closesocket(GPOINTER_TO_UINT (handle)); } int WSAStartup(guint32 requested, WapiWSAData *data) @@ -106,10 +111,10 @@ int WSACleanup(void) /* Close down all sockets */ for(i=0; ilen; i++) { - WapiHandle *handle; + gpointer handle; handle=g_ptr_array_index(sockets, i); - handle->ops->close(handle); + _wapi_handle_ops_close(handle); } g_ptr_array_free(sockets, FALSE); @@ -141,9 +146,10 @@ int WSAGetLastError(void) return(err); } -int closesocket(WapiHandle *handle) +int closesocket(guint32 handle) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -151,14 +157,18 @@ int closesocket(WapiHandle *handle) return(SOCKET_ERROR); } - if(handle->type!=WAPI_HANDLE_SOCKET) { + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); WSASetLastError(WSAENOTSOCK); return(SOCKET_ERROR); } - g_ptr_array_remove_fast(sockets, handle); + g_ptr_array_remove_fast(sockets, GUINT_TO_POINTER (handle)); - ret=close(socket_handle->fd); + ret=close(socket_private_handle->fd); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": close error: %s", @@ -183,11 +193,13 @@ int closesocket(WapiHandle *handle) return(ret); } -WapiHandle *_wapi_accept(WapiHandle *handle, struct sockaddr *addr, socklen_t *addrlen) +guint32 _wapi_accept(guint32 handle, struct sockaddr *addr, + socklen_t *addrlen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; - struct _WapiHandle_socket *new_socket_handle; - WapiHandle *new_handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + struct _WapiHandlePrivate_socket *new_socket_private_handle; + gpointer new_handle; + gboolean ok; int fd; if(startup_count==0) { @@ -195,7 +207,16 @@ WapiHandle *_wapi_accept(WapiHandle *handle, struct sockaddr *addr, socklen_t *a return(INVALID_SOCKET); } - fd=accept(socket_handle->fd, addr, addrlen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(INVALID_SOCKET); + } + + fd=accept(socket_private_handle->fd, addr, addrlen); if(fd==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": accept error: %s", @@ -252,26 +273,42 @@ WapiHandle *_wapi_accept(WapiHandle *handle, struct sockaddr *addr, socklen_t *a return(INVALID_SOCKET); } + + new_handle=_wapi_handle_new (WAPI_HANDLE_SOCKET); + if(new_handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating socket handle"); + return(INVALID_SOCKET); + } + + _wapi_handle_lock_handle (new_handle); - new_socket_handle=g_new0(struct _WapiHandle_socket, 1); - new_handle=(WapiHandle *)new_socket_handle; - - _WAPI_HANDLE_INIT(new_handle, WAPI_HANDLE_SOCKET, socket_ops); + ok=_wapi_lookup_handle (new_handle, WAPI_HANDLE_SOCKET, NULL, + (gpointer *)&new_socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + _wapi_handle_unlock_handle (new_handle); + return(INVALID_SOCKET); + } - new_socket_handle->fd=fd; + new_socket_private_handle->fd=fd; #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": returning newly accepted socket handle %p with fd %d", - new_handle, new_socket_handle->fd); + new_handle, new_socket_private_handle->fd); #endif - return(new_handle); + _wapi_handle_unlock_handle (new_handle); + + return(GPOINTER_TO_UINT (new_handle)); } -int _wapi_bind(WapiHandle *handle, struct sockaddr *my_addr, socklen_t addrlen) +int _wapi_bind(guint32 handle, struct sockaddr *my_addr, socklen_t addrlen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -279,7 +316,16 @@ int _wapi_bind(WapiHandle *handle, struct sockaddr *my_addr, socklen_t addrlen) return(SOCKET_ERROR); } - ret=bind(socket_handle->fd, my_addr, addrlen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=bind(socket_private_handle->fd, my_addr, addrlen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": bind error: %s", @@ -318,9 +364,11 @@ int _wapi_bind(WapiHandle *handle, struct sockaddr *my_addr, socklen_t addrlen) return(ret); } -int _wapi_connect(WapiHandle *handle, const struct sockaddr *serv_addr, socklen_t addrlen) +int _wapi_connect(guint32 handle, const struct sockaddr *serv_addr, + socklen_t addrlen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -328,7 +376,16 @@ int _wapi_connect(WapiHandle *handle, const struct sockaddr *serv_addr, socklen_ return(SOCKET_ERROR); } - ret=connect(socket_handle->fd, serv_addr, addrlen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=connect(socket_private_handle->fd, serv_addr, addrlen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": bind error: %s", @@ -382,17 +439,28 @@ int _wapi_connect(WapiHandle *handle, const struct sockaddr *serv_addr, socklen_ return(ret); } -int _wapi_getpeername(WapiHandle *handle, struct sockaddr *name, socklen_t *namelen) +int _wapi_getpeername(guint32 handle, struct sockaddr *name, + socklen_t *namelen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { WSASetLastError(WSANOTINITIALISED); return(SOCKET_ERROR); } + + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } - ret=getpeername(socket_handle->fd, name, namelen); + ret=getpeername(socket_private_handle->fd, name, namelen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": getpeername error: %s", @@ -425,17 +493,28 @@ int _wapi_getpeername(WapiHandle *handle, struct sockaddr *name, socklen_t *name return(ret); } -int _wapi_getsockname(WapiHandle *handle, struct sockaddr *name, socklen_t *namelen) +int _wapi_getsockname(guint32 handle, struct sockaddr *name, + socklen_t *namelen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { WSASetLastError(WSANOTINITIALISED); return(SOCKET_ERROR); } + + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } - ret=getsockname(socket_handle->fd, name, namelen); + ret=getsockname(socket_private_handle->fd, name, namelen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": getsockname error: %s", @@ -465,9 +544,11 @@ int _wapi_getsockname(WapiHandle *handle, struct sockaddr *name, socklen_t *name return(ret); } -int _wapi_getsockopt(WapiHandle *handle, int level, int optname, void *optval, socklen_t *optlen) +int _wapi_getsockopt(guint32 handle, int level, int optname, void *optval, + socklen_t *optlen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -475,7 +556,17 @@ int _wapi_getsockopt(WapiHandle *handle, int level, int optname, void *optval, s return(SOCKET_ERROR); } - ret=getsockopt(socket_handle->fd, level, optname, optval, optlen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=getsockopt(socket_private_handle->fd, level, optname, optval, + optlen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": getsockopt error: %s", @@ -504,9 +595,10 @@ int _wapi_getsockopt(WapiHandle *handle, int level, int optname, void *optval, s return(ret); } -int _wapi_listen(WapiHandle *handle, int backlog) +int _wapi_listen(guint32 handle, int backlog) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -514,7 +606,16 @@ int _wapi_listen(WapiHandle *handle, int backlog) return(SOCKET_ERROR); } - ret=listen(socket_handle->fd, backlog); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=listen(socket_private_handle->fd, backlog); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": listen error: %s", @@ -543,14 +644,16 @@ int _wapi_listen(WapiHandle *handle, int backlog) return(0); } -int _wapi_recv(WapiHandle *handle, void *buf, size_t len, int recv_flags) +int _wapi_recv(guint32 handle, void *buf, size_t len, int recv_flags) { return(_wapi_recvfrom(handle, buf, len, recv_flags, NULL, 0)); } -int _wapi_recvfrom(WapiHandle *handle, void *buf, size_t len, int recv_flags, struct sockaddr *from, socklen_t *fromlen) +int _wapi_recvfrom(guint32 handle, void *buf, size_t len, int recv_flags, + struct sockaddr *from, socklen_t *fromlen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -558,7 +661,17 @@ int _wapi_recvfrom(WapiHandle *handle, void *buf, size_t len, int recv_flags, st return(SOCKET_ERROR); } - ret=recvfrom(socket_handle->fd, buf, len, recv_flags, from, fromlen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=recvfrom(socket_private_handle->fd, buf, len, recv_flags, from, + fromlen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": recv error: %s", @@ -599,9 +712,10 @@ int _wapi_recvfrom(WapiHandle *handle, void *buf, size_t len, int recv_flags, st return(ret); } -int _wapi_send(WapiHandle *handle, const void *msg, size_t len, int send_flags) +int _wapi_send(guint32 handle, const void *msg, size_t len, int send_flags) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -609,7 +723,16 @@ int _wapi_send(WapiHandle *handle, const void *msg, size_t len, int send_flags) return(SOCKET_ERROR); } - ret=send(socket_handle->fd, msg, len, send_flags); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=send(socket_private_handle->fd, msg, len, send_flags); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": send error: %s", @@ -656,9 +779,11 @@ int _wapi_send(WapiHandle *handle, const void *msg, size_t len, int send_flags) return(ret); } -int _wapi_sendto(WapiHandle *handle, const void *msg, size_t len, int send_flags, const struct sockaddr *to, socklen_t tolen) +int _wapi_sendto(guint32 handle, const void *msg, size_t len, int send_flags, + const struct sockaddr *to, socklen_t tolen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -666,7 +791,16 @@ int _wapi_sendto(WapiHandle *handle, const void *msg, size_t len, int send_flags return(SOCKET_ERROR); } - ret=sendto(socket_handle->fd, msg, len, send_flags, to, tolen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=sendto(socket_private_handle->fd, msg, len, send_flags, to, tolen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": send error: %s", @@ -713,9 +847,11 @@ int _wapi_sendto(WapiHandle *handle, const void *msg, size_t len, int send_flags return(ret); } -int _wapi_setsockopt(WapiHandle *handle, int level, int optname, const void *optval, socklen_t optlen) +int _wapi_setsockopt(guint32 handle, int level, int optname, + const void *optval, socklen_t optlen) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -723,7 +859,17 @@ int _wapi_setsockopt(WapiHandle *handle, int level, int optname, const void *opt return(SOCKET_ERROR); } - ret=setsockopt(socket_handle->fd, level, optname, optval, optlen); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=setsockopt(socket_private_handle->fd, level, optname, optval, + optlen); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": setsockopt error: %s", @@ -752,9 +898,10 @@ int _wapi_setsockopt(WapiHandle *handle, int level, int optname, const void *opt return(ret); } -int _wapi_shutdown(WapiHandle *handle, int how) +int _wapi_shutdown(guint32 handle, int how) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -762,7 +909,16 @@ int _wapi_shutdown(WapiHandle *handle, int how) return(SOCKET_ERROR); } - ret=shutdown(socket_handle->fd, how); + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(SOCKET_ERROR); + } + + ret=shutdown(socket_private_handle->fd, how); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": shutdown error: %s", @@ -788,10 +944,11 @@ int _wapi_shutdown(WapiHandle *handle, int how) return(ret); } -WapiHandle *_wapi_socket(int domain, int type, int protocol) +guint32 _wapi_socket(int domain, int type, int protocol) { - struct _WapiHandle_socket *socket_handle; - WapiHandle *handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gpointer handle; + gboolean ok; int fd; fd=socket(domain, type, protocol); @@ -803,27 +960,43 @@ WapiHandle *_wapi_socket(int domain, int type, int protocol) return(INVALID_SOCKET); } - socket_handle=g_new0(struct _WapiHandle_socket, 1); - handle=(WapiHandle *)socket_handle; + pthread_once (&socket_ops_once, socket_ops_init); + + handle=_wapi_handle_new (WAPI_HANDLE_SOCKET); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating socket handle"); + return(INVALID_SOCKET); + } + + _wapi_handle_lock_handle (handle); - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_SOCKET, socket_ops); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET, NULL, + (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(INVALID_SOCKET); + } - socket_handle->fd=fd; + socket_private_handle->fd=fd; #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": returning socket handle %p with fd %d", handle, - socket_handle->fd); + socket_private_handle->fd); #endif - return(handle); + _wapi_handle_unlock_handle (handle); + + return(GPOINTER_TO_UINT (handle)); } struct hostent *_wapi_gethostbyname(const char *hostname) { struct hostent *he; - if(startup_count==0) { WSASetLastError(WSANOTINITIALISED); return(NULL); @@ -861,9 +1034,10 @@ struct hostent *_wapi_gethostbyname(const char *hostname) return(he); } -int ioctlsocket(WapiHandle *handle, gint32 command, gpointer arg) +int ioctlsocket(guint32 handle, gint32 command, gpointer arg) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; int ret; if(startup_count==0) { @@ -871,7 +1045,11 @@ int ioctlsocket(WapiHandle *handle, gint32 command, gpointer arg) return(SOCKET_ERROR); } - if(handle->type!=WAPI_HANDLE_SOCKET) { + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); WSASetLastError(WSAENOTSOCK); return(SOCKET_ERROR); } @@ -886,7 +1064,7 @@ int ioctlsocket(WapiHandle *handle, gint32 command, gpointer arg) return(SOCKET_ERROR); } - ret=ioctl(socket_handle->fd, command, arg); + ret=ioctl(socket_private_handle->fd, command, arg); if(ret==-1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": ioctl error: %s", @@ -954,24 +1132,54 @@ int _wapi_select(int nfds G_GNUC_UNUSED, fd_set *readfds, fd_set *writefds, return(ret); } -void _wapi_FD_CLR(WapiHandle *handle, fd_set *set) +void _wapi_FD_CLR(guint32 handle, fd_set *set) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; + + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return; + } - FD_CLR(socket_handle->fd, set); + FD_CLR(socket_private_handle->fd, set); } -int _wapi_FD_ISSET(WapiHandle *handle, fd_set *set) +int _wapi_FD_ISSET(guint32 handle, fd_set *set) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; + + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return(0); + } - return(FD_ISSET(socket_handle->fd, set)); + return(FD_ISSET(socket_private_handle->fd, set)); } -void _wapi_FD_SET(WapiHandle *handle, fd_set *set) +void _wapi_FD_SET(guint32 handle, fd_set *set) { - struct _WapiHandle_socket *socket_handle=(struct _WapiHandle_socket *)handle; + struct _WapiHandlePrivate_socket *socket_private_handle; + gboolean ok; + + ok=_wapi_lookup_handle (GUINT_TO_POINTER (handle), WAPI_HANDLE_SOCKET, + NULL, (gpointer *)&socket_private_handle); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up socket handle 0x%x", handle); + WSASetLastError(WSAENOTSOCK); + return; + } - FD_SET(socket_handle->fd, set); + FD_SET(socket_private_handle->fd, set); } diff --git a/mono/io-layer/sockets.h b/mono/io-layer/sockets.h index 4033793245137..0db5bda133539 100644 --- a/mono/io-layer/sockets.h +++ b/mono/io-layer/sockets.h @@ -25,14 +25,14 @@ typedef struct guchar *lpVendorInfo; } WapiWSAData; -#define INVALID_SOCKET (WapiHandle *)-1 +#define INVALID_SOCKET (guint32)(~0) #define SOCKET_ERROR -1 extern int WSAStartup(guint32 requested, WapiWSAData *data); extern int WSACleanup(void); extern void WSASetLastError(int error); extern int WSAGetLastError(void); -extern int closesocket(WapiHandle *handle); +extern int closesocket(guint32 handle); #ifndef _WAPI_BUILDING #define accept _wapi_accept @@ -73,26 +73,39 @@ extern int closesocket(WapiHandle *handle); #endif /* _WAPI_BUILDING */ -extern WapiHandle *_wapi_accept(WapiHandle *handle, struct sockaddr *addr, socklen_t *addrlen); -extern int _wapi_bind(WapiHandle *handle, struct sockaddr *my_addr, socklen_t addrlen); -extern int _wapi_connect(WapiHandle *handle, const struct sockaddr *serv_addr, socklen_t addrlen); -extern int _wapi_getpeername(WapiHandle *handle, struct sockaddr *name, socklen_t *namelen); -extern int _wapi_getsockname(WapiHandle *handle, struct sockaddr *name, socklen_t *namelen); -extern int _wapi_getsockopt(WapiHandle *handle, int level, int optname, void *optval, socklen_t *optlen); -extern int _wapi_listen(WapiHandle *handle, int backlog); -extern int _wapi_recv(WapiHandle *handle, void *buf, size_t len, int recv_flags); -extern int _wapi_recvfrom(WapiHandle *handle, void *buf, size_t len, int recv_flags, struct sockaddr *from, socklen_t *fromlen); -extern int _wapi_send(WapiHandle *handle, const void *msg, size_t len, int send_flags); -extern int _wapi_sendto(WapiHandle *handle, const void *msg, size_t len, int send_flags, const struct sockaddr *to, socklen_t tolen); -extern int _wapi_setsockopt(WapiHandle *handle, int level, int optname, const void *optval, socklen_t optlen); -extern int _wapi_shutdown(WapiHandle *handle, int how); -extern WapiHandle *_wapi_socket(int domain, int type, int protocol);; +extern guint32 _wapi_accept(guint32 handle, struct sockaddr *addr, + socklen_t *addrlen); +extern int _wapi_bind(guint32 handle, struct sockaddr *my_addr, + socklen_t addrlen); +extern int _wapi_connect(guint32 handle, const struct sockaddr *serv_addr, + socklen_t addrlen); +extern int _wapi_getpeername(guint32 handle, struct sockaddr *name, + socklen_t *namelen); +extern int _wapi_getsockname(guint32 handle, struct sockaddr *name, + socklen_t *namelen); +extern int _wapi_getsockopt(guint32 handle, int level, int optname, + void *optval, socklen_t *optlen); +extern int _wapi_listen(guint32 handle, int backlog); +extern int _wapi_recv(guint32 handle, void *buf, size_t len, int recv_flags); +extern int _wapi_recvfrom(guint32 handle, void *buf, size_t len, + int recv_flags, struct sockaddr *from, + socklen_t *fromlen); +extern int _wapi_send(guint32 handle, const void *msg, size_t len, + int send_flags); +extern int _wapi_sendto(guint32 handle, const void *msg, size_t len, + int send_flags, const struct sockaddr *to, + socklen_t tolen); +extern int _wapi_setsockopt(guint32 handle, int level, int optname, + const void *optval, socklen_t optlen); +extern int _wapi_shutdown(guint32 handle, int how); +extern guint32 _wapi_socket(int domain, int type, int protocol);; extern struct hostent *_wapi_gethostbyname(const char *hostname); -extern int _wapi_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); -extern void _wapi_FD_CLR(WapiHandle *handle, fd_set *set); -extern int _wapi_FD_ISSET(WapiHandle *handle, fd_set *set); -extern void _wapi_FD_SET(WapiHandle *handle, fd_set *set); +extern int _wapi_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); +extern void _wapi_FD_CLR(guint32 handle, fd_set *set); +extern int _wapi_FD_ISSET(guint32 handle, fd_set *set); +extern void _wapi_FD_SET(guint32 handle, fd_set *set); -extern int ioctlsocket(WapiHandle *handle, gint32 command, gpointer arg); +extern int ioctlsocket(guint32 handle, gint32 command, gpointer arg); #endif /* _WAPI_SOCKETS_H_ */ diff --git a/mono/io-layer/thread-private.h b/mono/io-layer/thread-private.h new file mode 100644 index 0000000000000..962ce45eb8354 --- /dev/null +++ b/mono/io-layer/thread-private.h @@ -0,0 +1,28 @@ +#ifndef _WAPI_THREAD_PRIVATE_H_ +#define _WAPI_THREAD_PRIVATE_H_ + +#include +#include + +#include + +typedef enum { + THREAD_STATE_START, + THREAD_STATE_EXITED, +} WapiThreadState; + +struct _WapiHandle_thread +{ + WapiThreadState state; + TimedThread *thread; + guint32 exitstatus; + gboolean joined; + gpointer process_handle; +}; + +struct _WapiHandlePrivate_thread +{ + int dummy; +}; + +#endif /* _WAPI_THREAD_PRIVATE_H_ */ diff --git a/mono/io-layer/threads.c b/mono/io-layer/threads.c index 941e60e9f9d5c..47028c52d4df5 100644 --- a/mono/io-layer/threads.c +++ b/mono/io-layer/threads.c @@ -10,32 +10,17 @@ #include #include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" -#include "timed-thread.h" -#include "wait-private.h" -#include "handles-private.h" -#include "misc-private.h" - -#include "mono-mutex.h" +#include +#include +#include +#include +#include +#include +#include +#include #undef DEBUG - -typedef enum { - THREAD_STATE_START, - THREAD_STATE_EXITED, -} WapiThreadState; - -struct _WapiHandle_thread -{ - WapiHandle handle; - WapiThreadState state; - TimedThread *thread; - guint32 exitstatus; -}; - -static mono_mutex_t thread_signal_mutex = MONO_MUTEX_INITIALIZER; -static pthread_cond_t thread_signal_cond = PTHREAD_COND_INITIALIZER; +#undef TLS_DEBUG /* Hash threads with tids. I thought of using TLS for this, but that * would have to set the data in the new thread, which is more hassle @@ -48,10 +33,8 @@ static GHashTable *thread_hash=NULL; static MonoGHashTable *tls_gc_hash = NULL; #endif -static void thread_close(WapiHandle *handle); -static gboolean thread_wait(WapiHandle *handle, WapiHandle *signal, - guint32 ms); -static guint32 thread_wait_multiple(gpointer data); +static void thread_close(gpointer handle); +static void thread_own (gpointer handle); static struct _WapiHandleOps thread_ops = { thread_close, /* close */ @@ -64,198 +47,98 @@ static struct _WapiHandleOps thread_ops = { NULL, /* getfilesize */ NULL, /* getfiletime */ NULL, /* setfiletime */ - thread_wait, /* wait */ - thread_wait_multiple, /* wait_multiple */ NULL, /* signal */ + thread_own, /* own */ + NULL, /* is_owned */ }; -static void thread_close(WapiHandle *handle) -{ - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)handle; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": closing thread handle %p with thread %p id %ld", - thread_handle, thread_handle->thread, - thread_handle->thread->id); -#endif +static pthread_once_t thread_ops_once=PTHREAD_ONCE_INIT; - mono_mutex_destroy (&thread_handle->thread->join_mutex); - g_free(thread_handle->thread); +static void thread_ops_init (void) +{ + _wapi_handle_register_ops (WAPI_HANDLE_THREAD, &thread_ops); + _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD, + WAPI_HANDLE_CAP_WAIT); } -static gboolean thread_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms) +static void thread_close(gpointer handle) { - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)handle; - int ret; + struct _WapiHandle_thread *thread_handle; + gboolean ok; - /* A thread can never become unsignalled after it was - * signalled, so we can signal this handle now without - * worrying about lost wakeups - */ - if(signal!=NULL) { - signal->ops->signal(signal); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up thread handle %p", handle); + return; } - if(handle->signalled==TRUE) { - /* Already signalled, so return straight away */ -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread handle %p already signalled, returning now", handle); -#endif - - return(TRUE); - } - #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION - ": waiting for %d ms for thread handle %p with id %ld", ms, - thread_handle, thread_handle->thread->id); + ": closing thread handle %p with thread %p id %ld", + handle, thread_handle->thread, + thread_handle->thread->id); #endif - if(ms==INFINITE) { - ret=_wapi_timed_thread_join(thread_handle->thread, NULL, NULL); - } else { - struct timespec timeout; - - _wapi_calc_timeout(&timeout, ms); - - ret=_wapi_timed_thread_join(thread_handle->thread, &timeout, - NULL); - } - - if(ret==0) { - /* Thread joined */ - return(TRUE); - } else { - /* ret might be ETIMEDOUT for timeout, or other for error */ - return(FALSE); + if(thread_handle->thread!=NULL) { + _wapi_timed_thread_destroy (thread_handle->thread); } } -static guint32 thread_wait_multiple(gpointer data) +static void thread_own (gpointer handle) { - WaitQueueItem *item=(WaitQueueItem *)data; - int ret; - guint32 numhandles, count; - struct timespec timeout; - - numhandles=item->handles[WAPI_HANDLE_THREAD]->len; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting on %d thread handles for %d ms", numhandles, - item->timeout); -#endif - - /* First, check if any of the handles are already - * signalled. If waitall is specified we only return if all - * handles have been signalled. - */ - count=_wapi_handle_count_signalled(item, WAPI_HANDLE_THREAD); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Preliminary check found %d handles signalled", count); -#endif - - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - goto success; - } - - /* OK, we need to wait for some */ - if(item->timeout!=INFINITE) { - _wapi_calc_timeout(&timeout, item->timeout); - } + struct _WapiHandle_thread *thread_handle; + gboolean ok; - /* We can restart from here without resetting the timeout, - * because it is calculated from absolute time, not an offset - */ -again: - mono_mutex_lock(&thread_signal_mutex); - if(item->timeout==INFINITE) { - ret=mono_cond_wait(&thread_signal_cond, - &thread_signal_mutex); - } else { - ret=mono_cond_timedwait(&thread_signal_cond, - &thread_signal_mutex, - &timeout); + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up thread handle %p", handle); + return; } - mono_mutex_unlock(&thread_signal_mutex); - - if(ret==ETIMEDOUT) { - /* Check signalled state here, just in case a thread - * exited between the first check and the cond wait. - * We return the number of signalled handles, which - * may be fewer than the total. - */ -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out"); -#endif - count=_wapi_handle_count_signalled(item, WAPI_HANDLE_THREAD); - goto success; + if(thread_handle->joined==FALSE) { + _wapi_timed_thread_join (thread_handle->thread, NULL, NULL); + thread_handle->joined=TRUE; } - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Thread exited, checking status"); -#endif - - /* Another thread exited, so see if it was one we are - * interested in - */ - count=_wapi_handle_count_signalled(item, WAPI_HANDLE_THREAD); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Check after thread exit found %d handles signalled", - count); -#endif - - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - goto success; - } - - /* Either we have waitall set with more handles to wait for, - * or the thread that exited wasn't interesting to us - */ -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Waiting a bit longer"); -#endif - - goto again; - -success: - item->waited[WAPI_HANDLE_THREAD]=TRUE; - item->waitcount[WAPI_HANDLE_THREAD]=count; - - return(count); } -static void thread_exit(guint32 exitstatus, gpointer userdata) +static void thread_exit(guint32 exitstatus, gpointer handle) { - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)userdata; + struct _WapiHandle_thread *thread_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up thread handle %p", handle); + return; + } + _wapi_handle_lock_handle (handle); + thread_handle->exitstatus=exitstatus; thread_handle->state=THREAD_STATE_EXITED; - thread_handle->handle.signalled=TRUE; + _wapi_handle_set_signal_state (handle, TRUE, TRUE); + + _wapi_handle_unlock_handle (handle); #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Recording thread handle %p id %ld status as %d", - thread_handle, thread_handle->thread->id, exitstatus); + handle, thread_handle->thread->id, exitstatus); #endif /* Remove this thread from the hash */ mono_mutex_lock(&thread_hash_mutex); g_hash_table_remove(thread_hash, &thread_handle->thread->id); mono_mutex_unlock(&thread_hash_mutex); - - /* Signal any thread waiting on thread exit */ - mono_mutex_lock(&thread_signal_mutex); - pthread_cond_broadcast(&thread_signal_cond); - mono_mutex_unlock(&thread_signal_mutex); + + /* The thread is no longer active, so unref it */ + _wapi_handle_unref (handle); } static void thread_hash_init(void) @@ -279,24 +162,42 @@ static void thread_hash_init(void) * * Return value: a new handle, or NULL */ -WapiHandle *CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize G_GNUC_UNUSED, - WapiThreadStart start, gpointer param, guint32 create G_GNUC_UNUSED, - guint32 *tid) +gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize G_GNUC_UNUSED, + WapiThreadStart start, gpointer param, guint32 create G_GNUC_UNUSED, + guint32 *tid) { struct _WapiHandle_thread *thread_handle; - WapiHandle *handle; + gpointer handle; + gboolean ok; int ret; pthread_once(&thread_hash_once, thread_hash_init); + pthread_once (&thread_ops_once, thread_ops_init); if(start==NULL) { return(NULL); } - thread_handle=(struct _WapiHandle_thread *)g_new0(struct _WapiHandle_thread, 1); + handle=_wapi_handle_new (WAPI_HANDLE_THREAD); + if(handle==_WAPI_HANDLE_INVALID) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error creating thread handle"); + return(NULL); + } - handle=(WapiHandle *)thread_handle; - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_THREAD, thread_ops); + _wapi_handle_lock_handle (handle); + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up thread handle %p", handle); + _wapi_handle_unlock_handle (handle); + return(NULL); + } + + /* Hold a reference while the thread is active */ + _wapi_handle_ref (handle); thread_handle->state=THREAD_STATE_START; @@ -306,30 +207,36 @@ WapiHandle *CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 mono_mutex_lock(&thread_hash_mutex); ret=_wapi_timed_thread_create(&thread_handle->thread, NULL, start, - thread_exit, param, thread_handle); + thread_exit, param, handle); if(ret!=0) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Thread create error: %s", strerror(ret)); #endif mono_mutex_unlock(&thread_hash_mutex); - g_free(thread_handle); + _wapi_handle_unlock_handle (handle); + _wapi_handle_unref (handle); + + /* And again, because of the reference we took above */ + _wapi_handle_unref (handle); return(NULL); } g_hash_table_insert(thread_hash, &thread_handle->thread->id, - thread_handle); + handle); mono_mutex_unlock(&thread_hash_mutex); #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION - ": Started thread handle %p thread %p ID %ld", thread_handle, + ": Started thread handle %p thread %p ID %ld", handle, thread_handle->thread, thread_handle->thread->id); #endif if(tid!=NULL) { *tid=thread_handle->thread->id; } + + _wapi_handle_unlock_handle (handle); return(handle); } @@ -358,9 +265,18 @@ void ExitThread(guint32 exitcode) * * Return value: %TRUE, or %FALSE on error. */ -gboolean GetExitCodeThread(WapiHandle *handle, guint32 *exitcode) +gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode) { - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)handle; + struct _WapiHandle_thread *thread_handle; + gboolean ok; + + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle, NULL); + if(ok==FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up thread handle %p", handle); + return(FALSE); + } #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION @@ -417,9 +333,9 @@ guint32 GetCurrentThreadId(void) * (Unknown whether Windows has a possible failure here. It may be * necessary to implement the pseudohandle-constant behaviour). */ -WapiHandle *GetCurrentThread(void) +gpointer GetCurrentThread(void) { - WapiHandle *ret=NULL; + gpointer ret=NULL; guint32 tid; tid=GetCurrentThreadId(); @@ -442,7 +358,7 @@ WapiHandle *GetCurrentThread(void) * * Return value: the previous suspend count, or 0xFFFFFFFF on error. */ -guint32 ResumeThread(WapiHandle *handle G_GNUC_UNUSED) +guint32 ResumeThread(gpointer handle G_GNUC_UNUSED) { return(0xFFFFFFFF); } @@ -456,7 +372,7 @@ guint32 ResumeThread(WapiHandle *handle G_GNUC_UNUSED) * * Return value: the previous suspend count, or 0xFFFFFFFF on error. */ -guint32 SuspendThread(WapiHandle *handle G_GNUC_UNUSED) +guint32 SuspendThread(gpointer handle G_GNUC_UNUSED) { return(0xFFFFFFFF); } @@ -497,6 +413,11 @@ guint32 TlsAlloc(void) pthread_key_create(&TLS_keys[i], NULL); mono_mutex_unlock(&TLS_mutex); + +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": returning key %d", + i); +#endif return(i); } @@ -504,6 +425,11 @@ guint32 TlsAlloc(void) mono_mutex_unlock(&TLS_mutex); +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": out of indices"); +#endif + + return(TLS_OUT_OF_INDEXES); } @@ -520,6 +446,10 @@ guint32 TlsAlloc(void) */ gboolean TlsFree(guint32 idx) { +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": freeing key %d", idx); +#endif + mono_mutex_lock(&TLS_mutex); if(TLS_used[idx]==FALSE) { @@ -552,14 +482,26 @@ gpointer TlsGetValue(guint32 idx) { gpointer ret; +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": looking up key %d", idx); +#endif + mono_mutex_lock(&TLS_mutex); if(TLS_used[idx]==FALSE) { +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": key %d unused", idx); +#endif + mono_mutex_unlock(&TLS_mutex); return(NULL); } ret=pthread_getspecific(TLS_keys[idx]); + +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", ret); +#endif mono_mutex_unlock(&TLS_mutex); @@ -578,16 +520,30 @@ gpointer TlsGetValue(guint32 idx) gboolean TlsSetValue(guint32 idx, gpointer value) { int ret; + +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": setting key %d to %p", idx, + value); +#endif mono_mutex_lock(&TLS_mutex); if(TLS_used[idx]==FALSE) { +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": key %d unused", idx); +#endif + mono_mutex_unlock(&TLS_mutex); return(FALSE); } ret=pthread_setspecific(TLS_keys[idx], value); if(ret!=0) { +#ifdef TLS_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": pthread_setspecific error: %s", strerror (ret)); +#endif + mono_mutex_unlock(&TLS_mutex); return(FALSE); } diff --git a/mono/io-layer/threads.h b/mono/io-layer/threads.h index b14c81a1e183e..e10293cf43b6f 100644 --- a/mono/io-layer/threads.h +++ b/mono/io-layer/threads.h @@ -6,43 +6,25 @@ #include "mono/io-layer/handles.h" #include "mono/io-layer/io.h" #include "mono/io-layer/status.h" +#include "mono/io-layer/processes.h" #define TLS_MINIMUM_AVAILABLE 64 #define TLS_OUT_OF_INDEXES 0xFFFFFFFF #define STILL_ACTIVE STATUS_PENDING -#define DEBUG_PROCESS 0x00000001 -#define DEBUG_ONLY_THIS_PROCESS 0x00000002 -#define CREATE_SUSPENDED 0x00000004 -#define DETACHED_PROCESS 0x00000008 -#define CREATE_NEW_CONSOLE 0x00000010 -#define NORMAL_PRIORITY_CLASS 0x00000020 -#define IDLE_PRIORITY_CLASS 0x00000040 -#define HIGH_PRIORITY_CLASS 0x00000080 -#define REALTIME_PRIORITY_CLASS 0x00000100 -#define CREATE_NEW_PROCESS_GROUP 0x00000200 -#define CREATE_UNICODE_ENVIRONMENT 0x00000400 -#define CREATE_SEPARATE_WOW_VDM 0x00000800 -#define CREATE_SHARED_WOW_VDM 0x00001000 -#define CREATE_FORCEDOS 0x00002000 -#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 -#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 -#define CREATE_BREAKAWAY_FROM_JOB 0x01000000 -#define CREATE_WITH_USERPROFILE 0x02000000 -#define CREATE_DEFAULT_ERROR_MODE 0x04000000 -#define CREATE_NO_WINDOW 0x08000000 - typedef guint32 (*WapiThreadStart)(gpointer); -extern WapiHandle *CreateThread(WapiSecurityAttributes *security, guint32 stacksize, WapiThreadStart start, gpointer param, guint32 create, guint32 *tid); +extern gpointer CreateThread(WapiSecurityAttributes *security, + guint32 stacksize, WapiThreadStart start, + gpointer param, guint32 create, guint32 *tid); extern void ExitThread(guint32 exitcode) G_GNUC_NORETURN; -extern gboolean GetExitCodeThread(WapiHandle *handle, guint32 *exitcode); +extern gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode); extern guint32 GetCurrentThreadId(void); -extern WapiHandle *GetCurrentThread(void); -extern guint32 ResumeThread(WapiHandle *handle); -extern guint32 SuspendThread(WapiHandle *handle); +extern gpointer GetCurrentThread(void); +extern guint32 ResumeThread(gpointer handle); +extern guint32 SuspendThread(gpointer handle); extern guint32 TlsAlloc(void); extern gboolean TlsFree(guint32 idx); extern gpointer TlsGetValue(guint32 idx); diff --git a/mono/io-layer/timed-thread.c b/mono/io-layer/timed-thread.c index 2464beb5fcd8a..7fdf89322f880 100644 --- a/mono/io-layer/timed-thread.c +++ b/mono/io-layer/timed-thread.c @@ -142,3 +142,10 @@ int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout, return(result); } +void _wapi_timed_thread_destroy (TimedThread *thread) +{ + mono_mutex_destroy (&thread->join_mutex); + pthread_cond_destroy (&thread->exit_cond); + + g_free(thread); +} diff --git a/mono/io-layer/timed-thread.h b/mono/io-layer/timed-thread.h index 683472317e38e..ed630ae56d0d7 100644 --- a/mono/io-layer/timed-thread.h +++ b/mono/io-layer/timed-thread.h @@ -29,5 +29,6 @@ extern int _wapi_timed_thread_create(TimedThread **threadp, extern int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout, guint32 *exitstatus); +extern void _wapi_timed_thread_destroy (TimedThread *thread); #endif /* _WAPI_TIMED_THREAD_H_ */ diff --git a/mono/io-layer/uglify.h b/mono/io-layer/uglify.h index 4b2579fc462fb..a4211e06b1d5e 100644 --- a/mono/io-layer/uglify.h +++ b/mono/io-layer/uglify.h @@ -7,8 +7,10 @@ #include "mono/io-layer/wapi.h" -typedef const guchar *LPCTSTR; /* replace this with gunichar */ +typedef const gunichar2 *LPCTSTR; +typedef gunichar2 *LPTSTR; typedef guint8 BYTE; +typedef guint8 *LPBYTE; typedef guint16 WORD; typedef guint32 DWORD; typedef gpointer PVOID; @@ -21,9 +23,9 @@ typedef gint32 *PLONG; typedef guint64 LONGLONG; typedef gunichar2 TCHAR; -typedef WapiHandle *HANDLE; -typedef WapiHandle **LPHANDLE; -typedef WapiHandle *SOCKET; /* NB: w32 defines this to be int */ +typedef gpointer HANDLE; +typedef gpointer *LPHANDLE; +typedef guint32 SOCKET; typedef WapiSecurityAttributes *LPSECURITY_ATTRIBUTES; typedef WapiOverlapped *LPOVERLAPPED; typedef WapiThreadStart LPTHREAD_START_ROUTINE; @@ -47,6 +49,9 @@ typedef WapiFindData WIN32_FIND_DATA; typedef WapiFindData *LPWIN32_FIND_DATA; typedef WapiFileAttributesData WIN32_FILE_ATTRIBUTE_DATA; typedef WapiGetFileExInfoLevels GET_FILEEX_INFO_LEVELS; +typedef WapiStartupInfo STARTUPINFO; +typedef WapiStartupInfo *LPSTARTUPINFO; +typedef WapiProcessInformation PROCESS_INFORMATION; #define CONST const #define VOID void diff --git a/mono/io-layer/wait-private.h b/mono/io-layer/wait-private.h index 1f96f7c2b8ebb..2291c98527106 100644 --- a/mono/io-layer/wait-private.h +++ b/mono/io-layer/wait-private.h @@ -14,29 +14,4 @@ #include "mono-mutex.h" -typedef enum { - WQ_NEW, - WQ_WAITING, - WQ_SIGNALLED, -} WaitQueueState; - -typedef struct _WaitQueueItem -{ - mono_mutex_t mutex; - sem_t wait_sem; - WaitQueueState state; - guint32 update, ack; - guint32 timeout; - gboolean waitall; - GPtrArray *handles[WAPI_HANDLE_COUNT]; - TimedThread *thread[WAPI_HANDLE_COUNT]; - gboolean waited[WAPI_HANDLE_COUNT]; - guint32 waitcount[WAPI_HANDLE_COUNT]; - /* waitindex must be kept synchronised with the handles array, - * such that index n of one matches index n of the other - */ - GArray *waitindex[WAPI_HANDLE_COUNT]; - guint32 lowest_signal; -} WaitQueueItem; - #endif /* _WAPI_WAIT_PRIVATE_H_ */ diff --git a/mono/io-layer/wait.c b/mono/io-layer/wait.c index daf48a52998a4..dfe0a8c157e21 100644 --- a/mono/io-layer/wait.c +++ b/mono/io-layer/wait.c @@ -1,371 +1,252 @@ #include #include #include +#include #if HAVE_BOEHM_GC #include #endif -#include "mono/io-layer/wapi.h" -#include "wait-private.h" -#include "timed-thread.h" -#include "handles-private.h" -#include "wapi-private.h" - -#include "mono-mutex.h" +#include +#include +#include +#include +#include #undef DEBUG -static pthread_once_t wait_once=PTHREAD_ONCE_INIT; - -static GPtrArray *WaitQueue=NULL; - -static pthread_t wait_monitor_thread_id; -static gboolean wait_monitor_thread_running=FALSE; -static mono_mutex_t wait_monitor_mutex=MONO_MUTEX_INITIALIZER; -static sem_t wait_monitor_sem; - -static void launch_tidy(guint32 exitcode G_GNUC_UNUSED, gpointer user) -{ - WaitQueueItem *item=(WaitQueueItem *)user; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Informing monitor thread"); -#endif - - /* Update queue item */ - mono_mutex_lock(&item->mutex); - item->update++; - mono_mutex_unlock(&item->mutex); - - /* Signal monitor */ - sem_post(&wait_monitor_sem); -} - -/* This function is called by the monitor thread to launch handle-type - * specific threads to block in particular ways. +/** + * WaitForSingleObject: + * @handle: an object to wait for + * @timeout: the maximum time in milliseconds to wait for * - * The item mutex is held by the monitor thread when this function is - * called. + * This function returns when either @handle is signalled, or @timeout + * ms elapses. If @timeout is zero, the object's state is tested and + * the function returns immediately. If @timeout is %INFINITE, the + * function waits forever. + * + * Return value: %WAIT_ABANDONED - @handle is a mutex that was not + * released by the owning thread when it exited. Ownership of the + * mutex object is granted to the calling thread and the mutex is set + * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is + * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and + * @handle's state is still not signalled. %WAIT_FAILED - an error + * occurred. */ -static void launch_blocker_threads(WaitQueueItem *item) +guint32 WaitForSingleObject(gpointer handle, guint32 timeout) { - int i, ret; + guint32 ret, waited; + struct timespec abstime; + if(_wapi_handle_test_capabilities (handle, + WAPI_HANDLE_CAP_WAIT)==FALSE) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Launching blocker threads"); + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p can't be waited for", handle); #endif - for(i=0; ihandles[i]->len>0) { - WapiHandle *handle; + return(WAIT_FAILED); + } + + _wapi_handle_lock_handle (handle); - handle=g_ptr_array_index(item->handles[i], 0); - g_assert(handle!=NULL); - g_assert(handle->ops->wait_multiple!=NULL); - + if(_wapi_handle_test_capabilities (handle, + WAPI_HANDLE_CAP_OWN)==TRUE) { + if(_wapi_handle_ops_isowned (handle)==TRUE) { #ifdef DEBUG - g_message("Handle type %d active", i); + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p already owned", handle); #endif - item->waited[i]=FALSE; - - ret=_wapi_timed_thread_create( - &item->thread[i], NULL, - handle->ops->wait_multiple, launch_tidy, item, - item); - if(ret!=0) { - g_warning(G_GNUC_PRETTY_FUNCTION - ": Thread create error: %s", - strerror(ret)); - return; - } - } else { - /* Pretend to have already waited for the - * thread; it makes life easier for the - * monitor thread. - */ - item->waited[i]=TRUE; + _wapi_handle_ops_own (handle); + ret=WAIT_OBJECT_0; + goto done; } } -} - -static gboolean launch_threads_done(WaitQueueItem *item) -{ - int i; - for(i=0; iwaited[i]==FALSE) { - return(FALSE); - } - } + if(_wapi_handle_issignalled (handle)) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p already signalled", handle); +#endif - return(TRUE); -} + _wapi_handle_ops_own (handle); + ret=WAIT_OBJECT_0; + goto done; + } -/* This is the main loop for the monitor thread. It waits for a - * signal to check the wait queue, then takes any action necessary on - * any queue items that have indicated readiness. - */ -static void *wait_monitor_thread(void *unused G_GNUC_UNUSED) -{ - guint i; - - /* Signal that the monitor thread is ready */ - wait_monitor_thread_running=TRUE; + /* Have to wait for it */ + if(timeout!=INFINITE) { + _wapi_calc_timeout (&abstime, timeout); + } - while(TRUE) { - /* Use a semaphore rather than a cond so we dont miss - * any signals - */ - sem_wait(&wait_monitor_sem); - + do { + if(timeout==INFINITE) { + waited=_wapi_handle_wait_signal_handle (handle); + } else { + waited=_wapi_handle_timedwait_signal_handle (handle, + &abstime); + } + + if(waited==0) { + /* Condition was signalled, so hopefully + * handle is signalled now. (It might not be + * if someone else got in before us.) + */ + if(_wapi_handle_issignalled (handle)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Blocking thread doing stuff"); + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p signalled", handle); #endif - - /* We've been signalled, so scan the wait queue for - * activity. - */ - mono_mutex_lock(&wait_monitor_mutex); - for(i=0; ilen; i++) { - WaitQueueItem *item=g_ptr_array_index(WaitQueue, i); - - if(item->update > item->ack) { - /* Something changed */ - mono_mutex_lock(&item->mutex); - item->ack=item->update; - - switch(item->state) { - case WQ_NEW: - /* Launch a new thread for each type of - * handle to be waited for here. - */ - launch_blocker_threads(item); - - item->state=WQ_WAITING; - break; - - case WQ_WAITING: - /* See if we have waited for - * the last blocker thread. - */ - if(launch_threads_done(item)) { - /* All handles have - * been signalled, so - * signal the waiting - * thread. Let the - * waiting thread - * remove this item - * from the queue, - * because it makes - * the logic a lot - * easier here. - */ - item->state=WQ_SIGNALLED; - sem_post(&item->wait_sem); - } - break; - - case WQ_SIGNALLED: - /* This shouldn't happen. Prod - * the blocking thread again - * just to make sure. - */ - g_warning(G_GNUC_PRETTY_FUNCTION - ": Prodding blocker again"); - sem_post(&item->wait_sem); - break; - } - - mono_mutex_unlock(&item->mutex); + + _wapi_handle_ops_own (handle); + ret=WAIT_OBJECT_0; + goto done; } + + /* Better luck next time */ } + } while(waited==0); - mono_mutex_unlock(&wait_monitor_mutex); - } - - return(NULL); -} - -static void wait_init(void) -{ - int ret; - + /* Timeout or other error */ #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Starting monitor thread"); + g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s", + handle, strerror (ret)); #endif - - WaitQueue=g_ptr_array_new(); - - sem_init(&wait_monitor_sem, 0, 0); - - /* Launch a thread which manages the wait queue, and deals - * with waiting for handles of various types. - */ - ret=pthread_create(&wait_monitor_thread_id, NULL, - wait_monitor_thread, NULL); - if(ret!=0) { - g_warning(G_GNUC_PRETTY_FUNCTION - ": Couldn't start handle monitor thread: %s", - strerror(ret)); - } - - /* Wait for the monitor thread to get going */ - while(wait_monitor_thread_running==FALSE) { - sched_yield(); - } -} -static WaitQueueItem *wait_item_new(guint32 timeout, gboolean waitall) -{ - WaitQueueItem *new; - int i; - - new=g_new0(WaitQueueItem, 1); - - mono_mutex_init(&new->mutex, NULL); - sem_init(&new->wait_sem, 0, 0); - - new->update=1; /* As soon as this item is queued it - * will need attention. - */ - new->state=WQ_NEW; - new->timeout=timeout; - new->waitall=waitall; - new->lowest_signal=MAXIMUM_WAIT_OBJECTS; + ret=WAIT_TIMEOUT; - for(i=0; ihandles[i]=g_ptr_array_new(); - new->waitindex[i]=g_array_new(FALSE, FALSE, sizeof(guint32)); - } - - return(new); +done: + _wapi_handle_unlock_handle (handle); + return(ret); } -/* Adds our queue item to the work queue, and blocks until the monitor - * thread thinks it's done the work. Returns TRUE for done, FALSE for - * timed out. Sets lowest to the index of the first signalled handle - * in the list. +/** + * SignalObjectAndWait: + * @signal_handle: An object to signal + * @wait: An object to wait for + * @timeout: The maximum time in milliseconds to wait for + * @alertable: Specifies whether the function returnes when the system + * queues an I/O completion routine or an APC for the calling thread. + * + * Atomically signals @signal and waits for @wait to become signalled, + * or @timeout ms elapses. If @timeout is zero, the object's state is + * tested and the function returns immediately. If @timeout is + * %INFINITE, the function waits forever. + * + * @signal can be a semaphore, mutex or event object. + * + * If @alertable is %TRUE and the system queues an I/O completion + * routine or an APC for the calling thread, the function returns and + * the thread calls the completion routine or APC function. If + * %FALSE, the function does not return, and the thread does not call + * the completion routine or APC function. A completion routine is + * queued when the ReadFileEx() or WriteFileEx() function in which it + * was specified has completed. The calling thread is the thread that + * initiated the read or write operation. An APC is queued when + * QueueUserAPC() is called. Currently completion routines and APC + * functions are not supported. + * + * Return value: %WAIT_ABANDONED - @wait is a mutex that was not + * released by the owning thread when it exited. Ownershop of the + * mutex object is granted to the calling thread and the mutex is set + * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one + * or more user-mode asynchronous procedure calls queued to the + * thread. %WAIT_OBJECT_0 - The state of @wait is signalled. + * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is + * still not signalled. %WAIT_FAILED - an error occurred. */ -static gboolean wait_for_item(WaitQueueItem *item, guint32 *lowest) +guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait, + guint32 timeout, gboolean alertable) { - gboolean ret; - int i; - - /* Add the wait item to the monitor queue, and signal the - * monitor thread */ - mono_mutex_lock(&wait_monitor_mutex); - g_ptr_array_add(WaitQueue, item); - sem_post(&wait_monitor_sem); - mono_mutex_unlock(&wait_monitor_mutex); + guint32 ret, waited; + struct timespec abstime; - /* Wait for the item to become ready */ - sem_wait(&item->wait_sem); - - mono_mutex_lock(&item->mutex); + if(_wapi_handle_test_capabilities (signal_handle, + WAPI_HANDLE_CAP_SIGNAL)==FALSE) { + return(WAIT_FAILED); + } - /* If waitall is TRUE, then the number signalled in each handle type - * must be the length of the handle type array for the wait to be - * successful. Otherwise, any signalled handle is good enough - */ - if(item->waitall==TRUE) { - ret=TRUE; - for(i=0; iwaitcount[i]!=item->handles[i]->len) { - ret=FALSE; - break; - } - } - } else { - ret=FALSE; - for(i=0; iwaitcount[i]>0) { - ret=TRUE; - break; - } - } + if(_wapi_handle_test_capabilities (wait, + WAPI_HANDLE_CAP_WAIT)==FALSE) { + return(WAIT_FAILED); } - - *lowest=item->lowest_signal; - mono_mutex_unlock(&item->mutex); - - return(ret); -} + _wapi_handle_lock_handle (wait); -static gboolean wait_dequeue_item(WaitQueueItem *item) -{ - gboolean ret; - - g_assert(WaitQueue!=NULL); - - mono_mutex_lock(&wait_monitor_mutex); - ret=g_ptr_array_remove_fast(WaitQueue, item); - mono_mutex_unlock(&wait_monitor_mutex); - - return(ret); -} + _wapi_handle_ops_signal (signal_handle); -static void wait_item_destroy(WaitQueueItem *item) -{ - int i; - - mono_mutex_destroy(&item->mutex); - sem_destroy(&item->wait_sem); - - for(i=0; ithread[i]!=NULL) { - g_free(item->thread[i]); + if(_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) { + if(_wapi_handle_ops_isowned (wait)==TRUE) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p already owned", wait); +#endif + _wapi_handle_ops_own (wait); + ret=WAIT_OBJECT_0; + goto done; } - g_ptr_array_free(item->handles[i], FALSE); - g_array_free(item->waitindex[i], FALSE); } -} + + if(_wapi_handle_issignalled (wait)) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p already signalled", wait); +#endif + _wapi_handle_ops_own (wait); + ret=WAIT_OBJECT_0; + goto done; + } -/** - * WaitForSingleObject: - * @handle: an object to wait for - * @timeout: the maximum time in milliseconds to wait for - * - * This function returns when either @handle is signalled, or @timeout - * ms elapses. If @timeout is zero, the object's state is tested and - * the function returns immediately. If @timeout is %INFINITE, the - * function waits forever. - * - * Return value: %WAIT_ABANDONED - @handle is a mutex that was not - * released by the owning thread when it exited. Ownership of the - * mutex object is granted to the calling thread and the mutex is set - * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is - * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and - * @handle's state is still not signalled. %WAIT_FAILED - an error - * occurred. - */ -guint32 WaitForSingleObject(WapiHandle *handle, guint32 timeout) -{ - gboolean wait; - - if(handle->ops->wait==NULL) { - return(WAIT_FAILED); + /* Have to wait for it */ + if(timeout!=INFINITE) { + _wapi_calc_timeout (&abstime, timeout); } + + do { + if(timeout==INFINITE) { + waited=_wapi_handle_wait_signal_handle (wait); + } else { + waited=_wapi_handle_timedwait_signal_handle (wait, + &abstime); + } - wait=handle->ops->wait(handle, NULL, timeout); - if(wait==TRUE) { - /* Object signalled before timeout expired */ + if(waited==0) { + /* Condition was signalled, so hopefully + * handle is signalled now. (It might not be + * if someone else got in before us.) + */ + if(_wapi_handle_issignalled (wait)) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Object %p (type %d) signalled", handle, - handle->type); + g_message (G_GNUC_PRETTY_FUNCTION + ": handle %p signalled", wait); #endif - return(WAIT_OBJECT_0); - } else { + + _wapi_handle_ops_own (wait); + ret=WAIT_OBJECT_0; + goto done; + } + + /* Better luck next time */ + } + } while(waited==0); + + /* Timeout or other error */ #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Object %p wait timed out", - handle); + g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s", + wait, strerror (ret)); #endif - return(WAIT_TIMEOUT); + + ret=WAIT_TIMEOUT; + +done: + _wapi_handle_unlock_handle (wait); + + if(alertable==TRUE) { + /* Deal with queued APC or IO completion routines */ } + + return(ret); } /** @@ -396,17 +277,15 @@ guint32 WaitForSingleObject(WapiHandle *handle, guint32 timeout) * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in * @handles are signalled. %WAIT_FAILED - an error occurred. */ -guint32 WaitForMultipleObjects(guint32 numobjects, WapiHandle **handles, +guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles, gboolean waitall, guint32 timeout) { - WaitQueueItem *item; GHashTable *dups; - gboolean duplicate=FALSE, bogustype=FALSE; - gboolean wait; + gboolean duplicate=FALSE, bogustype=FALSE, done; + guint32 count, lowest; + struct timespec abstime; guint i; - guint32 lowest; - - pthread_once(&wait_once, wait_init); + guint32 ret; if(numobjects>MAXIMUM_WAIT_OBJECTS) { #ifdef DEBUG @@ -431,11 +310,11 @@ guint32 WaitForMultipleObjects(guint32 numobjects, WapiHandle **handles, break; } - if(handles[i]->ops->wait_multiple==NULL) { + if(_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT)==FALSE) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Handle %p can't be waited for (type %d)", - handles[i], handles[i]->type); + g_message (G_GNUC_PRETTY_FUNCTION + ": Handle %p can't be waited for", + handles[i]); #endif bogustype=TRUE; @@ -462,23 +341,66 @@ guint32 WaitForMultipleObjects(guint32 numobjects, WapiHandle **handles, return(WAIT_FAILED); } - - item=wait_item_new(timeout, waitall); - /* Sort the handles by type */ - for(i=0; ihandles[handles[i]->type], handles[i]); - g_array_append_val(item->waitindex[handles[i]->type], i); + done=_wapi_handle_count_signalled_handles (numobjects, handles, + waitall, &count, &lowest); + if(done==TRUE) { + for(i=0; i #include "mono/io-layer/handles.h" +#include "mono/io-layer/io.h" + +/* Increment this whenever an incompatible change is made to the + * shared handle structure. + * + * If this ever reaches 255, we have problems :-( + */ +#define _WAPI_HANDLE_VERSION 1 typedef enum { + WAPI_HANDLE_UNUSED=0, WAPI_HANDLE_FILE, WAPI_HANDLE_CONSOLE, WAPI_HANDLE_THREAD, @@ -14,63 +23,134 @@ typedef enum { WAPI_HANDLE_MUTEX, WAPI_HANDLE_EVENT, WAPI_HANDLE_SOCKET, + WAPI_HANDLE_FIND, + WAPI_HANDLE_PROCESS, WAPI_HANDLE_COUNT, - WAPI_HANDLE_FIND } WapiHandleType; +typedef enum { + WAPI_HANDLE_CAP_WAIT=0x01, + WAPI_HANDLE_CAP_SIGNAL=0x02, + WAPI_HANDLE_CAP_OWN=0x04, +} WapiHandleCapability; + struct _WapiHandleOps { /* All handle types */ - void (*close)(WapiHandle *handle); + void (*close)(gpointer handle); /* File, console and pipe handles */ WapiFileType (*getfiletype)(void); /* File and console handles */ - gboolean (*readfile)(WapiHandle *handle, gpointer buffer, + gboolean (*readfile)(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, WapiOverlapped *overlapped); - gboolean (*writefile)(WapiHandle *handle, gconstpointer buffer, + gboolean (*writefile)(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, WapiOverlapped *overlapped); - gboolean (*flushfile)(WapiHandle *handle); + gboolean (*flushfile)(gpointer handle); /* File handles */ - guint32 (*seek)(WapiHandle *handle, gint32 movedistance, + guint32 (*seek)(gpointer handle, gint32 movedistance, gint32 *highmovedistance, WapiSeekMethod method); - gboolean (*setendoffile)(WapiHandle *handle); - guint32 (*getfilesize)(WapiHandle *handle, guint32 *highsize); - gboolean (*getfiletime)(WapiHandle *handle, WapiFileTime *create_time, + gboolean (*setendoffile)(gpointer handle); + guint32 (*getfilesize)(gpointer handle, guint32 *highsize); + gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time, WapiFileTime *last_access, WapiFileTime *last_write); - gboolean (*setfiletime)(WapiHandle *handle, + gboolean (*setfiletime)(gpointer handle, const WapiFileTime *create_time, const WapiFileTime *last_access, const WapiFileTime *last_write); - /* WaitForSingleObject */ - gboolean (*wait)(WapiHandle *handle, WapiHandle *signal, guint32 ms); + /* SignalObjectAndWait */ + void (*signal)(gpointer signal); - /* WaitForMultipleObjects */ - guint32 (*wait_multiple)(gpointer data); + /* Called by WaitForSingleObject and WaitForMultipleObjects, + * with the handle locked + */ + void (*own_handle)(gpointer handle); - /* SignalObjectAndWait */ - void (*signal)(WapiHandle *signal); + /* Called by WaitForSingleObject and WaitForMultipleObjects, if the + * handle in question is "ownable" (ie mutexes), to see if the current + * thread already owns this handle + */ + gboolean (*is_owned)(gpointer handle); }; -struct _WapiHandle +#include +#include +#include +#include +#include +#include +#include + +struct _WapiHandleShared { WapiHandleType type; guint ref; gboolean signalled; - struct _WapiHandleOps *ops; + mono_mutex_t signal_mutex; + pthread_cond_t signal_cond; + + union + { + struct _WapiHandle_event event; + struct _WapiHandle_file file; + struct _WapiHandle_find find; + struct _WapiHandle_mutex mutex; + struct _WapiHandle_sem sem; + struct _WapiHandle_socket sock; + struct _WapiHandle_thread thread; + struct _WapiHandle_process process; + } u; +}; + +#define _WAPI_MAX_HANDLES 4096 +#define _WAPI_HANDLE_INVALID (gpointer)-1 + +/* + * This is the layout of the shared memory segment + */ +struct _WapiHandleShared_list +{ +#ifdef _POSIX_THREAD_PROCESS_SHARED + mono_mutex_t signal_mutex; + pthread_cond_t signal_cond; +#endif + guint32 lock; + struct _WapiHandleShared handles[_WAPI_MAX_HANDLES]; + guchar scratch_base[0]; +}; + +struct _WapiHandlePrivate +{ + union + { + struct _WapiHandlePrivate_event event; + struct _WapiHandlePrivate_file file; + struct _WapiHandlePrivate_find find; + struct _WapiHandlePrivate_mutex mutex; + struct _WapiHandlePrivate_sem sem; + struct _WapiHandlePrivate_socket sock; + struct _WapiHandlePrivate_thread thread; + struct _WapiHandlePrivate_process process; + } u; +}; + +/* Per-process handle info. For lookup convenience, each index matches + * the corresponding shared data. + */ +struct _WapiHandlePrivate_list +{ +#ifndef _POSIX_THREAD_PROCESS_SHARED + mono_mutex_t signal_mutex; + pthread_cond_t signal_cond; +#endif + struct _WapiHandlePrivate handles[_WAPI_MAX_HANDLES]; }; -#define _WAPI_HANDLE_INIT(_handle, _type, _ops) G_STMT_START {\ - _handle->type=_type;\ - _handle->ref=1;\ - _handle->signalled=FALSE;\ - _handle->ops=&_ops;\ - } G_STMT_END; #endif /* _WAPI_PRIVATE_H_ */ diff --git a/mono/io-layer/wapi.h b/mono/io-layer/wapi.h index e0a0d2aed6a80..f6c99396bf318 100644 --- a/mono/io-layer/wapi.h +++ b/mono/io-layer/wapi.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/mono/jit/ChangeLog b/mono/jit/ChangeLog index 520e8191bc50b..135af7d0daffc 100644 --- a/mono/jit/ChangeLog +++ b/mono/jit/ChangeLog @@ -1,3 +1,8 @@ +2002-04-30 Dick Porter + + * mono.c (main): Tell glib to not abort when g_log() etc print + recursively + 2002-04-30 Dietmar Maurer * x86.brg: opt. LDELEMA impl. diff --git a/mono/jit/mono.c b/mono/jit/mono.c index 29968fca67eb8..59fec4710f3f2 100644 --- a/mono/jit/mono.c +++ b/mono/jit/mono.c @@ -82,6 +82,9 @@ main (int argc, char *argv []) mono_end_of_stack = &stack; /* a pointer to a local variable is always < BP */ + g_log_set_always_fatal (G_LOG_LEVEL_ERROR); + g_log_set_fatal_mask (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR); + if (argc < 2) usage (argv [0]); diff --git a/mono/metadata/ChangeLog b/mono/metadata/ChangeLog index 507788e88811a..d9de61fc23157 100644 --- a/mono/metadata/ChangeLog +++ b/mono/metadata/ChangeLog @@ -1,3 +1,15 @@ +2002-04-30 Dick Porter + + * socket-io.c: Cope with SOCKET being an integer rather than a + pointer now. + + * threads.c: Added Thread_free_internal, to deal with thread + handle cleanup. Moved calls to handle_store() and handle_remove() + to start_wrapper(), so each can only be called once. Allocate + synchronisation blocks with GC_malloc(), and use GC finalisation + to close the handles. + + * icall.c: added System.Threading.Thread::Thread_free_internal Mon Apr 29 15:33:27 CEST 2002 Paolo Molaro diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c index 3faeaf2f0e429..42ba1e783a0a7 100644 --- a/mono/metadata/icall.c +++ b/mono/metadata/icall.c @@ -2275,6 +2275,7 @@ static gconstpointer icall_map [] = { * System.Threading */ "System.Threading.Thread::Thread_internal", ves_icall_System_Threading_Thread_Thread_internal, + "System.Threading.Thread::Thread_free_internal", ves_icall_System_Threading_Thread_Thread_free_internal, "System.Threading.Thread::Start_internal", ves_icall_System_Threading_Thread_Start_internal, "System.Threading.Thread::Sleep_internal", ves_icall_System_Threading_Thread_Sleep_internal, "System.Threading.Thread::CurrentThread_internal", ves_icall_System_Threading_Thread_CurrentThread_internal, diff --git a/mono/metadata/socket-io.c b/mono/metadata/socket-io.c index 1e84cba064535..9f290ec5f9fff 100644 --- a/mono/metadata/socket-io.c +++ b/mono/metadata/socket-io.c @@ -455,7 +455,7 @@ static MonoException *get_socket_exception(guint32 error_code) return(ex); } -SOCKET ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this, gint32 family, gint32 type, gint32 proto) +gpointer ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this, gint32 family, gint32 type, gint32 proto) { SOCKET sock; gint32 sock_family; @@ -498,7 +498,7 @@ SOCKET ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this, gin return(NULL); } - return(sock); + return(GUINT_TO_POINTER (sock)); } void ves_icall_System_Net_Sockets_Socket_Close_internal(SOCKET sock) @@ -536,7 +536,7 @@ void ves_icall_System_Net_Sockets_Socket_Blocking_internal(SOCKET sock, } } -SOCKET ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock) +gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock) { SOCKET newsock; @@ -546,7 +546,7 @@ SOCKET ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock) return(NULL); } - return(newsock); + return(GUINT_TO_POINTER (newsock)); } void ves_icall_System_Net_Sockets_Socket_Listen_internal(SOCKET sock, diff --git a/mono/metadata/socket-io.h b/mono/metadata/socket-io.h index e256127db8009..43499b19fed82 100644 --- a/mono/metadata/socket-io.h +++ b/mono/metadata/socket-io.h @@ -131,12 +131,12 @@ typedef enum { SocketOptionName_ChecksumCoverage=20, } MonoSocketOptionName; -extern SOCKET ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this, gint32 family, gint32 type, gint32 proto); +extern gpointer ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this, gint32 family, gint32 type, gint32 proto); extern void ves_icall_System_Net_Sockets_Socket_Close_internal(SOCKET sock); extern gint32 ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal(void); extern gint32 ves_icall_System_Net_Sockets_Socket_Available_internal(SOCKET sock); extern void ves_icall_System_Net_Sockets_Socket_Blocking_internal(SOCKET sock, gboolean block); -extern SOCKET ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock); +extern gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock); extern void ves_icall_System_Net_Sockets_Socket_Listen_internal(SOCKET sock, guint32 backlog); extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SOCKET sock); extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(SOCKET sock); diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 6ab46d24d3294..d8d99f9d21575 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -18,6 +18,10 @@ #include #include +#if HAVE_BOEHM_GC +#include +#endif + #undef THREAD_DEBUG #undef THREAD_LOCK_DEBUG #undef THREAD_WAIT_DEBUG @@ -57,12 +61,55 @@ static guint32 slothash_key; /* Spin lock for InterlockedXXX 64 bit functions */ static CRITICAL_SECTION interlocked_mutex; + +/* handle_store() and handle_remove() manage the array of threads that + * still need to be waited for when the main thread exits. + */ +static void handle_store(HANDLE thread) +{ +#ifdef THREAD_DEBUG + g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread); +#endif + + EnterCriticalSection(&threads_mutex); + if(threads==NULL) { + threads=g_ptr_array_new(); + } + g_ptr_array_add(threads, thread); + LeaveCriticalSection(&threads_mutex); +} + +static void handle_remove(HANDLE thread) +{ +#ifdef THREAD_DEBUG + g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread); +#endif + + EnterCriticalSection(&threads_mutex); + g_ptr_array_remove_fast(threads, thread); + LeaveCriticalSection(&threads_mutex); + + /* Don't close the handle here, wait for the object finalizer + * to do it. Otherwise, the following race condition applies: + * + * 1) Thread exits (and handle_remove() closes the handle) + * + * 2) Some other handle is reassigned the same slot + * + * 3) Another thread tries to join the first thread, and + * blocks waiting for the reassigned handle to be signalled + * (which might never happen). This is possible, because the + * thread calling Join() still has a reference to the first + * thread's object. + */ +} static guint32 start_wrapper(void *data) { struct StartInfo *start_info=(struct StartInfo *)data; guint32 (*start_func)(void *); void *this; + HANDLE thread; #ifdef THREAD_DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper"); @@ -79,41 +126,23 @@ static guint32 start_wrapper(void *data) mono_domain_set (start_info->domain); this = start_info->this; g_free (start_info); + + thread=GetCurrentThread (); + handle_store(thread); + mono_profiler_thread_start (thread); + start_func (this); #ifdef THREAD_DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating"); #endif + + mono_profiler_thread_end (thread); + handle_remove (thread); return(0); } - -static void handle_store(HANDLE thread) -{ -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread); -#endif - - EnterCriticalSection(&threads_mutex); - if(threads==NULL) { - threads=g_ptr_array_new(); - } - g_ptr_array_add(threads, thread); - LeaveCriticalSection(&threads_mutex); -} - -static void handle_remove(HANDLE thread) -{ -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread); -#endif - - EnterCriticalSection(&threads_mutex); - g_ptr_array_remove_fast(threads, thread); - LeaveCriticalSection(&threads_mutex); - CloseHandle(thread); -} MonoObject * mono_thread_create (MonoDomain *domain, gpointer func) @@ -133,16 +162,13 @@ mono_thread_create (MonoDomain *domain, gpointer func) start_info->func = func; start_info->obj = thread; start_info->domain = domain; + /* start_info->this needs to be set? */ thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid); g_assert (thread_handle); *(gpointer *)(((char *)thread) + field->offset) = thread_handle; - /* - * This thread is not added to the threads array: why? - */ - mono_profiler_thread_start (thread_handle); return thread; } @@ -184,18 +210,25 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this, } #ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d", - tid); + g_message(G_GNUC_PRETTY_FUNCTION + ": Started thread ID %d (handle %p)", tid, thread); #endif - - /* Store handle for cleanup later */ - handle_store(thread); - mono_profiler_thread_start (thread); return(thread); } } +void ves_icall_System_Threading_Thread_Thread_free_internal (MonoObject *this, + HANDLE thread) +{ +#ifdef THREAD_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p", + this, thread); +#endif + + CloseHandle (thread); +} + void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this, HANDLE thread) { @@ -227,6 +260,11 @@ MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void) /* Find the current thread object */ thread=TlsGetValue(current_object_key); + +#ifdef THREAD_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread); +#endif + return(thread); } @@ -238,16 +276,24 @@ gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this, if(ms== -1) { ms=INFINITE; } +#ifdef THREAD_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms", + thread, ms); +#endif ret=WaitForSingleObject(thread, ms); if(ret==WAIT_OBJECT_0) { - /* is the handle still valid at this point? */ - mono_profiler_thread_end (thread); - /* Clean up the handle */ - handle_remove(thread); +#ifdef THREAD_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": join successful"); +#endif + return(TRUE); } +#ifdef THREAD_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": join failed"); +#endif + return(FALSE); } @@ -274,20 +320,39 @@ MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void) return(data); } +static void mon_finalize (void *o, void *unused) +{ + MonoThreadsSync *mon=(MonoThreadsSync *)o; + +#ifdef THREAD_LOCK_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync"); +#endif + + CloseHandle (mon->monitor); + CloseHandle (mon->sema); + CloseHandle (mon->waiters_done); +} + static MonoThreadsSync *mon_new(void) { MonoThreadsSync *new; +#if HAVE_BOEHM_GC + new=(MonoThreadsSync *)GC_debug_malloc (sizeof(MonoThreadsSync), "sync", 1); + GC_register_finalizer (new, mon_finalize, NULL, NULL, NULL); +#else /* This should be freed when the object that owns it is * deleted */ - - new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1); - new->monitor=CreateMutex(NULL, FALSE, NULL); -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": ThreadsSync mutex created: %p", - new->monitor); + new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1); #endif + + new->monitor=CreateMutex(NULL, FALSE, "sync"); + if(new->monitor==NULL) { + /* Throw some sort of system exception? (ditto for the + * sem and event handles below) + */ + } new->waiters_count=0; new->was_broadcast=FALSE; @@ -295,6 +360,12 @@ static MonoThreadsSync *mon_new(void) new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL); new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL); +#ifdef THREAD_LOCK_DEBUG + g_message(G_GNUC_PRETTY_FUNCTION + ": ThreadsSync %p mutex created: %p, sem: %p, event: %p", + new, new->monitor, new->sema, new->waiters_done); +#endif + return(new); } @@ -306,7 +377,7 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj, #ifdef THREAD_LOCK_DEBUG g_message(G_GNUC_PRETTY_FUNCTION - ": Trying to lock %p in thread %d", obj, + ": Trying to lock object %p in thread %d", obj, GetCurrentThreadId()); #endif @@ -334,8 +405,8 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj, mon->tid=GetCurrentThreadId(); #ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj, - mon->count); + g_message(G_GNUC_PRETTY_FUNCTION + ": object %p now locked %d times", obj, mon->count); #endif return(TRUE); @@ -403,6 +474,11 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj) } if(mon->tid!=GetCurrentThreadId()) { +#ifdef THREAD_LOCK_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": object %p is owned by thread %d", obj, mon->tid); +#endif + goto finished; } @@ -755,7 +831,7 @@ void mono_thread_init(MonoDomain *domain) main_thread = mono_object_new (domain, thread_class); #ifdef THREAD_DEBUG g_message(G_GNUC_PRETTY_FUNCTION - ": Finished to building main Thread object"); + ": Finished building main Thread object: %p", main_thread); #endif InitializeCriticalSection(&threads_mutex); @@ -763,6 +839,11 @@ void mono_thread_init(MonoDomain *domain) InitializeCriticalSection(&interlocked_mutex); current_object_key=TlsAlloc(); +#ifdef THREAD_DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d", + current_object_key); +#endif + TlsSetValue(current_object_key, main_thread); slothash_key=TlsAlloc(); @@ -789,7 +870,9 @@ void mono_thread_cleanup(void) * * The first method call should be started in its own thread, * and then the main thread should poll an event and wait for - * any terminated threads, until there are none left. + * any terminated threads, until there are none left. (This + * loop will break if a subthread creates new threads after + * the main thread ends.) */ #ifdef THREAD_DEBUG g_message("There are %d threads to join", threads->len); diff --git a/mono/metadata/threads.h b/mono/metadata/threads.h index ab4d63c8f427f..ea043d076ca76 100644 --- a/mono/metadata/threads.h +++ b/mono/metadata/threads.h @@ -22,6 +22,7 @@ extern void mono_thread_cleanup(void); MonoObject *mono_thread_create (MonoDomain *domain, gpointer func); extern HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this, MonoObject *start); +extern void ves_icall_System_Threading_Thread_Thread_free_internal(MonoObject *this, HANDLE thread); extern void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this, HANDLE thread); extern void ves_icall_System_Threading_Thread_Sleep_internal(int ms); extern MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void); diff --git a/mono/tests/dataslot.cs b/mono/tests/dataslot.cs new file mode 100755 index 0000000000000..437a137727f3b --- /dev/null +++ b/mono/tests/dataslot.cs @@ -0,0 +1,54 @@ + +using System; +using System.Threading; + +public class Test { + static LocalDataStoreSlot[] slot = new LocalDataStoreSlot[102400]; + + private void Thread_func() { + Console.WriteLine("In a thread!"); + + for(int i=51200; i<102400; i++) { + slot[i]=Thread.AllocateDataSlot(); + Thread.SetData(slot[i], i); + } + + Thread.Sleep(5000); + + Thread.SetData(slot[11111], 42); + Thread.SetData(slot[76801], 42); + + Thread.Sleep(20000); + + Console.WriteLine("Subthread done"); + Console.WriteLine("slot 11111 contains " + Thread.GetData(slot[11111])); + Console.WriteLine("slot 26801 contains " + Thread.GetData(slot[26801])); + Console.WriteLine("slot 76801 contains " + Thread.GetData(slot[76801])); + Console.WriteLine("slot 96801 contains " + Thread.GetData(slot[96801])); + } + + public static int Main () { + Console.WriteLine ("Hello, World!"); + Test test=new Test(); + Thread thr=new Thread(new ThreadStart(test.Thread_func)); + thr.Start(); + + for(int i=0; i<51200; i++) { + slot[i]=Thread.AllocateDataSlot(); + Thread.SetData(slot[i], i); + } + Thread.SetData(slot[11111], 69); + Thread.SetData(slot[26801], 69); + + Thread.Sleep(10000); + + Console.WriteLine("Main thread done"); + Console.WriteLine("slot 11111 contains " + Thread.GetData(slot[11111])); + Console.WriteLine("slot 16801 contains " + Thread.GetData(slot[16801])); + Console.WriteLine("slot 26801 contains " + Thread.GetData(slot[26801])); + Console.WriteLine("slot 76801 contains " + Thread.GetData(slot[76801])); + + return 0; + } +} + diff --git a/mono/tests/lock.cs b/mono/tests/lock.cs new file mode 100755 index 0000000000000..c639983b37815 --- /dev/null +++ b/mono/tests/lock.cs @@ -0,0 +1,27 @@ +using System; + +public class LockTest +{ + public class Test + { + public int val { + get { + return(v); + } + set { + v=value; + } + } + + int v; + } + + public static void Main() { + Test a=new Test(); + + lock(a) { + a.val=2; + } + Console.WriteLine("a is " + a.val); + } +} diff --git a/mono/tests/thread-signal-wait.cs b/mono/tests/thread-signal-wait.cs new file mode 100755 index 0000000000000..bc182e29cab51 --- /dev/null +++ b/mono/tests/thread-signal-wait.cs @@ -0,0 +1,83 @@ + +using System; +using System.Threading; + +public class Test { + static Object mon = new Object(); + static Object reply = new Object(); + static Object bogus = new Object(); + + private void Thread_func() { + Console.WriteLine("thread: In a thread!"); + + Thread.Sleep(10000); + + Console.WriteLine("thread: Waiting for mon"); + + Monitor.Enter(mon); + Monitor.Pulse(mon); + Console.WriteLine("thread: Pulsed mon"); + Monitor.Exit(mon); + + Thread.Sleep(10000); + + Monitor.Enter(reply); + Console.WriteLine("thread: Got reply mutex"); + Monitor.Pulse(reply); + Console.WriteLine("thread: Pulsed reply"); + Monitor.Exit(reply); + } + + public static int Main () { + Console.WriteLine ("main: Hello, World!"); + + Test test = new Test(); + Thread thr=new Thread(new ThreadStart(test.Thread_func)); + thr.Start(); + + Monitor.Enter(mon); + Console.WriteLine("main: mon lock 1"); + + Monitor.Enter(mon); + Console.WriteLine("main: mon lock 2"); + + Console.WriteLine("main: Waiting for mon"); + Monitor.Wait(mon); + Console.WriteLine("main: mon waited"); + Monitor.Exit(mon); + + Monitor.Enter(reply); + Console.WriteLine("main: reply locked"); + Monitor.Enter(reply); + Console.WriteLine("main: reply locked"); + Monitor.Enter(reply); + Console.WriteLine("main: reply locked"); + Monitor.Enter(reply); + Console.WriteLine("main: reply locked"); + Monitor.Wait(reply); + Console.WriteLine("main: reply waited"); + Monitor.Exit(reply); + + Console.WriteLine("Seeing how many locks we have..."); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + Monitor.Exit(reply); + Console.WriteLine("main: Exit reply"); + + Monitor.Exit(bogus); + Console.WriteLine("main: Exit bogus"); + + return 0; + } +} +