diff --git a/include/ConflictSet.h b/include/ConflictSet.h index 3822058..aa929f1 100644 --- a/include/ConflictSet.h +++ b/include/ConflictSet.h @@ -21,7 +21,12 @@ limitations under the License. #ifdef __cplusplus namespace weaselab { /** A data structure for optimistic concurrency control on ranges of - * bitwise-lexicographically-ordered keys. All versions must be >= 0. + * bitwise-lexicographically-ordered keys. + * + * All versions are >= 0. `oldestVersion` is the smallest version a subsequent + * `check` can read and `newestVersion` is the largest version passed to + * `addWrites` or the constructor. `oldestVersion <= newestVersion`, and + * `oldestVersion >= newestVersion - two billion`. * * Thread safety: * - It's safe to operate on two different ConflictSets in two different @@ -36,8 +41,7 @@ struct __attribute__((__visibility__("default"))) ConflictSet { /** The result of a check which intersects a write at a version > readVersion */ Conflict, - /** The result of a check with a readVersion older than the highest call to - `setOldestVersion` */ + /** The result of a check with a readVersion older than `oldestVersion` */ TooOld }; @@ -54,9 +58,6 @@ struct __attribute__((__visibility__("default"))) ConflictSet { /** `end` having length 0 denotes that this range is the single key {begin}. * Otherwise this denotes the range [begin, end), and begin must be < end */ Key end; - /** `readVersion` older than the oldestVersion or the version of the - * latest call to `addWrites` minus two billion will result in `TooOld`. - * Must be <= the version of the latest call to `addWrites` */ int64_t readVersion; }; @@ -68,23 +69,39 @@ struct __attribute__((__visibility__("default"))) ConflictSet { Key end; }; - /** The result of checking reads[i] is written in results[i] */ + /** The result of checking reads[i] is written in results[i]. + * @pre `reads` and `results` each hold at least `count` elements; each + * `reads[i]` is a valid `ReadRange` (see its field documentation); each + * `reads[i].readVersion <= newestVersion`. + * @post For every i < count, `results[i]` is `TooOld` if + * `reads[i].readVersion < oldestVersion`; `Conflict` if any write at a + * version greater than `reads[i].readVersion` intersects the range + * `reads[i]`; and `Commit` otherwise. */ void check(const ReadRange *reads, Result *results, int count) const; - /** Reads intersecting writes where readVersion < `writeVersion` will result - * in `Conflict` (or `TooOld`, eventually). `writeVersion` must be greater - * than or equal to all previous write versions. Call `addWrites` with `count` - * zero to only advance the version. */ + /** Records writes at `writeVersion`, making them visible to `check`. + * @pre `writes` holds at least `count` elements; every `writes[i]` is a valid + * `WriteRange` (see its field documentation); `count >= 0`; `writeVersion >= + * newestVersion`. + * @post Writes (writes[i], writeVersion) are recorded for every i < count, + * and `newestVersion` becomes `writeVersion`. `oldestVersion` advances as + * needed so that `oldestVersion >= newestVersion - two billion`; + * `oldestVersion <= newestVersion` is preserved. Calling with `count == 0` + * adds no write; `newestVersion` (and possibly `oldestVersion`) still + * advance. */ void addWrites(const WriteRange *writes, int count, int64_t writeVersion); - /** Reads where readVersion < `oldestVersion` will result in `TooOld`. Must be - * greater than or equal to all previous oldest versions. Must be <= the - * version of the latest call to `addWrites` */ + /** Reads where readVersion < `oldestVersion` will result in `TooOld`. + * @pre `oldestVersion >= 0` and `oldestVersion <= newestVersion`. + * @post `oldestVersion` becomes the maximum of its previous value and the + * argument; `newestVersion` is unchanged. */ void setOldestVersion(int64_t oldestVersion); - /** Reads where readVersion < oldestVersion will result in `TooOld`. There are - * no writes initially */ - explicit ConflictSet(int64_t oldestVersion); + /** Reads where readVersion < `oldestVersion` will result in `TooOld`. + * @pre `version >= 0`. + * @post No writes are recorded, and `oldestVersion == newestVersion == + * version`. */ + explicit ConflictSet(int64_t version); ~ConflictSet(); @@ -146,7 +163,12 @@ private: #else /** A data structure for optimistic concurrency control on ranges of - * bitwise-lexicographically-ordered keys. All versions must be >= 0. + * bitwise-lexicographically-ordered keys. + * + * All versions are >= 0. `oldestVersion` is the smallest version a subsequent + * `check` can read and `newestVersion` is the largest version passed to + * `addWrites` or the constructor. `oldestVersion <= newestVersion`, and + * `oldestVersion >= newestVersion - two billion`. * * Thread safety: * - It's safe to operate on two different ConflictSets in two different @@ -160,11 +182,10 @@ typedef struct ConflictSet ConflictSet; typedef enum { /** The result of a check which does not intersect any conflicting writes */ ConflictSet_Commit, - /** The result of a check which intersects a write at a version > readVersion - */ + /** The result of a check which intersects a write at a version > + readVersion */ ConflictSet_Conflict, - /** The result of a check with a readVersion older than the highest call to - `setOldestVersion` */ + /** The result of a check with a readVersion older than `oldestVersion` */ ConflictSet_TooOld } ConflictSet_Result; @@ -181,9 +202,6 @@ typedef struct { /** `end` having length 0 denotes that this range is the single key {begin}. * Otherwise this denotes the range [begin, end), and begin must be < end */ ConflictSet_Key end; - /** `readVersion` older than the oldestVersion or the version of the - * latest call to `addWrites` minus two billion will result in `TooOld`. - * Must be <= the version of the latest call to `addWrites` */ int64_t readVersion; } ConflictSet_ReadRange; @@ -195,27 +213,44 @@ typedef struct { ConflictSet_Key end; } ConflictSet_WriteRange; -/** The result of checking reads[i] is written in results[i] */ +/** The result of checking reads[i] is written in results[i]. + * @pre `reads` and `results` each hold at least `count` elements; each + * `reads[i]` is a valid `ConflictSet_ReadRange` (see its field documentation); + * each `reads[i].readVersion <= newestVersion`. + * @post For every i < count, `results[i]` is `ConflictSet_TooOld` if + * `reads[i].readVersion < oldestVersion`; `ConflictSet_Conflict` if any write + * at a version greater than `reads[i].readVersion` intersects the range + * `reads[i]`; and `ConflictSet_Commit` otherwise. */ void ConflictSet_check(const ConflictSet *cs, const ConflictSet_ReadRange *reads, ConflictSet_Result *results, int count); -/** Reads intersecting writes where readVersion < `writeVersion` will result - * in `Conflict` (or `TooOld`, eventually). `writeVersion` must be greater - * than or equal to all previous write versions. Call `addWrites` with `count` - * zero to only advance the version. */ +/** Records writes at `writeVersion`, making them visible to + * `ConflictSet_check`. + * @pre `writes` holds at least `count` elements; every `writes[i]` is a valid + * `ConflictSet_WriteRange` (see its field documentation); `count >= 0`; + * `writeVersion >= newestVersion`. + * @post Writes (writes[i], writeVersion) are recorded for every i < count, + * and `newestVersion` becomes `writeVersion`. `oldestVersion` advances as + * needed so that `oldestVersion >= newestVersion - two billion`; + * `oldestVersion <= newestVersion` is preserved. Calling with `count == 0` + * adds no write; `newestVersion` (and possibly `oldestVersion`) still + * advance. */ void ConflictSet_addWrites(ConflictSet *cs, const ConflictSet_WriteRange *writes, int count, int64_t writeVersion); -/** Reads where readVersion < `oldestVersion` will result in `TooOld`. Must be - * greater than or equal to all previous oldest versions. Must be <= the - * version of the latest call to `addWrites` */ +/** Reads where readVersion < `oldestVersion` will result in `TooOld`. + * @pre `oldestVersion >= 0` and `oldestVersion <= newestVersion`. + * @post `oldestVersion` becomes the maximum of its previous value and the + * argument; `newestVersion` is unchanged. */ void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion); -/** Reads where readVersion < oldestVersion will result in `TooOld`. There are - * no writes initially */ -ConflictSet *ConflictSet_create(int64_t oldestVersion); +/** Reads where readVersion < `oldestVersion` will result in `TooOld`. + * @pre `version >= 0`. + * @post No writes are recorded, and `oldestVersion == newestVersion == + * version`. */ +ConflictSet *ConflictSet_create(int64_t version); void ConflictSet_destroy(ConflictSet *cs);