snapshot v3/p2; WIP
This commit is contained in:
parent
84a8aacce3
commit
5bc9ec7147
2 changed files with 169 additions and 42 deletions
|
@ -232,8 +232,7 @@ main()
|
||||||
|
|
||||||
#ifdef SNAPSHOTS
|
#ifdef SNAPSHOTS
|
||||||
/* Test creating a snapshot of an empty Skiplist */
|
/* Test creating a snapshot of an empty Skiplist */
|
||||||
size_t snp[TEST_ARRAY_SIZE * 2 + 10];
|
api_skip_snapshot_slex(list);
|
||||||
snp[0] = api_skip_snapshot_slex(list);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Insert 7 key/value pairs into the list. */
|
/* Insert 7 key/value pairs into the list. */
|
||||||
|
@ -254,7 +253,7 @@ main()
|
||||||
//rc = api_skip_put_slex(list, array[i], numeral);
|
//rc = api_skip_put_slex(list, array[i], numeral);
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
#ifdef SNAPSHOTS
|
#ifdef SNAPSHOTS
|
||||||
snp[i + 1] = api_skip_snapshot_slex(list);
|
api_skip_snapshot_slex(list);
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
|
@ -267,9 +266,6 @@ main()
|
||||||
api_skip_set_slex(list, array[i], to_upper(v));
|
api_skip_set_slex(list, array[i], to_upper(v));
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
}
|
}
|
||||||
#ifdef SNAPSHOTS
|
|
||||||
int r = i;
|
|
||||||
#endif
|
|
||||||
numeral = int_to_roman_numeral(-1);
|
numeral = int_to_roman_numeral(-1);
|
||||||
api_skip_dup_slex(list, -1, numeral);
|
api_skip_dup_slex(list, -1, numeral);
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
|
@ -287,7 +283,7 @@ main()
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SNAPSHOTS
|
#ifdef SNAPSHOTS
|
||||||
snp[++i] = api_skip_snapshot_slex(list);
|
api_skip_snapshot_slex(list);
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -308,7 +304,7 @@ main()
|
||||||
api_skip_del_slex(list, key);
|
api_skip_del_slex(list, key);
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
#ifdef SNAPSHOTS
|
#ifdef SNAPSHOTS
|
||||||
snp[++i] = api_skip_snapshot_slex(list);
|
api_skip_snapshot_slex(list);
|
||||||
INTEGRITY_CHK;
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
199
include/sl.h
199
include/sl.h
|
@ -165,7 +165,7 @@
|
||||||
#define SKIPLIST_ENTRY(type) \
|
#define SKIPLIST_ENTRY(type) \
|
||||||
struct __skiplist_##decl_entry { \
|
struct __skiplist_##decl_entry { \
|
||||||
struct type *sle_prev, **sle_next; \
|
struct type *sle_prev, **sle_next; \
|
||||||
size_t sle_height; \
|
size_t sle_height, sle_gen; \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -268,7 +268,11 @@
|
||||||
void *slh_aux; \
|
void *slh_aux; \
|
||||||
decl##_node_t *slh_head; \
|
decl##_node_t *slh_head; \
|
||||||
decl##_node_t *slh_tail; \
|
decl##_node_t *slh_tail; \
|
||||||
decl##_node_t *slh_snap; \
|
struct { \
|
||||||
|
decl##_node_t *head; \
|
||||||
|
decl##_node_t *tail; \
|
||||||
|
size_t gen; \
|
||||||
|
} slh_snap; \
|
||||||
} decl##_t; \
|
} decl##_t; \
|
||||||
\
|
\
|
||||||
/* Skip List comparison function type */ \
|
/* Skip List comparison function type */ \
|
||||||
|
@ -324,6 +328,26 @@
|
||||||
return level; \
|
return level; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
/** \
|
||||||
|
* -- __skip_gen_ \
|
||||||
|
* \
|
||||||
|
* Returns the current generation for snapshot purposes. \
|
||||||
|
*/ \
|
||||||
|
static inline size_t __skip_gen_##decl(decl##_t *slist) \
|
||||||
|
{ \
|
||||||
|
return slist->slh_snap.gen; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/** \
|
||||||
|
* -- __skip_incr_gen_ \
|
||||||
|
* \
|
||||||
|
* Returns the current generation for snapshot purposes. \
|
||||||
|
*/ \
|
||||||
|
static inline size_t __skip_incr_gen_##decl(decl##_t *slist) \
|
||||||
|
{ \
|
||||||
|
return ++slist->slh_snap.gen; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
/** \
|
/** \
|
||||||
* -- skip_alloc_node_ \
|
* -- skip_alloc_node_ \
|
||||||
* \
|
* \
|
||||||
|
@ -585,7 +609,7 @@
|
||||||
if (path == NULL) \
|
if (path == NULL) \
|
||||||
return ENOMEM; \
|
return ENOMEM; \
|
||||||
} \
|
} \
|
||||||
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max + 1); \
|
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max + 1); \
|
||||||
\
|
\
|
||||||
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
|
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
|
||||||
len = __skip_locate_##decl(slist, new, path); \
|
len = __skip_locate_##decl(slist, new, path); \
|
||||||
|
@ -642,6 +666,8 @@
|
||||||
slist->slh_head->field.sle_height = new_height; \
|
slist->slh_head->field.sle_height = new_height; \
|
||||||
slist->slh_tail->field.sle_height = new_height; \
|
slist->slh_tail->field.sle_height = new_height; \
|
||||||
} \
|
} \
|
||||||
|
/* Record this node's generation for snapshots. */ \
|
||||||
|
new->field.sle_gen = __skip_gen_##decl(slist); \
|
||||||
/* Increase our list length (aka. size, count, etc.) by one. */ \
|
/* Increase our list length (aka. size, count, etc.) by one. */ \
|
||||||
slist->slh_length++; \
|
slist->slh_length++; \
|
||||||
\
|
\
|
||||||
|
@ -892,7 +918,7 @@
|
||||||
if (path == NULL) \
|
if (path == NULL) \
|
||||||
return ENOMEM; \
|
return ENOMEM; \
|
||||||
} \
|
} \
|
||||||
memset(path, 0, sizeof(sizeof(decl##_node_t *) * slist->slh_max + 1)); \
|
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max + 1); \
|
||||||
\
|
\
|
||||||
__skip_locate_##decl(slist, new, path); \
|
__skip_locate_##decl(slist, new, path); \
|
||||||
node = path[0]; \
|
node = path[0]; \
|
||||||
|
@ -903,6 +929,7 @@
|
||||||
if (node) { \
|
if (node) { \
|
||||||
decl##_node_t *src = node, *dest = new; \
|
decl##_node_t *src = node, *dest = new; \
|
||||||
update_node_blk; \
|
update_node_blk; \
|
||||||
|
node->field.sle_gen = __skip_gen_##decl(slist); \
|
||||||
return rc; \
|
return rc; \
|
||||||
} \
|
} \
|
||||||
return -1; \
|
return -1; \
|
||||||
|
@ -930,7 +957,7 @@
|
||||||
if (path == NULL) \
|
if (path == NULL) \
|
||||||
return ENOMEM; \
|
return ENOMEM; \
|
||||||
} \
|
} \
|
||||||
memset(path, 0, sizeof(sizeof(decl##_node_t *) * slist->slh_max + 1)); \
|
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max + 1); \
|
||||||
\
|
\
|
||||||
/* Attempt to locate the node in the list. */ \
|
/* Attempt to locate the node in the list. */ \
|
||||||
len = __skip_locate_##decl(slist, n, path); \
|
len = __skip_locate_##decl(slist, n, path); \
|
||||||
|
@ -993,14 +1020,104 @@
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
/** \
|
||||||
|
* -- skip_snap_head_ \
|
||||||
|
* \
|
||||||
|
* Returns the node containing the first (smallest) element in the \
|
||||||
|
* snapshot which can be used to traverse the list. \
|
||||||
|
*/ \
|
||||||
|
decl##_node_t *prefix##skip_snap_head_##decl(decl##_t *slist) \
|
||||||
|
{ \
|
||||||
|
return slist->slh_snap.head->field.sle_next[0] == slist->slh_snap.tail ? NULL : slist->slh_snap.head->field.sle_next[0]; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/** \
|
||||||
|
* -- skip_snap_tail_ \
|
||||||
|
* \
|
||||||
|
* Returns the node containing the last (largest) element in the \
|
||||||
|
* snapshot which can be used to traverse the list. \
|
||||||
|
*/ \
|
||||||
|
decl##_node_t *prefix##skip_snap_tail_##decl(decl##_t *slist) \
|
||||||
|
{ \
|
||||||
|
return slist->slh_snap.tail->field.sle_prev == slist->slh_snap.head->field.sle_next[0] ? NULL : slist->slh_snap.tail->field.sle_prev; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
/** \
|
/** \
|
||||||
* -- skip_release_snapshot_ \
|
* -- skip_release_snapshot_ \
|
||||||
* \
|
* \
|
||||||
* Removes ... TODO \
|
* ALGORITHM: \
|
||||||
|
* A snapshot starts with a copy of the head and the tail of the list. \
|
||||||
|
* Those can be free'd here as our last step. We record a generation, which \
|
||||||
|
* is a monatomically growing unsigned int, at the time of the snapshot, \
|
||||||
|
* nodes inserted or mutated have there generation updated. To find the \
|
||||||
|
* nodes in the list that are only referenced by the snapshot we can perform \
|
||||||
|
* a "mergesort" like traversal of both the snapshot and the live list. For \
|
||||||
|
* each node, if the keys match and the nodes are not equal (same address in \
|
||||||
|
* memory) and we've reached a node greater than the snap node then we can \
|
||||||
|
* free the node in the snap. Duplicates complicate this a bit. \
|
||||||
*/ \
|
*/ \
|
||||||
void prefix##skip_release_snapshot_##decl(decl##_t *slist) \
|
void prefix##skip_release_snapshot_##decl(decl##_t *slist) \
|
||||||
{ \
|
{ \
|
||||||
((void)slist); \
|
int cmp; \
|
||||||
|
size_t nth, cnt = 0; \
|
||||||
|
decl##_node_t *sn, *ln, *node, *next; \
|
||||||
|
\
|
||||||
|
if (slist == NULL) \
|
||||||
|
return; \
|
||||||
|
\
|
||||||
|
sn = prefix##skip_snap_head_##decl(slist); \
|
||||||
|
SKIPLIST_FOREACH_H2T(decl, prefix, slist, ln, nth) \
|
||||||
|
{ \
|
||||||
|
goto top_of_loop; \
|
||||||
|
sn_forward_only:; \
|
||||||
|
sn = sn->field.sle_next[0]; \
|
||||||
|
if (sn == slist->slh_snap.tail) \
|
||||||
|
break; \
|
||||||
|
top_of_loop:; \
|
||||||
|
/* When a node is referenced by both, keep going. */ \
|
||||||
|
if (ln == sn) \
|
||||||
|
goto both_sn_and_ln_forward; \
|
||||||
|
\
|
||||||
|
cmp = slist->slh_cmp(slist, sn, ln, slist->slh_aux); \
|
||||||
|
/* When the `sn` > the `ln`, move forward `ln` only. */ \
|
||||||
|
if (cmp > 0) \
|
||||||
|
goto ln_forward_only; \
|
||||||
|
/* When the `sn` < the `ln`, move both forward. */ \
|
||||||
|
if (cmp < 0) { \
|
||||||
|
sn = sn->field.sle_next[0]; \
|
||||||
|
if (sn == slist->slh_snap.tail) \
|
||||||
|
break; \
|
||||||
|
goto sn_forward_only; \
|
||||||
|
} \
|
||||||
|
/* When nodes match but have different addresses... */ \
|
||||||
|
if (cmp == 0) { \
|
||||||
|
/* ... we could have: \
|
||||||
|
a) a duplicate, or \
|
||||||
|
b) a copied a node, but not it's value, because it was \
|
||||||
|
adjacent to some mutated node (it was in the `path[]` \
|
||||||
|
during an insert, update, or delete), or \
|
||||||
|
c) ...? \
|
||||||
|
*/ \
|
||||||
|
cnt++; \
|
||||||
|
} \
|
||||||
|
/* ln forward only */ \
|
||||||
|
goto ln_forward_only; \
|
||||||
|
\
|
||||||
|
ln_forward_only:; \
|
||||||
|
continue; \
|
||||||
|
both_sn_and_ln_forward:; \
|
||||||
|
sn = sn->field.sle_next[0]; \
|
||||||
|
if (sn == slist->slh_snap.tail) \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
/* If `sn` isn't at tail then there are a few more nodes to release. */ \
|
||||||
|
while (sn != slist->slh_snap.tail) { \
|
||||||
|
node = sn; \
|
||||||
|
free_node_blk; \
|
||||||
|
next = sn->field.sle_next[0]; \
|
||||||
|
free(sn); \
|
||||||
|
sn = next; \
|
||||||
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/** \
|
/** \
|
||||||
|
@ -1008,35 +1125,29 @@
|
||||||
* \
|
* \
|
||||||
* Preserve given node in the slh_pres list. \
|
* Preserve given node in the slh_pres list. \
|
||||||
*/ \
|
*/ \
|
||||||
static int __skip_preserve_node_##decl(decl##_t *slist, decl##_node_t *node, decl##_node_t **preserved) \
|
static int __skip_preserve_node_##decl(decl##_t *slist, const decl##_node_t *src, decl##_node_t **preserved) \
|
||||||
{ \
|
{ \
|
||||||
int rc = 0; \
|
int rc = 0; \
|
||||||
size_t amt, i; \
|
decl##_node_t *dest; \
|
||||||
char *d; \
|
|
||||||
const char *s; \
|
|
||||||
decl##_node_t *src, *dest; \
|
|
||||||
\
|
\
|
||||||
if (slist == NULL || node == NULL) \
|
if (slist == NULL || src == NULL) \
|
||||||
return 0; \
|
return 0; \
|
||||||
\
|
\
|
||||||
/* (a) alloc */ \
|
/* (a) alloc */ \
|
||||||
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
|
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
|
||||||
if (rc) \
|
if (rc) \
|
||||||
return rc; \
|
return rc; \
|
||||||
amt = sizeof(decl##_node_t); \
|
|
||||||
\
|
\
|
||||||
/* (b) shallow copy */ \
|
/* (b) shallow copy */ \
|
||||||
s = (const char *)node; \
|
memcpy(dest, src, sizeof(decl##_node_t)); \
|
||||||
d = (char *)dest; \
|
|
||||||
for (i = 0; i < amt; i++) \
|
|
||||||
d[i] = s[i]; \
|
|
||||||
\
|
\
|
||||||
/* (d) deep copy */ \
|
if (!(src == slist->slh_head || src == slist->slh_tail)) { \
|
||||||
src = node; \
|
/* (d) deep copy */ \
|
||||||
archive_node_blk; \
|
archive_node_blk; \
|
||||||
if (rc) { \
|
if (rc) { \
|
||||||
prefix##skip_free_node_##decl(dest); \
|
prefix##skip_free_node_##decl(dest); \
|
||||||
return rc; \
|
return rc; \
|
||||||
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
*preserved = dest; \
|
*preserved = dest; \
|
||||||
|
@ -1065,15 +1176,36 @@
|
||||||
size_t prefix##skip_snapshot_##decl(decl##_t *slist) \
|
size_t prefix##skip_snapshot_##decl(decl##_t *slist) \
|
||||||
{ \
|
{ \
|
||||||
int rc; \
|
int rc; \
|
||||||
|
decl##_node_t *head, *tail; \
|
||||||
\
|
\
|
||||||
if (slist == NULL) \
|
if (slist == NULL) \
|
||||||
return 0; \
|
return 0; \
|
||||||
if (slist->slh_snap != NULL) \
|
/* (a) preserve the head node */ \
|
||||||
prefix##skip_release_snapshot_##decl(slist); \
|
rc = __skip_preserve_node_##decl(slist, slist->slh_head, &head); \
|
||||||
rc = __skip_preserve_node_##decl(slist, slist->slh_head, &slist->slh_snap); \
|
|
||||||
if (rc > 0) \
|
if (rc > 0) \
|
||||||
return rc; \
|
return rc; \
|
||||||
slist->slh_gen++; \
|
/* (b) preserve the tail node */ \
|
||||||
|
rc = __skip_preserve_node_##decl(slist, slist->slh_tail, &tail); \
|
||||||
|
if (rc > 0) { \
|
||||||
|
prefix##skip_free_node_##decl(head); \
|
||||||
|
return rc; \
|
||||||
|
} \
|
||||||
|
/* (c) update any references in the new head's next[] array to point to \
|
||||||
|
the new tail; update the prev field in the new tail to the new head \
|
||||||
|
for similar reasons. */ \
|
||||||
|
__SKIP_ENTRIES_B2T(field, head) \
|
||||||
|
{ \
|
||||||
|
if (head->field.sle_next[lvl] == slist->slh_tail) \
|
||||||
|
head->field.sle_next[lvl] = tail; \
|
||||||
|
} \
|
||||||
|
if (tail->field.sle_prev == slist->slh_head) \
|
||||||
|
tail->field.sle_prev = head; \
|
||||||
|
/* (d) release previous snapshot, assign the new one */ \
|
||||||
|
if (slist->slh_snap.head != NULL) \
|
||||||
|
prefix##skip_release_snapshot_##decl(slist); \
|
||||||
|
slist->slh_snap.head = head; \
|
||||||
|
slist->slh_snap.tail = tail; \
|
||||||
|
slist->slh_snap.gen = __skip_incr_gen_##decl(slist); \
|
||||||
return slist->slh_gen; \
|
return slist->slh_gen; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,6 +1224,7 @@
|
||||||
*/ \
|
*/ \
|
||||||
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
|
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
|
||||||
{ \
|
{ \
|
||||||
|
size_t n = 0; \
|
||||||
unsigned long nth, n_err = 0; \
|
unsigned long nth, n_err = 0; \
|
||||||
decl##_node_t *node, *prev, *next; \
|
decl##_node_t *node, *prev, *next; \
|
||||||
struct __skiplist_##decl_entry *this; \
|
struct __skiplist_##decl_entry *this; \
|
||||||
|
@ -1152,7 +1285,6 @@
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
size_t n = 0; \
|
|
||||||
node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
__SKIP_ENTRIES_B2T(field, node) \
|
__SKIP_ENTRIES_B2T(field, node) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -1220,7 +1352,6 @@
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
size_t n = 0; \
|
|
||||||
__SKIP_ENTRIES_B2T(field, node) \
|
__SKIP_ENTRIES_B2T(field, node) \
|
||||||
{ \
|
{ \
|
||||||
if (this->sle_next[lvl] == NULL) { \
|
if (this->sle_next[lvl] == NULL) { \
|
||||||
|
@ -1488,7 +1619,7 @@
|
||||||
if (next) \
|
if (next) \
|
||||||
fprintf(os, "%p } |", (void *)next); \
|
fprintf(os, "%p } |", (void *)next); \
|
||||||
else \
|
else \
|
||||||
fprintf(os, "tail } |"); \
|
fprintf(os, "0x0 } |"); \
|
||||||
fflush(os); \
|
fflush(os); \
|
||||||
} \
|
} \
|
||||||
if (fn) { \
|
if (fn) { \
|
||||||
|
@ -1509,7 +1640,7 @@
|
||||||
fprintf(os, ":f%lu -> ", lvl); \
|
fprintf(os, ":f%lu -> ", lvl); \
|
||||||
__skip_dot_write_node_##decl(os, nsg, next); \
|
__skip_dot_write_node_##decl(os, nsg, next); \
|
||||||
fprintf(os, ":w%lu [];\n", lvl); \
|
fprintf(os, ":w%lu [];\n", lvl); \
|
||||||
fflush(os); \
|
fflush(os); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
@ -1597,7 +1728,7 @@
|
||||||
if (next) \
|
if (next) \
|
||||||
fprintf(os, "%p }", (void *)next); \
|
fprintf(os, "%p }", (void *)next); \
|
||||||
else \
|
else \
|
||||||
fprintf(os, "tail }"); \
|
fprintf(os, "0x0 }"); \
|
||||||
__SKIP_IS_LAST_ENTRY_T2B() continue; \
|
__SKIP_IS_LAST_ENTRY_T2B() continue; \
|
||||||
fprintf(os, " | "); \
|
fprintf(os, " | "); \
|
||||||
} \
|
} \
|
||||||
|
@ -1645,7 +1776,7 @@
|
||||||
size_t th = slist->slh_head->field.sle_height; \
|
size_t th = slist->slh_head->field.sle_height; \
|
||||||
for (size_t lvl = th; lvl != (size_t)-1; lvl--) { \
|
for (size_t lvl = th; lvl != (size_t)-1; lvl--) { \
|
||||||
next = (node->field.sle_next[lvl] == slist->slh_tail) ? NULL : node->field.sle_next[lvl]; \
|
next = (node->field.sle_next[lvl] == slist->slh_tail) ? NULL : node->field.sle_next[lvl]; \
|
||||||
fprintf(os, "<w%lu> TAIL", lvl); \
|
fprintf(os, "<w%lu> 0x0", lvl); \
|
||||||
__SKIP_IS_LAST_ENTRY_T2B() continue; \
|
__SKIP_IS_LAST_ENTRY_T2B() continue; \
|
||||||
fprintf(os, " | "); \
|
fprintf(os, " | "); \
|
||||||
} \
|
} \
|
||||||
|
|
Loading…
Reference in a new issue