1 /* Copyright (C) 2012 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 size_t finalization_count
;
46 #ifndef HAVE_GC_SET_FINALIZER_NOTIFIER
48 GC_set_finalizer_notifier (void (*notifier
) (void))
50 GC_finalizer_notifier
= notifier
;
58 scm_i_set_finalizer (void *obj
, scm_t_finalizer_proc proc
, void *data
)
60 GC_finalization_proc prev
;
62 GC_REGISTER_FINALIZER_NO_ORDER (obj
, proc
, data
, &prev
, &prev_data
);
65 struct scm_t_chained_finalizer
68 scm_t_finalizer_proc proc
;
70 scm_t_finalizer_proc prev
;
75 chained_finalizer (void *obj
, void *data
)
77 struct scm_t_chained_finalizer
*chained_data
= data
;
78 if (chained_data
->resuscitating_p
)
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
);
86 chained_data
->proc (obj
, chained_data
->data
);
87 if (chained_data
->prev
)
88 chained_data
->prev (obj
, chained_data
->prev_data
);
93 scm_i_add_resuscitator (void *obj
, scm_t_finalizer_proc proc
, void *data
)
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
,
102 &chained_data
->prev_data
);
106 shuffle_resuscitators_to_front (struct scm_t_chained_finalizer
*cd
)
108 while (cd
->prev
== chained_finalizer
)
110 struct scm_t_chained_finalizer
*prev
= cd
->prev_data
;
111 scm_t_finalizer_proc proc
= cd
->proc
;
112 void *data
= cd
->data
;
114 if (!prev
->resuscitating_p
)
117 cd
->resuscitating_p
= 1;
118 cd
->proc
= prev
->proc
;
119 cd
->data
= prev
->data
;
121 prev
->resuscitating_p
= 0;
130 scm_i_add_finalizer (void *obj
, scm_t_finalizer_proc proc
, void *data
)
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
,
139 &chained_data
->prev_data
);
140 shuffle_resuscitators_to_front (chained_data
);
146 static SCM finalizer_async_cell
;
149 run_finalizers_async_thunk (void)
151 finalization_count
+= GC_invoke_finalizers ();
152 return SCM_UNSPECIFIED
;
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.
161 queue_finalizer_async (void)
163 scm_i_thread
*t
= SCM_I_CURRENT_THREAD
;
164 static scm_i_pthread_mutex_t lock
= SCM_I_PTHREAD_MUTEX_INITIALIZER
;
166 scm_i_pthread_mutex_lock (&lock
);
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
)))
172 SCM_SETCDR (finalizer_async_cell
, t
->active_asyncs
);
173 t
->active_asyncs
= finalizer_async_cell
;
174 t
->pending_asyncs
= 1;
176 scm_i_pthread_mutex_unlock (&lock
);
182 #if SCM_USE_PTHREAD_THREADS
184 static int finalization_pipe
[2];
185 static scm_i_pthread_mutex_t finalization_thread_lock
=
186 SCM_I_PTHREAD_MUTEX_INITIALIZER
;
187 static pthread_t finalization_thread
;
190 notify_finalizers_to_run (void)
193 full_write (finalization_pipe
[1], &byte
, 1);
197 notify_about_to_fork (void)
200 full_write (finalization_pipe
[1], &byte
, 1);
203 struct finalization_pipe_data
211 read_finalization_pipe_data (void *data
)
213 struct finalization_pipe_data
*fdata
= data
;
215 fdata
->n
= read (finalization_pipe
[0], &fdata
->byte
, 1);
222 finalization_thread_proc (void *unused
)
226 struct finalization_pipe_data data
;
228 scm_without_guile (read_finalization_pipe_data
, &data
);
230 if (data
.n
<= 0 && data
.err
!= EINTR
)
232 perror ("error in finalization thread");
239 finalization_count
+= GC_invoke_finalizers ();
250 run_finalization_thread (void *arg
)
252 return scm_with_guile (finalization_thread_proc
, arg
);
256 start_finalization_thread (void)
258 scm_i_pthread_mutex_lock (&finalization_thread_lock
);
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
264 if (pthread_create (&finalization_thread
, NULL
,
265 run_finalization_thread
, NULL
))
266 perror ("error creating finalization thread");
267 scm_i_pthread_mutex_unlock (&finalization_thread_lock
);
271 stop_finalization_thread (void)
273 scm_i_pthread_mutex_lock (&finalization_thread_lock
);
274 if (finalization_thread
)
276 notify_about_to_fork ();
277 if (pthread_join (finalization_thread
, NULL
))
278 perror ("joining finalization thread");
279 finalization_thread
= 0;
281 scm_i_pthread_mutex_unlock (&finalization_thread_lock
);
285 spawn_finalizer_thread (void)
287 GC_set_finalizer_notifier (notify_finalizers_to_run
);
288 start_finalization_thread ();
291 #endif /* SCM_USE_PTHREAD_THREADS */
297 scm_i_finalizer_pre_fork (void)
299 #if SCM_USE_PTHREAD_THREADS
300 stop_finalization_thread ();
301 GC_set_finalizer_notifier (spawn_finalizer_thread
);
309 scm_init_finalizers (void)
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
),
317 GC_set_finalizer_notifier (queue_finalizer_async
);
321 scm_init_finalizer_thread (void)
323 #if SCM_USE_PTHREAD_THREADS
324 if (pipe2 (finalization_pipe
, O_CLOEXEC
) != 0)
326 GC_set_finalizer_notifier (spawn_finalizer_thread
);