Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ a.out
/test_clientid
/test_stamp
/test_map
/test_map_abort
/test_elementid
/test_element

# Tooling
Expand Down
30 changes: 9 additions & 21 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ test-string: string.c test_string.c test_util.h
./test_string

.PHONY: test-counter
test-counter: arena.c string.c hashtable.c clientid.c elementid.c host_posix.c counter.c test_counter.c test_util.h
$(CC) $(CFLAGS) -o test_counter arena.c string.c hashtable.c clientid.c elementid.c host_posix.c counter.c test_counter.c
test-counter: arena.c string.c hashtable.c clientid.c host_posix.c counter.c test_counter.c test_util.h
$(CC) $(CFLAGS) -o test_counter arena.c string.c hashtable.c clientid.c host_posix.c counter.c test_counter.c
./test_counter

.PHONY: test-scalar
Expand All @@ -47,25 +47,18 @@ test-scalar: arena.c string.c host_posix.c scalar.c test_scalar.c test_util.h
./test_scalar

.PHONY: test-register
test-register: arena.c string.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c test_register.c test_util.h
$(CC) $(CFLAGS) -o test_register arena.c string.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c test_register.c
test-register: arena.c string.c clientid.c host_posix.c stamp.c scalar.c register.c test_register.c test_util.h
$(CC) $(CFLAGS) -o test_register arena.c string.c clientid.c host_posix.c stamp.c scalar.c register.c test_register.c
./test_register

.PHONY: test-map
test-map: arena.c string.c hashtable.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c counter.c element.c map.c test_map.c test_util.h
$(CC) $(CFLAGS) -o test_map arena.c string.c hashtable.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c counter.c element.c map.c test_map.c
test-map: arena.c string.c hashtable.c clientid.c host_posix.c stamp.c scalar.c register.c counter.c element.c map.c test_map.c test_util.h
$(CC) $(CFLAGS) -o test_map arena.c string.c hashtable.c clientid.c host_posix.c stamp.c scalar.c register.c counter.c element.c map.c test_map.c
./test_map

# Death test: binary must abort. Exit status is inverted so success means
# the process died via host_abort.
.PHONY: test-map-abort
test-map-abort: arena.c string.c hashtable.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c counter.c element.c map.c test_map_abort.c
$(CC) $(CFLAGS) -o test_map_abort arena.c string.c hashtable.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c counter.c element.c map.c test_map_abort.c
! ./test_map_abort 2>/dev/null

.PHONY: test-element
test-element: arena.c string.c hashtable.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c counter.c map.c element.c test_element.c test_util.h
$(CC) $(CFLAGS) -o test_element arena.c string.c hashtable.c clientid.c elementid.c host_posix.c stamp.c scalar.c register.c counter.c map.c element.c test_element.c
test-element: arena.c string.c hashtable.c clientid.c host_posix.c stamp.c scalar.c register.c counter.c map.c element.c test_element.c test_util.h
$(CC) $(CFLAGS) -o test_element arena.c string.c hashtable.c clientid.c host_posix.c stamp.c scalar.c register.c counter.c map.c element.c test_element.c
./test_element

.PHONY: test-clientid
Expand All @@ -78,10 +71,5 @@ test-stamp: string.c clientid.c host_posix.c stamp.c test_stamp.c test_util.h
$(CC) $(CFLAGS) -o test_stamp string.c clientid.c host_posix.c stamp.c test_stamp.c
./test_stamp

.PHONY: test-elementid
test-elementid: string.c clientid.c host_posix.c elementid.c test_elementid.c test_util.h
$(CC) $(CFLAGS) -o test_elementid string.c clientid.c host_posix.c elementid.c test_elementid.c
./test_elementid

.PHONY: test
test: test-arena test-hashtable test-string test-counter test-scalar test-register test-clientid test-stamp test-map test-map-abort test-elementid test-element
test: test-arena test-hashtable test-string test-counter test-scalar test-register test-clientid test-stamp test-map test-element
92 changes: 49 additions & 43 deletions counter.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "host.h"

struct Counter {
ElementId id;
Arena *arena;
HashTable *entries; // client_id (uint32_t) -> CounterEntry
};
Expand All @@ -17,23 +16,46 @@ static inline uint32_t max_u32(uint32_t a, uint32_t b) {
return b;
}

Counter *counter_create(Arena *arena, ElementId id) {
Counter *counter_create(Arena *arena) {
Counter *counter = arena_alloc(arena, sizeof(Counter));
if (!counter) {
host_abortf(
"counter_create: arena OOM (requested %zu bytes for Counter)",
sizeof(Counter));
}
counter->id = id;
counter->arena = arena;
counter->entries = hashtable_create(arena);
if (!counter->entries) {
host_abort("counter_create: hashtable_create OOM");
host_abort("counter_create: hashtable_create OOM (per-client tallies "
"table)");
}
return counter;
}

ElementId counter_id(const Counter *counter) { return counter->id; }
// Get-or-create the per-client CounterEntry for `client_id`. Initializes a
// fresh entry to {inc=0, dec=0} on first call for a given client.
static CounterEntry *counter_entry_for(Counter *counter, ClientId client_id) {
void *entry_ptr;
if (hashtable_get(counter->entries, &client_id, sizeof(client_id),
&entry_ptr)) {
return entry_ptr;
}
CounterEntry *entry = arena_alloc(counter->arena, sizeof *entry);
if (!entry) {
host_abortf("counter: arena OOM (requested %zu bytes for CounterEntry)",
sizeof *entry);
}
entry->client_id = client_id;
entry->inc = 0;
entry->dec = 0;
HashTableInsertResult r = hashtable_insert(counter->entries, &client_id,
sizeof(client_id), entry);
if (r != HASHTABLE_OK) {
host_abortf("counter: hashtable_insert -> %s",
hashtable_insert_result_name(r));
}
return entry;
}

int64_t counter_read(const Counter *counter) {
int64_t total = 0;
Expand Down Expand Up @@ -80,51 +102,35 @@ void counter_merge(Counter *dst, const Counter *src) {
}

void counter_inc(Counter *counter, ClientId client_id, uint32_t amount) {
void *entry_ptr;
if (hashtable_get(counter->entries, &client_id, sizeof(client_id),
&entry_ptr)) {
CounterEntry *entry = entry_ptr;
entry->inc += amount;
} else {
CounterEntry *entry = arena_alloc(counter->arena, sizeof *entry);
if (!entry) {
host_abortf("counter_inc: arena OOM (requested %zu bytes for "
"CounterEntry)",
sizeof *entry);
}
entry->client_id = client_id;
entry->inc = amount;
entry->dec = 0;
HashTableInsertResult r = hashtable_insert(counter->entries, &client_id,
sizeof(client_id), entry);
if (r != HASHTABLE_OK) {
host_abortf("counter_inc: hashtable_insert -> %s",
hashtable_insert_result_name(r));
}
}
counter_entry_for(counter, client_id)->inc += amount;
}

void counter_dec(Counter *counter, ClientId client_id, uint32_t amount) {
void *entry_ptr;
if (hashtable_get(counter->entries, &client_id, sizeof(client_id),
&entry_ptr)) {
CounterEntry *entry = entry_ptr;
entry->dec += amount;
} else {
CounterEntry *entry = arena_alloc(counter->arena, sizeof *entry);
if (!entry) {
host_abortf("counter_dec: arena OOM (requested %zu bytes for "
counter_entry_for(counter, client_id)->dec += amount;
}

Counter *counter_clone(Arena *arena, const Counter *counter) {
Counter *clone = counter_create(arena);
HashTableIter it = hashtable_iter(counter->entries);
const void *key;
size_t key_len;
void *value;
while (hashtable_iter_next(&it, &key, &key_len, &value)) {
CounterEntry *entry = value;
CounterEntry *entry_copy =
arena_alloc(clone->arena, sizeof *entry_copy);
if (!entry_copy) {
host_abortf("counter_clone: arena OOM (requested %zu bytes for "
"CounterEntry)",
sizeof *entry);
sizeof *entry_copy);
}
entry->client_id = client_id;
entry->inc = 0;
entry->dec = amount;
HashTableInsertResult r = hashtable_insert(counter->entries, &client_id,
sizeof(client_id), entry);
*entry_copy = *entry;
HashTableInsertResult r =
hashtable_insert(clone->entries, key, key_len, entry_copy);
if (r != HASHTABLE_OK) {
host_abortf("counter_dec: hashtable_insert -> %s",
host_abortf("counter_clone: hashtable_insert -> %s",
hashtable_insert_result_name(r));
}
}
return clone;
}
12 changes: 6 additions & 6 deletions counter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
#define _CRDT_COUNTER_H

// PN-Counter: integer counter with concurrent increments and decrements.
// Carries an ElementId set at create, exposed via counter_id — that's how
// parent containers identify "same logical Counter across replicas".
// Identity is positional: a Counter is "the Counter for this slot" via the
// (key, kind) tuple of the containing Map. The Counter struct itself holds
// no identifier.
//
// Semantics:
// - Per-client (inc, dec) tallies, one CounterEntry per ClientId that
Expand All @@ -25,7 +26,6 @@

#include "arena.h"
#include "clientid.h"
#include "elementid.h"
#include "hashtable.h"
#include <stdint.h>

Expand All @@ -37,9 +37,7 @@ typedef struct CounterEntry {

typedef struct Counter Counter;

Counter *counter_create(Arena *arena, ElementId id);

ElementId counter_id(const Counter *counter);
Counter *counter_create(Arena *arena);

int64_t counter_read(const Counter *counter);

Expand All @@ -49,4 +47,6 @@ void counter_inc(Counter *counter, ClientId client_id, uint32_t amount);

void counter_dec(Counter *counter, ClientId client_id, uint32_t amount);

Counter *counter_clone(Arena *arena, const Counter *counter);

#endif // _CRDT_COUNTER_H
29 changes: 29 additions & 0 deletions element.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include "element.h"
#include "counter.h"
#include "host.h"
#include "map.h"
#include "register.h"
#include "scalar.h"

Element element_scalar(Scalar s) {
Element e = {.kind = ELEMENT_SCALAR, .as.scalar = s};
Expand Down Expand Up @@ -58,3 +61,29 @@ void element_merge(Element dst, Element src) {
break;
}
}

Element element_clone(Arena *arena, Element e) {
Element result;

switch (e.kind) {
case ELEMENT_SCALAR: {
Scalar cloned = scalar_clone(arena, e.as.scalar);

result = element_scalar(cloned);
} break;
case ELEMENT_REGISTER: {
Register *reg = register_clone(arena, e.as.reg);
result = element_register(reg);
} break;
case ELEMENT_COUNTER: {
Counter *counter = counter_clone(arena, e.as.counter);
result = element_counter(counter);
} break;
case ELEMENT_MAP: {
Map *map = map_clone(arena, e.as.map);
result = element_map(map);
} break;
}

return result;
}
1 change: 1 addition & 0 deletions element.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ Element element_map(Map *m);
ElementKind element_kind(Element e);
const char *element_kind_name(ElementKind k);
void element_merge(Element dst, Element src);
Element element_clone(Arena *arena, Element e);

#endif // _CRDT_ELEMENT_H
31 changes: 0 additions & 31 deletions elementid.c

This file was deleted.

31 changes: 0 additions & 31 deletions elementid.h

This file was deleted.

Loading