aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code/ld.c
diff options
context:
space:
mode:
authorNick Barnes2001-10-31 14:40:56 +0000
committerNick Barnes2001-10-31 14:40:56 +0000
commit7acfca905d76140f4cc0b09c9a12de237de364cd (patch)
tree3ed8babfa3a73d30f29e08ca5d5adcda4ca4e826 /mps/code/ld.c
parentb7ce4893f9902d57cd67ac9a92fa6c3d5a8fc833 (diff)
downloademacs-7acfca905d76140f4cc0b09c9a12de237de364cd.tar.gz
emacs-7acfca905d76140f4cc0b09c9a12de237de364cd.zip
Branch imports for masters.
Copied from Perforce Change: 23678 ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code/ld.c')
-rw-r--r--mps/code/ld.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/mps/code/ld.c b/mps/code/ld.c
new file mode 100644
index 00000000000..518786f4b21
--- /dev/null
+++ b/mps/code/ld.c
@@ -0,0 +1,210 @@
1/* impl.c.ld: LOCATION DEPENDENCY IMPLEMENTATION
2 *
3 * $HopeName: MMsrc!ld.c(trunk.8) $
4 * Copyright (C) 1996 Harlequin Limited. All rights reserved.
5 *
6 * .def: A location dependency records the fact that the bit-patterns
7 * of some references will be used directly (most likely for
8 * hashing), and provides a protocol for finding out whether that
9 * dependency has become stale because a reference has been changed (by
10 * a moving memory manager).
11 *
12 * .rationale: The client may build hash-tables using pointer hashing.
13 * The collector may change the values of the pointers transparently,
14 * by fixing them and moving the objects. The hash function will no
15 * longer return the same value, and the object can't be found in
16 * the expected bucket. When the client can't find an object in a
17 * hashtable it must check to see if any of the references in the table
18 * have moved, and rehash if they have. Location dependency provides
19 * a reasonably accurate way of determining whether this has happened.
20 *
21 * .impl: A location dependency consists of an epoch (monotonically
22 * increasing notion of time) and a reference set. The epoch records
23 * when the location dependency started, and the reference set
24 * accumulates an approximation to the set of references which are
25 * depended on. The client can check to see if any of these
26 * references have moved since the epoch.
27 *
28 * .history: The current epoch, and a history of object movement
29 * are recorded in the arena. Each slot in the history contains a
30 * summary of all the movement since an earlier epoch (maintained by
31 * LDAge). To see if a dependency has become stale all that
32 * is needed is to see whether its reference set intersects with the
33 * movement since its epoch.
34 *
35 * .mod: LDHistoryLENGTH is used as a modulus to calculate the offset
36 * of an epoch in the history, so it's best if this is a power of two.
37 * (impl.h.mpmconf)
38 *
39 * .epoch-size: The epoch should probably be a longer integer to avoid
40 * the possibility of overflow.
41 * (32 bits only gives 50 days at 1ms frequency)
42 *
43 * .ld.access: Accesses (reads and writes) to the ld structure must be
44 * "wrapped" with an ShieldExpose/Cover pair if and only if the access
45 * is taking place inside the arena. Currently this is only the case for
46 * LDReset.
47 */
48
49#include "mpm.h"
50
51SRCID(ld, "$HopeName: MMsrc!ld.c(trunk.8) $");
52
53
54/* LDReset -- reset a dependency to empty
55 *
56 * .reset.sync: This does not need to be synchronized with LDAge
57 * because if the epoch advances after it is read the dependency
58 * will simply include movement for more time than necessary.
59 */
60void LDReset(LD ld, Arena arena)
61{
62 Bool b;
63 Seg seg;
64
65 AVER(ld != NULL);
66 AVERT(Arena, arena);
67
68 b = SegOfAddr(&seg, arena, (Addr)ld);
69 if (b)
70 ShieldExpose(arena, seg); /* .ld.access */
71 ld->epoch = arena->epoch;
72 ld->rs = RefSetEMPTY;
73 if (b)
74 ShieldCover(arena, seg);
75}
76
77
78/* LDAdd -- add a reference to a dependency
79 *
80 * .add.lock-free: This function is thread safe with respect to the
81 * (rest of the) mps. It is unnecessary to claim locks before calling
82 * this function.
83 *
84 * .add.user-serial:
85 * However, this function is _not_ thread safe with respect to itself.
86 * Users should ensure that calls to LDAdd operating on the same LD are
87 * serialized.
88 *
89 * .add.sync: Add must take place _before_ the location of the reference
90 * is depended on. If the reference changes between adding and
91 * depending it will show up as moved because the movement will have
92 * occured since the epoch recorded in the dependency. If the location
93 * were used first only the new location of the reference would end up
94 * in the set.
95 */
96void LDAdd(LD ld, Arena arena, Addr addr)
97{
98 AVER(ld->epoch <= arena->epoch);
99 /* AVERT(Arena, arena) -- see .add.lock-free */
100
101 ld->rs = RefSetAdd(arena, ld->rs, addr);
102}
103
104
105/* LDIsStale -- check whether a dependency is stale
106 *
107 * .stale.thread-safe: This function is thread safe. It will return a
108 * correct (but possibly conservative) answer regardless of the number
109 * of calls to LDAge anywhere during the function. Update with care.
110 *
111 * .stale.current: If the dependency's epoch is the current epoch,
112 * nothing can have moved since it was initialized.
113 *
114 * .stale.recent: If the dependency is recent, see if it intersects
115 * with everything which has moved since it was initialized.
116 *
117 * .stale.recent.conservative: The refset from the history table is
118 * loaded before we check whether ld->epoch is "recent" with respect to
119 * the current epoch. This means that we may (conservatively) decide
120 * to use the prehistory instead.
121 *
122 * .stale.old: Otherwise, if the dependency is older than the length
123 * of the history, check it against all movement that has ever occured.
124 */
125Bool LDIsStale(LD ld, Arena arena, Addr addr)
126{
127 RefSet rs;
128
129 UNUSED(addr);
130
131 AVER(ld->epoch <= arena->epoch);
132 /* AVERT(Arena, arena) -- .stale.thread-safe */
133
134 if (arena->epoch == ld->epoch) /* .stale.current */
135 return FALSE;
136
137 /* Load the history refset, _then_ check to see if it's recent.
138 * This may in fact load an okay refset, which we decide to throw
139 * away and use the pre-history instead. */
140 rs = arena->history[ld->epoch % LDHistoryLENGTH];
141 /* .stale.recent */
142 /* .stale.recent.conservative */
143 if (arena->epoch - ld->epoch > LDHistoryLENGTH) {
144 rs = arena->prehistory; /* .stale.old */
145 }
146
147 return RefSetInter(ld->rs, rs) != RefSetEMPTY;
148}
149
150
151/* LDAge -- age the arena by adding a moved set
152 *
153 * This stores the fact that a set of references has changed in
154 * the history in the arena structure, and increments the epoch.
155 *
156 * This is only called during a 'flip', because it must be atomic
157 * w.r.t. the mutator (and therefore w.r.t. LdIsStale). This is
158 * because it updates the notion of the 'current' and 'oldest' history
159 * entries.
160 */
161void LDAge(Arena arena, RefSet rs)
162{
163 Size i;
164
165 AVERT(Arena, arena);
166 AVER(rs != RefSetEMPTY);
167
168 /* Replace the entry for epoch - LDHistoryLENGTH by an empty */
169 /* set which will become the set which has moved since the */
170 /* current epoch. */
171 arena->history[arena->epoch % LDHistoryLENGTH] = RefSetEMPTY;
172
173 /* Record the fact that the moved set has moved, by adding it */
174 /* to all the sets in the history, including the set for the */
175 /* current epoch. */
176 for(i = 0; i < LDHistoryLENGTH; ++i)
177 arena->history[i] = RefSetUnion(arena->history[i], rs);
178
179 /* This is the union of all movement since time zero. */
180 arena->prehistory = RefSetUnion(arena->prehistory, rs);
181
182 /* Advance the epoch by one. */
183 ++arena->epoch;
184 AVER(arena->epoch != 0); /* .epoch-size */
185}
186
187
188/* LDMerge -- merge two location dependencies
189 *
190 * .merge.lock-free: This function is thread-safe with respect to the
191 * (rest of the) MPS. It is unnecessary to claim locks before calling
192 * this function.
193 */
194void LDMerge(LD ld, Arena arena, LD from)
195{
196 /* AVERT(Arena, arena); -- .merge.lock-free */
197 AVER(ld != NULL);
198 AVER(ld->epoch <= arena->epoch);
199 AVER(from != NULL);
200 AVER(from->epoch <= arena->epoch);
201
202 /* If a reference has been added since epoch e1 then I've */
203 /* certainly added since epoch e0 where e0 < e1. Therefore */
204 /* the epoch of the merged ld is the minimum. */
205 if (from->epoch < ld->epoch)
206 ld->epoch = from->epoch;
207
208 /* The set of references added is the union of the two. */
209 ld->rs = RefSetUnion(ld->rs, from->rs);
210}