From a04f30944b966383b17715e2e1e469dd68a92fac Mon Sep 17 00:00:00 2001 From: Gonzalo Paniagua Javier Date: Sat, 7 May 2005 22:22:47 +0000 Subject: [PATCH] Last patches on io-layer from Lluis and Dick in HEAD svn path=/branches/mono-1-1-7/mono/; revision=44210 --- mono/handles/hps.c | 19 +- mono/io-layer/ChangeLog | 20 ++ mono/io-layer/collection.h | 29 ++- mono/io-layer/handles-private.h | 122 ++---------- mono/io-layer/handles.c | 288 +++++++--------------------- mono/io-layer/mutexes.c | 19 +- mono/io-layer/shared.c | 329 +++++++++++++++++++++----------- mono/io-layer/shared.h | 12 +- mono/io-layer/wait.c | 17 +- mono/io-layer/wapi-private.h | 14 +- 10 files changed, 377 insertions(+), 492 deletions(-) diff --git a/mono/handles/hps.c b/mono/handles/hps.c index 2eb4d6bde58bd..3914d48aadfe2 100644 --- a/mono/handles/hps.c +++ b/mono/handles/hps.c @@ -45,28 +45,28 @@ int main (int argc, char **argv) { guint32 i; - _wapi_shared_layout = _wapi_shm_attach(); + _wapi_shared_layout = _wapi_shm_attach(WAPI_SHM_DATA); if (_wapi_shared_layout == FALSE) { g_error ("Failed to attach shared memory!"); exit (-1); } - _wapi_fileshare_layout = _wapi_fileshare_shm_attach(); + _wapi_fileshare_layout = _wapi_shm_attach(WAPI_SHM_FILESHARE); if (_wapi_fileshare_layout == FALSE) { g_error ("Failed to attach fileshare shared memory!"); exit (-1); } if (argc > 1) { + _wapi_shm_semaphores_init (); _wapi_collection_init (); _wapi_handle_collect (); } - g_print ("master: %d namespace: %d collection: %d signals: %d\n", - _wapi_shared_layout->master_timestamp, - _wapi_shared_layout->namespace_check, + g_print ("collection: %d signals: %d sem: 0x%x\n", _wapi_shared_layout->collection_count, - _wapi_shared_layout->signal_count); + _wapi_shared_layout->signal_count, + _wapi_shared_layout->sem_key); for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { struct _WapiHandleShared *shared; @@ -84,19 +84,16 @@ int main (int argc, char **argv) shared = &_wapi_shared_layout->handles[meta->offset]; if (shared->type != WAPI_HANDLE_UNUSED) { - g_print ("%3x (%3x) [%7s] %c %4u %s (%s)\n", + g_print ("%3x (%3x) [%7s] %4u %s (%s)\n", i, meta->offset, _wapi_handle_typename[shared->type], - meta->checking == 0?' ':'X', now - meta->timestamp, meta->signalled?"Sg":"Un", details[shared->type](shared)); } } - g_print ("Fileshare check: %d hwm: %d\n", - _wapi_fileshare_layout->share_check, - _wapi_fileshare_layout->hwm); + g_print ("Fileshare hwm: %d\n", _wapi_fileshare_layout->hwm); for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) { struct _WapiFileShare *file_share; diff --git a/mono/io-layer/ChangeLog b/mono/io-layer/ChangeLog index b5faa1dd7d485..f3f11149b2148 100644 --- a/mono/io-layer/ChangeLog +++ b/mono/io-layer/ChangeLog @@ -1,3 +1,23 @@ +2005-05-06 Dick Porter + + * handles-private.h: + * mutexes.c: + * wapi-private.h: + * shared.h: + * shared.c: + * wait.c: + * handles.c: + * collection.h: Use SysV semaphores for managing access to the + shared memory - in return for the ludicrous api we get + synchronisation primitives that can be cleaned up by the kernel + even when a process quits unexpectedly. This removes the + timestamp issues. + +2005-05-05 Lluis Sanchez Gual + + * handles.c: Always use polling in the waits, since + mono_cond_timedwait can't be interruped by the thread abort signal. + 2005-04-30 Gonzalo Paniagua Javier * events.c: calling Set on AutoResetEvent several times has the same diff --git a/mono/io-layer/collection.h b/mono/io-layer/collection.h index 07ca3b3ea4bd5..725bd47077c47 100644 --- a/mono/io-layer/collection.h +++ b/mono/io-layer/collection.h @@ -15,27 +15,20 @@ #define _WAPI_HANDLE_COLLECTION_UPDATE_INTERVAL 10 #define _WAPI_HANDLE_COLLECTION_EXPIRED_INTERVAL 60 -#define _WAPI_HANDLE_COLLECTION_UNSAFE \ - { \ - guint32 _wapi_save_start; \ - int _wapi_thr_ret; \ - \ - do { \ - _wapi_save_start = (guint32)(time(NULL) & 0xFFFFFFFF);\ - \ - _wapi_thr_ret = _wapi_timestamp_exclusion (&_wapi_shared_layout->master_timestamp, _wapi_save_start); \ - if (_wapi_thr_ret == EBUSY) { \ - _wapi_handle_spin (100); \ - } \ - } while (_wapi_thr_ret == EBUSY); \ - g_assert (_wapi_thr_ret == 0); +#include - -#define _WAPI_HANDLE_COLLECTION_SAFE \ - _wapi_thr_ret = _wapi_timestamp_release (&_wapi_shared_layout->master_timestamp, _wapi_save_start); \ +#define _WAPI_HANDLE_COLLECTION_UNSAFE \ + { \ + int _wapi_thr_ret; \ + \ + _wapi_thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_COLLECTION); \ + g_assert(_wapi_thr_ret == 0); + +#define _WAPI_HANDLE_COLLECTION_SAFE \ + _wapi_thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_COLLECTION); \ + g_assert (_wapi_thr_ret == 0); \ } - extern void _wapi_collection_init (void); extern void _wapi_handle_collect (void); diff --git a/mono/io-layer/handles-private.h b/mono/io-layer/handles-private.h index 88d90ae5d6d02..c39674ad697cf 100644 --- a/mono/io-layer/handles-private.h +++ b/mono/io-layer/handles-private.h @@ -19,6 +19,7 @@ #include #include #include +#include #define _WAPI_PRIVATE_MAX_SLOTS 1024 #define _WAPI_PRIVATE_HANDLES(x) (_wapi_private_handles [x / _WAPI_HANDLE_INITIAL_COUNT][x % _WAPI_HANDLE_INITIAL_COUNT]) @@ -33,6 +34,7 @@ extern struct _WapiFileShareLayout *_wapi_fileshare_layout; extern guint32 _wapi_fd_reserve; extern mono_mutex_t _wapi_global_signal_mutex; extern pthread_cond_t _wapi_global_signal_cond; +extern int _wapi_sem_id; extern gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific); @@ -50,8 +52,6 @@ extern gpointer _wapi_search_handle (WapiHandleType type, gboolean (*check)(gpointer, gpointer), gpointer user_data, gpointer *handle_specific); -extern int _wapi_namespace_timestamp_release (gpointer nowptr); -extern int _wapi_namespace_timestamp (guint32 now); extern gint32 _wapi_search_handle_namespace (WapiHandleType type, gchar *utf8_name); extern void _wapi_handle_ref (gpointer handle); @@ -71,10 +71,9 @@ extern gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, gpointer *handles, gboolean waitall, guint32 *retcount, - guint32 *lowest, - guint32 *now); + guint32 *lowest); extern void _wapi_handle_unlock_handles (guint32 numhandles, - gpointer *handles, guint32 now); + gpointer *handles); extern int _wapi_handle_wait_signal (void); extern int _wapi_handle_timedwait_signal (struct timespec *timeout); extern int _wapi_handle_wait_signal_poll_share (void); @@ -275,43 +274,6 @@ static inline int _wapi_handle_unlock_handle (gpointer handle) return(ret); } -static inline int _wapi_timestamp_exclusion (volatile gint32 *timestamp, - guint32 now) -{ - guint32 then; - int ret; - - then = InterlockedCompareExchange (timestamp, now, 0); - if (then == 0) { - ret = 0; - } else if (now - then > 10) { - /* Try to overwrite the previous - * attempt, but make sure noone else - * got in first - */ - g_warning ("%s: Breaking a previous timestamp", __func__); - - ret = InterlockedCompareExchange (timestamp, now, - then) == then?0:EBUSY; - } else { - /* Someone else is working on this one */ - ret = EBUSY; - } - - return(ret); -} - -static inline int _wapi_timestamp_release (volatile gint32 *timestamp, - guint32 now) -{ - /* The timestamp can be either: now, in which case we reset - * it; 0, in which case we don't do anything; any other value, - * in which case we don't do anything because someone else is - * in charge of resetting it. - */ - return(InterlockedCompareExchange (timestamp, 0, now) != now); -} - static inline void _wapi_handle_spin (guint32 ms) { struct timespec sleepytime; @@ -324,89 +286,47 @@ static inline void _wapi_handle_spin (guint32 ms) nanosleep (&sleepytime, NULL); } -static inline int _wapi_handle_shared_lock_handle (gpointer handle, guint32 *now) +static inline int _wapi_handle_lock_shared_handles (void) { - int ret; - - g_assert (_WAPI_SHARED_HANDLE(_wapi_handle_type(handle))); - - _wapi_handle_ref (handle); - - /* We don't lock shared handles, but we need to be able to - * tell other threads to hold off. - * - * We do this by atomically putting the least-significant 32 - * bits of time(2) into the 'checking' field if it is zero. - * If it isn't zero, then it means that either another thread - * is looking at this handle right now, or someone crashed - * here. Assume that if the time value is more than 10 - * seconds old, its a crash and override it. 10 seconds - * should be enough for anyone... - * - * If the time value is within 10 seconds, back off and try - * again as per the non-shared case. - */ - do { - *now = (guint32)(time (NULL) & 0xFFFFFFFF); - - ret = _wapi_timestamp_exclusion (&WAPI_SHARED_HANDLE_METADATA(handle).checking, *now); - if (ret == EBUSY) { - _wapi_handle_spin (100); - } - } while (ret == EBUSY); + return(_wapi_shm_sem_lock (_WAPI_SHARED_SEM_HANDLE)); +} - return (ret); +static inline int _wapi_handle_trylock_shared_handles (void) +{ + return(_wapi_shm_sem_trylock (_WAPI_SHARED_SEM_HANDLE)); } -static inline int _wapi_handle_shared_trylock_handle (gpointer handle, guint32 now) +static inline int _wapi_handle_unlock_shared_handles (void) { - int ret; - - g_assert (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))); - - _wapi_handle_ref (handle); - - ret = _wapi_timestamp_exclusion (&WAPI_SHARED_HANDLE_METADATA(handle).checking, now); - if (ret == EBUSY) { - _wapi_handle_unref (handle); - } - - return (ret); + return(_wapi_shm_sem_unlock (_WAPI_SHARED_SEM_HANDLE)); } -static inline int _wapi_handle_shared_unlock_handle (gpointer handle, guint32 now) +static inline int _wapi_namespace_lock (void) { - g_assert (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))); - - _wapi_timestamp_release (&WAPI_SHARED_HANDLE_METADATA(handle).checking, now); - _wapi_handle_unref (handle); + return(_wapi_shm_sem_lock (_WAPI_SHARED_SEM_NAMESPACE)); +} - return (0); +/* This signature makes it easier to use in pthread cleanup handlers */ +static inline int _wapi_namespace_unlock (gpointer data G_GNUC_UNUSED) +{ + return(_wapi_shm_sem_unlock (_WAPI_SHARED_SEM_NAMESPACE)); } static inline void _wapi_handle_share_release (struct _WapiFileShare *info) { - guint32 now; int thr_ret; g_assert (info->handle_refs > 0); /* Prevent new entries racing with us */ - do { - now = (guint32)(time(NULL) & 0xFFFFFFFF); - - thr_ret = _wapi_timestamp_exclusion (&_wapi_fileshare_layout->share_check, now); - if (thr_ret == EBUSY) { - _wapi_handle_spin (100); - } - } while (thr_ret == EBUSY); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); g_assert(thr_ret == 0); if (InterlockedDecrement (&info->handle_refs) == 0) { memset (info, '\0', sizeof(struct _WapiFileShare)); } - thr_ret = _wapi_timestamp_release (&_wapi_fileshare_layout->share_check, now); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); } #endif /* _WAPI_HANDLES_PRIVATE_H_ */ diff --git a/mono/io-layer/handles.c b/mono/io-layer/handles.c index 204cbe86d2fda..4478bcbd731ee 100644 --- a/mono/io-layer/handles.c +++ b/mono/io-layer/handles.c @@ -102,6 +102,8 @@ guint32 _wapi_fd_reserve; mono_mutex_t _wapi_global_signal_mutex; pthread_cond_t _wapi_global_signal_cond; +int _wapi_sem_id; + static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER; static mono_once_t shared_init_once = MONO_ONCE_INIT; @@ -119,10 +121,12 @@ static void shared_init (void) _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT; } while(_wapi_fd_reserve > _wapi_private_handle_count); - _wapi_shared_layout = _wapi_shm_attach (); + _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA); g_assert (_wapi_shared_layout != NULL); + + _wapi_shm_semaphores_init (); - _wapi_fileshare_layout = _wapi_fileshare_shm_attach (); + _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE); g_assert (_wapi_fileshare_layout != NULL); _wapi_collection_init (); @@ -138,7 +142,6 @@ static void _wapi_handle_init_shared_metadata (struct _WapiHandleSharedMetadata { meta->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF); meta->signalled = FALSE; - meta->checking = 0; } static void _wapi_handle_init_shared (struct _WapiHandleShared *handle, @@ -811,30 +814,6 @@ gpointer _wapi_search_handle (WapiHandleType type, return(ret); } -/* This signature makes it easier to use in pthread cleanup handlers */ -int _wapi_namespace_timestamp_release (gpointer nowptr) -{ - guint32 now = GPOINTER_TO_UINT(nowptr); - - return (_wapi_timestamp_release (&_wapi_shared_layout->namespace_check, - now)); -} - -int _wapi_namespace_timestamp (guint32 now) -{ - int ret; - - do { - ret = _wapi_timestamp_exclusion (&_wapi_shared_layout->namespace_check, now); - /* sleep for a bit */ - if (ret == EBUSY) { - _wapi_handle_spin (100); - } - } while (ret == EBUSY); - - return(ret); -} - /* Returns the offset of the metadata array, or -1 on error, or 0 for * not found (0 is not a valid offset) */ @@ -847,7 +826,6 @@ gint32 _wapi_search_handle_namespace (WapiHandleType type, gint32 ret = 0; g_assert(_WAPI_SHARED_HANDLE(type)); - g_assert(_wapi_shared_layout->namespace_check != 0); #ifdef DEBUG g_message ("%s: Lookup for handle named [%s] type %s", __func__, @@ -1116,8 +1094,7 @@ gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, gpointer *handles, gboolean waitall, guint32 *retcount, - guint32 *lowest, - guint32 *now) + guint32 *lowest) { guint32 count, i, iter=0; gboolean ret; @@ -1126,6 +1103,9 @@ gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, /* Lock all the handles, with backoff */ again: + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + for(i=0; i timeout->tv_sec) || + (fake_timeout.tv_sec == timeout->tv_sec && + fake_timeout.tv_nsec > timeout->tv_nsec))) { + /* Real timeout is less than 100ms time */ + ret=mono_cond_timedwait (cond, mutex, timeout); + } else { + ret=mono_cond_timedwait (cond, mutex, &fake_timeout); + + /* Mask the fake timeout, this will cause + * another poll if the cond was not really signaled + */ + if (ret==ETIMEDOUT) { + ret=0; + } + } + + return(ret); +} + int _wapi_handle_wait_signal (void) { - return(mono_cond_wait (&_wapi_global_signal_cond, - &_wapi_global_signal_mutex)); + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL); } int _wapi_handle_timedwait_signal (struct timespec *timeout) { - return(mono_cond_timedwait (&_wapi_global_signal_cond, - &_wapi_global_signal_mutex, - timeout)); + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout); } int _wapi_handle_wait_signal_poll_share (void) { - struct timespec fake_timeout; - int ret; - #ifdef DEBUG g_message ("%s: poll private and shared handles", __func__); #endif - - _wapi_calc_timeout (&fake_timeout, 100); - - ret = mono_cond_timedwait (&_wapi_global_signal_cond, - &_wapi_global_signal_mutex, &fake_timeout); - if (ret == ETIMEDOUT) { - /* This will cause the waiting thread to check signal - * status for all handles - */ -#ifdef DEBUG - g_message ("%s: poll timed out, returning success", __func__); -#endif - - return (0); - } else { - /* This will be 0 indicating a private handle was - * signalled, or an error - */ -#ifdef DEBUG - g_message ("%s: returning: %d", __func__, ret); -#endif - - return (ret); - } + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL); } int _wapi_handle_timedwait_signal_poll_share (struct timespec *timeout) { - struct timespec fake_timeout; - guint32 signal_count = _wapi_shared_layout->signal_count; - int ret; - #ifdef DEBUG g_message ("%s: poll private and shared handles", __func__); #endif - do { - _wapi_calc_timeout (&fake_timeout, 100); - - if ((fake_timeout.tv_sec > timeout->tv_sec) || - (fake_timeout.tv_sec == timeout->tv_sec && - fake_timeout.tv_nsec > timeout->tv_nsec)) { - /* Real timeout is less than 100ms time */ - -#ifdef DEBUG - g_message ("%s: last few ms", __func__); -#endif - - ret = mono_cond_timedwait (&_wapi_global_signal_cond, - &_wapi_global_signal_mutex, - timeout); - /* If this times out, it will compare the - * shared signal counter and then if that - * hasn't increased will fall out of the - * do-while loop. - */ - if (ret != ETIMEDOUT) { - /* Either a private handle was - * signalled, or an error. - */ -#ifdef DEBUG - g_message ("%s: returning: %d", __func__, ret); -#endif - return (ret); - } - } else { - ret = mono_cond_timedwait (&_wapi_global_signal_cond, - &_wapi_global_signal_mutex, - &fake_timeout); - - /* Mask the fake timeout, this will cause - * another poll if the shared counter hasn't - * changed - */ - if (ret == ETIMEDOUT) { - ret = 0; - } else { - /* Either a private handle was - * signalled, or an error - */ -#ifdef DEBUG - g_message ("%s: returning: %d", __func__, ret); -#endif - return (ret); - } - } - - /* No private handle was signalled, so check the - * shared signal counter - */ - if (signal_count != _wapi_shared_layout->signal_count) { -#ifdef DEBUG - g_message ("%s: A shared handle was signalled", - __func__); -#endif - return (0); - } - } while (ret != ETIMEDOUT); - -#ifdef DEBUG - g_message ("%s: returning ETIMEDOUT", __func__); -#endif - - return (ret); + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout); } int _wapi_handle_wait_signal_handle (gpointer handle) { - guint32 idx = GPOINTER_TO_UINT(handle); - #ifdef DEBUG g_message ("%s: waiting for %p", __func__, handle); #endif - if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - while(1) { - if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { - return (0); - } - - _wapi_handle_spin (100); - } - } else { - return(mono_cond_wait (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, - &_WAPI_PRIVATE_HANDLES(idx).signal_mutex)); - } + return _wapi_handle_timedwait_signal_handle (handle, NULL); } int _wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout) { - guint32 idx = GPOINTER_TO_UINT(handle); - #ifdef DEBUG g_message ("%s: waiting for %p (type %s)", __func__, handle, _wapi_handle_typename[_wapi_handle_type (handle)]); #endif if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - struct timespec fake_timeout; - - while (1) { - if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { - return (0); - } - + if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { + return (0); + } + if (timeout != NULL) { + struct timespec fake_timeout; _wapi_calc_timeout (&fake_timeout, 100); if ((fake_timeout.tv_sec > timeout->tv_sec) || - (fake_timeout.tv_sec == timeout->tv_sec && - fake_timeout.tv_nsec > timeout->tv_nsec)) { + (fake_timeout.tv_sec == timeout->tv_sec && + fake_timeout.tv_nsec > timeout->tv_nsec)) { /* FIXME: Real timeout is less than * 100ms time, but is it really worth * calculating to the exact ms? */ _wapi_handle_spin (100); - + if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { return (0); } else { return (ETIMEDOUT); } - } else { - _wapi_handle_spin (100); } } + _wapi_handle_spin (100); + return (0); + } else { - return(mono_cond_timedwait (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout)); + guint32 idx = GPOINTER_TO_UINT(handle); + return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout); } } @@ -1479,14 +1357,7 @@ gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, _WAPI_HANDLE_COLLECTION_UNSAFE; /* Prevent new entries racing with us */ - do { - now = (guint32)(time(NULL) & 0xFFFFFFFF); - - thr_ret = _wapi_timestamp_exclusion (&_wapi_fileshare_layout->share_check, now); - if (thr_ret == EBUSY) { - _wapi_handle_spin (100); - } - } while (thr_ret == EBUSY); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); g_assert (thr_ret == 0); /* If a linear scan gets too slow we'll have to fit a hash @@ -1551,7 +1422,7 @@ gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, InterlockedExchange (&(*share_info)->timestamp, now); } - thr_ret = _wapi_timestamp_release (&_wapi_fileshare_layout->share_check, now); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); _WAPI_HANDLE_COLLECTION_SAFE; @@ -1591,7 +1462,6 @@ void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd) gboolean found = FALSE, proc_fds = FALSE; pid_t self = getpid(); int pid; - guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF); int thr_ret, i; /* If there is no /proc, there's nothing more we can do here */ @@ -1606,14 +1476,7 @@ void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd) _WAPI_HANDLE_COLLECTION_UNSAFE; /* Prevent new entries racing with us */ - do { - now = (guint32)(time(NULL) & 0xFFFFFFFF); - - thr_ret = _wapi_timestamp_exclusion (&_wapi_fileshare_layout->share_check, now); - if (thr_ret == EBUSY) { - _wapi_handle_spin (100); - } - } while (thr_ret == EBUSY); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); g_assert (thr_ret == 0); for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { @@ -1692,7 +1555,7 @@ void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd) memset (share_info, '\0', sizeof(struct _WapiFileShare)); } - thr_ret = _wapi_timestamp_release (&_wapi_fileshare_layout->share_check, now); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); _WAPI_HANDLE_COLLECTION_SAFE; } @@ -1730,21 +1593,13 @@ static void _wapi_shared_details (gpointer handle_info) void _wapi_handle_update_refs (void) { - guint32 i, k, lock_now; + guint32 i, k; int thr_ret; _WAPI_HANDLE_COLLECTION_UNSAFE; /* Prevent file share entries racing with us */ - do { - lock_now = (guint32)(time(NULL) & 0xFFFFFFFF); - - thr_ret = _wapi_timestamp_exclusion (&_wapi_fileshare_layout->share_check, lock_now); - - if (thr_ret == EBUSY) { - _wapi_handle_spin (100); - } - } while (thr_ret == EBUSY); + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); g_assert(thr_ret == 0); for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { @@ -1790,6 +1645,7 @@ void _wapi_handle_update_refs (void) } } - thr_ret = _wapi_timestamp_release (&_wapi_fileshare_layout->share_check, lock_now); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + _WAPI_HANDLE_COLLECTION_SAFE; } diff --git a/mono/io-layer/mutexes.c b/mono/io-layer/mutexes.c index 0a25fced41272..dccb486ead125 100644 --- a/mono/io-layer/mutexes.c +++ b/mono/io-layer/mutexes.c @@ -298,7 +298,6 @@ static gboolean namedmutex_check (gpointer handle, gpointer user_data) gboolean ok; struct mutex_check_data *data = (struct mutex_check_data *)user_data; int thr_ret; - guint32 now; ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDMUTEX, (gpointer *)&mutex_handle); @@ -308,7 +307,7 @@ static gboolean namedmutex_check (gpointer handle, gpointer user_data) return(FALSE); } - thr_ret = _wapi_handle_shared_lock_handle (handle, &now); + thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); if (mutex_handle->pid == data->pid && @@ -324,7 +323,7 @@ static gboolean namedmutex_check (gpointer handle, gpointer user_data) _wapi_shared_handle_set_signal_state (handle, TRUE); } - _wapi_handle_shared_unlock_handle (handle, now); + _wapi_handle_unlock_shared_handles (); /* Return false to keep searching */ return(FALSE); @@ -397,15 +396,14 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE gchar *utf8_name; int thr_ret; gpointer ret = NULL; - guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF), locknow; guint32 namelen; gint32 offset; /* w32 seems to guarantee that opening named mutexes can't * race each other */ - pthread_cleanup_push ((void(*)(void *))_wapi_namespace_timestamp_release, GUINT_TO_POINTER(now)); - thr_ret = _wapi_namespace_timestamp (now); + pthread_cleanup_push ((void(*)(void *))_wapi_namespace_unlock, NULL); + thr_ret = _wapi_namespace_lock (); g_assert (thr_ret == 0); /* Need to blow away any old errors here, because code tests @@ -469,7 +467,7 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE /* Set the initial state, as this is a completely new * handle */ - thr_ret = _wapi_handle_shared_lock_handle (handle, &locknow); + thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); if (owned == TRUE) { @@ -478,7 +476,7 @@ static gpointer namedmutex_create (WapiSecurityAttributes *security G_GNUC_UNUSE _wapi_shared_handle_set_signal_state (handle, TRUE); } - _wapi_handle_shared_unlock_handle (handle, locknow); + _wapi_handle_unlock_shared_handles (); } #ifdef DEBUG @@ -588,7 +586,6 @@ static gboolean namedmutex_release (gpointer handle) pid_t pid=getpid (); int thr_ret; gboolean ret = FALSE; - guint32 now; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDMUTEX, (gpointer *)&mutex_handle); @@ -598,7 +595,7 @@ static gboolean namedmutex_release (gpointer handle) return(FALSE); } - thr_ret = _wapi_handle_shared_lock_handle (handle, &now); + thr_ret = _wapi_handle_lock_shared_handles (); g_assert (thr_ret == 0); #ifdef DEBUG @@ -628,7 +625,7 @@ static gboolean namedmutex_release (gpointer handle) } cleanup: - _wapi_handle_shared_unlock_handle (handle, now); + _wapi_handle_unlock_shared_handles (); return(ret); } diff --git a/mono/io-layer/shared.c b/mono/io-layer/shared.c index db4197dcfd3ce..9f06ad89a25af 100644 --- a/mono/io-layer/shared.c +++ b/mono/io-layer/shared.c @@ -7,41 +7,6 @@ * (C) 2002 Ximian, Inc. */ -/* - * 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. And I've since - * replaced that with a separate process to serialise access to the - * shared memory, to avoid the possibility of DOS by leaving the - * shared memory locked, and also to allow the shared memory to be - * cleaned up. - * - * 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. - * - * Having tried sysv shm, I tested mmap() and found that MAP_SHARED - * makes msync() irrelevent, and both types need cleaning up. Seeing - * as mmap() doesn't suffer from the bonkers method of allocating - * segments, it seems to be the best method. - * - * This shared memory is needed because 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 @@ -53,14 +18,17 @@ #include #include #include +#include +#include #include #include #include +#include #undef DEBUG -static guchar *_wapi_shm_file (void) +static guchar *_wapi_shm_file (_wapi_shm_t type) { static guchar file[_POSIX_PATH_MAX]; guchar *name = NULL, *filename, *dir, *wapi_dir; @@ -69,53 +37,17 @@ static guchar *_wapi_shm_file (void) if (gethostname(machine_name, sizeof(machine_name)) != 0) machine_name[0] = '\0'; - /* Change the filename whenever the format of the contents - * changes - */ - name = g_strdup_printf ("shared_data-%s-%d-%d", - machine_name, _WAPI_HANDLE_VERSION, 0); - - /* I don't know how nfs affects mmap. If mmap() of files on - * nfs mounts breaks, then there should be an option to set - * the directory. - */ - wapi_dir = getenv ("MONO_SHARED_DIR"); - if (wapi_dir == NULL) { - filename = g_build_filename (g_get_home_dir (), ".wapi", name, - NULL); - } else { - filename = g_build_filename (wapi_dir, ".wapi", name, NULL); - } - g_free (name); - - g_snprintf (file, _POSIX_PATH_MAX, "%s", filename); - g_free (filename); + switch (type) { + case WAPI_SHM_DATA: + name = g_strdup_printf ("shared_data-%s-%d-%d", + machine_name, _WAPI_HANDLE_VERSION, 0); + break; - /* No need to check if the dir already exists or check - * mkdir() errors, because on any error the open() call will - * report the problem. - */ - dir = g_path_get_dirname (file); - mkdir (dir, 0755); - g_free (dir); - - return(file); -} - -static guchar *_wapi_fileshare_shm_file (void) -{ - static guchar file[_POSIX_PATH_MAX]; - guchar *name = NULL, *filename, *dir, *wapi_dir; - gchar machine_name[256]; - - if (gethostname(machine_name, sizeof(machine_name)) != 0) - machine_name[0] = '\0'; - - /* Change the filename whenever the format of the contents - * changes - */ - name = g_strdup_printf ("shared_fileshare-%s-%d-%d", - machine_name, _WAPI_HANDLE_VERSION, 0); + case WAPI_SHM_FILESHARE: + name = g_strdup_printf ("shared_fileshare-%s-%d-%d", + machine_name, _WAPI_HANDLE_VERSION, 0); + break; + } /* I don't know how nfs affects mmap. If mmap() of files on * nfs mounts breaks, then there should be an option to set @@ -253,31 +185,41 @@ static int _wapi_shm_file_open (const guchar *filename, guint32 wanted_size) * Attach to the shared memory file or create it if it did not exist. * Returns the memory area the file was mmapped to. */ -gpointer _wapi_shm_attach (void) +gpointer _wapi_shm_attach (_wapi_shm_t type) { gpointer shm_seg; int fd; struct stat statbuf; - guchar *filename=_wapi_shm_file (); + guchar *filename=_wapi_shm_file (type); + guint32 size; + + switch(type) { + case WAPI_SHM_DATA: + size = sizeof(struct _WapiHandleSharedLayout); + break; + + case WAPI_SHM_FILESHARE: + size = sizeof(struct _WapiFileShareLayout); + break; + } - fd=_wapi_shm_file_open (filename, - sizeof(struct _WapiHandleSharedLayout)); - if(fd==-1) { + fd = _wapi_shm_file_open (filename, size); + if (fd == -1) { g_critical ("%s: shared file [%s] open error", __func__, filename); return(NULL); } - if(fstat (fd, &statbuf)==-1) { + if (fstat (fd, &statbuf)==-1) { g_critical ("%s: fstat error: %s", __func__, g_strerror (errno)); close (fd); return(NULL); } - shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, - fd, 0); - if(shm_seg==MAP_FAILED) { + shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, 0); + if (shm_seg == MAP_FAILED) { g_critical ("%s: mmap error: %s", __func__, g_strerror (errno)); close (fd); @@ -288,36 +230,191 @@ gpointer _wapi_shm_attach (void) return(shm_seg); } -gpointer _wapi_fileshare_shm_attach (void) +void _wapi_shm_semaphores_init () { - gpointer shm_seg; - int fd; - struct stat statbuf; - guchar *filename=_wapi_fileshare_shm_file (); + key_t key = ftok (_wapi_shm_file (WAPI_SHM_DATA), 'M'); + key_t oldkey; + + /* Yet more barmy API - this union is a well-defined parameter + * in a syscall, yet I still have to define it here as it + * doesn't appear in a header + */ + union semun { + int val; + struct semid_ds *buf; + ushort *array; + } defs; + ushort def_vals[_WAPI_SHARED_SEM_COUNT]; + int i; - fd=_wapi_shm_file_open (filename, sizeof(struct _WapiFileShareLayout)); - if(fd==-1) { - g_critical ("%s: shared file [%s] open error", __func__, - filename); - return(NULL); + for (i = 0; i < _WAPI_SHARED_SEM_COUNT; i++) { + def_vals[i] = 1; } + defs.array = def_vals; - if(fstat (fd, &statbuf)==-1) { - g_critical ("%s: fstat error: %s", __func__, - g_strerror (errno)); - close (fd); - return(NULL); +again: + oldkey = _wapi_shared_layout->sem_key; + + if (oldkey == 0) { +#ifdef DEBUG + g_message ("%s: Creating with new key (0x%x)", __func__, key); +#endif + + /* The while loop attempts to make some sense of the + * bonkers 'think of a random number' method of + * picking a key without collision with other + * applications + */ + while ((_wapi_sem_id = semget (key, _WAPI_SHARED_SEM_COUNT, + IPC_CREAT | IPC_EXCL | 0600)) == -1) { + if (errno != EEXIST) { + g_warning ("%s: semget error: %s key 0x%x - trying again", __func__, g_strerror (errno), key); + } + + key++; +#ifdef DEBUG + g_message ("%s: Got (%s), trying with new key (0x%x)", + __func__, g_strerror (errno), key); +#endif + } + /* Got a semaphore array, so initialise it and install + * the key into the shared memory + */ + + if (semctl (_wapi_sem_id, 0, SETALL, defs) == -1) { + g_warning ("%s: semctl init error: %s - trying again", __func__, g_strerror (errno)); + + /* Something went horribly wrong, so try + * getting a new set from scratch + */ + semctl (_wapi_sem_id, 0, IPC_RMID); + goto again; + } + + if (InterlockedCompareExchange (&_wapi_shared_layout->sem_key, + key, 0) != 0) { + /* Someone else created one and installed the + * key while we were working, so delete the + * array we created and fall through to the + * 'key already known' case. + */ + semctl (_wapi_sem_id, 0, IPC_RMID); + oldkey = _wapi_shared_layout->sem_key; + } else { + /* We've installed this semaphore set's key into + * the shared memory + */ + return; + } } - shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, - fd, 0); - if(shm_seg==MAP_FAILED) { - g_critical ("%s: mmap error: %s", __func__, - g_strerror (errno)); - close (fd); - return(NULL); - } +#ifdef DEBUG + g_message ("%s: Trying with old key 0x%x", __func__, oldkey); +#endif + + _wapi_sem_id = semget (oldkey, _WAPI_SHARED_SEM_COUNT, 0600); + if (_wapi_sem_id == -1) { + g_warning ("%s: semget error opening old key 0x%x (%s) - trying again", __func__, oldkey, g_strerror (errno)); + + /* Someone must have deleted the semaphore set, so + * blow away the bad key and try again + */ + InterlockedCompareExchange (&_wapi_shared_layout->sem_key, 0, + oldkey); - close (fd); - return(shm_seg); + goto again; + } +} + +int _wapi_shm_sem_lock (int sem) +{ + struct sembuf ops; + int ret; + +#ifdef DEBUG + g_message ("%s: locking sem %d", __func__, sem); +#endif + + ops.sem_num = sem; + ops.sem_op = -1; + ops.sem_flg = SEM_UNDO; + + do { + ret = semop (_wapi_sem_id, &ops, 1); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* Turn this into a pthreads-style return value */ + ret = errno; + } + +#ifdef DEBUG + g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret)); +#endif + + return(ret); } + +int _wapi_shm_sem_trylock (int sem) +{ + struct sembuf ops; + int ret; + +#ifdef DEBUG + g_message ("%s: trying to lock sem %d", __func__, sem); +#endif + + ops.sem_num = sem; + ops.sem_op = -1; + ops.sem_flg = IPC_NOWAIT | SEM_UNDO; + + do { + ret = semop (_wapi_sem_id, &ops, 1); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* Turn this into a pthreads-style return value */ + ret = errno; + } + + if (ret == EAGAIN) { + /* But pthreads uses this code instead */ + ret = EBUSY; + } + +#ifdef DEBUG + g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret)); +#endif + + return(ret); +} + +int _wapi_shm_sem_unlock (int sem) +{ + struct sembuf ops; + int ret; + +#ifdef DEBUG + g_message ("%s: unlocking sem %d", __func__, sem); +#endif + + ops.sem_num = sem; + ops.sem_op = 1; + ops.sem_flg = SEM_UNDO; + + do { + ret = semop (_wapi_sem_id, &ops, 1); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* Turn this into a pthreads-style return value */ + ret = errno; + } + +#ifdef DEBUG + g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret)); +#endif + + return(ret); +} + diff --git a/mono/io-layer/shared.h b/mono/io-layer/shared.h index 91af59ae9b7c4..af31fe1d9085d 100644 --- a/mono/io-layer/shared.h +++ b/mono/io-layer/shared.h @@ -12,7 +12,15 @@ #include -extern gpointer _wapi_shm_attach (void); -extern gpointer _wapi_fileshare_shm_attach (void); +typedef enum { + WAPI_SHM_DATA, + WAPI_SHM_FILESHARE +} _wapi_shm_t; + +extern gpointer _wapi_shm_attach (_wapi_shm_t type); +extern void _wapi_shm_semaphores_init (void); +extern int _wapi_shm_sem_lock (int sem); +extern int _wapi_shm_sem_trylock (int sem); +extern int _wapi_shm_sem_unlock (int sem); #endif /* _WAPI_SHARED_H_ */ diff --git a/mono/io-layer/wait.c b/mono/io-layer/wait.c index d66f8994269b3..485a14e5b6440 100644 --- a/mono/io-layer/wait.c +++ b/mono/io-layer/wait.c @@ -25,10 +25,9 @@ static gboolean own_if_signalled(gpointer handle) { gboolean ret = FALSE; - guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - if (_wapi_handle_shared_trylock_handle (handle, now) == EBUSY) { + if (_wapi_handle_trylock_shared_handles () == EBUSY) { return (FALSE); } } @@ -39,7 +38,7 @@ static gboolean own_if_signalled(gpointer handle) } if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - _wapi_handle_shared_unlock_handle (handle, now); + _wapi_handle_unlock_shared_handles (); } return(ret); @@ -48,10 +47,9 @@ static gboolean own_if_signalled(gpointer handle) static gboolean own_if_owned(gpointer handle) { gboolean ret = FALSE; - guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - if (_wapi_handle_shared_trylock_handle (handle, now) == EBUSY) { + if (_wapi_handle_trylock_shared_handles () == EBUSY) { return (FALSE); } } @@ -62,7 +60,7 @@ static gboolean own_if_owned(gpointer handle) } if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { - _wapi_handle_shared_unlock_handle (handle, now); + _wapi_handle_unlock_shared_handles (); } return(ret); @@ -412,15 +410,13 @@ struct handle_cleanup_data { guint32 numobjects; gpointer *handles; - guint32 now; }; static void handle_cleanup (void *data) { struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data; - _wapi_handle_unlock_handles (handles->numobjects, handles->handles, - handles->now); + _wapi_handle_unlock_handles (handles->numobjects, handles->handles); } static gboolean test_and_own (guint32 numobjects, gpointer *handles, @@ -439,8 +435,7 @@ static gboolean test_and_own (guint32 numobjects, gpointer *handles, pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data); done = _wapi_handle_count_signalled_handles (numobjects, handles, - waitall, count, lowest, - &cleanup_data.now); + waitall, count, lowest); if (done == TRUE) { if (waitall == TRUE) { for (i = 0; i < numobjects; i++) { diff --git a/mono/io-layer/wapi-private.h b/mono/io-layer/wapi-private.h index 33f1b56948291..33f7c8f8e4dbe 100644 --- a/mono/io-layer/wapi-private.h +++ b/mono/io-layer/wapi-private.h @@ -24,7 +24,7 @@ /* Increment this whenever an incompatible change is made to the * shared handle structure. */ -#define _WAPI_HANDLE_VERSION 5 +#define _WAPI_HANDLE_VERSION 6 typedef enum { WAPI_HANDLE_UNUSED=0, @@ -142,7 +142,6 @@ struct _WapiHandleSharedMetadata volatile guint32 offset; guint32 timestamp; volatile gboolean signalled; - volatile guint32 checking; }; struct _WapiHandleShared @@ -161,13 +160,17 @@ struct _WapiHandleShared } u; }; +#define _WAPI_SHARED_SEM_NAMESPACE 0 +#define _WAPI_SHARED_SEM_COLLECTION 1 +#define _WAPI_SHARED_SEM_SHARE 2 +#define _WAPI_SHARED_SEM_HANDLE 3 +#define _WAPI_SHARED_SEM_COUNT 8 /* Leave some future expansion space */ + struct _WapiHandleSharedLayout { - guint32 namespace_check; volatile guint32 signal_count; - - guint32 master_timestamp; volatile guint32 collection_count; + volatile key_t sem_key; struct _WapiHandleSharedMetadata metadata[_WAPI_HANDLE_INITIAL_COUNT]; struct _WapiHandleShared handles[_WAPI_HANDLE_INITIAL_COUNT]; @@ -188,7 +191,6 @@ struct _WapiFileShare struct _WapiFileShareLayout { - guint32 share_check; guint32 hwm; struct _WapiFileShare share_info[_WAPI_FILESHARE_SIZE];