finalizers.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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. #ifdef HAVE_CONFIG_H
  19. # include <config.h>
  20. #endif
  21. #ifdef HAVE_UNISTD_H
  22. #include <unistd.h>
  23. #endif
  24. #include <fcntl.h>
  25. #include <full-write.h>
  26. #include "libguile/bdw-gc.h"
  27. #include "libguile/_scm.h"
  28. #include "libguile/finalizers.h"
  29. #include "libguile/gc.h"
  30. #include "libguile/threads.h"
  31. static size_t finalization_count;
  32. #ifndef HAVE_GC_SET_FINALIZER_NOTIFIER
  33. static void
  34. GC_set_finalizer_notifier (void (*notifier) (void))
  35. {
  36. GC_finalizer_notifier = notifier;
  37. }
  38. #endif
  39. void
  40. scm_i_set_finalizer (void *obj, scm_t_finalizer_proc proc, void *data)
  41. {
  42. GC_finalization_proc prev;
  43. GC_PTR prev_data;
  44. GC_REGISTER_FINALIZER_NO_ORDER (obj, proc, data, &prev, &prev_data);
  45. }
  46. struct scm_t_chained_finalizer
  47. {
  48. int resuscitating_p;
  49. scm_t_finalizer_proc proc;
  50. void *data;
  51. scm_t_finalizer_proc prev;
  52. void *prev_data;
  53. };
  54. static void
  55. chained_finalizer (void *obj, void *data)
  56. {
  57. struct scm_t_chained_finalizer *chained_data = data;
  58. if (chained_data->resuscitating_p)
  59. {
  60. if (chained_data->prev)
  61. scm_i_set_finalizer (obj, chained_data->prev, chained_data->prev_data);
  62. chained_data->proc (obj, chained_data->data);
  63. }
  64. else
  65. {
  66. chained_data->proc (obj, chained_data->data);
  67. if (chained_data->prev)
  68. chained_data->prev (obj, chained_data->prev_data);
  69. }
  70. }
  71. void
  72. scm_i_add_resuscitator (void *obj, scm_t_finalizer_proc proc, void *data)
  73. {
  74. struct scm_t_chained_finalizer *chained_data;
  75. chained_data = scm_gc_malloc (sizeof (*chained_data), "chained finalizer");
  76. chained_data->resuscitating_p = 1;
  77. chained_data->proc = proc;
  78. chained_data->data = data;
  79. GC_REGISTER_FINALIZER_NO_ORDER (obj, chained_finalizer, chained_data,
  80. &chained_data->prev,
  81. &chained_data->prev_data);
  82. }
  83. static void
  84. shuffle_resuscitators_to_front (struct scm_t_chained_finalizer *cd)
  85. {
  86. while (cd->prev == chained_finalizer)
  87. {
  88. struct scm_t_chained_finalizer *prev = cd->prev_data;
  89. scm_t_finalizer_proc proc = cd->proc;
  90. void *data = cd->data;
  91. if (!prev->resuscitating_p)
  92. break;
  93. cd->resuscitating_p = 1;
  94. cd->proc = prev->proc;
  95. cd->data = prev->data;
  96. prev->resuscitating_p = 0;
  97. prev->proc = proc;
  98. prev->data = data;
  99. cd = prev;
  100. }
  101. }
  102. void
  103. scm_i_add_finalizer (void *obj, scm_t_finalizer_proc proc, void *data)
  104. {
  105. struct scm_t_chained_finalizer *chained_data;
  106. chained_data = scm_gc_malloc (sizeof (*chained_data), "chained finalizer");
  107. chained_data->resuscitating_p = 0;
  108. chained_data->proc = proc;
  109. chained_data->data = data;
  110. GC_REGISTER_FINALIZER_NO_ORDER (obj, chained_finalizer, chained_data,
  111. &chained_data->prev,
  112. &chained_data->prev_data);
  113. shuffle_resuscitators_to_front (chained_data);
  114. }
  115. static SCM finalizer_async_cell;
  116. static SCM
  117. run_finalizers_async_thunk (void)
  118. {
  119. finalization_count += GC_invoke_finalizers ();
  120. return SCM_UNSPECIFIED;
  121. }
  122. /* The function queue_finalizer_async is run by the GC when there are
  123. * objects to finalize. It will enqueue an asynchronous call to
  124. * GC_invoke_finalizers() at the next SCM_TICK in this thread.
  125. */
  126. static void
  127. queue_finalizer_async (void)
  128. {
  129. scm_i_thread *t = SCM_I_CURRENT_THREAD;
  130. static scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
  131. scm_i_pthread_mutex_lock (&lock);
  132. /* If t is NULL, that could be because we're allocating in
  133. threads.c:guilify_self_1. In that case, rely on the
  134. GC_invoke_finalizers call there after the thread spins up. */
  135. if (t && scm_is_false (SCM_CDR (finalizer_async_cell)))
  136. {
  137. SCM_SETCDR (finalizer_async_cell, t->active_asyncs);
  138. t->active_asyncs = finalizer_async_cell;
  139. t->pending_asyncs = 1;
  140. }
  141. scm_i_pthread_mutex_unlock (&lock);
  142. }
  143. #if SCM_USE_PTHREAD_THREADS
  144. static int finalization_pipe[2];
  145. static scm_i_pthread_mutex_t finalization_thread_lock =
  146. SCM_I_PTHREAD_MUTEX_INITIALIZER;
  147. static pthread_t finalization_thread;
  148. static void
  149. notify_finalizers_to_run (void)
  150. {
  151. char byte = 0;
  152. full_write (finalization_pipe[1], &byte, 1);
  153. }
  154. static void
  155. notify_about_to_fork (void)
  156. {
  157. char byte = 1;
  158. full_write (finalization_pipe[1], &byte, 1);
  159. }
  160. struct finalization_pipe_data
  161. {
  162. char byte;
  163. ssize_t n;
  164. int err;
  165. };
  166. static void*
  167. read_finalization_pipe_data (void *data)
  168. {
  169. struct finalization_pipe_data *fdata = data;
  170. fdata->n = read (finalization_pipe[0], &fdata->byte, 1);
  171. fdata->err = errno;
  172. return NULL;
  173. }
  174. static void*
  175. finalization_thread_proc (void *unused)
  176. {
  177. while (1)
  178. {
  179. struct finalization_pipe_data data;
  180. scm_without_guile (read_finalization_pipe_data, &data);
  181. if (data.n <= 0 && data.err != EINTR)
  182. {
  183. perror ("error in finalization thread");
  184. return NULL;
  185. }
  186. switch (data.byte)
  187. {
  188. case 0:
  189. finalization_count += GC_invoke_finalizers ();
  190. break;
  191. case 1:
  192. return NULL;
  193. default:
  194. abort ();
  195. }
  196. }
  197. }
  198. static void*
  199. run_finalization_thread (void *arg)
  200. {
  201. return scm_with_guile (finalization_thread_proc, arg);
  202. }
  203. static void
  204. start_finalization_thread (void)
  205. {
  206. scm_i_pthread_mutex_lock (&finalization_thread_lock);
  207. if (!finalization_thread)
  208. /* Use the raw pthread API and scm_with_guile, because we don't want
  209. to block on any lock that scm_spawn_thread might want to take,
  210. and we don't want to inherit the dynamic state (fluids) of the
  211. caller. */
  212. if (pthread_create (&finalization_thread, NULL,
  213. run_finalization_thread, NULL))
  214. perror ("error creating finalization thread");
  215. scm_i_pthread_mutex_unlock (&finalization_thread_lock);
  216. }
  217. static void
  218. stop_finalization_thread (void)
  219. {
  220. scm_i_pthread_mutex_lock (&finalization_thread_lock);
  221. if (finalization_thread)
  222. {
  223. notify_about_to_fork ();
  224. if (pthread_join (finalization_thread, NULL))
  225. perror ("joining finalization thread");
  226. finalization_thread = 0;
  227. }
  228. scm_i_pthread_mutex_unlock (&finalization_thread_lock);
  229. }
  230. static void
  231. spawn_finalizer_thread (void)
  232. {
  233. GC_set_finalizer_notifier (notify_finalizers_to_run);
  234. start_finalization_thread ();
  235. }
  236. #endif /* SCM_USE_PTHREAD_THREADS */
  237. void
  238. scm_i_finalizer_pre_fork (void)
  239. {
  240. #if SCM_USE_PTHREAD_THREADS
  241. stop_finalization_thread ();
  242. GC_set_finalizer_notifier (spawn_finalizer_thread);
  243. #endif
  244. }
  245. void
  246. scm_init_finalizers (void)
  247. {
  248. /* When the async is to run, the cdr of the pair gets set to the
  249. asyncs queue of the current thread. */
  250. finalizer_async_cell =
  251. scm_cons (scm_c_make_gsubr ("%run-finalizers", 0, 0, 0,
  252. run_finalizers_async_thunk),
  253. SCM_BOOL_F);
  254. GC_set_finalizer_notifier (queue_finalizer_async);
  255. }
  256. void
  257. scm_init_finalizer_thread (void)
  258. {
  259. #if SCM_USE_PTHREAD_THREADS
  260. if (pipe2 (finalization_pipe, O_CLOEXEC) != 0)
  261. scm_syserror (NULL);
  262. GC_set_finalizer_notifier (spawn_finalizer_thread);
  263. #endif
  264. }