diff --git a/applications/Makefile b/applications/Makefile index a779fc1..ba0ec94 100644 --- a/applications/Makefile +++ b/applications/Makefile @@ -31,6 +31,7 @@ all: \ .PHONY: clean clean: @make -C wasm_apps clean + @make -C scratch_storage clean @rm -rf dist @rm -rf ../runtime/bin/*.so @@ -43,6 +44,23 @@ wasm_apps/dist/%.wasm: ../libsledge/dist/libsledge.a: ../libsledge/dist/ make -C .. libsledge +PHONY: scratch_storage +scratch_storage: + make -C scratch_storage all + +PHONY: scratch_storage.install +scratch_storage.install: \ + scratch_storage_get.install \ + scratch_storage_set.install \ + scratch_storage_delete.install \ + scratch_storage_upsert.install + +scratch_storage/scratch_storage_%.wasm: + make -C scratch_storage all + +dist/scratch_storage_%.bc: scratch_storage/scratch_storage_%.wasm dist + ${AWSMCC} ${AWSMFLAGS} $< -o $@ + dist/%.bc: ./wasm_apps/dist/%.wasm dist ${AWSMCC} ${AWSMFLAGS} $< -o $@ diff --git a/applications/scratch_storage/Makefile b/applications/scratch_storage/Makefile new file mode 100644 index 0000000..1a751d8 --- /dev/null +++ b/applications/scratch_storage/Makefile @@ -0,0 +1,24 @@ +include ../wasm_apps/common.mk + +.PHONY: all +all: \ + scratch_storage_get.wasm \ + scratch_storage_set.wasm \ + scratch_storage_delete.wasm \ + scratch_storage_upsert.wasm \ + +.PHONY: clean +clean: + @rm -f scratch_storage_set.wa* scratch_storage_get.wa* scratch_storage_delete.wa* scratch_storage_upsert.wa* + +scratch_storage_set.wasm: scratch_storage_set.c + @${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@ + +scratch_storage_get.wasm: scratch_storage_get.c + @${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@ + +scratch_storage_delete.wasm: scratch_storage_delete.c + @${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@ + +scratch_storage_upsert.wasm: scratch_storage_upsert.c + @${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@ diff --git a/applications/scratch_storage/scratch_storage_delete.c b/applications/scratch_storage/scratch_storage_delete.c new file mode 100644 index 0000000..5111107 --- /dev/null +++ b/applications/scratch_storage/scratch_storage_delete.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +extern int scratch_storage_delete(void *key, uint32_t key_len) + __attribute__((__import_module__("scratch_storage"), __import_name__("delete"))); + +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + char *key = argv[1]; + + if (key == NULL || strlen(key) < 0) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + int rc = scratch_storage_delete(key, strlen(key)); + if (rc == 1) { + printf("Key '%s' not found\n", key); + return 0; + } else { + printf("Key %s deleted\n", key); + } +}; diff --git a/applications/scratch_storage/scratch_storage_get.c b/applications/scratch_storage/scratch_storage_get.c new file mode 100644 index 0000000..ba863e5 --- /dev/null +++ b/applications/scratch_storage/scratch_storage_get.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +extern int scratch_storage_get(void *key, uint32_t key_len, void *buf, uint32_t buf_len) + __attribute__((__import_module__("scratch_storage"), __import_name__("get"))); + +extern uint32_t scratch_storage_get_size(void *key, uint32_t key_len) + __attribute__((__import_module__("scratch_storage"), __import_name__("get_size"))); + +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + char *key = argv[1]; + + if (key == NULL || strlen(key) < 0) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + uint32_t val_size = scratch_storage_get_size(key, strlen(key)); + char *buf = calloc(val_size + 1, sizeof(char)); + int rc = scratch_storage_get(key, strlen(key), buf, val_size); + assert(rc != 2); + if (rc == 1) { + printf("Key '%s' not found\n", key); + return 0; + } else { + printf("Key %s resolved to value of size %u with contents %s\n", key, val_size, buf); + } +}; diff --git a/applications/scratch_storage/scratch_storage_set.c b/applications/scratch_storage/scratch_storage_set.c new file mode 100644 index 0000000..232dc17 --- /dev/null +++ b/applications/scratch_storage/scratch_storage_set.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +extern int scratch_storage_set(void *key, uint32_t key_len, void *value, uint32_t value_len) + __attribute__((__import_module__("scratch_storage"), __import_name__("set"))); + +int +main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + char *key = argv[1]; + char *value = argv[2]; + + if (key == NULL || strlen(key) < 0 || value == NULL || strlen(value) < 0) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + int rc = scratch_storage_set(key, strlen(key), value, strlen(value)); + if (rc == 1) { + printf("Key %s was already present\n", key); + return 0; + } + + assert(rc == 0); + printf("Key %s set to value %s\n", key, value); + return rc; +}; diff --git a/applications/scratch_storage/scratch_storage_upsert.c b/applications/scratch_storage/scratch_storage_upsert.c new file mode 100644 index 0000000..6e05a96 --- /dev/null +++ b/applications/scratch_storage/scratch_storage_upsert.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +extern int scratch_storage_upsert(void *key, uint32_t key_len, void *value, uint32_t value_len) + __attribute__((__import_module__("scratch_storage"), __import_name__("upsert"))); + +int +main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + char *key = argv[1]; + char *value = argv[2]; + + if (key == NULL || strlen(key) < 0 || value == NULL || strlen(value) < 0) { + fprintf(stderr, "%s ", argv[0]); + return 0; + } + + scratch_storage_upsert(key, strlen(key), value, strlen(value)); + printf("Key %s set to value %s\n", key, value); +}; diff --git a/applications/wasm_apps b/applications/wasm_apps index cf8a8ba..20a7c88 160000 --- a/applications/wasm_apps +++ b/applications/wasm_apps @@ -1 +1 @@ -Subproject commit cf8a8bafb10c37f207ccba1405cac07cd70bae8f +Subproject commit 20a7c88816c8f8882e03d42c76ff8c1e72bfeaec diff --git a/runtime/include/map.h b/runtime/include/map.h index 93ad65f..fa3e784 100644 --- a/runtime/include/map.h +++ b/runtime/include/map.h @@ -6,13 +6,12 @@ #include #include "lock.h" +#include "xmalloc.h" /* Simple K-V store based on The Practice of Programming by Kernighan and Pike */ /* Bucket count is sized to be a prime that is approximately 20% larger than the desired capacity (6k keys) */ #define MAP_BUCKET_COUNT 7907 -#define MAP_LOCK_STRIPES 17 - #define MAP_HASH jenkins_hash struct map_node { @@ -24,16 +23,22 @@ struct map_node { uint32_t hash; }; +struct map_bucket { + lock_t lock; + struct map_node *head; +}; + struct map { - struct map_node *buckets[MAP_BUCKET_COUNT]; - lock_t locks[MAP_LOCK_STRIPES]; + struct map_bucket buckets[MAP_BUCKET_COUNT]; }; static inline void map_init(struct map *restrict map) { - for (int i = 0; i < MAP_BUCKET_COUNT; i++) { map->buckets[i] = NULL; } - for (int i = 0; i < MAP_LOCK_STRIPES; i++) { LOCK_INIT(&map->locks[i]); } + for (int i = 0; i < MAP_BUCKET_COUNT; i++) { + map->buckets[i].head = NULL; + LOCK_INIT(&map->buckets[i].lock); + } }; /* See https://en.wikipedia.org/wiki/Jenkins_hash_function */ @@ -59,8 +64,11 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len uint8_t *value = NULL; uint32_t hash = MAP_HASH(key, key_len); - LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]); - for (struct map_node *node = map->buckets[hash % MAP_BUCKET_COUNT]; node != NULL; node = node->next) { + + struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; + + LOCK_LOCK(&bucket->lock); + for (struct map_node *node = bucket->head; node != NULL; node = node->next) { if (node->hash == hash) { value = node->value; *ret_value_len = node->value_len; @@ -71,7 +79,7 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len if (value == NULL) *ret_value_len = 0; DONE: - LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]); + LOCK_UNLOCK(&bucket->lock); return value; } @@ -80,33 +88,30 @@ map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_ { bool did_set = false; - uint32_t hash = MAP_HASH(key, key_len); - LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]); - for (struct map_node *node = map->buckets[hash % MAP_BUCKET_COUNT]; node != NULL; node = node->next) { + uint32_t hash = MAP_HASH(key, key_len); + struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; + LOCK_LOCK(&bucket->lock); + for (struct map_node *node = bucket->head; node != NULL; node = node->next) { if (node->hash == hash) goto DONE; } - struct map_node *new_node = (struct map_node *)malloc(sizeof(struct map_node)); - - *(new_node) = (struct map_node){ .hash = hash, - .key = malloc(key_len), - .key_len = key_len, - .value = malloc(value_len), - .value_len = value_len, - .next = map->buckets[hash % MAP_BUCKET_COUNT] }; - - assert(new_node->key); - assert(new_node->value); + struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node)); + *(new_node) = (struct map_node){ .hash = hash, + .key = xmalloc(key_len), + .key_len = key_len, + .value = xmalloc(value_len), + .value_len = value_len, + .next = bucket->head }; // Copy Key and Value memcpy(new_node->key, key, key_len); memcpy(new_node->value, value, value_len); - map->buckets[hash % MAP_BUCKET_COUNT] = new_node; - did_set = true; + bucket->head = new_node; + did_set = true; DONE: - LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]); + LOCK_UNLOCK(&bucket->lock); return did_set; } @@ -118,12 +123,13 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len) { bool did_delete = false; - uint32_t hash = MAP_HASH(key, key_len); - LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]); + uint32_t hash = MAP_HASH(key, key_len); + struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; + LOCK_LOCK(&bucket->lock); - struct map_node *prev = map->buckets[hash % MAP_BUCKET_COUNT]; - if (prev->hash == hash) { - map->buckets[hash % MAP_BUCKET_COUNT] = prev->next; + struct map_node *prev = bucket->head; + if (prev != NULL && prev->hash == hash) { + bucket->head = prev->next; free(prev->key); free(prev->value); free(prev); @@ -141,33 +147,35 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len) } DONE: - LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]); + LOCK_UNLOCK(&bucket->lock); return did_delete; } static inline void map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_t value_len) { - uint32_t hash = MAP_HASH(key, key_len); - LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]); - for (struct map_node *node = map->buckets[hash % MAP_BUCKET_COUNT]; node != NULL; node = node->next) { + uint32_t hash = MAP_HASH(key, key_len); + struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; + LOCK_LOCK(&bucket->lock); + + for (struct map_node *node = bucket->head; node != NULL; node = node->next) { if (node->hash == hash) { node->value_len = value_len; node->value = realloc(node->value, value_len); assert(node->value); memcpy(node->value, value, value_len); + goto DONE; } - goto DONE; } - struct map_node *new_node = (struct map_node *)malloc(sizeof(struct map_node)); + struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node)); *(new_node) = (struct map_node){ .hash = hash, - .key = malloc(key_len), + .key = xmalloc(key_len), .key_len = key_len, - .value = malloc(value_len), + .value = xmalloc(value_len), .value_len = value_len, - .next = map->buckets[hash % MAP_BUCKET_COUNT] }; + .next = bucket->head }; assert(new_node->key); assert(new_node->value); @@ -176,8 +184,8 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint memcpy(new_node->key, key, key_len); memcpy(new_node->value, value, value_len); - map->buckets[hash % MAP_BUCKET_COUNT] = new_node; + bucket->head = new_node; DONE: - LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]); + LOCK_UNLOCK(&bucket->lock); } diff --git a/runtime/include/xmalloc.h b/runtime/include/xmalloc.h new file mode 100644 index 0000000..8449da0 --- /dev/null +++ b/runtime/include/xmalloc.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "likely.h" +#include "panic.h" + +static inline void * +xmalloc(size_t size) +{ + void *allocation = malloc(size); + if (unlikely(allocation == NULL)) panic("xmalloc failed!\n"); + return allocation; +}