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