weak-table.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. /* Copyright (C) 2011, 2012, 2013, 2014, 2017 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/bdw-gc.h"
  23. #include <gc/gc_typed.h>
  24. #include "libguile/_scm.h"
  25. #include "libguile/hash.h"
  26. #include "libguile/eval.h"
  27. #include "libguile/ports.h"
  28. #include "libguile/validate.h"
  29. #include "libguile/weak-list.h"
  30. #include "libguile/weak-table.h"
  31. /* Weak Tables
  32. This file implements weak hash tables. Weak hash tables are
  33. generally used when you want to augment some object with additional
  34. data, but when you don't have space to store the data in the object.
  35. For example, procedure properties are implemented with weak tables.
  36. This is a normal bucket-and-chain hash table, except that the chain
  37. entries are allocated in such a way that the GC doesn't trace the
  38. weak values. For doubly-weak tables, this means that the entries are
  39. allocated as an "atomic" piece of memory. Key-weak and value-weak
  40. tables use a special GC kind with a custom mark procedure. When
  41. items are added weakly into table, a disappearing link is registered
  42. to their locations. If the referent is collected, then that link
  43. will be zeroed out.
  44. An entry in the table consists of the key and the value, together
  45. with the hash code of the key.
  46. Note that since the weak references are stored in an atomic region
  47. with disappearing links, they need to be accessed with the GC alloc
  48. lock. `read_weak_entry' will do that for you. The hash code itself
  49. can be read outside the lock, though.
  50. */
  51. typedef struct scm_weak_entry scm_t_weak_entry;
  52. struct scm_weak_entry {
  53. unsigned long hash;
  54. scm_t_weak_entry *next;
  55. scm_t_bits key;
  56. scm_t_bits value;
  57. };
  58. struct weak_entry_data {
  59. scm_t_weak_entry *entry;
  60. scm_t_bits key;
  61. scm_t_bits value;
  62. };
  63. static void*
  64. do_read_weak_entry (void *data)
  65. {
  66. struct weak_entry_data *e = data;
  67. e->key = e->entry->key;
  68. e->value = e->entry->value;
  69. return NULL;
  70. }
  71. static void
  72. read_weak_entry (scm_t_weak_entry *entry, scm_t_bits *key, scm_t_bits *value)
  73. {
  74. struct weak_entry_data data;
  75. data.entry = entry;
  76. GC_call_with_alloc_lock (do_read_weak_entry, &data);
  77. *key = data.key;
  78. *value = data.value;
  79. }
  80. static void
  81. register_disappearing_links (scm_t_weak_entry *entry,
  82. SCM k, SCM v,
  83. scm_t_weak_table_kind kind)
  84. {
  85. if (SCM_UNPACK (k) && SCM_HEAP_OBJECT_P (k)
  86. && (kind == SCM_WEAK_TABLE_KIND_KEY
  87. || kind == SCM_WEAK_TABLE_KIND_BOTH))
  88. SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &entry->key,
  89. SCM2PTR (k));
  90. if (SCM_UNPACK (v) && SCM_HEAP_OBJECT_P (v)
  91. && (kind == SCM_WEAK_TABLE_KIND_VALUE
  92. || kind == SCM_WEAK_TABLE_KIND_BOTH))
  93. SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &entry->value,
  94. SCM2PTR (v));
  95. }
  96. static void
  97. unregister_disappearing_links (scm_t_weak_entry *entry,
  98. scm_t_weak_table_kind kind)
  99. {
  100. if (kind == SCM_WEAK_TABLE_KIND_KEY || kind == SCM_WEAK_TABLE_KIND_BOTH)
  101. GC_unregister_disappearing_link ((void **) &entry->key);
  102. if (kind == SCM_WEAK_TABLE_KIND_VALUE || kind == SCM_WEAK_TABLE_KIND_BOTH)
  103. GC_unregister_disappearing_link ((void **) &entry->value);
  104. }
  105. typedef struct {
  106. scm_t_weak_entry **buckets; /* the data */
  107. scm_i_pthread_mutex_t lock; /* the lock */
  108. scm_t_weak_table_kind kind; /* what kind of table it is */
  109. unsigned long n_buckets; /* total number of buckets. */
  110. unsigned long n_items; /* number of items in table */
  111. unsigned long lower; /* when to shrink */
  112. unsigned long upper; /* when to grow */
  113. int size_index; /* index into hashtable_size */
  114. int min_size_index; /* minimum size_index */
  115. GC_word last_gc_no;
  116. } scm_t_weak_table;
  117. #define SCM_WEAK_TABLE_P(x) (SCM_HAS_TYP7 (x, scm_tc7_weak_table))
  118. #define SCM_VALIDATE_WEAK_TABLE(pos, arg) \
  119. SCM_MAKE_VALIDATE_MSG (pos, arg, WEAK_TABLE_P, "weak-table")
  120. #define SCM_WEAK_TABLE(x) ((scm_t_weak_table *) SCM_CELL_WORD_1 (x))
  121. /* GC descriptors for the various kinds of scm_t_weak_entry. */
  122. static GC_descr weak_key_descr;
  123. static GC_descr weak_value_descr;
  124. static GC_descr doubly_weak_descr;
  125. static scm_t_weak_entry *
  126. allocate_entry (scm_t_weak_table_kind kind)
  127. {
  128. scm_t_weak_entry *ret;
  129. switch (kind)
  130. {
  131. case SCM_WEAK_TABLE_KIND_KEY:
  132. ret = GC_malloc_explicitly_typed (sizeof (*ret), weak_key_descr);
  133. break;
  134. case SCM_WEAK_TABLE_KIND_VALUE:
  135. ret = GC_malloc_explicitly_typed (sizeof (*ret), weak_value_descr);
  136. break;
  137. case SCM_WEAK_TABLE_KIND_BOTH:
  138. ret = GC_malloc_explicitly_typed (sizeof (*ret), doubly_weak_descr);
  139. break;
  140. default:
  141. abort ();
  142. }
  143. return ret;
  144. }
  145. static void
  146. add_entry (scm_t_weak_table *table, scm_t_weak_entry *entry)
  147. {
  148. unsigned long bucket = entry->hash % table->n_buckets;
  149. entry->next = table->buckets[bucket];
  150. table->buckets[bucket] = entry;
  151. table->n_items++;
  152. }
  153. /* Growing or shrinking is triggered when the load factor
  154. *
  155. * L = N / S (N: number of items in table, S: bucket vector length)
  156. *
  157. * passes an upper limit of 0.9 or a lower limit of 0.25.
  158. *
  159. * The implementation stores the upper and lower number of items which
  160. * trigger a resize in the hashtable object.
  161. *
  162. * Possible hash table sizes (primes) are stored in the array
  163. * hashtable_size.
  164. */
  165. static unsigned long hashtable_size[] = {
  166. 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
  167. 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041, 28762081,
  168. 57524111, 115048217, 230096423
  169. };
  170. #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
  171. static void
  172. resize_table (scm_t_weak_table *table)
  173. {
  174. scm_t_weak_entry **old_buckets, **new_buckets;
  175. int new_size_index;
  176. unsigned long old_n_buckets, new_n_buckets, old_k;
  177. new_size_index = table->size_index;
  178. if (table->n_items < table->lower)
  179. {
  180. /* Rehashing is not triggered when i <= min_size. */
  181. do
  182. new_size_index -= 1;
  183. while (new_size_index > table->min_size_index
  184. && table->n_items < hashtable_size[new_size_index] / 4);
  185. }
  186. else if (table->n_items > table->upper)
  187. {
  188. new_size_index += 1;
  189. if (new_size_index >= HASHTABLE_SIZE_N)
  190. /* Limit max bucket count. */
  191. return;
  192. }
  193. else
  194. /* Nothing to do. */
  195. return;
  196. new_n_buckets = hashtable_size[new_size_index];
  197. new_buckets = scm_gc_malloc (sizeof (*new_buckets) * new_n_buckets,
  198. "weak table buckets");
  199. old_buckets = table->buckets;
  200. old_n_buckets = table->n_buckets;
  201. table->size_index = new_size_index;
  202. table->n_buckets = new_n_buckets;
  203. if (new_size_index <= table->min_size_index)
  204. table->lower = 0;
  205. else
  206. table->lower = new_n_buckets / 4;
  207. table->upper = 9 * new_n_buckets / 10;
  208. table->n_items = 0;
  209. table->buckets = new_buckets;
  210. for (old_k = 0; old_k < old_n_buckets; old_k++)
  211. {
  212. scm_t_weak_entry *entry = old_buckets[old_k];
  213. while (entry)
  214. {
  215. scm_t_weak_entry *next = entry->next;
  216. entry->next = NULL;
  217. add_entry (table, entry);
  218. entry = next;
  219. }
  220. }
  221. }
  222. /* Run after GC via do_vacuum_weak_table, this function runs over the
  223. whole table, removing lost weak references, reshuffling the table as it
  224. goes. It might resize the table if it reaps enough buckets. */
  225. static void
  226. vacuum_weak_table (scm_t_weak_table *table)
  227. {
  228. GC_word gc_no = GC_get_gc_no ();
  229. unsigned long k;
  230. if (gc_no == table->last_gc_no)
  231. return;
  232. table->last_gc_no = gc_no;
  233. for (k = 0; k < table->n_buckets; k++)
  234. {
  235. scm_t_weak_entry **loc = table->buckets + k;
  236. scm_t_weak_entry *entry;
  237. for (entry = *loc; entry; entry = *loc)
  238. {
  239. scm_t_bits key, value;
  240. read_weak_entry (entry, &key, &value);
  241. if (!key || !value)
  242. /* Lost weak reference; prune entry. */
  243. {
  244. *loc = entry->next;
  245. table->n_items--;
  246. entry->next = NULL;
  247. unregister_disappearing_links (entry, table->kind);
  248. }
  249. else
  250. loc = &entry->next;
  251. }
  252. }
  253. if (table->n_items < table->lower)
  254. resize_table (table);
  255. }
  256. static SCM
  257. weak_table_ref (scm_t_weak_table *table, unsigned long hash,
  258. scm_t_table_predicate_fn pred, void *closure,
  259. SCM dflt)
  260. {
  261. unsigned long bucket = hash % table->n_buckets;
  262. scm_t_weak_entry *entry;
  263. for (entry = table->buckets[bucket]; entry; entry = entry->next)
  264. {
  265. if (entry->hash == hash)
  266. {
  267. scm_t_bits key, value;
  268. read_weak_entry (entry, &key, &value);
  269. if (key && value && pred (SCM_PACK (key), SCM_PACK (value), closure))
  270. /* Found. */
  271. return SCM_PACK (value);
  272. }
  273. }
  274. return dflt;
  275. }
  276. static void
  277. weak_table_put_x (scm_t_weak_table *table, unsigned long hash,
  278. scm_t_table_predicate_fn pred, void *closure,
  279. SCM key, SCM value)
  280. {
  281. unsigned long bucket = hash % table->n_buckets;
  282. scm_t_weak_entry *entry;
  283. for (entry = table->buckets[bucket]; entry; entry = entry->next)
  284. {
  285. if (entry->hash == hash)
  286. {
  287. scm_t_bits k, v;
  288. read_weak_entry (entry, &k, &v);
  289. if (k && v && pred (SCM_PACK (k), SCM_PACK (v), closure))
  290. {
  291. unregister_disappearing_links (entry, table->kind);
  292. key = SCM_PACK (k);
  293. entry->value = SCM_UNPACK (value);
  294. register_disappearing_links (entry, key, value, table->kind);
  295. return;
  296. }
  297. }
  298. }
  299. if (table->n_items > table->upper)
  300. /* Full table, time to resize. */
  301. resize_table (table);
  302. entry = allocate_entry (table->kind);
  303. entry->hash = hash;
  304. entry->key = SCM_UNPACK (key);
  305. entry->value = SCM_UNPACK (value);
  306. register_disappearing_links (entry, key, value, table->kind);
  307. add_entry (table, entry);
  308. }
  309. static void
  310. weak_table_remove_x (scm_t_weak_table *table, unsigned long hash,
  311. scm_t_table_predicate_fn pred, void *closure)
  312. {
  313. unsigned long bucket = hash % table->n_buckets;
  314. scm_t_weak_entry **loc = table->buckets + bucket;
  315. scm_t_weak_entry *entry;
  316. for (entry = *loc; entry; entry = *loc)
  317. {
  318. if (entry->hash == hash)
  319. {
  320. scm_t_bits k, v;
  321. read_weak_entry (entry, &k, &v);
  322. if (k && v && pred (SCM_PACK (k), SCM_PACK (v), closure))
  323. {
  324. *loc = entry->next;
  325. table->n_items--;
  326. entry->next = NULL;
  327. unregister_disappearing_links (entry, table->kind);
  328. if (table->n_items < table->lower)
  329. resize_table (table);
  330. return;
  331. }
  332. }
  333. loc = &entry->next;
  334. }
  335. return;
  336. }
  337. static SCM
  338. make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
  339. {
  340. scm_t_weak_table *table;
  341. int i = 0, n = k ? k : 31;
  342. while (i + 1 < HASHTABLE_SIZE_N && n > hashtable_size[i])
  343. ++i;
  344. n = hashtable_size[i];
  345. table = scm_gc_malloc (sizeof (*table), "weak-table");
  346. table->buckets = scm_gc_malloc (sizeof (*table->buckets) * n,
  347. "weak table buckets");
  348. table->kind = kind;
  349. table->n_items = 0;
  350. table->n_buckets = n;
  351. table->lower = 0;
  352. table->upper = 9 * n / 10;
  353. table->size_index = i;
  354. table->min_size_index = i;
  355. table->last_gc_no = GC_get_gc_no ();
  356. scm_i_pthread_mutex_init (&table->lock, NULL);
  357. return scm_cell (scm_tc7_weak_table, (scm_t_bits)table);
  358. }
  359. void
  360. scm_i_weak_table_print (SCM exp, SCM port, scm_print_state *pstate)
  361. {
  362. scm_puts ("#<", port);
  363. scm_puts ("weak-table ", port);
  364. scm_uintprint (SCM_WEAK_TABLE (exp)->n_items, 10, port);
  365. scm_putc ('/', port);
  366. scm_uintprint (SCM_WEAK_TABLE (exp)->n_buckets, 10, port);
  367. scm_puts (">", port);
  368. }
  369. static void
  370. do_vacuum_weak_table (SCM table)
  371. {
  372. scm_t_weak_table *t;
  373. t = SCM_WEAK_TABLE (table);
  374. /* Unlike weak sets, the weak table interface allows custom predicates
  375. to call out to arbitrary Scheme. There are two ways that this code
  376. can be re-entrant, then: calling weak hash procedures while in a
  377. custom predicate, or via finalizers run explicitly by (gc) or in an
  378. async (for non-threaded Guile). We add a restriction that
  379. prohibits the first case, by convention. But since we can't
  380. prohibit the second case, here we trylock instead of lock. In any
  381. case, if the mutex is held by another thread, then the table is in
  382. active use, so the next user of the table will handle the vacuum
  383. for us. */
  384. if (scm_i_pthread_mutex_trylock (&t->lock) == 0)
  385. {
  386. vacuum_weak_table (t);
  387. scm_i_pthread_mutex_unlock (&t->lock);
  388. }
  389. return;
  390. }
  391. static scm_i_pthread_mutex_t all_weak_tables_lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
  392. static SCM all_weak_tables = SCM_EOL;
  393. static void
  394. vacuum_all_weak_tables (void)
  395. {
  396. scm_i_pthread_mutex_lock (&all_weak_tables_lock);
  397. scm_i_visit_weak_list (&all_weak_tables, do_vacuum_weak_table);
  398. scm_i_pthread_mutex_unlock (&all_weak_tables_lock);
  399. }
  400. SCM
  401. scm_c_make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
  402. {
  403. SCM ret;
  404. ret = make_weak_table (k, kind);
  405. scm_i_pthread_mutex_lock (&all_weak_tables_lock);
  406. all_weak_tables = scm_i_weak_cons (ret, all_weak_tables);
  407. scm_i_pthread_mutex_unlock (&all_weak_tables_lock);
  408. return ret;
  409. }
  410. SCM
  411. scm_weak_table_p (SCM obj)
  412. {
  413. return scm_from_bool (SCM_WEAK_TABLE_P (obj));
  414. }
  415. SCM
  416. scm_c_weak_table_ref (SCM table, unsigned long raw_hash,
  417. scm_t_table_predicate_fn pred,
  418. void *closure, SCM dflt)
  419. #define FUNC_NAME "weak-table-ref"
  420. {
  421. SCM ret;
  422. scm_t_weak_table *t;
  423. SCM_VALIDATE_WEAK_TABLE (1, table);
  424. t = SCM_WEAK_TABLE (table);
  425. scm_i_pthread_mutex_lock (&t->lock);
  426. vacuum_weak_table (t);
  427. ret = weak_table_ref (t, raw_hash, pred, closure, dflt);
  428. scm_i_pthread_mutex_unlock (&t->lock);
  429. return ret;
  430. }
  431. #undef FUNC_NAME
  432. void
  433. scm_c_weak_table_put_x (SCM table, unsigned long raw_hash,
  434. scm_t_table_predicate_fn pred,
  435. void *closure, SCM key, SCM value)
  436. #define FUNC_NAME "weak-table-put!"
  437. {
  438. scm_t_weak_table *t;
  439. SCM_VALIDATE_WEAK_TABLE (1, table);
  440. t = SCM_WEAK_TABLE (table);
  441. scm_i_pthread_mutex_lock (&t->lock);
  442. vacuum_weak_table (t);
  443. weak_table_put_x (t, raw_hash, pred, closure, key, value);
  444. scm_i_pthread_mutex_unlock (&t->lock);
  445. }
  446. #undef FUNC_NAME
  447. void
  448. scm_c_weak_table_remove_x (SCM table, unsigned long raw_hash,
  449. scm_t_table_predicate_fn pred,
  450. void *closure)
  451. #define FUNC_NAME "weak-table-remove!"
  452. {
  453. scm_t_weak_table *t;
  454. SCM_VALIDATE_WEAK_TABLE (1, table);
  455. t = SCM_WEAK_TABLE (table);
  456. scm_i_pthread_mutex_lock (&t->lock);
  457. vacuum_weak_table (t);
  458. weak_table_remove_x (t, raw_hash, pred, closure);
  459. scm_i_pthread_mutex_unlock (&t->lock);
  460. }
  461. #undef FUNC_NAME
  462. static int
  463. assq_predicate (SCM x, SCM y, void *closure)
  464. {
  465. return scm_is_eq (x, SCM_PACK_POINTER (closure));
  466. }
  467. SCM
  468. scm_weak_table_refq (SCM table, SCM key, SCM dflt)
  469. {
  470. return scm_c_weak_table_ref (table, scm_ihashq (key, -1),
  471. assq_predicate, SCM_UNPACK_POINTER (key),
  472. dflt);
  473. }
  474. void
  475. scm_weak_table_putq_x (SCM table, SCM key, SCM value)
  476. {
  477. scm_c_weak_table_put_x (table, scm_ihashq (key, -1),
  478. assq_predicate, SCM_UNPACK_POINTER (key),
  479. key, value);
  480. }
  481. void
  482. scm_weak_table_remq_x (SCM table, SCM key)
  483. {
  484. scm_c_weak_table_remove_x (table, scm_ihashq (key, -1),
  485. assq_predicate, SCM_UNPACK_POINTER (key));
  486. }
  487. void
  488. scm_weak_table_clear_x (SCM table)
  489. #define FUNC_NAME "weak-table-clear!"
  490. {
  491. scm_t_weak_table *t;
  492. unsigned long k;
  493. scm_t_weak_entry *entry;
  494. SCM_VALIDATE_WEAK_TABLE (1, table);
  495. t = SCM_WEAK_TABLE (table);
  496. scm_i_pthread_mutex_lock (&t->lock);
  497. t->last_gc_no = GC_get_gc_no ();
  498. for (k = 0; k < t->n_buckets; k++)
  499. {
  500. for (entry = t->buckets[k]; entry; entry = entry->next)
  501. unregister_disappearing_links (entry, t->kind);
  502. t->buckets[k] = NULL;
  503. }
  504. t->n_items = 0;
  505. scm_i_pthread_mutex_unlock (&t->lock);
  506. }
  507. #undef FUNC_NAME
  508. SCM
  509. scm_c_weak_table_fold (scm_t_table_fold_fn proc, void *closure,
  510. SCM init, SCM table)
  511. {
  512. scm_t_weak_table *t;
  513. unsigned long k;
  514. SCM alist = SCM_EOL;
  515. t = SCM_WEAK_TABLE (table);
  516. scm_i_pthread_mutex_lock (&t->lock);
  517. vacuum_weak_table (t);
  518. for (k = 0; k < t->n_buckets; k++)
  519. {
  520. scm_t_weak_entry *entry;
  521. for (entry = t->buckets[k]; entry; entry = entry->next)
  522. {
  523. scm_t_bits key, value;
  524. read_weak_entry (entry, &key, &value);
  525. if (key && value)
  526. alist = scm_acons (SCM_PACK (key), SCM_PACK (value), alist);
  527. }
  528. }
  529. scm_i_pthread_mutex_unlock (&t->lock);
  530. /* Call the proc outside the lock. */
  531. for (; !scm_is_null (alist); alist = scm_cdr (alist))
  532. init = proc (closure, scm_caar (alist), scm_cdar (alist), init);
  533. return init;
  534. }
  535. static SCM
  536. fold_trampoline (void *closure, SCM k, SCM v, SCM init)
  537. {
  538. return scm_call_3 (SCM_PACK_POINTER (closure), k, v, init);
  539. }
  540. SCM
  541. scm_weak_table_fold (SCM proc, SCM init, SCM table)
  542. #define FUNC_NAME "weak-table-fold"
  543. {
  544. SCM_VALIDATE_WEAK_TABLE (3, table);
  545. SCM_VALIDATE_PROC (1, proc);
  546. return scm_c_weak_table_fold (fold_trampoline, SCM_UNPACK_POINTER (proc), init, table);
  547. }
  548. #undef FUNC_NAME
  549. static SCM
  550. for_each_trampoline (void *closure, SCM k, SCM v, SCM seed)
  551. {
  552. scm_call_2 (SCM_PACK_POINTER (closure), k, v);
  553. return seed;
  554. }
  555. void
  556. scm_weak_table_for_each (SCM proc, SCM table)
  557. #define FUNC_NAME "weak-table-for-each"
  558. {
  559. SCM_VALIDATE_WEAK_TABLE (2, table);
  560. SCM_VALIDATE_PROC (1, proc);
  561. scm_c_weak_table_fold (for_each_trampoline, SCM_UNPACK_POINTER (proc), SCM_BOOL_F, table);
  562. }
  563. #undef FUNC_NAME
  564. static SCM
  565. map_trampoline (void *closure, SCM k, SCM v, SCM seed)
  566. {
  567. return scm_cons (scm_call_2 (SCM_PACK_POINTER (closure), k, v), seed);
  568. }
  569. SCM
  570. scm_weak_table_map_to_list (SCM proc, SCM table)
  571. #define FUNC_NAME "weak-table-map->list"
  572. {
  573. SCM_VALIDATE_WEAK_TABLE (2, table);
  574. SCM_VALIDATE_PROC (1, proc);
  575. return scm_c_weak_table_fold (map_trampoline, SCM_UNPACK_POINTER (proc), SCM_EOL, table);
  576. }
  577. #undef FUNC_NAME
  578. /* Legacy interface. */
  579. SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
  580. (SCM n),
  581. "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
  582. "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
  583. "Return a weak hash table with @var{size} buckets.\n"
  584. "\n"
  585. "You can modify weak hash tables in exactly the same way you\n"
  586. "would modify regular hash tables. (@pxref{Hash Tables})")
  587. #define FUNC_NAME s_scm_make_weak_key_hash_table
  588. {
  589. return scm_c_make_weak_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n),
  590. SCM_WEAK_TABLE_KIND_KEY);
  591. }
  592. #undef FUNC_NAME
  593. SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
  594. (SCM n),
  595. "Return a hash table with weak values with @var{size} buckets.\n"
  596. "(@pxref{Hash Tables})")
  597. #define FUNC_NAME s_scm_make_weak_value_hash_table
  598. {
  599. return scm_c_make_weak_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n),
  600. SCM_WEAK_TABLE_KIND_VALUE);
  601. }
  602. #undef FUNC_NAME
  603. SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 0, 1, 0,
  604. (SCM n),
  605. "Return a hash table with weak keys and values with @var{size}\n"
  606. "buckets. (@pxref{Hash Tables})")
  607. #define FUNC_NAME s_scm_make_doubly_weak_hash_table
  608. {
  609. return scm_c_make_weak_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n),
  610. SCM_WEAK_TABLE_KIND_BOTH);
  611. }
  612. #undef FUNC_NAME
  613. SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
  614. (SCM obj),
  615. "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
  616. "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
  617. "Return @code{#t} if @var{obj} is the specified weak hash\n"
  618. "table. Note that a doubly weak hash table is neither a weak key\n"
  619. "nor a weak value hash table.")
  620. #define FUNC_NAME s_scm_weak_key_hash_table_p
  621. {
  622. return scm_from_bool (SCM_WEAK_TABLE_P (obj) &&
  623. SCM_WEAK_TABLE (obj)->kind == SCM_WEAK_TABLE_KIND_KEY);
  624. }
  625. #undef FUNC_NAME
  626. SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
  627. (SCM obj),
  628. "Return @code{#t} if @var{obj} is a weak value hash table.")
  629. #define FUNC_NAME s_scm_weak_value_hash_table_p
  630. {
  631. return scm_from_bool (SCM_WEAK_TABLE_P (obj) &&
  632. SCM_WEAK_TABLE (obj)->kind == SCM_WEAK_TABLE_KIND_VALUE);
  633. }
  634. #undef FUNC_NAME
  635. SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
  636. (SCM obj),
  637. "Return @code{#t} if @var{obj} is a doubly weak hash table.")
  638. #define FUNC_NAME s_scm_doubly_weak_hash_table_p
  639. {
  640. return scm_from_bool (SCM_WEAK_TABLE_P (obj) &&
  641. SCM_WEAK_TABLE (obj)->kind == SCM_WEAK_TABLE_KIND_BOTH);
  642. }
  643. #undef FUNC_NAME
  644. void
  645. scm_weak_table_prehistory (void)
  646. {
  647. GC_word weak_key_bitmap[GC_BITMAP_SIZE (scm_t_weak_entry)] = { 0 };
  648. GC_word weak_value_bitmap[GC_BITMAP_SIZE (scm_t_weak_entry)] = { 0 };
  649. GC_word doubly_weak_bitmap[GC_BITMAP_SIZE (scm_t_weak_entry)] = { 0 };
  650. GC_set_bit (weak_key_bitmap, GC_WORD_OFFSET (scm_t_weak_entry, next));
  651. GC_set_bit (weak_value_bitmap, GC_WORD_OFFSET (scm_t_weak_entry, next));
  652. GC_set_bit (doubly_weak_bitmap, GC_WORD_OFFSET (scm_t_weak_entry, next));
  653. GC_set_bit (weak_key_bitmap, GC_WORD_OFFSET (scm_t_weak_entry, value));
  654. GC_set_bit (weak_value_bitmap, GC_WORD_OFFSET (scm_t_weak_entry, key));
  655. weak_key_descr = GC_make_descriptor (weak_key_bitmap,
  656. GC_WORD_LEN (scm_t_weak_entry));
  657. weak_value_descr = GC_make_descriptor (weak_value_bitmap,
  658. GC_WORD_LEN (scm_t_weak_entry));
  659. doubly_weak_descr = GC_make_descriptor (doubly_weak_bitmap,
  660. GC_WORD_LEN (scm_t_weak_entry));
  661. }
  662. void
  663. scm_init_weak_table ()
  664. {
  665. #include "libguile/weak-table.x"
  666. scm_i_register_async_gc_callback (vacuum_all_weak_tables);
  667. }
  668. /*
  669. Local Variables:
  670. c-file-style: "gnu"
  671. End:
  672. */