weak-set.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. /* Copyright (C) 2011, 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. #include <assert.h>
  22. #include "libguile/_scm.h"
  23. #include "libguile/hash.h"
  24. #include "libguile/eval.h"
  25. #include "libguile/ports.h"
  26. #include "libguile/bdw-gc.h"
  27. #include "libguile/validate.h"
  28. #include "libguile/weak-list.h"
  29. #include "libguile/weak-set.h"
  30. /* Weak Sets
  31. This file implements weak sets. One example of a weak set is the
  32. symbol table, where you want all instances of the `foo' symbol to map
  33. to one object. So when you load a file and it wants a symbol with
  34. the characters "foo", you one up in the table, using custom hash and
  35. equality predicates. Only if one is not found will you bother to
  36. cons one up and intern it.
  37. Another use case for weak sets is the set of open ports. Guile needs
  38. to be able to flush them all when the process exits, but the set
  39. shouldn't prevent the GC from collecting the port (and thus closing
  40. it).
  41. Weak sets are implemented using an open-addressed hash table.
  42. Basically this means that there is an array of entries, and the item
  43. is expected to be found the slot corresponding to its hash code,
  44. modulo the length of the array.
  45. Collisions are handled using linear probing with the Robin Hood
  46. technique. See Pedro Celis' paper, "Robin Hood Hashing":
  47. http://www.cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
  48. The vector of entries is allocated as an "atomic" piece of memory, so
  49. that the GC doesn't trace it. When an item is added to the set, a
  50. disappearing link is registered to its location. If the item is
  51. collected, then that link will be zeroed out.
  52. An entry is not just an item, though; the hash code is also stored in
  53. the entry. We munge hash codes so that they are never 0. In this
  54. way we can detect removed entries (key of zero but nonzero hash
  55. code), and can then reshuffle elements as needed to maintain the
  56. robin hood ordering.
  57. Compared to buckets-and-chains hash tables, open addressing has the
  58. advantage that it is very cache-friendly. It also uses less memory.
  59. Implementation-wise, there are two things to note.
  60. 1. We assume that hash codes are evenly distributed across the
  61. range of unsigned longs. The actual hash code stored in the
  62. entry is left-shifted by 1 bit (losing 1 bit of hash precision),
  63. and then or'd with 1. In this way we ensure that the hash field
  64. of an occupied entry is nonzero. To map to an index, we
  65. right-shift the hash by one, divide by the size, and take the
  66. remainder.
  67. 2. Since the "keys" (the objects in the set) are stored in an
  68. atomic region with disappearing links, they need to be accessed
  69. with the GC alloc lock. `copy_weak_entry' will do that for
  70. you. The hash code itself can be read outside the lock,
  71. though.
  72. */
  73. typedef struct {
  74. unsigned long hash;
  75. scm_t_bits key;
  76. } scm_t_weak_entry;
  77. struct weak_entry_data {
  78. scm_t_weak_entry *in;
  79. scm_t_weak_entry *out;
  80. };
  81. static void*
  82. do_copy_weak_entry (void *data)
  83. {
  84. struct weak_entry_data *e = data;
  85. e->out->hash = e->in->hash;
  86. e->out->key = e->in->key;
  87. return NULL;
  88. }
  89. static void
  90. copy_weak_entry (scm_t_weak_entry *src, scm_t_weak_entry *dst)
  91. {
  92. struct weak_entry_data data;
  93. data.in = src;
  94. data.out = dst;
  95. GC_call_with_alloc_lock (do_copy_weak_entry, &data);
  96. }
  97. typedef struct {
  98. scm_t_weak_entry *entries; /* the data */
  99. scm_i_pthread_mutex_t lock; /* the lock */
  100. unsigned long size; /* total number of slots. */
  101. unsigned long n_items; /* number of items in set */
  102. unsigned long lower; /* when to shrink */
  103. unsigned long upper; /* when to grow */
  104. int size_index; /* index into hashset_size */
  105. int min_size_index; /* minimum size_index */
  106. } scm_t_weak_set;
  107. #define SCM_WEAK_SET_P(x) (SCM_HAS_TYP7 (x, scm_tc7_weak_set))
  108. #define SCM_VALIDATE_WEAK_SET(pos, arg) \
  109. SCM_MAKE_VALIDATE_MSG (pos, arg, WEAK_SET_P, "weak-set")
  110. #define SCM_WEAK_SET(x) ((scm_t_weak_set *) SCM_CELL_WORD_1 (x))
  111. static unsigned long
  112. hash_to_index (unsigned long hash, unsigned long size)
  113. {
  114. return (hash >> 1) % size;
  115. }
  116. static unsigned long
  117. entry_distance (unsigned long hash, unsigned long k, unsigned long size)
  118. {
  119. unsigned long origin = hash_to_index (hash, size);
  120. if (k >= origin)
  121. return k - origin;
  122. else
  123. /* The other key was displaced and wrapped around. */
  124. return size - origin + k;
  125. }
  126. #ifndef HAVE_GC_MOVE_DISAPPEARING_LINK
  127. static void
  128. GC_move_disappearing_link (void **from, void **to)
  129. {
  130. GC_unregister_disappearing_link (from);
  131. SCM_I_REGISTER_DISAPPEARING_LINK (to, *to);
  132. }
  133. #endif
  134. static void
  135. move_weak_entry (scm_t_weak_entry *from, scm_t_weak_entry *to)
  136. {
  137. if (from->hash)
  138. {
  139. scm_t_weak_entry copy;
  140. copy_weak_entry (from, &copy);
  141. to->hash = copy.hash;
  142. to->key = copy.key;
  143. if (copy.key && SCM_HEAP_OBJECT_P (SCM_PACK (copy.key)))
  144. GC_move_disappearing_link ((void **) &from->key, (void **) &to->key);
  145. }
  146. else
  147. {
  148. to->hash = 0;
  149. to->key = 0;
  150. }
  151. }
  152. static void
  153. rob_from_rich (scm_t_weak_set *set, unsigned long k)
  154. {
  155. unsigned long empty, size;
  156. size = set->size;
  157. /* If we are to free up slot K in the set, we need room to do so. */
  158. assert (set->n_items < size);
  159. empty = k;
  160. do
  161. empty = (empty + 1) % size;
  162. /* Here we access key outside the lock. Is this a problem? At first
  163. glance, I wouldn't think so. */
  164. while (set->entries[empty].key);
  165. do
  166. {
  167. unsigned long last = empty ? (empty - 1) : (size - 1);
  168. move_weak_entry (&set->entries[last], &set->entries[empty]);
  169. empty = last;
  170. }
  171. while (empty != k);
  172. /* Just for sanity. */
  173. set->entries[empty].hash = 0;
  174. set->entries[empty].key = 0;
  175. }
  176. static void
  177. give_to_poor (scm_t_weak_set *set, unsigned long k)
  178. {
  179. /* Slot K was just freed up; possibly shuffle others down. */
  180. unsigned long size = set->size;
  181. while (1)
  182. {
  183. unsigned long next = (k + 1) % size;
  184. unsigned long hash;
  185. scm_t_weak_entry copy;
  186. hash = set->entries[next].hash;
  187. if (!hash || hash_to_index (hash, size) == next)
  188. break;
  189. copy_weak_entry (&set->entries[next], &copy);
  190. if (!copy.key)
  191. /* Lost weak reference. */
  192. {
  193. give_to_poor (set, next);
  194. set->n_items--;
  195. continue;
  196. }
  197. move_weak_entry (&set->entries[next], &set->entries[k]);
  198. k = next;
  199. }
  200. /* We have shuffled down any entries that should be shuffled down; now
  201. free the end. */
  202. set->entries[k].hash = 0;
  203. set->entries[k].key = 0;
  204. }
  205. /* Growing or shrinking is triggered when the load factor
  206. *
  207. * L = N / S (N: number of items in set, S: bucket vector length)
  208. *
  209. * passes an upper limit of 0.9 or a lower limit of 0.2.
  210. *
  211. * The implementation stores the upper and lower number of items which
  212. * trigger a resize in the hashset object.
  213. *
  214. * Possible hash set sizes (primes) are stored in the array
  215. * hashset_size.
  216. */
  217. static unsigned long hashset_size[] = {
  218. 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
  219. 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041, 28762081,
  220. 57524111, 115048217, 230096423
  221. };
  222. #define HASHSET_SIZE_N (sizeof(hashset_size)/sizeof(unsigned long))
  223. static int
  224. compute_size_index (scm_t_weak_set *set)
  225. {
  226. int i = set->size_index;
  227. if (set->n_items < set->lower)
  228. {
  229. /* rehashing is not triggered when i <= min_size */
  230. do
  231. --i;
  232. while (i > set->min_size_index
  233. && set->n_items < hashset_size[i] / 5);
  234. }
  235. else if (set->n_items > set->upper)
  236. {
  237. ++i;
  238. if (i >= HASHSET_SIZE_N)
  239. /* The biggest size currently is 230096423, which for a 32-bit
  240. machine will occupy 1.5GB of memory at a load of 80%. There
  241. is probably something better to do here, but if you have a
  242. weak map of that size, you are hosed in any case. */
  243. abort ();
  244. }
  245. return i;
  246. }
  247. static int
  248. is_acceptable_size_index (scm_t_weak_set *set, int size_index)
  249. {
  250. int computed = compute_size_index (set);
  251. if (size_index == computed)
  252. /* We were going to grow or shrink, and allocating the new vector
  253. didn't change the target size. */
  254. return 1;
  255. if (size_index == computed + 1)
  256. {
  257. /* We were going to enlarge the set, but allocating the new
  258. vector finalized some objects, making an enlargement
  259. unnecessary. It might still be a good idea to use the larger
  260. set, though. (This branch also gets hit if, while allocating
  261. the vector, some other thread was actively removing items from
  262. the set. That is less likely, though.) */
  263. unsigned long new_lower = hashset_size[size_index] / 5;
  264. return set->size > new_lower;
  265. }
  266. if (size_index == computed - 1)
  267. {
  268. /* We were going to shrink the set, but when we dropped the lock
  269. to allocate the new vector, some other thread added elements to
  270. the set. */
  271. return 0;
  272. }
  273. /* The computed size differs from our newly allocated size by more
  274. than one size index -- recalculate. */
  275. return 0;
  276. }
  277. static void
  278. resize_set (scm_t_weak_set *set)
  279. {
  280. scm_t_weak_entry *old_entries, *new_entries;
  281. int new_size_index;
  282. unsigned long old_size, new_size, old_k;
  283. do
  284. {
  285. new_size_index = compute_size_index (set);
  286. if (new_size_index == set->size_index)
  287. return;
  288. new_size = hashset_size[new_size_index];
  289. new_entries = scm_gc_malloc_pointerless (new_size * sizeof(scm_t_weak_entry),
  290. "weak set");
  291. }
  292. while (!is_acceptable_size_index (set, new_size_index));
  293. old_entries = set->entries;
  294. old_size = set->size;
  295. memset (new_entries, 0, new_size * sizeof(scm_t_weak_entry));
  296. set->size_index = new_size_index;
  297. set->size = new_size;
  298. if (new_size_index <= set->min_size_index)
  299. set->lower = 0;
  300. else
  301. set->lower = new_size / 5;
  302. set->upper = 9 * new_size / 10;
  303. set->n_items = 0;
  304. set->entries = new_entries;
  305. for (old_k = 0; old_k < old_size; old_k++)
  306. {
  307. scm_t_weak_entry copy;
  308. unsigned long new_k, distance;
  309. if (!old_entries[old_k].hash)
  310. continue;
  311. copy_weak_entry (&old_entries[old_k], &copy);
  312. if (!copy.key)
  313. continue;
  314. new_k = hash_to_index (copy.hash, new_size);
  315. for (distance = 0; ; distance++, new_k = (new_k + 1) % new_size)
  316. {
  317. unsigned long other_hash = new_entries[new_k].hash;
  318. if (!other_hash)
  319. /* Found an empty entry. */
  320. break;
  321. /* Displace the entry if our distance is less, otherwise keep
  322. looking. */
  323. if (entry_distance (other_hash, new_k, new_size) < distance)
  324. {
  325. rob_from_rich (set, new_k);
  326. break;
  327. }
  328. }
  329. set->n_items++;
  330. new_entries[new_k].hash = copy.hash;
  331. new_entries[new_k].key = copy.key;
  332. if (SCM_HEAP_OBJECT_P (SCM_PACK (copy.key)))
  333. SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &new_entries[new_k].key,
  334. (void *) new_entries[new_k].key);
  335. }
  336. }
  337. /* Run from a finalizer via do_vacuum_weak_set, this function runs over
  338. the whole table, removing lost weak references, reshuffling the set
  339. as it goes. It might resize the set if it reaps enough entries. */
  340. static void
  341. vacuum_weak_set (scm_t_weak_set *set)
  342. {
  343. scm_t_weak_entry *entries = set->entries;
  344. unsigned long size = set->size;
  345. unsigned long k;
  346. for (k = 0; k < size; k++)
  347. {
  348. unsigned long hash = entries[k].hash;
  349. if (hash)
  350. {
  351. scm_t_weak_entry copy;
  352. copy_weak_entry (&entries[k], &copy);
  353. if (!copy.key)
  354. /* Lost weak reference; reshuffle. */
  355. {
  356. give_to_poor (set, k);
  357. set->n_items--;
  358. }
  359. }
  360. }
  361. if (set->n_items < set->lower)
  362. resize_set (set);
  363. }
  364. static SCM
  365. weak_set_lookup (scm_t_weak_set *set, unsigned long hash,
  366. scm_t_set_predicate_fn pred, void *closure,
  367. SCM dflt)
  368. {
  369. unsigned long k, distance, size;
  370. scm_t_weak_entry *entries;
  371. size = set->size;
  372. entries = set->entries;
  373. hash = (hash << 1) | 0x1;
  374. k = hash_to_index (hash, size);
  375. for (distance = 0; distance < size; distance++, k = (k + 1) % size)
  376. {
  377. unsigned long other_hash;
  378. retry:
  379. other_hash = entries[k].hash;
  380. if (!other_hash)
  381. /* Not found. */
  382. return dflt;
  383. if (hash == other_hash)
  384. {
  385. scm_t_weak_entry copy;
  386. copy_weak_entry (&entries[k], &copy);
  387. if (!copy.key)
  388. /* Lost weak reference; reshuffle. */
  389. {
  390. give_to_poor (set, k);
  391. set->n_items--;
  392. goto retry;
  393. }
  394. if (pred (SCM_PACK (copy.key), closure))
  395. /* Found. */
  396. return SCM_PACK (copy.key);
  397. }
  398. /* If the entry's distance is less, our key is not in the set. */
  399. if (entry_distance (other_hash, k, size) < distance)
  400. return dflt;
  401. }
  402. /* If we got here, then we were unfortunate enough to loop through the
  403. whole set. Shouldn't happen, but hey. */
  404. return dflt;
  405. }
  406. static SCM
  407. weak_set_add_x (scm_t_weak_set *set, unsigned long hash,
  408. scm_t_set_predicate_fn pred, void *closure,
  409. SCM obj)
  410. {
  411. unsigned long k, distance, size;
  412. scm_t_weak_entry *entries;
  413. size = set->size;
  414. entries = set->entries;
  415. hash = (hash << 1) | 0x1;
  416. k = hash_to_index (hash, size);
  417. for (distance = 0; ; distance++, k = (k + 1) % size)
  418. {
  419. unsigned long other_hash;
  420. retry:
  421. other_hash = entries[k].hash;
  422. if (!other_hash)
  423. /* Found an empty entry. */
  424. break;
  425. if (other_hash == hash)
  426. {
  427. scm_t_weak_entry copy;
  428. copy_weak_entry (&entries[k], &copy);
  429. if (!copy.key)
  430. /* Lost weak reference; reshuffle. */
  431. {
  432. give_to_poor (set, k);
  433. set->n_items--;
  434. goto retry;
  435. }
  436. if (pred (SCM_PACK (copy.key), closure))
  437. /* Found an entry with this key. */
  438. return SCM_PACK (copy.key);
  439. }
  440. if (set->n_items > set->upper)
  441. /* Full set, time to resize. */
  442. {
  443. resize_set (set);
  444. return weak_set_add_x (set, hash >> 1, pred, closure, obj);
  445. }
  446. /* Displace the entry if our distance is less, otherwise keep
  447. looking. */
  448. if (entry_distance (other_hash, k, size) < distance)
  449. {
  450. rob_from_rich (set, k);
  451. break;
  452. }
  453. }
  454. set->n_items++;
  455. entries[k].hash = hash;
  456. entries[k].key = SCM_UNPACK (obj);
  457. if (SCM_HEAP_OBJECT_P (obj))
  458. SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &entries[k].key,
  459. (void *) SCM2PTR (obj));
  460. return obj;
  461. }
  462. static void
  463. weak_set_remove_x (scm_t_weak_set *set, unsigned long hash,
  464. scm_t_set_predicate_fn pred, void *closure)
  465. {
  466. unsigned long k, distance, size;
  467. scm_t_weak_entry *entries;
  468. size = set->size;
  469. entries = set->entries;
  470. hash = (hash << 1) | 0x1;
  471. k = hash_to_index (hash, size);
  472. for (distance = 0; distance < size; distance++, k = (k + 1) % size)
  473. {
  474. unsigned long other_hash;
  475. retry:
  476. other_hash = entries[k].hash;
  477. if (!other_hash)
  478. /* Not found. */
  479. return;
  480. if (other_hash == hash)
  481. {
  482. scm_t_weak_entry copy;
  483. copy_weak_entry (&entries[k], &copy);
  484. if (!copy.key)
  485. /* Lost weak reference; reshuffle. */
  486. {
  487. give_to_poor (set, k);
  488. set->n_items--;
  489. goto retry;
  490. }
  491. if (pred (SCM_PACK (copy.key), closure))
  492. /* Found an entry with this key. */
  493. {
  494. entries[k].hash = 0;
  495. entries[k].key = 0;
  496. if (SCM_HEAP_OBJECT_P (SCM_PACK (copy.key)))
  497. GC_unregister_disappearing_link ((void **) &entries[k].key);
  498. if (--set->n_items < set->lower)
  499. resize_set (set);
  500. else
  501. give_to_poor (set, k);
  502. return;
  503. }
  504. }
  505. /* If the entry's distance is less, our key is not in the set. */
  506. if (entry_distance (other_hash, k, size) < distance)
  507. return;
  508. }
  509. }
  510. static SCM
  511. make_weak_set (unsigned long k)
  512. {
  513. scm_t_weak_set *set;
  514. int i = 0, n = k ? k : 31;
  515. while (i + 1 < HASHSET_SIZE_N && n > hashset_size[i])
  516. ++i;
  517. n = hashset_size[i];
  518. set = scm_gc_malloc (sizeof (*set), "weak-set");
  519. set->entries = scm_gc_malloc_pointerless (n * sizeof(scm_t_weak_entry),
  520. "weak-set");
  521. memset (set->entries, 0, n * sizeof(scm_t_weak_entry));
  522. set->n_items = 0;
  523. set->size = n;
  524. set->lower = 0;
  525. set->upper = 9 * n / 10;
  526. set->size_index = i;
  527. set->min_size_index = i;
  528. scm_i_pthread_mutex_init (&set->lock, NULL);
  529. return scm_cell (scm_tc7_weak_set, (scm_t_bits)set);
  530. }
  531. void
  532. scm_i_weak_set_print (SCM exp, SCM port, scm_print_state *pstate)
  533. {
  534. scm_puts ("#<", port);
  535. scm_puts ("weak-set ", port);
  536. scm_uintprint (SCM_WEAK_SET (exp)->n_items, 10, port);
  537. scm_putc ('/', port);
  538. scm_uintprint (SCM_WEAK_SET (exp)->size, 10, port);
  539. scm_puts (">", port);
  540. }
  541. static void
  542. do_vacuum_weak_set (SCM set)
  543. {
  544. scm_t_weak_set *s;
  545. s = SCM_WEAK_SET (set);
  546. /* We should always be able to grab this lock, because we are run from
  547. a finalizer, which runs in another thread (or an async, which is
  548. mostly equivalent). */
  549. scm_i_pthread_mutex_lock (&s->lock);
  550. vacuum_weak_set (s);
  551. scm_i_pthread_mutex_unlock (&s->lock);
  552. }
  553. static scm_i_pthread_mutex_t all_weak_sets_lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
  554. static SCM all_weak_sets = SCM_EOL;
  555. static void
  556. vacuum_all_weak_sets (void)
  557. {
  558. scm_i_pthread_mutex_lock (&all_weak_sets_lock);
  559. scm_i_visit_weak_list (&all_weak_sets, do_vacuum_weak_set);
  560. scm_i_pthread_mutex_unlock (&all_weak_sets_lock);
  561. }
  562. SCM
  563. scm_c_make_weak_set (unsigned long k)
  564. {
  565. SCM ret;
  566. ret = make_weak_set (k);
  567. scm_i_pthread_mutex_lock (&all_weak_sets_lock);
  568. all_weak_sets = scm_i_weak_cons (ret, all_weak_sets);
  569. scm_i_pthread_mutex_unlock (&all_weak_sets_lock);
  570. return ret;
  571. }
  572. SCM
  573. scm_weak_set_p (SCM obj)
  574. {
  575. return scm_from_bool (SCM_WEAK_SET_P (obj));
  576. }
  577. SCM
  578. scm_weak_set_clear_x (SCM set)
  579. {
  580. scm_t_weak_set *s = SCM_WEAK_SET (set);
  581. scm_i_pthread_mutex_lock (&s->lock);
  582. memset (s->entries, 0, sizeof (scm_t_weak_entry) * s->size);
  583. s->n_items = 0;
  584. scm_i_pthread_mutex_unlock (&s->lock);
  585. return SCM_UNSPECIFIED;
  586. }
  587. SCM
  588. scm_c_weak_set_lookup (SCM set, unsigned long raw_hash,
  589. scm_t_set_predicate_fn pred,
  590. void *closure, SCM dflt)
  591. {
  592. SCM ret;
  593. scm_t_weak_set *s = SCM_WEAK_SET (set);
  594. scm_i_pthread_mutex_lock (&s->lock);
  595. ret = weak_set_lookup (s, raw_hash, pred, closure, dflt);
  596. scm_i_pthread_mutex_unlock (&s->lock);
  597. return ret;
  598. }
  599. SCM
  600. scm_c_weak_set_add_x (SCM set, unsigned long raw_hash,
  601. scm_t_set_predicate_fn pred,
  602. void *closure, SCM obj)
  603. {
  604. SCM ret;
  605. scm_t_weak_set *s = SCM_WEAK_SET (set);
  606. scm_i_pthread_mutex_lock (&s->lock);
  607. ret = weak_set_add_x (s, raw_hash, pred, closure, obj);
  608. scm_i_pthread_mutex_unlock (&s->lock);
  609. return ret;
  610. }
  611. void
  612. scm_c_weak_set_remove_x (SCM set, unsigned long raw_hash,
  613. scm_t_set_predicate_fn pred,
  614. void *closure)
  615. {
  616. scm_t_weak_set *s = SCM_WEAK_SET (set);
  617. scm_i_pthread_mutex_lock (&s->lock);
  618. weak_set_remove_x (s, raw_hash, pred, closure);
  619. scm_i_pthread_mutex_unlock (&s->lock);
  620. }
  621. static int
  622. eq_predicate (SCM x, void *closure)
  623. {
  624. return scm_is_eq (x, SCM_PACK_POINTER (closure));
  625. }
  626. SCM
  627. scm_weak_set_add_x (SCM set, SCM obj)
  628. {
  629. return scm_c_weak_set_add_x (set, scm_ihashq (obj, -1),
  630. eq_predicate, SCM_UNPACK_POINTER (obj), obj);
  631. }
  632. SCM
  633. scm_weak_set_remove_x (SCM set, SCM obj)
  634. {
  635. scm_c_weak_set_remove_x (set, scm_ihashq (obj, -1),
  636. eq_predicate, SCM_UNPACK_POINTER (obj));
  637. return SCM_UNSPECIFIED;
  638. }
  639. SCM
  640. scm_c_weak_set_fold (scm_t_set_fold_fn proc, void *closure,
  641. SCM init, SCM set)
  642. {
  643. scm_t_weak_set *s;
  644. scm_t_weak_entry *entries;
  645. unsigned long k, size;
  646. s = SCM_WEAK_SET (set);
  647. scm_i_pthread_mutex_lock (&s->lock);
  648. size = s->size;
  649. entries = s->entries;
  650. for (k = 0; k < size; k++)
  651. {
  652. if (entries[k].hash)
  653. {
  654. scm_t_weak_entry copy;
  655. copy_weak_entry (&entries[k], &copy);
  656. if (copy.key)
  657. {
  658. /* Release set lock while we call the function. */
  659. scm_i_pthread_mutex_unlock (&s->lock);
  660. init = proc (closure, SCM_PACK (copy.key), init);
  661. scm_i_pthread_mutex_lock (&s->lock);
  662. }
  663. }
  664. }
  665. scm_i_pthread_mutex_unlock (&s->lock);
  666. return init;
  667. }
  668. static SCM
  669. fold_trampoline (void *closure, SCM item, SCM init)
  670. {
  671. return scm_call_2 (SCM_PACK_POINTER (closure), item, init);
  672. }
  673. SCM
  674. scm_weak_set_fold (SCM proc, SCM init, SCM set)
  675. {
  676. return scm_c_weak_set_fold (fold_trampoline, SCM_UNPACK_POINTER (proc), init, set);
  677. }
  678. static SCM
  679. for_each_trampoline (void *closure, SCM item, SCM seed)
  680. {
  681. scm_call_1 (SCM_PACK_POINTER (closure), item);
  682. return seed;
  683. }
  684. SCM
  685. scm_weak_set_for_each (SCM proc, SCM set)
  686. {
  687. scm_c_weak_set_fold (for_each_trampoline, SCM_UNPACK_POINTER (proc), SCM_BOOL_F, set);
  688. return SCM_UNSPECIFIED;
  689. }
  690. static SCM
  691. map_trampoline (void *closure, SCM item, SCM seed)
  692. {
  693. return scm_cons (scm_call_1 (SCM_PACK_POINTER (closure), item), seed);
  694. }
  695. SCM
  696. scm_weak_set_map_to_list (SCM proc, SCM set)
  697. {
  698. return scm_c_weak_set_fold (map_trampoline, SCM_UNPACK_POINTER (proc), SCM_EOL, set);
  699. }
  700. void
  701. scm_init_weak_set ()
  702. {
  703. #include "libguile/weak-set.x"
  704. scm_i_register_async_gc_callback (vacuum_all_weak_sets);
  705. }
  706. /*
  707. Local Variables:
  708. c-file-style: "gnu"
  709. End:
  710. */