1 /* Copyright (C) 2012, 2013, 2014 Free Software Foundation, Inc.
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.
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.
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
31 #include <full-write.h>
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"
41 static int automatic_finalization_p
= 1;
43 static size_t finalization_count
;
49 scm_i_set_finalizer (void *obj
, scm_t_finalizer_proc proc
, void *data
)
51 GC_finalization_proc prev
;
53 GC_REGISTER_FINALIZER_NO_ORDER (obj
, proc
, data
, &prev
, &prev_data
);
56 struct scm_t_chained_finalizer
59 scm_t_finalizer_proc proc
;
61 scm_t_finalizer_proc prev
;
66 chained_finalizer (void *obj
, void *data
)
68 struct scm_t_chained_finalizer
*chained_data
= data
;
69 if (chained_data
->resuscitating_p
)
71 if (chained_data
->prev
)
72 scm_i_set_finalizer (obj
, chained_data
->prev
, chained_data
->prev_data
);
73 chained_data
->proc (obj
, chained_data
->data
);
77 chained_data
->proc (obj
, chained_data
->data
);
78 if (chained_data
->prev
)
79 chained_data
->prev (obj
, chained_data
->prev_data
);
84 scm_i_add_resuscitator (void *obj
, scm_t_finalizer_proc proc
, void *data
)
86 struct scm_t_chained_finalizer
*chained_data
;
87 chained_data
= scm_gc_malloc (sizeof (*chained_data
), "chained finalizer");
88 chained_data
->resuscitating_p
= 1;
89 chained_data
->proc
= proc
;
90 chained_data
->data
= data
;
91 GC_REGISTER_FINALIZER_NO_ORDER (obj
, chained_finalizer
, chained_data
,
93 &chained_data
->prev_data
);
97 shuffle_resuscitators_to_front (struct scm_t_chained_finalizer
*cd
)
99 while (cd
->prev
== chained_finalizer
)
101 struct scm_t_chained_finalizer
*prev
= cd
->prev_data
;
102 scm_t_finalizer_proc proc
= cd
->proc
;
103 void *data
= cd
->data
;
105 if (!prev
->resuscitating_p
)
108 cd
->resuscitating_p
= 1;
109 cd
->proc
= prev
->proc
;
110 cd
->data
= prev
->data
;
112 prev
->resuscitating_p
= 0;
121 scm_i_add_finalizer (void *obj
, scm_t_finalizer_proc proc
, void *data
)
123 struct scm_t_chained_finalizer
*chained_data
;
124 chained_data
= scm_gc_malloc (sizeof (*chained_data
), "chained finalizer");
125 chained_data
->resuscitating_p
= 0;
126 chained_data
->proc
= proc
;
127 chained_data
->data
= data
;
128 GC_REGISTER_FINALIZER_NO_ORDER (obj
, chained_finalizer
, chained_data
,
130 &chained_data
->prev_data
);
131 shuffle_resuscitators_to_front (chained_data
);
137 static SCM finalizer_async_cell
;
140 run_finalizers_async_thunk (void)
142 scm_run_finalizers ();
143 return SCM_UNSPECIFIED
;
147 /* The function queue_finalizer_async is run by the GC when there are
148 * objects to finalize. It will enqueue an asynchronous call to
149 * GC_invoke_finalizers() at the next SCM_TICK in this thread.
152 queue_finalizer_async (void)
154 scm_i_thread
*t
= SCM_I_CURRENT_THREAD
;
155 static scm_i_pthread_mutex_t lock
= SCM_I_PTHREAD_MUTEX_INITIALIZER
;
157 scm_i_pthread_mutex_lock (&lock
);
158 /* If t is NULL, that could be because we're allocating in
159 threads.c:guilify_self_1. In that case, rely on the
160 GC_invoke_finalizers call there after the thread spins up. */
161 if (t
&& scm_is_false (SCM_CDR (finalizer_async_cell
)))
163 SCM_SETCDR (finalizer_async_cell
, t
->active_asyncs
);
164 t
->active_asyncs
= finalizer_async_cell
;
165 t
->pending_asyncs
= 1;
167 scm_i_pthread_mutex_unlock (&lock
);
173 #if SCM_USE_PTHREAD_THREADS
175 static int finalization_pipe
[2];
176 static scm_i_pthread_mutex_t finalization_thread_lock
=
177 SCM_I_PTHREAD_MUTEX_INITIALIZER
;
178 static pthread_t finalization_thread
;
179 static int finalization_thread_is_running
= 0;
182 notify_finalizers_to_run (void)
185 full_write (finalization_pipe
[1], &byte
, 1);
189 notify_about_to_fork (void)
192 full_write (finalization_pipe
[1], &byte
, 1);
195 struct finalization_pipe_data
203 read_finalization_pipe_data (void *data
)
205 struct finalization_pipe_data
*fdata
= data
;
207 fdata
->n
= read (finalization_pipe
[0], &fdata
->byte
, 1);
214 finalization_thread_proc (void *unused
)
218 struct finalization_pipe_data data
;
220 scm_without_guile (read_finalization_pipe_data
, &data
);
222 if (data
.n
<= 0 && data
.err
!= EINTR
)
224 perror ("error in finalization thread");
231 scm_run_finalizers ();
242 run_finalization_thread (void *arg
)
244 return scm_with_guile (finalization_thread_proc
, arg
);
248 start_finalization_thread (void)
250 scm_i_pthread_mutex_lock (&finalization_thread_lock
);
251 if (!finalization_thread_is_running
)
253 /* Use the raw pthread API and scm_with_guile, because we don't want
254 to block on any lock that scm_spawn_thread might want to take,
255 and we don't want to inherit the dynamic state (fluids) of the
257 if (pthread_create (&finalization_thread
, NULL
,
258 run_finalization_thread
, NULL
))
259 perror ("error creating finalization thread");
261 finalization_thread_is_running
= 1;
263 scm_i_pthread_mutex_unlock (&finalization_thread_lock
);
267 stop_finalization_thread (void)
269 scm_i_pthread_mutex_lock (&finalization_thread_lock
);
270 if (finalization_thread_is_running
)
272 notify_about_to_fork ();
273 if (pthread_join (finalization_thread
, NULL
))
274 perror ("joining finalization thread");
275 finalization_thread_is_running
= 0;
277 scm_i_pthread_mutex_unlock (&finalization_thread_lock
);
281 spawn_finalizer_thread (void)
283 GC_set_finalizer_notifier (notify_finalizers_to_run
);
284 start_finalization_thread ();
287 #endif /* SCM_USE_PTHREAD_THREADS */
293 scm_i_finalizer_pre_fork (void)
295 #if SCM_USE_PTHREAD_THREADS
296 if (automatic_finalization_p
)
298 stop_finalization_thread ();
299 GC_set_finalizer_notifier (spawn_finalizer_thread
);
308 weak_pointer_ref (void *weak_pointer
)
310 return *(void **) weak_pointer
;
314 weak_gc_finalizer (void *ptr
, void *data
)
318 void (*callback
) (SCM
) = weak
[1];
320 val
= GC_call_with_alloc_lock (weak_pointer_ref
, &weak
[0]);
325 callback (SCM_PACK_POINTER (val
));
327 scm_i_set_finalizer (ptr
, weak_gc_finalizer
, data
);
330 /* CALLBACK will be called on OBJ, as long as OBJ is accessible. It
331 will be called from a finalizer, which may be from an async or from
334 As an implementation detail, the way this works is that we allocate
335 a fresh pointer-less object holding two words. We know that this
336 object should get collected the next time GC is run, so we attach a
337 finalizer to it so that we get a callback after GC happens.
339 The first word of the object holds a weak reference to OBJ, and the
340 second holds the callback pointer. When the callback is called, we
341 check if the weak reference on OBJ still holds. If it doesn't hold,
342 then OBJ is no longer accessible, and we're done. Otherwise we call
343 the callback and re-register a finalizer for our two-word GC object,
344 effectively resuscitating the object so that we will get a callback
347 We could use the scm_after_gc_hook, but using a finalizer has the
348 advantage of potentially running in another thread, decreasing pause
351 scm_i_register_weak_gc_callback (SCM obj
, void (*callback
) (SCM
))
353 void **weak
= GC_MALLOC_ATOMIC (sizeof (void*) * 2);
355 weak
[0] = SCM_UNPACK_POINTER (obj
);
356 weak
[1] = (void*)callback
;
357 GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak
, SCM2PTR (obj
));
359 scm_i_set_finalizer (weak
, weak_gc_finalizer
, NULL
);
364 scm_set_automatic_finalization_enabled (int enabled_p
)
366 int was_enabled_p
= automatic_finalization_p
;
368 if (enabled_p
== was_enabled_p
)
369 return was_enabled_p
;
371 if (!scm_initialized_p
)
373 automatic_finalization_p
= enabled_p
;
374 return was_enabled_p
;
379 #if SCM_USE_PTHREAD_THREADS
380 if (pipe2 (finalization_pipe
, O_CLOEXEC
) != 0)
382 GC_set_finalizer_notifier (spawn_finalizer_thread
);
384 GC_set_finalizer_notifier (queue_finalizer_async
);
389 GC_set_finalizer_notifier (0);
391 #if SCM_USE_PTHREAD_THREADS
392 stop_finalization_thread ();
393 close (finalization_pipe
[0]);
394 close (finalization_pipe
[1]);
395 finalization_pipe
[0] = -1;
396 finalization_pipe
[1] = -1;
400 automatic_finalization_p
= enabled_p
;
402 return was_enabled_p
;
406 scm_run_finalizers (void)
408 int finalized
= GC_invoke_finalizers ();
410 finalization_count
+= finalized
;
419 scm_init_finalizers (void)
421 /* When the async is to run, the cdr of the pair gets set to the
422 asyncs queue of the current thread. */
423 finalizer_async_cell
=
424 scm_cons (scm_c_make_gsubr ("%run-finalizers", 0, 0, 0,
425 run_finalizers_async_thunk
),
428 if (automatic_finalization_p
)
429 GC_set_finalizer_notifier (queue_finalizer_async
);
433 scm_init_finalizer_thread (void)
435 #if SCM_USE_PTHREAD_THREADS
436 if (automatic_finalization_p
)
438 if (pipe2 (finalization_pipe
, O_CLOEXEC
) != 0)
440 GC_set_finalizer_notifier (spawn_finalizer_thread
);