Commit | Line | Data |
---|---|---|
6e9ec86d AW |
1 | /* Copyright (C) 2012 Free Software Foundation, Inc. |
2 | * | |
3 | * This library is free software; you can redistribute it and/or | |
4 | * modify it under the terms of the GNU Lesser General Public License | |
5 | * as published by the Free Software Foundation; either version 3 of | |
6 | * the License, or (at your option) any later version. | |
7 | * | |
8 | * This library is distributed in the hope that it will be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * Lesser General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU Lesser General Public | |
14 | * License along with this library; if not, write to the Free Software | |
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
16 | * 02110-1301 USA | |
17 | */ | |
18 | ||
19 | ||
20 | \f | |
21 | ||
22 | #ifdef HAVE_CONFIG_H | |
23 | # include <config.h> | |
24 | #endif | |
25 | ||
eaf99988 AW |
26 | #ifdef HAVE_UNISTD_H |
27 | #include <unistd.h> | |
28 | #endif | |
29 | #include <fcntl.h> | |
30 | ||
31 | #include <full-write.h> | |
32 | ||
6e9ec86d AW |
33 | #include "libguile/bdw-gc.h" |
34 | #include "libguile/_scm.h" | |
35 | #include "libguile/finalizers.h" | |
36 | #include "libguile/gc.h" | |
37 | #include "libguile/threads.h" | |
38 | ||
39 | \f | |
40 | ||
eaf99988 AW |
41 | static size_t finalization_count; |
42 | ||
43 | ||
44 | \f | |
45 | ||
bc612809 AW |
46 | #ifndef HAVE_GC_SET_FINALIZER_NOTIFIER |
47 | static void | |
48 | GC_set_finalizer_notifier (void (*notifier) (void)) | |
49 | { | |
50 | GC_finalizer_notifier = notifier; | |
51 | } | |
52 | #endif | |
53 | ||
54 | ||
55 | \f | |
56 | ||
6e9ec86d AW |
57 | void |
58 | scm_i_set_finalizer (void *obj, scm_t_finalizer_proc proc, void *data) | |
59 | { | |
60 | GC_finalization_proc prev; | |
61 | GC_PTR prev_data; | |
62 | GC_REGISTER_FINALIZER_NO_ORDER (obj, proc, data, &prev, &prev_data); | |
63 | } | |
64 | ||
65 | struct scm_t_chained_finalizer | |
66 | { | |
67 | int resuscitating_p; | |
68 | scm_t_finalizer_proc proc; | |
69 | void *data; | |
70 | scm_t_finalizer_proc prev; | |
71 | void *prev_data; | |
72 | }; | |
73 | ||
74 | static void | |
75 | chained_finalizer (void *obj, void *data) | |
76 | { | |
77 | struct scm_t_chained_finalizer *chained_data = data; | |
78 | if (chained_data->resuscitating_p) | |
79 | { | |
80 | if (chained_data->prev) | |
81 | scm_i_set_finalizer (obj, chained_data->prev, chained_data->prev_data); | |
82 | chained_data->proc (obj, chained_data->data); | |
83 | } | |
84 | else | |
85 | { | |
86 | chained_data->proc (obj, chained_data->data); | |
87 | if (chained_data->prev) | |
88 | chained_data->prev (obj, chained_data->prev_data); | |
89 | } | |
90 | } | |
91 | ||
92 | void | |
93 | scm_i_add_resuscitator (void *obj, scm_t_finalizer_proc proc, void *data) | |
94 | { | |
95 | struct scm_t_chained_finalizer *chained_data; | |
96 | chained_data = scm_gc_malloc (sizeof (*chained_data), "chained finalizer"); | |
97 | chained_data->resuscitating_p = 1; | |
98 | chained_data->proc = proc; | |
99 | chained_data->data = data; | |
100 | GC_REGISTER_FINALIZER_NO_ORDER (obj, chained_finalizer, chained_data, | |
101 | &chained_data->prev, | |
102 | &chained_data->prev_data); | |
103 | } | |
104 | ||
105 | static void | |
106 | shuffle_resuscitators_to_front (struct scm_t_chained_finalizer *cd) | |
107 | { | |
108 | while (cd->prev == chained_finalizer) | |
109 | { | |
110 | struct scm_t_chained_finalizer *prev = cd->prev_data; | |
111 | scm_t_finalizer_proc proc = cd->proc; | |
112 | void *data = cd->data; | |
113 | ||
114 | if (!prev->resuscitating_p) | |
115 | break; | |
116 | ||
117 | cd->resuscitating_p = 1; | |
118 | cd->proc = prev->proc; | |
119 | cd->data = prev->data; | |
120 | ||
121 | prev->resuscitating_p = 0; | |
122 | prev->proc = proc; | |
123 | prev->data = data; | |
124 | ||
125 | cd = prev; | |
126 | } | |
127 | } | |
128 | ||
129 | void | |
130 | scm_i_add_finalizer (void *obj, scm_t_finalizer_proc proc, void *data) | |
131 | { | |
132 | struct scm_t_chained_finalizer *chained_data; | |
133 | chained_data = scm_gc_malloc (sizeof (*chained_data), "chained finalizer"); | |
134 | chained_data->resuscitating_p = 0; | |
135 | chained_data->proc = proc; | |
136 | chained_data->data = data; | |
137 | GC_REGISTER_FINALIZER_NO_ORDER (obj, chained_finalizer, chained_data, | |
138 | &chained_data->prev, | |
139 | &chained_data->prev_data); | |
140 | shuffle_resuscitators_to_front (chained_data); | |
141 | } | |
142 | ||
eaf99988 | 143 | |
6e9ec86d AW |
144 | \f |
145 | ||
eaf99988 AW |
146 | static SCM finalizer_async_cell; |
147 | ||
148 | static SCM | |
149 | run_finalizers_async_thunk (void) | |
150 | { | |
151 | finalization_count += GC_invoke_finalizers (); | |
152 | return SCM_UNSPECIFIED; | |
153 | } | |
154 | ||
155 | ||
f740445a AW |
156 | /* The function queue_finalizer_async is run by the GC when there are |
157 | * objects to finalize. It will enqueue an asynchronous call to | |
158 | * GC_invoke_finalizers() at the next SCM_TICK in this thread. | |
eaf99988 AW |
159 | */ |
160 | static void | |
161 | queue_finalizer_async (void) | |
162 | { | |
163 | scm_i_thread *t = SCM_I_CURRENT_THREAD; | |
164 | static scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER; | |
165 | ||
166 | scm_i_pthread_mutex_lock (&lock); | |
f740445a AW |
167 | /* If t is NULL, that could be because we're allocating in |
168 | threads.c:guilify_self_1. In that case, rely on the | |
169 | GC_invoke_finalizers call there after the thread spins up. */ | |
170 | if (t && scm_is_false (SCM_CDR (finalizer_async_cell))) | |
eaf99988 AW |
171 | { |
172 | SCM_SETCDR (finalizer_async_cell, t->active_asyncs); | |
173 | t->active_asyncs = finalizer_async_cell; | |
174 | t->pending_asyncs = 1; | |
175 | } | |
176 | scm_i_pthread_mutex_unlock (&lock); | |
177 | } | |
178 | ||
179 | ||
180 | \f | |
181 | ||
182 | #if SCM_USE_PTHREAD_THREADS | |
183 | ||
184 | static int finalization_pipe[2]; | |
185 | static scm_i_pthread_mutex_t finalization_thread_lock = | |
186 | SCM_I_PTHREAD_MUTEX_INITIALIZER; | |
2dcf6b59 | 187 | static pthread_t finalization_thread; |
eaf99988 AW |
188 | |
189 | static void | |
190 | notify_finalizers_to_run (void) | |
191 | { | |
192 | char byte = 0; | |
193 | full_write (finalization_pipe[1], &byte, 1); | |
194 | } | |
195 | ||
196 | static void | |
197 | notify_about_to_fork (void) | |
198 | { | |
199 | char byte = 1; | |
200 | full_write (finalization_pipe[1], &byte, 1); | |
201 | } | |
202 | ||
203 | struct finalization_pipe_data | |
204 | { | |
205 | char byte; | |
206 | ssize_t n; | |
207 | int err; | |
208 | }; | |
209 | ||
210 | static void* | |
211 | read_finalization_pipe_data (void *data) | |
212 | { | |
213 | struct finalization_pipe_data *fdata = data; | |
214 | ||
215 | fdata->n = read (finalization_pipe[0], &fdata->byte, 1); | |
216 | fdata->err = errno; | |
217 | ||
218 | return NULL; | |
219 | } | |
220 | ||
2dcf6b59 | 221 | static void* |
eaf99988 AW |
222 | finalization_thread_proc (void *unused) |
223 | { | |
224 | while (1) | |
225 | { | |
226 | struct finalization_pipe_data data; | |
227 | ||
228 | scm_without_guile (read_finalization_pipe_data, &data); | |
229 | ||
230 | if (data.n <= 0 && data.err != EINTR) | |
231 | { | |
2dcf6b59 AW |
232 | perror ("error in finalization thread"); |
233 | return NULL; | |
eaf99988 AW |
234 | } |
235 | ||
236 | switch (data.byte) | |
237 | { | |
238 | case 0: | |
239 | finalization_count += GC_invoke_finalizers (); | |
240 | break; | |
241 | case 1: | |
2dcf6b59 | 242 | return NULL; |
eaf99988 AW |
243 | default: |
244 | abort (); | |
245 | } | |
246 | } | |
247 | } | |
248 | ||
2dcf6b59 AW |
249 | static void* |
250 | run_finalization_thread (void *arg) | |
251 | { | |
252 | return scm_with_guile (finalization_thread_proc, arg); | |
253 | } | |
254 | ||
eaf99988 AW |
255 | static void |
256 | start_finalization_thread (void) | |
257 | { | |
258 | scm_i_pthread_mutex_lock (&finalization_thread_lock); | |
2dcf6b59 AW |
259 | if (!finalization_thread) |
260 | /* Use the raw pthread API and scm_with_guile, because we don't want | |
261 | to block on any lock that scm_spawn_thread might want to take, | |
262 | and we don't want to inherit the dynamic state (fluids) of the | |
263 | caller. */ | |
264 | if (pthread_create (&finalization_thread, NULL, | |
265 | run_finalization_thread, NULL)) | |
266 | perror ("error creating finalization thread"); | |
eaf99988 AW |
267 | scm_i_pthread_mutex_unlock (&finalization_thread_lock); |
268 | } | |
269 | ||
270 | static void | |
271 | stop_finalization_thread (void) | |
272 | { | |
273 | scm_i_pthread_mutex_lock (&finalization_thread_lock); | |
2dcf6b59 | 274 | if (finalization_thread) |
eaf99988 AW |
275 | { |
276 | notify_about_to_fork (); | |
2dcf6b59 AW |
277 | if (pthread_join (finalization_thread, NULL)) |
278 | perror ("joining finalization thread"); | |
279 | finalization_thread = 0; | |
eaf99988 AW |
280 | } |
281 | scm_i_pthread_mutex_unlock (&finalization_thread_lock); | |
282 | } | |
283 | ||
284 | static void | |
285 | spawn_finalizer_thread (void) | |
286 | { | |
287 | GC_set_finalizer_notifier (notify_finalizers_to_run); | |
288 | start_finalization_thread (); | |
289 | } | |
290 | ||
291 | #endif /* SCM_USE_PTHREAD_THREADS */ | |
292 | ||
293 | ||
294 | \f | |
295 | ||
296 | void | |
297 | scm_i_finalizer_pre_fork (void) | |
298 | { | |
299 | #if SCM_USE_PTHREAD_THREADS | |
300 | stop_finalization_thread (); | |
301 | GC_set_finalizer_notifier (spawn_finalizer_thread); | |
302 | #endif | |
303 | } | |
304 | ||
305 | ||
306 | \f | |
6e9ec86d AW |
307 | |
308 | void | |
309 | scm_init_finalizers (void) | |
310 | { | |
eaf99988 AW |
311 | /* When the async is to run, the cdr of the pair gets set to the |
312 | asyncs queue of the current thread. */ | |
313 | finalizer_async_cell = | |
314 | scm_cons (scm_c_make_gsubr ("%run-finalizers", 0, 0, 0, | |
315 | run_finalizers_async_thunk), | |
316 | SCM_BOOL_F); | |
317 | GC_set_finalizer_notifier (queue_finalizer_async); | |
318 | } | |
319 | ||
320 | void | |
321 | scm_init_finalizer_thread (void) | |
322 | { | |
323 | #if SCM_USE_PTHREAD_THREADS | |
324 | if (pipe2 (finalization_pipe, O_CLOEXEC) != 0) | |
325 | scm_syserror (NULL); | |
326 | GC_set_finalizer_notifier (spawn_finalizer_thread); | |
327 | #endif | |
6e9ec86d | 328 | } |