diff options
| author | Nick Barnes | 2001-10-31 14:40:56 +0000 |
|---|---|---|
| committer | Nick Barnes | 2001-10-31 14:40:56 +0000 |
| commit | 7acfca905d76140f4cc0b09c9a12de237de364cd (patch) | |
| tree | 3ed8babfa3a73d30f29e08ca5d5adcda4ca4e826 /mps/code/ld.c | |
| parent | b7ce4893f9902d57cd67ac9a92fa6c3d5a8fc833 (diff) | |
| download | emacs-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.c | 210 |
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 | |||
| 51 | SRCID(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 | */ | ||
| 60 | void 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 | */ | ||
| 96 | void 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 | */ | ||
| 125 | Bool 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 | */ | ||
| 161 | void 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 | */ | ||
| 194 | void 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 | } | ||