/* * mutexes.c: Mutex handles * * Author: * Dick Porter (dick@ximian.com) * * (C) 2002 Ximian, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #undef DEBUG /* This is used to serialise mutex creation when names are given * (FIXME: make it process-shared) */ static mono_mutex_t named_mutex_mutex = MONO_MUTEX_INITIALIZER; static void mutex_close_shared (gpointer handle); static void mutex_signal(gpointer handle); static void mutex_own (gpointer handle); static gboolean mutex_is_owned (gpointer handle); struct _WapiHandleOps _wapi_mutex_ops = { mutex_close_shared, /* close_shared */ NULL, /* close_private */ mutex_signal, /* signal */ mutex_own, /* own */ mutex_is_owned, /* is_owned */ }; static mono_once_t mutex_ops_once=MONO_ONCE_INIT; static void mutex_ops_init (void) { _wapi_handle_register_capabilities (WAPI_HANDLE_MUTEX, WAPI_HANDLE_CAP_WAIT | WAPI_HANDLE_CAP_SIGNAL | WAPI_HANDLE_CAP_OWN); } static void mutex_close_shared (gpointer handle) { struct _WapiHandle_mutex *mutex_handle; gboolean ok; 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; } #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p", handle); #endif if(mutex_handle->sharedns.name!=0) { _wapi_handle_scratch_delete (mutex_handle->sharedns.name); mutex_handle->sharedns.name=0; } } static void mutex_signal(gpointer handle) { ReleaseMutex(handle); } static void mutex_own (gpointer handle) { struct _WapiHandle_mutex *mutex_handle; gboolean ok; 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; } #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": owning mutex handle %p", handle); #endif _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 ": mutex handle %p locked %d times by %d:%ld", handle, mutex_handle->recursion, mutex_handle->pid, mutex_handle->tid); #endif } static gboolean mutex_is_owned (gpointer handle) { struct _WapiHandle_mutex *mutex_handle; gboolean ok; 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 ": testing ownership mutex handle %p", handle); #endif if(mutex_handle->recursion>0 && mutex_handle->pid==getpid () && mutex_handle->tid==pthread_self ()) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": mutex handle %p owned by %d:%ld", handle, getpid (), pthread_self ()); #endif return(TRUE); } else { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": mutex handle %p not owned by %d:%ld", handle, getpid (), pthread_self ()); #endif return(FALSE); } } /** * CreateMutex: * @security: Ignored for now. * @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. * * Creates a new mutex handle. A mutex is signalled when no thread * owns it. A thread acquires ownership of the mutex by waiting for * it with WaitForSingleObject() or WaitForMultipleObjects(). A * thread relinquishes ownership with ReleaseMutex(). * * A thread that owns a mutex can specify the same mutex in repeated * wait function calls without blocking. The thread must call * ReleaseMutex() an equal number of times to release the mutex. * * Return value: A new handle, or %NULL on error. */ gpointer CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned, const gunichar2 *name) { struct _WapiHandle_mutex *mutex_handle; gpointer handle; gboolean ok; gchar *utf8_name; mono_once (&mutex_ops_once, mutex_ops_init); if(name!=NULL) { utf8_name=g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); /* w32 seems to guarantee that opening named mutexes can't * race each other */ mono_mutex_lock (&named_mutex_mutex); } else { utf8_name=NULL; } #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Creating mutex (name [%s])", utf8_name==NULL?"":utf8_name); #endif if(name!=NULL) { handle=_wapi_search_handle_namespace ( WAPI_HANDLE_MUTEX, utf8_name, (gpointer *)&mutex_handle, NULL); if(handle==_WAPI_HANDLE_INVALID) { /* The name has already been used for a different * object. */ g_free (utf8_name); mono_mutex_unlock (&named_mutex_mutex); SetLastError (ERROR_INVALID_HANDLE); return(NULL); } else if (handle!=NULL) { g_free (utf8_name); mono_mutex_unlock (&named_mutex_mutex); _wapi_handle_ref (handle); return(handle); } /* Otherwise fall through to create the mutex. */ } handle=_wapi_handle_new (WAPI_HANDLE_MUTEX); if(handle==_WAPI_HANDLE_INVALID) { g_warning (G_GNUC_PRETTY_FUNCTION ": error creating mutex handle"); if(utf8_name!=NULL) { g_free (utf8_name); } if(name!=NULL) { mono_mutex_unlock (&named_mutex_mutex); } return(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); if(utf8_name!=NULL) { g_free (utf8_name); } if(name!=NULL) { mono_mutex_unlock (&named_mutex_mutex); } _wapi_handle_unlock_handle (handle); return(NULL); } if(utf8_name!=NULL) { mutex_handle->sharedns.name=_wapi_handle_scratch_store ( utf8_name, strlen (utf8_name)); } if(owned==TRUE) { 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); if(utf8_name!=NULL) { g_free (utf8_name); } if(name!=NULL) { mono_mutex_unlock (&named_mutex_mutex); } return(handle); } /** * ReleaseMutex: * @handle: The mutex handle. * * Releases ownership if the mutex handle @handle. * * Return value: %TRUE on success, %FALSE otherwise. This function * fails if the calling thread does not own the mutex @handle. */ gboolean ReleaseMutex(gpointer 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", handle); #endif 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 %d:%ld, me %d:%ld)", handle, mutex_handle->pid, mutex_handle->tid, pid, tid); #endif _wapi_handle_unlock_handle (handle); return(FALSE); } /* OK, we own this mutex */ mutex_handle->recursion--; if(mutex_handle->recursion==0) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p", handle); #endif mutex_handle->pid=0; mutex_handle->tid=0; _wapi_handle_set_signal_state (handle, TRUE, FALSE); } _wapi_handle_unlock_handle (handle); return(TRUE); }