aboutsummaryrefslogtreecommitdiffstats
path: root/core/module-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/module-list.c')
-rw-r--r--core/module-list.c40
1 files changed, 37 insertions, 3 deletions
diff --git a/core/module-list.c b/core/module-list.c
index f32fd05..861798e 100644
--- a/core/module-list.c
+++ b/core/module-list.c
@@ -67,7 +67,7 @@ struct PureList {
unsigned char *journal;
size_t journal_size, journal_capacity;
uint64_t id, version; /* which list, and a number every change moves on */
- uint64_t shares; /* copies made so far: a node or text made before the last one is shared */
+ uint64_t shares; /* the last copy's epoch: a node or text made before it is shared */
PureRetired *retired; /* where shared nodes and texts go when the list lets them go */
Node *spare[2]; /* inner nodes and leaves let go that no copy read, kept for the next ones (linked by first) */
unsigned spares[2];
@@ -195,6 +195,36 @@ void pure_retired_free(PureRetired *retired) {
*retired = (PureRetired){0};
}
+static uint64_t made_at(const Retired *r) {
+ return r->text ? ((const uint64_t *)r->pointer)[-1] : ((const Node *)r->pointer)->made;
+}
+
+/* Room for more entries in retired; false when memory runs out. */
+static bool retired_room(PureRetired *retired, size_t more) {
+ if (retired->count + more <= retired->capacity) return true;
+ size_t capacity = 2 * retired->capacity > retired->count + more ? 2 * retired->capacity : retired->count + more;
+ Retired *grown = realloc(retired->entries, capacity * sizeof(*grown));
+ if (!grown) return false;
+ retired->entries = grown;
+ retired->capacity = capacity;
+ return true;
+}
+
+void pure_retired_pass(PureRetired *from, uint64_t epoch, PureRetired *kept, PureRetired *gone) {
+ Retired *entries = from->entries;
+ size_t keeping = 0;
+ for (size_t i = 0; kept && i < from->count; ++i) keeping += made_at(&entries[i]) < epoch;
+ bool room = (!keeping || retired_room(kept, keeping)) &&
+ (!gone || keeping == from->count || retired_room(gone, from->count - keeping));
+ for (size_t i = 0; i < from->count; ++i) {
+ PureRetired *to = kept && made_at(&entries[i]) < epoch ? kept : gone;
+ if (room && to) ((Retired *)to->entries)[to->count++] = entries[i];
+ /* else kept for good rather than freed while read (memory ran out) */
+ }
+ free(entries);
+ *from = (PureRetired){0};
+}
+
static Node *new_node(PureList *list, bool leaf) {
Node *node = list->spare[leaf];
if (node) {
@@ -570,16 +600,20 @@ void pure_list_clear(PureList *list) {
size_t pure_list_count(const PureList *list) { return list->count; }
-PureList *pure_list_share(PureList *list, PureRetired *retired) {
+PureList *pure_list_share(PureList *list, PureRetired *retired, uint64_t epoch) {
PureList *copy = malloc(sizeof(*copy));
if (!copy) return NULL;
*copy = (PureList){.layout = list->layout, .root = list->root, .count = list->count, .bytes = list->bytes,
.id = list->id, .version = list->version, .refs = 1, .copy = true};
list->retired = retired;
- ++list->shares;
+ list->shares = epoch > list->shares ? epoch : list->shares + 1;
return copy;
}
+void pure_list_mark(PureList *list, uint64_t epoch) {
+ if (list->retired && epoch > list->shares) list->shares = epoch;
+}
+
void pure_list_hold(PureList *copy) { __atomic_add_fetch(&copy->refs, 1, __ATOMIC_RELAXED); }
bool pure_list_current(const PureList *copy, const PureList *list) {