weak-table.c 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252
  1. /* Copyright (C) 2011, 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. #include <assert.h>
  22. #include "libguile/bdw-gc.h"
  23. #include <gc/gc_mark.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-table.h"
  30. /* Weak Tables
  31. This file implements weak hash tables. Weak hash tables are
  32. generally used when you want to augment some object with additional
  33. data, but when you don't have space to store the data in the object.
  34. For example, procedure properties are implemented with weak tables.
  35. Weak tables are implemented using an open-addressed hash table.
  36. Basically this means that there is an array of entries, and the item
  37. is expected to be found the slot corresponding to its hash code,
  38. modulo the length of the array.
  39. Collisions are handled using linear probing with the Robin Hood
  40. technique. See Pedro Celis' paper, "Robin Hood Hashing":
  41. http://www.cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
  42. The vector of entries is allocated in such a way that the GC doesn't
  43. trace the weak values. For doubly-weak tables, this means that the
  44. entries are allocated as an "atomic" piece of memory. Key-weak and
  45. value-weak tables use a special GC kind with a custom mark procedure.
  46. When items are added weakly into table, a disappearing link is
  47. registered to their locations. If the referent is collected, then
  48. that link will be zeroed out.
  49. An entry in the table consists of the key and the value, together
  50. with the hash code of the key. We munge hash codes so that they are
  51. never 0. In this way we can detect removed entries (key of zero but
  52. nonzero hash code), and can then reshuffle elements as needed to
  53. maintain the robin hood ordering.
  54. Compared to buckets-and-chains hash tables, open addressing has the
  55. advantage that it is very cache-friendly. It also uses less memory.
  56. Implementation-wise, there are two things to note.
  57. 1. We assume that hash codes are evenly distributed across the
  58. range of unsigned longs. The actual hash code stored in the
  59. entry is left-shifted by 1 bit (losing 1 bit of hash precision),
  60. and then or'd with 1. In this way we ensure that the hash field
  61. of an occupied entry is nonzero. To map to an index, we
  62. right-shift the hash by one, divide by the size, and take the
  63. remainder.
  64. 2. Since the weak references are stored in an atomic region with
  65. disappearing links, they need to be accessed with the GC alloc
  66. lock. `copy_weak_entry' will do that for you. The hash code
  67. itself can be read outside the lock, though.
  68. */
  69. typedef struct {
  70. unsigned long hash;
  71. scm_t_bits key;
  72. scm_t_bits value;
  73. } scm_t_weak_entry;
  74. struct weak_entry_data {
  75. scm_t_weak_entry *in;
  76. scm_t_weak_entry *out;
  77. };
  78. static void*
  79. do_copy_weak_entry (void *data)
  80. {
  81. struct weak_entry_data *e = data;
  82. e->out->hash = e->in->hash;
  83. e->out->key = e->in->key;
  84. e->out->value = e->in->value;
  85. return NULL;
  86. }
  87. static void
  88. copy_weak_entry (scm_t_weak_entry *src, scm_t_weak_entry *dst)
  89. {
  90. struct weak_entry_data data;
  91. data.in = src;
  92. data.out = dst;
  93. GC_call_with_alloc_lock (do_copy_weak_entry, &data);
  94. }
  95. static void
  96. register_disappearing_links (scm_t_weak_entry *entry,
  97. SCM k, SCM v,
  98. scm_t_weak_table_kind kind)
  99. {
  100. if (SCM_UNPACK (k) && SCM_HEAP_OBJECT_P (k)
  101. && (kind == SCM_WEAK_TABLE_KIND_KEY
  102. || kind == SCM_WEAK_TABLE_KIND_BOTH))
  103. SCM_I_REGISTER_DISAPPEARING_LINK ((GC_PTR) &entry->key,
  104. (GC_PTR) SCM2PTR (k));
  105. if (SCM_UNPACK (v) && SCM_HEAP_OBJECT_P (v)
  106. && (kind == SCM_WEAK_TABLE_KIND_VALUE
  107. || kind == SCM_WEAK_TABLE_KIND_BOTH))
  108. SCM_I_REGISTER_DISAPPEARING_LINK ((GC_PTR) &entry->value,
  109. (GC_PTR) SCM2PTR (v));
  110. }
  111. static void
  112. unregister_disappearing_links (scm_t_weak_entry *entry,
  113. scm_t_weak_table_kind kind)
  114. {
  115. if (kind == SCM_WEAK_TABLE_KIND_KEY || kind == SCM_WEAK_TABLE_KIND_BOTH)
  116. GC_unregister_disappearing_link ((GC_PTR) &entry->key);
  117. if (kind == SCM_WEAK_TABLE_KIND_VALUE || kind == SCM_WEAK_TABLE_KIND_BOTH)
  118. GC_unregister_disappearing_link ((GC_PTR) &entry->value);
  119. }
  120. static void
  121. move_disappearing_links (scm_t_weak_entry *from, scm_t_weak_entry *to,
  122. SCM key, SCM value, scm_t_weak_table_kind kind)
  123. {
  124. if ((kind == SCM_WEAK_TABLE_KIND_KEY || kind == SCM_WEAK_TABLE_KIND_BOTH)
  125. && SCM_HEAP_OBJECT_P (key))
  126. {
  127. #ifdef HAVE_GC_MOVE_DISAPPEARING_LINK
  128. GC_move_disappearing_link ((GC_PTR) &from->key, (GC_PTR) &to->key);
  129. #else
  130. GC_unregister_disappearing_link (&from->key);
  131. SCM_I_REGISTER_DISAPPEARING_LINK (&to->key, SCM2PTR (key));
  132. #endif
  133. }
  134. if ((kind == SCM_WEAK_TABLE_KIND_VALUE || kind == SCM_WEAK_TABLE_KIND_BOTH)
  135. && SCM_HEAP_OBJECT_P (value))
  136. {
  137. #ifdef HAVE_GC_MOVE_DISAPPEARING_LINK
  138. GC_move_disappearing_link ((GC_PTR) &from->value, (GC_PTR) &to->value);
  139. #else
  140. GC_unregister_disappearing_link (&from->value);
  141. SCM_I_REGISTER_DISAPPEARING_LINK (&to->value, SCM2PTR (value));
  142. #endif
  143. }
  144. }
  145. static void
  146. move_weak_entry (scm_t_weak_entry *from, scm_t_weak_entry *to,
  147. scm_t_weak_table_kind kind)
  148. {
  149. if (from->hash)
  150. {
  151. scm_t_weak_entry copy;
  152. copy_weak_entry (from, &copy);
  153. to->hash = copy.hash;
  154. to->key = copy.key;
  155. to->value = copy.value;
  156. move_disappearing_links (from, to,
  157. SCM_PACK (copy.key), SCM_PACK (copy.value),
  158. kind);
  159. }
  160. else
  161. {
  162. to->hash = 0;
  163. to->key = 0;
  164. to->value = 0;
  165. }
  166. }
  167. typedef struct {
  168. scm_t_weak_entry *entries; /* the data */
  169. scm_i_pthread_mutex_t lock; /* the lock */
  170. scm_t_weak_table_kind kind; /* what kind of table it is */
  171. unsigned long size; /* total number of slots. */
  172. unsigned long n_items; /* number of items in table */
  173. unsigned long lower; /* when to shrink */
  174. unsigned long upper; /* when to grow */
  175. int size_index; /* index into hashtable_size */
  176. int min_size_index; /* minimum size_index */
  177. } scm_t_weak_table;
  178. #define SCM_WEAK_TABLE_P(x) (SCM_HAS_TYP7 (x, scm_tc7_weak_table))
  179. #define SCM_VALIDATE_WEAK_TABLE(pos, arg) \
  180. SCM_MAKE_VALIDATE_MSG (pos, arg, WEAK_TABLE_P, "weak-table")
  181. #define SCM_WEAK_TABLE(x) ((scm_t_weak_table *) SCM_CELL_WORD_1 (x))
  182. static unsigned long
  183. hash_to_index (unsigned long hash, unsigned long size)
  184. {
  185. return (hash >> 1) % size;
  186. }
  187. static unsigned long
  188. entry_distance (unsigned long hash, unsigned long k, unsigned long size)
  189. {
  190. unsigned long origin = hash_to_index (hash, size);
  191. if (k >= origin)
  192. return k - origin;
  193. else
  194. /* The other key was displaced and wrapped around. */
  195. return size - origin + k;
  196. }
  197. static void
  198. rob_from_rich (scm_t_weak_table *table, unsigned long k)
  199. {
  200. unsigned long empty, size;
  201. size = table->size;
  202. /* If we are to free up slot K in the table, we need room to do so. */
  203. assert (table->n_items < size);
  204. empty = k;
  205. do
  206. empty = (empty + 1) % size;
  207. while (table->entries[empty].hash);
  208. do
  209. {
  210. unsigned long last = empty ? (empty - 1) : (size - 1);
  211. move_weak_entry (&table->entries[last], &table->entries[empty],
  212. table->kind);
  213. empty = last;
  214. }
  215. while (empty != k);
  216. table->entries[empty].hash = 0;
  217. table->entries[empty].key = 0;
  218. table->entries[empty].value = 0;
  219. }
  220. static void
  221. give_to_poor (scm_t_weak_table *table, unsigned long k)
  222. {
  223. /* Slot K was just freed up; possibly shuffle others down. */
  224. unsigned long size = table->size;
  225. while (1)
  226. {
  227. unsigned long next = (k + 1) % size;
  228. unsigned long hash;
  229. scm_t_weak_entry copy;
  230. hash = table->entries[next].hash;
  231. if (!hash || hash_to_index (hash, size) == next)
  232. break;
  233. copy_weak_entry (&table->entries[next], &copy);
  234. if (!copy.key || !copy.value)
  235. /* Lost weak reference. */
  236. {
  237. give_to_poor (table, next);
  238. table->n_items--;
  239. continue;
  240. }
  241. move_weak_entry (&table->entries[next], &table->entries[k],
  242. table->kind);
  243. k = next;
  244. }
  245. /* We have shuffled down any entries that should be shuffled down; now
  246. free the end. */
  247. table->entries[k].hash = 0;
  248. table->entries[k].key = 0;
  249. table->entries[k].value = 0;
  250. }
  251. /* The GC "kinds" for singly-weak tables. */
  252. static int weak_key_gc_kind;
  253. static int weak_value_gc_kind;
  254. static struct GC_ms_entry *
  255. mark_weak_key_table (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
  256. struct GC_ms_entry *mark_stack_limit, GC_word env)
  257. {
  258. scm_t_weak_entry *entries = (scm_t_weak_entry*) addr;
  259. unsigned long k, size = GC_size (addr) / sizeof (scm_t_weak_entry);
  260. for (k = 0; k < size; k++)
  261. if (entries[k].hash && entries[k].key)
  262. {
  263. SCM value = SCM_PACK (entries[k].value);
  264. mark_stack_ptr = GC_MARK_AND_PUSH ((GC_word*) SCM2PTR (value),
  265. mark_stack_ptr, mark_stack_limit,
  266. NULL);
  267. }
  268. return mark_stack_ptr;
  269. }
  270. static struct GC_ms_entry *
  271. mark_weak_value_table (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
  272. struct GC_ms_entry *mark_stack_limit, GC_word env)
  273. {
  274. scm_t_weak_entry *entries = (scm_t_weak_entry*) addr;
  275. unsigned long k, size = GC_size (addr) / sizeof (scm_t_weak_entry);
  276. for (k = 0; k < size; k++)
  277. if (entries[k].hash && entries[k].value)
  278. {
  279. SCM key = SCM_PACK (entries[k].key);
  280. mark_stack_ptr = GC_MARK_AND_PUSH ((GC_word*) SCM2PTR (key),
  281. mark_stack_ptr, mark_stack_limit,
  282. NULL);
  283. }
  284. return mark_stack_ptr;
  285. }
  286. static scm_t_weak_entry *
  287. allocate_entries (unsigned long size, scm_t_weak_table_kind kind)
  288. {
  289. scm_t_weak_entry *ret;
  290. size_t bytes = size * sizeof (*ret);
  291. switch (kind)
  292. {
  293. case SCM_WEAK_TABLE_KIND_KEY:
  294. ret = GC_generic_malloc (bytes, weak_key_gc_kind);
  295. break;
  296. case SCM_WEAK_TABLE_KIND_VALUE:
  297. ret = GC_generic_malloc (bytes, weak_value_gc_kind);
  298. break;
  299. case SCM_WEAK_TABLE_KIND_BOTH:
  300. ret = scm_gc_malloc_pointerless (bytes, "weak-table");
  301. break;
  302. default:
  303. abort ();
  304. }
  305. memset (ret, 0, bytes);
  306. return ret;
  307. }
  308. /* Growing or shrinking is triggered when the load factor
  309. *
  310. * L = N / S (N: number of items in table, S: bucket vector length)
  311. *
  312. * passes an upper limit of 0.9 or a lower limit of 0.2.
  313. *
  314. * The implementation stores the upper and lower number of items which
  315. * trigger a resize in the hashtable object.
  316. *
  317. * Possible hash table sizes (primes) are stored in the array
  318. * hashtable_size.
  319. */
  320. static unsigned long hashtable_size[] = {
  321. 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
  322. 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041, 28762081,
  323. 57524111, 115048217, 230096423
  324. };
  325. #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
  326. static int
  327. compute_size_index (scm_t_weak_table *table)
  328. {
  329. int i = table->size_index;
  330. if (table->n_items < table->lower)
  331. {
  332. /* rehashing is not triggered when i <= min_size */
  333. do
  334. --i;
  335. while (i > table->min_size_index
  336. && table->n_items < hashtable_size[i] / 5);
  337. }
  338. else if (table->n_items > table->upper)
  339. {
  340. ++i;
  341. if (i >= HASHTABLE_SIZE_N)
  342. /* The biggest size currently is 230096423, which for a 32-bit
  343. machine will occupy 2.3GB of memory at a load of 80%. There
  344. is probably something better to do here, but if you have a
  345. weak map of that size, you are hosed in any case. */
  346. abort ();
  347. }
  348. return i;
  349. }
  350. static void
  351. resize_table (scm_t_weak_table *table)
  352. {
  353. scm_t_weak_entry *old_entries, *new_entries;
  354. int new_size_index;
  355. unsigned long old_size, new_size, old_k;
  356. do
  357. {
  358. new_size_index = compute_size_index (table);
  359. if (new_size_index == table->size_index)
  360. return;
  361. new_size = hashtable_size[new_size_index];
  362. scm_i_pthread_mutex_unlock (&table->lock);
  363. /* Allocating memory might cause finalizers to run, which could
  364. run anything, so drop our lock to avoid deadlocks. */
  365. new_entries = allocate_entries (new_size, table->kind);
  366. scm_i_pthread_mutex_unlock (&table->lock);
  367. }
  368. while (new_size_index != compute_size_index (table));
  369. old_entries = table->entries;
  370. old_size = table->size;
  371. table->size_index = new_size_index;
  372. table->size = new_size;
  373. if (new_size_index <= table->min_size_index)
  374. table->lower = 0;
  375. else
  376. table->lower = new_size / 5;
  377. table->upper = 9 * new_size / 10;
  378. table->n_items = 0;
  379. table->entries = new_entries;
  380. for (old_k = 0; old_k < old_size; old_k++)
  381. {
  382. scm_t_weak_entry copy;
  383. unsigned long new_k, distance;
  384. if (!old_entries[old_k].hash)
  385. continue;
  386. copy_weak_entry (&old_entries[old_k], &copy);
  387. if (!copy.key || !copy.value)
  388. continue;
  389. new_k = hash_to_index (copy.hash, new_size);
  390. for (distance = 0; ; distance++, new_k = (new_k + 1) % new_size)
  391. {
  392. unsigned long other_hash = new_entries[new_k].hash;
  393. if (!other_hash)
  394. /* Found an empty entry. */
  395. break;
  396. /* Displace the entry if our distance is less, otherwise keep
  397. looking. */
  398. if (entry_distance (other_hash, new_k, new_size) < distance)
  399. {
  400. rob_from_rich (table, new_k);
  401. break;
  402. }
  403. }
  404. table->n_items++;
  405. new_entries[new_k].hash = copy.hash;
  406. new_entries[new_k].key = copy.key;
  407. new_entries[new_k].value = copy.value;
  408. register_disappearing_links (&new_entries[new_k],
  409. SCM_PACK (copy.key), SCM_PACK (copy.value),
  410. table->kind);
  411. }
  412. }
  413. /* Run after GC via do_vacuum_weak_table, this function runs over the
  414. whole table, removing lost weak references, reshuffling the table as it
  415. goes. It might resize the table if it reaps enough entries. */
  416. static void
  417. vacuum_weak_table (scm_t_weak_table *table)
  418. {
  419. scm_t_weak_entry *entries = table->entries;
  420. unsigned long size = table->size;
  421. unsigned long k;
  422. for (k = 0; k < size; k++)
  423. {
  424. unsigned long hash = entries[k].hash;
  425. if (hash)
  426. {
  427. scm_t_weak_entry copy;
  428. copy_weak_entry (&entries[k], &copy);
  429. if (!copy.key || !copy.value)
  430. /* Lost weak reference; reshuffle. */
  431. {
  432. give_to_poor (table, k);
  433. table->n_items--;
  434. }
  435. }
  436. }
  437. if (table->n_items < table->lower)
  438. resize_table (table);
  439. }
  440. static SCM
  441. weak_table_ref (scm_t_weak_table *table, unsigned long hash,
  442. scm_t_table_predicate_fn pred, void *closure,
  443. SCM dflt)
  444. {
  445. unsigned long k, distance, size;
  446. scm_t_weak_entry *entries;
  447. size = table->size;
  448. entries = table->entries;
  449. hash = (hash << 1) | 0x1;
  450. k = hash_to_index (hash, size);
  451. for (distance = 0; distance < size; distance++, k = (k + 1) % size)
  452. {
  453. unsigned long other_hash;
  454. retry:
  455. other_hash = entries[k].hash;
  456. if (!other_hash)
  457. /* Not found. */
  458. return dflt;
  459. if (hash == other_hash)
  460. {
  461. scm_t_weak_entry copy;
  462. copy_weak_entry (&entries[k], &copy);
  463. if (!copy.key || !copy.value)
  464. /* Lost weak reference; reshuffle. */
  465. {
  466. give_to_poor (table, k);
  467. table->n_items--;
  468. goto retry;
  469. }
  470. if (pred (SCM_PACK (copy.key), SCM_PACK (copy.value), closure))
  471. /* Found. */
  472. return SCM_PACK (copy.value);
  473. }
  474. /* If the entry's distance is less, our key is not in the table. */
  475. if (entry_distance (other_hash, k, size) < distance)
  476. return dflt;
  477. }
  478. /* If we got here, then we were unfortunate enough to loop through the
  479. whole table. Shouldn't happen, but hey. */
  480. return dflt;
  481. }
  482. static void
  483. weak_table_put_x (scm_t_weak_table *table, unsigned long hash,
  484. scm_t_table_predicate_fn pred, void *closure,
  485. SCM key, SCM value)
  486. {
  487. unsigned long k, distance, size;
  488. scm_t_weak_entry *entries;
  489. size = table->size;
  490. entries = table->entries;
  491. hash = (hash << 1) | 0x1;
  492. k = hash_to_index (hash, size);
  493. for (distance = 0; ; distance++, k = (k + 1) % size)
  494. {
  495. unsigned long other_hash;
  496. retry:
  497. other_hash = entries[k].hash;
  498. if (!other_hash)
  499. /* Found an empty entry. */
  500. break;
  501. if (other_hash == hash)
  502. {
  503. scm_t_weak_entry copy;
  504. copy_weak_entry (&entries[k], &copy);
  505. if (!copy.key || !copy.value)
  506. /* Lost weak reference; reshuffle. */
  507. {
  508. give_to_poor (table, k);
  509. table->n_items--;
  510. goto retry;
  511. }
  512. if (pred (SCM_PACK (copy.key), SCM_PACK (copy.value), closure))
  513. /* Found an entry with this key. */
  514. break;
  515. }
  516. if (table->n_items > table->upper)
  517. /* Full table, time to resize. */
  518. {
  519. resize_table (table);
  520. return weak_table_put_x (table, hash >> 1, pred, closure, key, value);
  521. }
  522. /* Displace the entry if our distance is less, otherwise keep
  523. looking. */
  524. if (entry_distance (other_hash, k, size) < distance)
  525. {
  526. rob_from_rich (table, k);
  527. break;
  528. }
  529. }
  530. if (entries[k].hash)
  531. unregister_disappearing_links (&entries[k], table->kind);
  532. else
  533. table->n_items++;
  534. entries[k].hash = hash;
  535. entries[k].key = SCM_UNPACK (key);
  536. entries[k].value = SCM_UNPACK (value);
  537. register_disappearing_links (&entries[k], key, value, table->kind);
  538. }
  539. static void
  540. weak_table_remove_x (scm_t_weak_table *table, unsigned long hash,
  541. scm_t_table_predicate_fn pred, void *closure)
  542. {
  543. unsigned long k, distance, size;
  544. scm_t_weak_entry *entries;
  545. size = table->size;
  546. entries = table->entries;
  547. hash = (hash << 1) | 0x1;
  548. k = hash_to_index (hash, size);
  549. for (distance = 0; distance < size; distance++, k = (k + 1) % size)
  550. {
  551. unsigned long other_hash;
  552. retry:
  553. other_hash = entries[k].hash;
  554. if (!other_hash)
  555. /* Not found. */
  556. return;
  557. if (other_hash == hash)
  558. {
  559. scm_t_weak_entry copy;
  560. copy_weak_entry (&entries[k], &copy);
  561. if (!copy.key || !copy.value)
  562. /* Lost weak reference; reshuffle. */
  563. {
  564. give_to_poor (table, k);
  565. table->n_items--;
  566. goto retry;
  567. }
  568. if (pred (SCM_PACK (copy.key), SCM_PACK (copy.value), closure))
  569. /* Found an entry with this key. */
  570. {
  571. entries[k].hash = 0;
  572. entries[k].key = 0;
  573. entries[k].value = 0;
  574. unregister_disappearing_links (&entries[k], table->kind);
  575. if (--table->n_items < table->lower)
  576. resize_table (table);
  577. else
  578. give_to_poor (table, k);
  579. return;
  580. }
  581. }
  582. /* If the entry's distance is less, our key is not in the table. */
  583. if (entry_distance (other_hash, k, size) < distance)
  584. return;
  585. }
  586. }
  587. static void
  588. lock_weak_table (scm_t_weak_table *table)
  589. {
  590. scm_i_pthread_mutex_lock (&table->lock);
  591. }
  592. static void
  593. unlock_weak_table (scm_t_weak_table *table)
  594. {
  595. scm_i_pthread_mutex_unlock (&table->lock);
  596. }
  597. /* A weak table of weak tables, for use in the pthread_atfork handler. */
  598. static SCM all_weak_tables = SCM_BOOL_F;
  599. static void
  600. lock_all_weak_tables (void *unused)
  601. {
  602. scm_t_weak_table *s;
  603. scm_t_weak_entry *entries;
  604. unsigned long k, size;
  605. scm_t_weak_entry copy;
  606. s = SCM_WEAK_TABLE (all_weak_tables);
  607. lock_weak_table (s);
  608. size = s->size;
  609. entries = s->entries;
  610. for (k = 0; k < size; k++)
  611. if (entries[k].hash)
  612. {
  613. copy_weak_entry (&entries[k], &copy);
  614. if (copy.key)
  615. lock_weak_table (SCM_WEAK_TABLE (SCM_PACK (copy.key)));
  616. }
  617. }
  618. static void
  619. unlock_all_weak_tables (void *unused)
  620. {
  621. scm_t_weak_table *s;
  622. scm_t_weak_entry *entries;
  623. unsigned long k, size;
  624. scm_t_weak_entry copy;
  625. s = SCM_WEAK_TABLE (all_weak_tables);
  626. size = s->size;
  627. entries = s->entries;
  628. for (k = 0; k < size; k++)
  629. if (entries[k].hash)
  630. {
  631. copy_weak_entry (&entries[k], &copy);
  632. if (copy.key)
  633. unlock_weak_table (SCM_WEAK_TABLE (SCM_PACK (copy.key)));
  634. }
  635. unlock_weak_table (s);
  636. }
  637. static SCM
  638. make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
  639. {
  640. scm_t_weak_table *table;
  641. SCM ret;
  642. int i = 0, n = k ? k : 31;
  643. while (i + 1 < HASHTABLE_SIZE_N && n > hashtable_size[i])
  644. ++i;
  645. n = hashtable_size[i];
  646. table = scm_gc_malloc (sizeof (*table), "weak-table");
  647. table->entries = allocate_entries (n, kind);
  648. table->kind = kind;
  649. table->n_items = 0;
  650. table->size = n;
  651. table->lower = 0;
  652. table->upper = 9 * n / 10;
  653. table->size_index = i;
  654. table->min_size_index = i;
  655. scm_i_pthread_mutex_init (&table->lock, NULL);
  656. ret = scm_cell (scm_tc7_weak_table, (scm_t_bits)table);
  657. if (scm_is_true (all_weak_tables))
  658. scm_weak_table_putq_x (all_weak_tables, ret, SCM_BOOL_T);
  659. return ret;
  660. }
  661. void
  662. scm_i_weak_table_print (SCM exp, SCM port, scm_print_state *pstate)
  663. {
  664. scm_puts_unlocked ("#<", port);
  665. scm_puts_unlocked ("weak-table ", port);
  666. scm_uintprint (SCM_WEAK_TABLE (exp)->n_items, 10, port);
  667. scm_putc_unlocked ('/', port);
  668. scm_uintprint (SCM_WEAK_TABLE (exp)->size, 10, port);
  669. scm_puts_unlocked (">", port);
  670. }
  671. static void
  672. do_vacuum_weak_table (SCM table)
  673. {
  674. scm_t_weak_table *t;
  675. t = SCM_WEAK_TABLE (table);
  676. if (scm_i_pthread_mutex_trylock (&t->lock) == 0)
  677. {
  678. vacuum_weak_table (t);
  679. unlock_weak_table (t);
  680. }
  681. return;
  682. }
  683. /* The before-gc C hook only runs if GC_table_start_callback is available,
  684. so if not, fall back on a finalizer-based implementation. */
  685. static int
  686. weak_gc_callback (void **weak)
  687. {
  688. void *val = weak[0];
  689. void (*callback) (SCM) = weak[1];
  690. if (!val)
  691. return 0;
  692. callback (SCM_PACK_POINTER (val));
  693. return 1;
  694. }
  695. #ifdef HAVE_GC_TABLE_START_CALLBACK
  696. static void*
  697. weak_gc_hook (void *hook_data, void *fn_data, void *data)
  698. {
  699. if (!weak_gc_callback (fn_data))
  700. scm_c_hook_remove (&scm_before_gc_c_hook, weak_gc_hook, fn_data);
  701. return NULL;
  702. }
  703. #else
  704. static void
  705. weak_gc_finalizer (void *ptr, void *data)
  706. {
  707. if (weak_gc_callback (ptr))
  708. GC_REGISTER_FINALIZER_NO_ORDER (ptr, weak_gc_finalizer, data, NULL, NULL);
  709. }
  710. #endif
  711. static void
  712. scm_c_register_weak_gc_callback (SCM obj, void (*callback) (SCM))
  713. {
  714. void **weak = GC_MALLOC_ATOMIC (sizeof (void*) * 2);
  715. weak[0] = SCM_UNPACK_POINTER (obj);
  716. weak[1] = (void*)callback;
  717. GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak, SCM2PTR (obj));
  718. #ifdef HAVE_GC_TABLE_START_CALLBACK
  719. scm_c_hook_add (&scm_after_gc_c_hook, weak_gc_hook, weak, 0);
  720. #else
  721. GC_REGISTER_FINALIZER_NO_ORDER (weak, weak_gc_finalizer, NULL, NULL, NULL);
  722. #endif
  723. }
  724. SCM
  725. scm_c_make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
  726. {
  727. SCM ret;
  728. ret = make_weak_table (k, kind);
  729. scm_c_register_weak_gc_callback (ret, do_vacuum_weak_table);
  730. return ret;
  731. }
  732. SCM
  733. scm_weak_table_p (SCM obj)
  734. {
  735. return scm_from_bool (SCM_WEAK_TABLE_P (obj));
  736. }
  737. SCM
  738. scm_c_weak_table_ref (SCM table, unsigned long raw_hash,
  739. scm_t_table_predicate_fn pred,
  740. void *closure, SCM dflt)
  741. #define FUNC_NAME "weak-table-ref"
  742. {
  743. SCM ret;
  744. scm_t_weak_table *t;
  745. SCM_VALIDATE_WEAK_TABLE (1, table);
  746. t = SCM_WEAK_TABLE (table);
  747. lock_weak_table (t);
  748. ret = weak_table_ref (t, raw_hash, pred, closure, dflt);
  749. unlock_weak_table (t);
  750. return ret;
  751. }
  752. #undef FUNC_NAME
  753. void
  754. scm_c_weak_table_put_x (SCM table, unsigned long raw_hash,
  755. scm_t_table_predicate_fn pred,
  756. void *closure, SCM key, SCM value)
  757. #define FUNC_NAME "weak-table-put!"
  758. {
  759. scm_t_weak_table *t;
  760. SCM_VALIDATE_WEAK_TABLE (1, table);
  761. t = SCM_WEAK_TABLE (table);
  762. lock_weak_table (t);
  763. weak_table_put_x (t, raw_hash, pred, closure, key, value);
  764. unlock_weak_table (t);
  765. }
  766. #undef FUNC_NAME
  767. void
  768. scm_c_weak_table_remove_x (SCM table, unsigned long raw_hash,
  769. scm_t_table_predicate_fn pred,
  770. void *closure)
  771. #define FUNC_NAME "weak-table-remove!"
  772. {
  773. scm_t_weak_table *t;
  774. SCM_VALIDATE_WEAK_TABLE (1, table);
  775. t = SCM_WEAK_TABLE (table);
  776. lock_weak_table (t);
  777. weak_table_remove_x (t, raw_hash, pred, closure);
  778. unlock_weak_table (t);
  779. }
  780. #undef FUNC_NAME
  781. static int
  782. assq_predicate (SCM x, SCM y, void *closure)
  783. {
  784. return scm_is_eq (x, SCM_PACK_POINTER (closure));
  785. }
  786. SCM
  787. scm_weak_table_refq (SCM table, SCM key, SCM dflt)
  788. {
  789. if (SCM_UNBNDP (dflt))
  790. dflt = SCM_BOOL_F;
  791. return scm_c_weak_table_ref (table, scm_ihashq (key, -1),
  792. assq_predicate, SCM_UNPACK_POINTER (key),
  793. dflt);
  794. }
  795. SCM
  796. scm_weak_table_putq_x (SCM table, SCM key, SCM value)
  797. {
  798. scm_c_weak_table_put_x (table, scm_ihashq (key, -1),
  799. assq_predicate, SCM_UNPACK_POINTER (key),
  800. key, value);
  801. return SCM_UNSPECIFIED;
  802. }
  803. SCM
  804. scm_weak_table_remq_x (SCM table, SCM key)
  805. {
  806. scm_c_weak_table_remove_x (table, scm_ihashq (key, -1),
  807. assq_predicate, SCM_UNPACK_POINTER (key));
  808. return SCM_UNSPECIFIED;
  809. }
  810. SCM
  811. scm_weak_table_clear_x (SCM table)
  812. #define FUNC_NAME "weak-table-clear!"
  813. {
  814. scm_t_weak_table *t;
  815. SCM_VALIDATE_WEAK_TABLE (1, table);
  816. t = SCM_WEAK_TABLE (table);
  817. lock_weak_table (t);
  818. memset (t->entries, 0, sizeof (scm_t_weak_entry) * t->size);
  819. t->n_items = 0;
  820. unlock_weak_table (t);
  821. return SCM_UNSPECIFIED;
  822. }
  823. #undef FUNC_NAME
  824. SCM
  825. scm_c_weak_table_fold (scm_t_table_fold_fn proc, void *closure,
  826. SCM init, SCM table)
  827. {
  828. scm_t_weak_table *t;
  829. scm_t_weak_entry *entries;
  830. unsigned long k, size;
  831. t = SCM_WEAK_TABLE (table);
  832. lock_weak_table (t);
  833. size = t->size;
  834. entries = t->entries;
  835. for (k = 0; k < size; k++)
  836. {
  837. if (entries[k].hash)
  838. {
  839. scm_t_weak_entry copy;
  840. copy_weak_entry (&entries[k], &copy);
  841. if (copy.key && copy.value)
  842. {
  843. /* Release table lock while we call the function. */
  844. unlock_weak_table (t);
  845. init = proc (closure,
  846. SCM_PACK (copy.key), SCM_PACK (copy.value),
  847. init);
  848. lock_weak_table (t);
  849. }
  850. }
  851. }
  852. unlock_weak_table (t);
  853. return init;
  854. }
  855. static SCM
  856. fold_trampoline (void *closure, SCM k, SCM v, SCM init)
  857. {
  858. return scm_call_3 (SCM_PACK_POINTER (closure), k, v, init);
  859. }
  860. SCM
  861. scm_weak_table_fold (SCM proc, SCM init, SCM table)
  862. #define FUNC_NAME "weak-table-fold"
  863. {
  864. SCM_VALIDATE_WEAK_TABLE (3, table);
  865. SCM_VALIDATE_PROC (1, proc);
  866. return scm_c_weak_table_fold (fold_trampoline, SCM_UNPACK_POINTER (proc), init, table);
  867. }
  868. #undef FUNC_NAME
  869. static SCM
  870. for_each_trampoline (void *closure, SCM k, SCM v, SCM seed)
  871. {
  872. scm_call_2 (SCM_PACK_POINTER (closure), k, v);
  873. return seed;
  874. }
  875. SCM
  876. scm_weak_table_for_each (SCM proc, SCM table)
  877. #define FUNC_NAME "weak-table-for-each"
  878. {
  879. SCM_VALIDATE_WEAK_TABLE (2, table);
  880. SCM_VALIDATE_PROC (1, proc);
  881. scm_c_weak_table_fold (for_each_trampoline, SCM_UNPACK_POINTER (proc), SCM_BOOL_F, table);
  882. return SCM_UNSPECIFIED;
  883. }
  884. #undef FUNC_NAME
  885. static SCM
  886. map_trampoline (void *closure, SCM k, SCM v, SCM seed)
  887. {
  888. return scm_cons (scm_call_2 (SCM_PACK_POINTER (closure), k, v), seed);
  889. }
  890. SCM
  891. scm_weak_table_map_to_list (SCM proc, SCM table)
  892. #define FUNC_NAME "weak-table-map->list"
  893. {
  894. SCM_VALIDATE_WEAK_TABLE (2, table);
  895. SCM_VALIDATE_PROC (1, proc);
  896. return scm_c_weak_table_fold (map_trampoline, SCM_UNPACK_POINTER (proc), SCM_EOL, table);
  897. }
  898. #undef FUNC_NAME
  899. /* Legacy interface. */
  900. SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
  901. (SCM n),
  902. "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
  903. "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
  904. "Return a weak hash table with @var{size} buckets.\n"
  905. "\n"
  906. "You can modify weak hash tables in exactly the same way you\n"
  907. "would modify regular hash tables. (@pxref{Hash Tables})")
  908. #define FUNC_NAME s_scm_make_weak_key_hash_table
  909. {
  910. return scm_c_make_weak_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n),
  911. SCM_WEAK_TABLE_KIND_KEY);
  912. }
  913. #undef FUNC_NAME
  914. SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
  915. (SCM n),
  916. "Return a hash table with weak values with @var{size} buckets.\n"
  917. "(@pxref{Hash Tables})")
  918. #define FUNC_NAME s_scm_make_weak_value_hash_table
  919. {
  920. return scm_c_make_weak_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n),
  921. SCM_WEAK_TABLE_KIND_VALUE);
  922. }
  923. #undef FUNC_NAME
  924. SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
  925. (SCM n),
  926. "Return a hash table with weak keys and values with @var{size}\n"
  927. "buckets. (@pxref{Hash Tables})")
  928. #define FUNC_NAME s_scm_make_doubly_weak_hash_table
  929. {
  930. return scm_c_make_weak_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n),
  931. SCM_WEAK_TABLE_KIND_BOTH);
  932. }
  933. #undef FUNC_NAME
  934. SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
  935. (SCM obj),
  936. "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
  937. "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
  938. "Return @code{#t} if @var{obj} is the specified weak hash\n"
  939. "table. Note that a doubly weak hash table is neither a weak key\n"
  940. "nor a weak value hash table.")
  941. #define FUNC_NAME s_scm_weak_key_hash_table_p
  942. {
  943. return scm_from_bool (SCM_WEAK_TABLE_P (obj) &&
  944. SCM_WEAK_TABLE (obj)->kind == SCM_WEAK_TABLE_KIND_KEY);
  945. }
  946. #undef FUNC_NAME
  947. SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
  948. (SCM obj),
  949. "Return @code{#t} if @var{obj} is a weak value hash table.")
  950. #define FUNC_NAME s_scm_weak_value_hash_table_p
  951. {
  952. return scm_from_bool (SCM_WEAK_TABLE_P (obj) &&
  953. SCM_WEAK_TABLE (obj)->kind == SCM_WEAK_TABLE_KIND_VALUE);
  954. }
  955. #undef FUNC_NAME
  956. SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
  957. (SCM obj),
  958. "Return @code{#t} if @var{obj} is a doubly weak hash table.")
  959. #define FUNC_NAME s_scm_doubly_weak_hash_table_p
  960. {
  961. return scm_from_bool (SCM_WEAK_TABLE_P (obj) &&
  962. SCM_WEAK_TABLE (obj)->kind == SCM_WEAK_TABLE_KIND_BOTH);
  963. }
  964. #undef FUNC_NAME
  965. void
  966. scm_weak_table_prehistory (void)
  967. {
  968. weak_key_gc_kind =
  969. GC_new_kind (GC_new_free_list (),
  970. GC_MAKE_PROC (GC_new_proc (mark_weak_key_table), 0),
  971. 0, 0);
  972. weak_value_gc_kind =
  973. GC_new_kind (GC_new_free_list (),
  974. GC_MAKE_PROC (GC_new_proc (mark_weak_value_table), 0),
  975. 0, 0);
  976. all_weak_tables = scm_c_make_weak_table (0, SCM_WEAK_TABLE_KIND_KEY);
  977. scm_c_atfork (lock_all_weak_tables, unlock_all_weak_tables, NULL);
  978. }
  979. void
  980. scm_init_weak_table ()
  981. {
  982. #include "libguile/weak-table.x"
  983. }
  984. /*
  985. Local Variables:
  986. c-file-style: "gnu"
  987. End:
  988. */