+
+static void
+mono_processes_cleanup (void)
+{
+ struct MonoProcess *mp;
+ struct MonoProcess *prev = NULL;
+ struct MonoProcess *candidate = NULL;
+ gpointer unref_handle;
+ int spin;
+
+ DEBUG ("%s", __func__);
+
+ /* Ensure we're not in here in multiple threads at once, nor recursive. */
+ if (InterlockedCompareExchange (&mono_processes_cleaning_up, 1, 0) != 0)
+ return;
+
+ mp = mono_processes;
+ while (mp != NULL) {
+ if (mp->pid == 0 && mp->handle != NULL) {
+ /* This process has exited and we need to remove the artifical ref
+ * on the handle */
+ mono_mutex_lock (&mono_processes_mutex);
+ unref_handle = mp->handle;
+ mp->handle = NULL;
+ mono_mutex_unlock (&mono_processes_mutex);
+ if (unref_handle)
+ _wapi_handle_unref (unref_handle);
+ continue;
+ }
+ mp = mp->next;
+ }
+
+ mp = mono_processes;
+ spin = 0;
+ while (mp != NULL) {
+ if ((mp->handle_count == 0 && mp->pid == 0) || candidate != NULL) {
+ if (spin > 0) {
+ _wapi_handle_spin (spin);
+ spin <<= 1;
+ }
+
+ /* We've found a candidate */
+ mono_mutex_lock (&mono_processes_mutex);
+ if (candidate == NULL) {
+ /* unlink it */
+ if (mp == mono_processes) {
+ mono_processes = mp->next;
+ } else {
+ prev->next = mp->next;
+ }
+ candidate = mp;
+ }
+
+ /* It's still safe to traverse the structure.*/
+ mono_memory_barrier ();
+
+ if (mono_processes_read_lock != 0) {
+ /* The sigchld handler is watching us. Spin a bit and try again */
+ if (spin == 0) {
+ spin = 1;
+ } else if (spin >= 8) {
+ /* Just give up for now */
+ mono_mutex_unlock (&mono_processes_mutex);
+ break;
+ }
+ } else {
+ /* We've modified the list of processes, and we know the sigchld handler
+ * isn't executing, so even if it executes at any moment, it'll see the
+ * new version of the list. So now we can free the candidate. */
+ DEBUG ("%s: freeing candidate %p", __func__, candidate);
+ mp = candidate->next;
+ MONO_SEM_DESTROY (&candidate->exit_sem);
+ g_free (candidate);
+ candidate = NULL;
+ }
+
+ mono_mutex_unlock (&mono_processes_mutex);
+
+ continue;
+ }
+ spin = 0;
+ prev = mp;
+ mp = mp->next;
+ }
+
+ DEBUG ("%s done", __func__);
+
+ InterlockedDecrement (&mono_processes_cleaning_up);
+}
+
+static void
+process_close (gpointer handle, gpointer data)
+{
+ struct _WapiHandle_process *process_handle;
+
+ DEBUG ("%s", __func__);
+
+ process_handle = (struct _WapiHandle_process *) data;
+ if (process_handle->mono_process && process_handle->self == _wapi_getpid ())
+ InterlockedDecrement (&process_handle->mono_process->handle_count);
+ mono_processes_cleanup ();
+}
+
+#if HAVE_SIGACTION
+MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
+{
+ int status;
+ int pid;
+ struct MonoProcess *p;
+
+#if DEBUG
+ fprintf (stdout, "SIG CHILD handler for pid: %i\n", info->si_pid);
+#endif
+
+ InterlockedIncrement (&mono_processes_read_lock);
+
+ do {
+ do {
+ pid = waitpid (-1, &status, WNOHANG);
+ } while (pid == -1 && errno == EINTR);
+
+ if (pid <= 0)
+ break;
+
+#if DEBUG
+ fprintf (stdout, "child ended: %i", pid);
+#endif
+ p = mono_processes;
+ while (p != NULL) {
+ if (p->pid == pid) {
+ p->pid = 0; /* this pid doesn't exist anymore, clear it */
+ p->status = status;
+ MONO_SEM_POST (&p->exit_sem);
+ break;
+ }
+ p = p->next;
+ }
+ } while (1);
+
+ InterlockedDecrement (&mono_processes_read_lock);
+
+#if DEBUG
+ fprintf (stdout, "SIG CHILD handler: done looping.");
+#endif
+}
+
+#endif
+
+static void process_add_sigchld_handler (void)
+{
+#if HAVE_SIGACTION
+ 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, &previous_chld_sa) != -1);
+ DEBUG ("Added SIGCHLD handler");
+#endif
+}
+
+static guint32 process_wait (gpointer handle, guint32 timeout, gboolean alertable)
+{
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ pid_t pid, ret;
+ int status;
+ guint32 start;
+ guint32 now;
+ struct MonoProcess *mp;
+ gboolean spin;
+ gpointer current_thread;
+
+ current_thread = _wapi_thread_handle_from_id (pthread_self ());
+ if (current_thread == NULL) {
+ SetLastError (ERROR_INVALID_HANDLE);
+ return WAIT_FAILED;
+ }
+
+ /* FIXME: We can now easily wait on processes that aren't our own children,
+ * but WaitFor*Object won't call us for pseudo handles. */
+ g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+
+ DEBUG ("%s (%p, %u)", __func__, handle, timeout);
+
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle);
+ if (ok == FALSE) {
+ g_warning ("%s: error looking up process handle %p", __func__, handle);
+ return WAIT_FAILED;
+ }
+
+ if (process_handle->exited) {
+ /* We've already done this one */
+ DEBUG ("%s (%p, %u): Process already exited", __func__, handle, timeout);
+ return WAIT_OBJECT_0;
+ }
+
+ pid = process_handle->id;
+
+ DEBUG ("%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
+
+ /* We don't need to lock mono_processes here, the entry
+ * has a handle_count > 0 which means it will not be freed. */
+ mp = process_handle->mono_process;
+ if (mp && process_handle->self != _wapi_getpid ()) {
+ /* mono_process points to memory in another process' address space: we can't use it */
+ mp = NULL;
+ }
+
+ start = mono_msec_ticks ();
+ now = start;
+ spin = mp == NULL;
+
+ while (1) {
+ if (mp != NULL) {
+ /* We have a semaphore we can wait on */
+ if (timeout != INFINITE) {
+ DEBUG ("%s (%p, %u): waiting on semaphore for %li ms...",
+ __func__, handle, timeout, (timeout - (now - start)));
+
+ ret = MONO_SEM_TIMEDWAIT_ALERTABLE (&mp->exit_sem, (timeout - (now - start)), alertable);
+ } else {
+ DEBUG ("%s (%p, %u): waiting on semaphore forever...",
+ __func__, handle, timeout);
+ ret = MONO_SEM_WAIT_ALERTABLE (&mp->exit_sem, alertable);
+ }
+
+ if (ret == -1 && errno != EINTR && errno != ETIMEDOUT) {
+ DEBUG ("%s (%p, %u): sem_timedwait failure: %s",
+ __func__, handle, timeout, g_strerror (errno));
+ /* Should we return a failure here? */
+ }
+
+ if (ret == 0) {
+ /* Success, process has exited */
+ MONO_SEM_POST (&mp->exit_sem);
+ break;
+ }
+ } else {
+ /* We did not create this process, so we can't waidpid / sem_wait it.
+ * We need to poll for the pid existence */
+ DEBUG ("%s (%p, %u): polling on pid...", __func__, handle, timeout);
+ if (!is_pid_valid (pid)) {
+ /* Success, process has exited */
+ break;
+ }
+ }
+
+ if (timeout == 0) {
+ DEBUG ("%s (%p, %u): WAIT_TIMEOUT (timeout = 0)", __func__, handle, timeout);
+ return WAIT_TIMEOUT;
+ }
+
+ now = mono_msec_ticks ();
+ if (now - start >= timeout) {
+ DEBUG ("%s (%p, %u): WAIT_TIMEOUT", __func__, handle, timeout);
+ return WAIT_TIMEOUT;
+ }
+
+ if (spin) {
+ /* "timeout - (now - start)" will not underflow, since timeout is always >=0,
+ * and we passed the check just above */
+ _wapi_handle_spin (MIN (100, timeout - (now - start)));
+ }
+
+ if (alertable && _wapi_thread_apc_pending (current_thread)) {
+ DEBUG ("%s (%p, %u): WAIT_IO_COMPLETION", __func__, handle, timeout);
+ return WAIT_IO_COMPLETION;
+ }
+ }
+
+ /* Process must have exited */
+ DEBUG ("%s (%p, %u): Waited successfully", __func__, handle, timeout);
+
+ ret = _wapi_handle_lock_shared_handles ();
+ g_assert (ret == 0);
+
+ status = mp ? mp->status : 0;
+ if (WIFSIGNALED (status)) {
+ process_handle->exitstatus = 128 + WTERMSIG (status);
+ } else {
+ process_handle->exitstatus = WEXITSTATUS (status);
+ }
+ _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
+
+ process_handle->exited = TRUE;
+
+ DEBUG ("%s (%p, %u): Setting pid %d signalled, exit status %d",
+ __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
+
+ _wapi_shared_handle_set_signal_state (handle, TRUE);
+
+ _wapi_handle_unlock_shared_handles ();
+
+ return WAIT_OBJECT_0;
+}
+