Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / finalizers.c
CommitLineData
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
41static size_t finalization_count;
42
43
44\f
45
bc612809
AW
46#ifndef HAVE_GC_SET_FINALIZER_NOTIFIER
47static void
48GC_set_finalizer_notifier (void (*notifier) (void))
49{
50 GC_finalizer_notifier = notifier;
51}
52#endif
53
54
55\f
56
6e9ec86d
AW
57void
58scm_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
65struct 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
74static void
75chained_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
92void
93scm_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
105static void
106shuffle_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
129void
130scm_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
146static SCM finalizer_async_cell;
147
148static SCM
149run_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 */
160static void
161queue_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
184static int finalization_pipe[2];
185static scm_i_pthread_mutex_t finalization_thread_lock =
186 SCM_I_PTHREAD_MUTEX_INITIALIZER;
2dcf6b59 187static pthread_t finalization_thread;
eaf99988
AW
188
189static void
190notify_finalizers_to_run (void)
191{
192 char byte = 0;
193 full_write (finalization_pipe[1], &byte, 1);
194}
195
196static void
197notify_about_to_fork (void)
198{
199 char byte = 1;
200 full_write (finalization_pipe[1], &byte, 1);
201}
202
203struct finalization_pipe_data
204{
205 char byte;
206 ssize_t n;
207 int err;
208};
209
210static void*
211read_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 221static void*
eaf99988
AW
222finalization_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
249static void*
250run_finalization_thread (void *arg)
251{
252 return scm_with_guile (finalization_thread_proc, arg);
253}
254
eaf99988
AW
255static void
256start_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
270static void
271stop_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
284static void
285spawn_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
296void
297scm_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
308void
309scm_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
320void
321scm_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}