Merge pull request #1840 from ludovic-henry/iolayer-thread-interrupt
[mono.git] / mono / io-layer / handles.c
index 32e2e33568d6d6620a405b9737b3022f267a0f6b..63d225aab6cfa63cf1e7ad9f665a894e4b54bfb3 100644 (file)
@@ -13,7 +13,9 @@
 #include <pthread.h>
 #include <errno.h>
 #include <unistd.h>
+#ifdef HAVE_SIGNAL_H
 #include <signal.h>
+#endif
 #include <string.h>
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
@@ -29,6 +31,9 @@
 #  include <dirent.h>
 #endif
 #include <sys/stat.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#  include <sys/resource.h>
+#endif
 
 #include <mono/io-layer/wapi.h>
 #include <mono/io-layer/wapi-private.h>
 #include <mono/io-layer/shared.h>
 #include <mono/io-layer/collection.h>
 #include <mono/io-layer/process-private.h>
-#include <mono/io-layer/critical-section-private.h>
 
 #include <mono/utils/mono-mutex.h>
+#include <mono/utils/mono-proclib.h>
+#include <mono/utils/mono-threads.h>
 #undef DEBUG_REFS
 
 #if 0
@@ -214,6 +220,12 @@ static void handle_cleanup (void)
                g_free (_wapi_private_handles [i]);
 }
 
+int
+wapi_getdtablesize (void)
+{
+       return eg_getdtablesize ();
+}
+
 /*
  * wapi_init:
  *
@@ -224,8 +236,8 @@ wapi_init (void)
 {
        g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
                  == WAPI_HANDLE_COUNT);
-       
-       _wapi_fd_reserve = getdtablesize();
+
+       _wapi_fd_reserve = wapi_getdtablesize ();
 
        /* This is needed by the code in _wapi_handle_new_internal */
        _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
@@ -267,13 +279,7 @@ wapi_init (void)
        _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
        _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
 
-
-       /* Using g_atexit here instead of an explicit function call in
-        * a cleanup routine lets us cope when a third-party library
-        * calls exit (eg if an X client loses the connection to its
-        * server.)
-        */
-       g_atexit (handle_cleanup);
+       wapi_processes_init ();
 }
 
 void
@@ -286,6 +292,7 @@ wapi_cleanup (void)
        _wapi_error_cleanup ();
        _wapi_thread_cleanup ();
        wapi_processes_cleanup ();
+       handle_cleanup ();
 }
 
 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
@@ -1497,6 +1504,13 @@ static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex
        int ret;
 
        if (!alertable) {
+               /*
+                * pthread_cond_(timed)wait() can return 0 even if the condition was not
+                * signalled.  This happens at least on Darwin.  We surface this, i.e., we
+                * get spurious wake-ups.
+                *
+                * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
+                */
                if (timeout)
                        ret=mono_cond_timedwait (cond, mutex, timeout);
                else
@@ -1524,29 +1538,50 @@ static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex
        return(ret);
 }
 
-int _wapi_handle_wait_signal (gboolean poll)
+int
+_wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll, gboolean *alerted)
 {
-       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
+       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll, alerted);
 }
 
-int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
+static void
+signal_handle_and_unref (gpointer handle)
 {
-       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
-}
+       pthread_cond_t *cond;
+       mono_mutex_t *mutex;
+       guint32 idx;
 
-int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
-{
-       DEBUG ("%s: waiting for %p", __func__, handle);
-       
-       return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
+       g_assert (handle);
+
+       /* If we reach here, then interrupt token is set to the flag value, which
+        * means that the target thread is either
+        * - before the first CAS in timedwait, which means it won't enter the wait.
+        * - it is after the first CAS, so it is already waiting, or it will enter
+        *    the wait, and it will be interrupted by the broadcast. */
+       idx = GPOINTER_TO_UINT (handle);
+       cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
+       mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
+
+       mono_mutex_lock (mutex);
+       mono_cond_broadcast (cond);
+       mono_mutex_unlock (mutex);
+
+       _wapi_handle_unref (handle);
 }
 
-int _wapi_handle_timedwait_signal_handle (gpointer handle,
-                                                                                 struct timespec *timeout, gboolean alertable, gboolean poll)
+int
+_wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout,
+               gboolean alertable, gboolean poll, gboolean *alerted)
 {
        DEBUG ("%s: waiting for %p (type %s)", __func__, handle,
                   _wapi_handle_typename[_wapi_handle_type (handle)]);
-       
+
+       if (alertable)
+               g_assert (alerted);
+
+       if (alerted)
+               *alerted = FALSE;
+
        if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
                if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
                        return (0);
@@ -1580,8 +1615,12 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle,
                pthread_cond_t *cond;
                mono_mutex_t *mutex;
 
-               if (alertable && !wapi_thread_set_wait_handle (handle))
-                       return 0;
+               if (alertable) {
+                       mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
+                       if (*alerted)
+                               return 0;
+                       _wapi_handle_ref (handle);
+               }
 
                cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
                mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
@@ -1596,8 +1635,13 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle,
                                res = mono_cond_wait (cond, mutex);
                }
 
-               if (alertable)
-                       wapi_thread_clear_wait_handle (handle);
+               if (alertable) {
+                       mono_thread_info_uninstall_interrupt (alerted);
+                       if (!*alerted) {
+                               /* if it is alerted, then the handle is unref in the interrupt callback */
+                               _wapi_handle_unref (handle);
+                       }
+               }
 
                return res;
        }
@@ -1633,7 +1677,7 @@ wapi_share_info_hash (gconstpointer data)
        return s->inode;
 }
 
-gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
+gboolean _wapi_handle_get_or_set_share (guint64 device, guint64 inode,
                                        guint32 new_sharemode,
                                        guint32 new_access,
                                        guint32 *old_sharemode,
@@ -1774,7 +1818,7 @@ static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
 {
 #if defined(__native_client__)
        g_assert_not_reached ();
-#else
+#elif defined(HAVE_KILL)
        if (kill (share_info->opened_by_pid, 0) == -1 &&
            (errno == ESRCH ||
             errno == EPERM)) {
@@ -1800,8 +1844,6 @@ static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
 {
        gboolean found = FALSE, proc_fds = FALSE;
-       pid_t self = _wapi_getpid ();
-       int pid;
        int thr_ret, i;
        
        /* Prevents entries from expiring under us if we remove this
@@ -1842,65 +1884,6 @@ void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
                }
        }
 
-       for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
-               struct _WapiHandleShared *shared;
-               struct _WapiHandle_process *process_handle;
-
-               shared = &_wapi_shared_layout->handles[i];
-               
-               if (shared->type == WAPI_HANDLE_PROCESS) {
-                       DIR *fd_dir;
-                       struct dirent *fd_entry;
-                       char subdir[_POSIX_PATH_MAX];
-
-                       process_handle = &shared->u.process;
-                       pid = process_handle->id;
-               
-                       /* Look in /proc/<pid>/fd/ but ignore
-                        * /proc/<our pid>/fd/<fd>, as we have the
-                        * file open too
-                        */
-                       g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
-                                   pid);
-                       
-                       fd_dir = opendir (subdir);
-                       if (fd_dir == NULL) {
-                               continue;
-                       }
-
-                       DEBUG ("%s: Looking in %s", __func__, subdir);
-                       
-                       proc_fds = TRUE;
-                       
-                       while ((fd_entry = readdir (fd_dir)) != NULL) {
-                               char path[_POSIX_PATH_MAX];
-                               struct stat link_stat;
-                               
-                               if (!strcmp (fd_entry->d_name, ".") ||
-                                   !strcmp (fd_entry->d_name, "..") ||
-                                   (pid == self &&
-                                    fd == atoi (fd_entry->d_name))) {
-                                       continue;
-                               }
-
-                               g_snprintf (path, _POSIX_PATH_MAX,
-                                           "/proc/%d/fd/%s", pid,
-                                           fd_entry->d_name);
-                               
-                               stat (path, &link_stat);
-                               if (link_stat.st_dev == share_info->device &&
-                                   link_stat.st_ino == share_info->inode) {
-                                       DEBUG ("%s:  Found it at %s",
-                                                  __func__, path);
-
-                                       found = TRUE;
-                               }
-                       }
-                       
-                       closedir (fd_dir);
-               }
-       }
-
        if (proc_fds == FALSE) {
                _wapi_handle_check_share_by_pid (share_info);
        } else if (found == FALSE) {