1 
2 #include "../processx.h"
3 
4 processx__child_list_t child_list_head = { 0, 0, 0 };
5 processx__child_list_t *child_list = &child_list_head;
6 processx__child_list_t child_free_list_head = { 0, 0, 0 };
7 processx__child_list_t *child_free_list = &child_free_list_head;
8 
processx__freelist_add(processx__child_list_t * ptr)9 void processx__freelist_add(processx__child_list_t *ptr) {
10   ptr->next = child_free_list->next;
11   child_free_list->next = ptr;
12 }
13 
14 /* This is not a race condition with the SIGCHLD handler, because this
15    function is only called with the handler blocked, from processx.c */
16 
processx__freelist_free()17 void processx__freelist_free() {
18   processx__child_list_t *ptr = child_free_list->next;
19   while (ptr) {
20     processx__child_list_t *next = ptr->next;
21     R_ReleaseObject(ptr->weak_status);
22     free(ptr);
23     ptr = next;
24   }
25   child_free_list->next = 0;
26 }
27 
processx__child_finalizer(SEXP x)28 void processx__child_finalizer(SEXP x) {
29   /* Nothing to do here, there is a finalizer on the xPTR */
30 }
31 
processx__child_add(pid_t pid,SEXP status)32 int processx__child_add(pid_t pid, SEXP status) {
33   processx__child_list_t *child = calloc(1, sizeof(processx__child_list_t));
34   SEXP weak_ref;
35   if (!child) return 1;
36 
37   weak_ref = R_MakeWeakRefC(status, R_NilValue, processx__child_finalizer, 1);
38 
39   child->pid = pid;
40   R_PreserveObject(weak_ref);
41   child->weak_status = weak_ref;
42   child->next = child_list->next;
43   child_list->next = child;
44   return 0;
45 }
46 
47 /* This is actually not used currently. But it should work fine. */
48 /* LCOV_EXCL_START */
49 
processx__child_remove(pid_t pid)50 void processx__child_remove(pid_t pid) {
51   processx__child_list_t *prev = child_list, *ptr = child_list->next;
52   while (ptr) {
53     if (ptr->pid == pid) {
54       prev->next = ptr->next;
55       /* Defer freeing the memory, because malloc/free are typically not
56 	 reentrant, and if we free in the SIGCHLD handler, that can cause
57 	 crashes. The test case in test-run.R (see comments there)
58 	 typically brings this out. */
59       processx__freelist_add(ptr);
60       return;
61     }
62     prev = ptr;
63     ptr = ptr->next;
64   }
65 }
66 
67 /* This is actually not used currently. But it should work fine. */
68 
processx__child_find(pid_t pid)69 processx__child_list_t *processx__child_find(pid_t pid) {
70   processx__child_list_t *ptr = child_list->next;
71   while (ptr) {
72     if (ptr->pid == pid) return ptr;
73     ptr = ptr->next;
74   }
75   return 0;
76 }
77 
78 /* LCOV_EXCL_STOP */
79 
processx__unload_cleanup()80 SEXP processx__unload_cleanup() {
81   processx__child_list_t *ptr = child_list->next;
82   int killed = 0;
83 
84   processx__remove_sigchld();
85 
86   while (ptr) {
87     processx__child_list_t *next = ptr->next;
88     SEXP status = R_WeakRefKey(ptr->weak_status);
89     processx_handle_t *handle =
90       isNull(status) ? 0 : (processx_handle_t*) R_ExternalPtrAddr(status);
91     int wp, wstat;
92 
93     if (handle && handle->cleanup) {
94       int ret = kill(ptr->pid, SIGKILL);
95       do {
96 	wp = waitpid(ptr->pid, &wstat, 0);
97       } while (wp == -1 && errno == EINTR);
98       if (ret == 0) killed++;
99     }
100 
101     /* waitpid errors are ignored here... */
102 
103     if (!isNull(status)) R_ClearExternalPtr(status);
104     /* The handle will be freed in the finalizer, otherwise there is
105        a race condition here. */
106 
107     free(ptr);
108 
109     ptr = next;
110   }
111 
112   child_list->next = 0;
113   processx__freelist_free();
114 
115   if (killed > 0) {
116     REprintf("Unloading processx shared library, killed %d processes\n",
117 	     killed);
118   }
119 
120   return R_NilValue;
121 }
122