finalizers.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /* Copyright (C) 2012, 2013 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. void
  33. scm_i_set_finalizer (void *obj, scm_t_finalizer_proc proc, void *data)
  34. {
  35. GC_finalization_proc prev;
  36. void *prev_data;
  37. GC_REGISTER_FINALIZER_NO_ORDER (obj, proc, data, &prev, &prev_data);
  38. }
  39. struct scm_t_chained_finalizer
  40. {
  41. int resuscitating_p;
  42. scm_t_finalizer_proc proc;
  43. void *data;
  44. scm_t_finalizer_proc prev;
  45. void *prev_data;
  46. };
  47. static void
  48. chained_finalizer (void *obj, void *data)
  49. {
  50. struct scm_t_chained_finalizer *chained_data = data;
  51. if (chained_data->resuscitating_p)
  52. {
  53. if (chained_data->prev)
  54. scm_i_set_finalizer (obj, chained_data->prev, chained_data->prev_data);
  55. chained_data->proc (obj, chained_data->data);
  56. }
  57. else
  58. {
  59. chained_data->proc (obj, chained_data->data);
  60. if (chained_data->prev)
  61. chained_data->prev (obj, chained_data->prev_data);
  62. }
  63. }
  64. void
  65. scm_i_add_resuscitator (void *obj, scm_t_finalizer_proc proc, void *data)
  66. {
  67. struct scm_t_chained_finalizer *chained_data;
  68. chained_data = scm_gc_malloc (sizeof (*chained_data), "chained finalizer");
  69. chained_data->resuscitating_p = 1;
  70. chained_data->proc = proc;
  71. chained_data->data = data;
  72. GC_REGISTER_FINALIZER_NO_ORDER (obj, chained_finalizer, chained_data,
  73. &chained_data->prev,
  74. &chained_data->prev_data);
  75. }
  76. static void
  77. shuffle_resuscitators_to_front (struct scm_t_chained_finalizer *cd)
  78. {
  79. while (cd->prev == chained_finalizer)
  80. {
  81. struct scm_t_chained_finalizer *prev = cd->prev_data;
  82. scm_t_finalizer_proc proc = cd->proc;
  83. void *data = cd->data;
  84. if (!prev->resuscitating_p)
  85. break;
  86. cd->resuscitating_p = 1;
  87. cd->proc = prev->proc;
  88. cd->data = prev->data;
  89. prev->resuscitating_p = 0;
  90. prev->proc = proc;
  91. prev->data = data;
  92. cd = prev;
  93. }
  94. }
  95. void
  96. scm_i_add_finalizer (void *obj, scm_t_finalizer_proc proc, void *data)
  97. {
  98. struct scm_t_chained_finalizer *chained_data;
  99. chained_data = scm_gc_malloc (sizeof (*chained_data), "chained finalizer");
  100. chained_data->resuscitating_p = 0;
  101. chained_data->proc = proc;
  102. chained_data->data = data;
  103. GC_REGISTER_FINALIZER_NO_ORDER (obj, chained_finalizer, chained_data,
  104. &chained_data->prev,
  105. &chained_data->prev_data);
  106. shuffle_resuscitators_to_front (chained_data);
  107. }
  108. static SCM finalizer_async_cell;
  109. static SCM
  110. run_finalizers_async_thunk (void)
  111. {
  112. finalization_count += GC_invoke_finalizers ();
  113. return SCM_UNSPECIFIED;
  114. }
  115. /* The function queue_finalizer_async is run by the GC when there are
  116. * objects to finalize. It will enqueue an asynchronous call to
  117. * GC_invoke_finalizers() at the next SCM_TICK in this thread.
  118. */
  119. static void
  120. queue_finalizer_async (void)
  121. {
  122. scm_i_thread *t = SCM_I_CURRENT_THREAD;
  123. static scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
  124. scm_i_pthread_mutex_lock (&lock);
  125. /* If t is NULL, that could be because we're allocating in
  126. threads.c:guilify_self_1. In that case, rely on the
  127. GC_invoke_finalizers call there after the thread spins up. */
  128. if (t && scm_is_false (SCM_CDR (finalizer_async_cell)))
  129. {
  130. SCM_SETCDR (finalizer_async_cell, t->active_asyncs);
  131. t->active_asyncs = finalizer_async_cell;
  132. t->pending_asyncs = 1;
  133. }
  134. scm_i_pthread_mutex_unlock (&lock);
  135. }
  136. #if SCM_USE_PTHREAD_THREADS
  137. static int finalization_pipe[2];
  138. static scm_i_pthread_mutex_t finalization_thread_lock =
  139. SCM_I_PTHREAD_MUTEX_INITIALIZER;
  140. static pthread_t finalization_thread;
  141. static int finalization_thread_is_running = 0;
  142. static void
  143. notify_finalizers_to_run (void)
  144. {
  145. char byte = 0;
  146. full_write (finalization_pipe[1], &byte, 1);
  147. }
  148. static void
  149. notify_about_to_fork (void)
  150. {
  151. char byte = 1;
  152. full_write (finalization_pipe[1], &byte, 1);
  153. }
  154. struct finalization_pipe_data
  155. {
  156. char byte;
  157. ssize_t n;
  158. int err;
  159. };
  160. static void*
  161. read_finalization_pipe_data (void *data)
  162. {
  163. struct finalization_pipe_data *fdata = data;
  164. fdata->n = read (finalization_pipe[0], &fdata->byte, 1);
  165. fdata->err = errno;
  166. return NULL;
  167. }
  168. static void*
  169. finalization_thread_proc (void *unused)
  170. {
  171. while (1)
  172. {
  173. struct finalization_pipe_data data;
  174. scm_without_guile (read_finalization_pipe_data, &data);
  175. if (data.n <= 0 && data.err != EINTR)
  176. {
  177. perror ("error in finalization thread");
  178. return NULL;
  179. }
  180. switch (data.byte)
  181. {
  182. case 0:
  183. finalization_count += GC_invoke_finalizers ();
  184. break;
  185. case 1:
  186. return NULL;
  187. default:
  188. abort ();
  189. }
  190. }
  191. }
  192. static void*
  193. run_finalization_thread (void *arg)
  194. {
  195. return scm_with_guile (finalization_thread_proc, arg);
  196. }
  197. static void
  198. start_finalization_thread (void)
  199. {
  200. scm_i_pthread_mutex_lock (&finalization_thread_lock);
  201. if (!finalization_thread_is_running)
  202. {
  203. /* Use the raw pthread API and scm_with_guile, because we don't want
  204. to block on any lock that scm_spawn_thread might want to take,
  205. and we don't want to inherit the dynamic state (fluids) of the
  206. caller. */
  207. if (pthread_create (&finalization_thread, NULL,
  208. run_finalization_thread, NULL))
  209. perror ("error creating finalization thread");
  210. else
  211. finalization_thread_is_running = 1;
  212. }
  213. scm_i_pthread_mutex_unlock (&finalization_thread_lock);
  214. }
  215. static void
  216. stop_finalization_thread (void)
  217. {
  218. scm_i_pthread_mutex_lock (&finalization_thread_lock);
  219. if (finalization_thread_is_running)
  220. {
  221. notify_about_to_fork ();
  222. if (pthread_join (finalization_thread, NULL))
  223. perror ("joining finalization thread");
  224. finalization_thread_is_running = 0;
  225. }
  226. scm_i_pthread_mutex_unlock (&finalization_thread_lock);
  227. }
  228. static void
  229. spawn_finalizer_thread (void)
  230. {
  231. GC_set_finalizer_notifier (notify_finalizers_to_run);
  232. start_finalization_thread ();
  233. }
  234. #endif /* SCM_USE_PTHREAD_THREADS */
  235. void
  236. scm_i_finalizer_pre_fork (void)
  237. {
  238. #if SCM_USE_PTHREAD_THREADS
  239. stop_finalization_thread ();
  240. GC_set_finalizer_notifier (spawn_finalizer_thread);
  241. #endif
  242. }
  243. static void*
  244. weak_pointer_ref (void *weak_pointer)
  245. {
  246. return *(void **) weak_pointer;
  247. }
  248. static void
  249. weak_gc_finalizer (void *ptr, void *data)
  250. {
  251. void **weak = ptr;
  252. void *val;
  253. void (*callback) (SCM) = weak[1];
  254. val = GC_call_with_alloc_lock (weak_pointer_ref, &weak[0]);
  255. if (!val)
  256. return;
  257. callback (SCM_PACK_POINTER (val));
  258. scm_i_set_finalizer (ptr, weak_gc_finalizer, data);
  259. }
  260. /* CALLBACK will be called on OBJ, as long as OBJ is accessible. It
  261. will be called from a finalizer, which may be from an async or from
  262. another thread.
  263. As an implementation detail, the way this works is that we allocate
  264. a fresh pointer-less object holding two words. We know that this
  265. object should get collected the next time GC is run, so we attach a
  266. finalizer to it so that we get a callback after GC happens.
  267. The first word of the object holds a weak reference to OBJ, and the
  268. second holds the callback pointer. When the callback is called, we
  269. check if the weak reference on OBJ still holds. If it doesn't hold,
  270. then OBJ is no longer accessible, and we're done. Otherwise we call
  271. the callback and re-register a finalizer for our two-word GC object,
  272. effectively resuscitating the object so that we will get a callback
  273. on the next GC.
  274. We could use the scm_after_gc_hook, but using a finalizer has the
  275. advantage of potentially running in another thread, decreasing pause
  276. time. */
  277. void
  278. scm_i_register_weak_gc_callback (SCM obj, void (*callback) (SCM))
  279. {
  280. void **weak = GC_MALLOC_ATOMIC (sizeof (void*) * 2);
  281. weak[0] = SCM_UNPACK_POINTER (obj);
  282. weak[1] = (void*)callback;
  283. GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak, SCM2PTR (obj));
  284. scm_i_set_finalizer (weak, weak_gc_finalizer, NULL);
  285. }
  286. void
  287. scm_init_finalizers (void)
  288. {
  289. /* When the async is to run, the cdr of the pair gets set to the
  290. asyncs queue of the current thread. */
  291. finalizer_async_cell =
  292. scm_cons (scm_c_make_gsubr ("%run-finalizers", 0, 0, 0,
  293. run_finalizers_async_thunk),
  294. SCM_BOOL_F);
  295. GC_set_finalizer_notifier (queue_finalizer_async);
  296. }
  297. void
  298. scm_init_finalizer_thread (void)
  299. {
  300. #if SCM_USE_PTHREAD_THREADS
  301. if (pipe2 (finalization_pipe, O_CLOEXEC) != 0)
  302. scm_syserror (NULL);
  303. GC_set_finalizer_notifier (spawn_finalizer_thread);
  304. #endif
  305. }