[w32handle] Remove use of w32handle for File, Console, Pipe and Socket (#5319)
[mono.git] / mono / metadata / w32process-unix.c
index 559939c9b61722492b2738121a798ca189c8a6e9..280c3a9213d0a90fd528961ca4c14d878eba7d76 100644 (file)
@@ -130,9 +130,7 @@ typedef struct {
 /*
  * Process describes processes we create.
  * It contains a semaphore that can be waited on in order to wait
- * for process termination. It's accessed in our SIGCHLD handler,
- * when status is updated (and pid cleared, to not clash with
- * subsequent processes that may get executed).
+ * for process termination.
  */
 typedef struct _Process {
        pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
@@ -143,7 +141,6 @@ typedef struct _Process {
         * the process has exited, so that the information there isn't lost.
         */
        gpointer handle;
-       gboolean freeable;
        gboolean signalled;
        struct _Process *next;
 } Process;
@@ -530,18 +527,6 @@ static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIA
 
 static gchar *cli_launcher;
 
-/* The signal-safe logic to use processes goes like this:
- * - The list must be safe to traverse for the signal handler at all times.
- *   It's safe to: prepend an entry (which is a single store to 'processes'),
- *   unlink an entry (assuming the unlinked entry isn't freed and doesn't
- *   change its 'next' pointer so that it can still be traversed).
- * When cleaning up we first unlink an entry, then we verify that
- * the read lock isn't locked. Then we can free the entry, since
- * we know that nobody is using the old version of the list (including
- * the unlinked entry).
- * We also need to lock when adding and cleaning up so that those two
- * operations don't mess with eachother. (This lock is not used in the
- * signal handler) */
 static Process *processes;
 static mono_mutex_t processes_mutex;
 
@@ -723,8 +708,6 @@ processes_cleanup (void)
        static gint32 cleaning_up;
        Process *process;
        Process *prev = NULL;
-       GSList *finished = NULL;
-       GSList *l;
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
 
@@ -732,54 +715,39 @@ processes_cleanup (void)
        if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
                return;
 
+       /*
+        * This needs to be done outside the lock but atomically, hence the CAS above.
+        */
        for (process = processes; process; process = process->next) {
                if (process->signalled && process->handle) {
                        /* This process has exited and we need to remove the artifical ref
                         * on the handle */
-                       mono_w32handle_unref (process->handle);
+                       mono_w32handle_close (process->handle);
                        process->handle = NULL;
                }
        }
 
-       /*
-        * Remove processes which exited from the processes list.
-        * We need to synchronize with the sigchld handler here, which runs
-        * asynchronously. The handler requires that the processes list
-        * remain valid.
-        */
        mono_os_mutex_lock (&processes_mutex);
 
-       for (process = processes; process; process = process->next) {
-               if (process->handle_count == 0 && process->freeable) {
+       for (process = processes; process;) {
+               Process *next = process->next;
+               if (process->handle_count == 0 && process->signalled) {
                        /*
                         * Unlink the entry.
-                        * This code can run parallel with the sigchld handler, but the
-                        * modifications it makes are safe.
                         */
                        if (process == processes)
                                processes = process->next;
                        else
                                prev->next = process->next;
-                       finished = g_slist_prepend (finished, process);
+
+                       mono_os_sem_destroy (&process->exit_sem);
+                       g_free (process);
                } else {
                        prev = process;
                }
+               process = next;
        }
 
-       mono_memory_barrier ();
-
-       for (l = finished; l; l = l->next) {
-               /*
-                * All the entries in the finished list are unlinked from processes, and
-                * they have the 'finished' flag set, which means the sigchld handler is done
-                * accessing them.
-                */
-               process = (Process *)l->data;
-               mono_os_sem_destroy (&process->exit_sem);
-               g_free (process);
-       }
-       g_slist_free (finished);
-
        mono_os_mutex_unlock (&processes_mutex);
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
@@ -862,7 +830,7 @@ mono_w32process_init (void)
        process_set_name (&process_handle);
 
        current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
-       g_assert (current_process);
+       g_assert (current_process != INVALID_HANDLE_VALUE);
 
        mono_os_mutex_init (&processes_mutex);
 }
@@ -965,8 +933,7 @@ get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointe
        if (mono_w32handle_issignalled (handle))
                return FALSE;
 
-       mono_w32handle_ref (handle);
-       foreach_data->handle = handle;
+       foreach_data->handle = mono_w32handle_duplicate (handle);
        return TRUE;
 }
 
@@ -1360,6 +1327,39 @@ switch_dir_separators (char *path)
 #if HAVE_SIGACTION
 
 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
+{
+       /*
+        * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
+        * mono_w32process_signal_finished ().
+        */
+       int old_errno = errno;
+
+       mono_gc_finalize_notify ();
+
+       errno = old_errno;
+}
+
+static void
+process_add_sigchld_handler (void)
+{
+       struct sigaction sa;
+
+       sa.sa_sigaction = mono_sigchld_signal_handler;
+       sigemptyset (&sa.sa_mask);
+       sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;
+       g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
+}
+
+#endif
+
+/*
+ * mono_w32process_signal_finished:
+ *
+ *   Signal the exit semaphore for processes which have finished.
+ */
+void
+mono_w32process_signal_finished (void)
 {
        int status;
        int pid;
@@ -1373,9 +1373,8 @@ MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, sigi
                if (pid <= 0)
                        break;
 
-               /*
-                * This can run concurrently with the code in the rest of this module.
-                */
+               mono_os_mutex_lock (&processes_mutex);
+
                for (process = processes; process; process = process->next) {
                        if (process->pid != pid)
                                continue;
@@ -1385,28 +1384,13 @@ MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, sigi
                        process->signalled = TRUE;
                        process->status = status;
                        mono_os_sem_post (&process->exit_sem);
-                       mono_memory_barrier ();
-                       /* Mark this as freeable, the pointer becomes invalid afterwards */
-                       process->freeable = TRUE;
                        break;
                }
-       } while (1);
-}
 
-static void
-process_add_sigchld_handler (void)
-{
-       struct sigaction sa;
-
-       sa.sa_sigaction = mono_sigchld_signal_handler;
-       sigemptyset (&sa.sa_mask);
-       sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
-       g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
+               mono_os_mutex_unlock (&processes_mutex);
+       } while (1);
 }
 
-#endif
-
 static gboolean
 is_readable_or_executable (const char *prog)
 {
@@ -1961,7 +1945,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline,
                dup2 (err_fd, 2);
 
                /* Close all file descriptors */
-               for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
+               for (i = eg_getdtablesize() - 1; i > 2; i--)
                        close (i);
 
 #ifdef DEBUG_ENABLED
@@ -2019,8 +2003,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline,
 
                /* Keep the process handle artificially alive until the process
                 * exits so that the information in the handle isn't lost. */
-               mono_w32handle_ref (handle);
-               process->handle = handle;
+               process->handle = mono_w32handle_duplicate (handle);
 
                mono_os_mutex_lock (&processes_mutex);
                process->next = processes;