diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a5c547..ec29abb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add support for nOS-V progressing events VP{pra}. - Add breakdown model for nOS-V. +- New API to manage stream metadata `ovni_attr_*()`. ## [1.9.1] - 2024-05-10 diff --git a/include/ovni.h.in b/include/ovni.h.in index 0047280..aa0343f 100644 --- a/include/ovni.h.in +++ b/include/ovni.h.in @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) +/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: MIT */ #ifndef OVNI_H @@ -130,6 +130,18 @@ void ovni_ev_jumbo_emit(struct ovni_ev *ev, const uint8_t *buf, uint32_t bufsize void ovni_flush(void); +/* Attributes */ +int ovni_attr_has(const char *key); +void ovni_attr_set_double(const char *key, double num); +void ovni_attr_set_boolean(const char *key, int value); +void ovni_attr_set_str(const char *key, const char *value); +void ovni_attr_set_json(const char *key, const char *json); +double ovni_attr_get_double(const char *key); +int ovni_attr_get_boolean(const char *key); +const char *ovni_attr_get_str(const char *key); +char *ovni_attr_get_json(const char *key); +void ovni_attr_flush(void); + #ifdef __cplusplus } #endif diff --git a/src/rt/ovni.c b/src/rt/ovni.c index 537a8fb..aa910d4 100644 --- a/src/rt/ovni.c +++ b/src/rt/ovni.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) +/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: MIT */ #include @@ -848,3 +848,216 @@ ovni_ev_emit(struct ovni_ev *ev) { ovni_ev_add(ev); } + +/* Attributes */ + +static JSON_Object * +get_thread_metadata(void) +{ + if (rthread.finished) + die("thread already finished"); + + if (!rthread.ready) + die("thread not initialized"); + + JSON_Object *meta = json_value_get_object(rthread.meta); + + if (meta == NULL) + die("json_value_get_object failed"); + + return meta; +} + +/** + * Determines if the key exists in the metadata. + * + * @returns 1 if the key exists, 0 otherwise. + */ +int +ovni_attr_has(const char *key) +{ + JSON_Object *obj = get_thread_metadata(); + JSON_Value *val = json_object_dotget_value(obj, key); + + if (val == NULL) + return 0; + + return 1; +} + +/** + * Stores a double attribute. + * + * @param key The key of the attribute as a dot path. + * @param num The double value to be stored. + */ +void +ovni_attr_set_double(const char *key, double num) +{ + JSON_Object *obj = get_thread_metadata(); + + if (json_object_dotset_number(obj, key, num) != 0) + die("json_object_dotset_number() failed"); +} + +/** + * Retrieves a double attribute. + * + * @param key The key of the attribute as a dot path. + * + * @return The double on the key. If there is any problem it aborts. + */ +double +ovni_attr_get_double(const char *key) +{ + JSON_Object *obj = get_thread_metadata(); + JSON_Value *val = json_object_dotget_value(obj, key); + if (val == NULL) + die("key not found: %s", key); + + if (json_value_get_type(val) != JSONNumber) + die("value with key '%s' is not a number", key); + + return json_value_get_number(val); +} + +/** + * Retrieves a boolean attribute. + * + * @param key The key of the attribute as a dot path. + * + * @return The boolean on the key. If there is any problem it aborts. + */ +int +ovni_attr_get_boolean(const char *key) +{ + JSON_Object *obj = get_thread_metadata(); + JSON_Value *val = json_object_dotget_value(obj, key); + if (val == NULL) + die("key not found: %s", key); + + if (json_value_get_type(val) != JSONBoolean) + die("value with key '%s' is not a boolean", key); + + return json_value_get_boolean(val); +} + +/** + * Stores a boolean attribute. + * + * @param key The key of the attribute as a dot path. + * @param num The boolean value to be stored. + */ +void +ovni_attr_set_boolean(const char *key, int value) +{ + JSON_Object *obj = get_thread_metadata(); + + if (json_object_dotset_boolean(obj, key, value) != 0) + die("json_object_dotset_boolean() failed"); +} + +/** + * Stores a string attribute. + * + * @param key The key of the attribute as a dot path. + * @param str The string value to be stored. It will be internally duplicated, + * so it can be free on return. + */ +void +ovni_attr_set_str(const char *key, const char *value) +{ + JSON_Object *obj = get_thread_metadata(); + + if (json_object_dotset_string(obj, key, value) != 0) + die("json_object_dotset_string() failed"); +} + +/** + * Retrieves a string attribute from a key. + * If not found or if there is any problem it aborts before returning. + * + * @param key The key of the attribute as a dot path. + * + * @return A pointer to a not-NULL read-only string value for the given key. + */ +const char * +ovni_attr_get_str(const char *key) +{ + JSON_Object *obj = get_thread_metadata(); + JSON_Value *val = json_object_dotget_value(obj, key); + if (val == NULL) + die("key not found: %s", key); + + if (json_value_get_type(val) != JSONString) + die("value with key '%s' is not a string", key); + + return json_value_get_string(val); +} + +/** + * Stores a JSON value into an attribute. + * + * @param key The key of the attribute as a dot path. + * @param json The JSON value to be stored. + * + * The value specified as a JSON string can be of any type (dictionary, array, + * string, double...) as long as it is valid JSON. Any errors in parsing the + * JSON string or in storing the resulting value will abort the program. + */ +void +ovni_attr_set_json(const char *key, const char *json) +{ + JSON_Object *obj = get_thread_metadata(); + JSON_Value *val = json_parse_string(json); + if (val == NULL) + die("cannot parse json: %s", json); + + if (json_object_dotset_value(obj, key, val) != 0) + die("json_object_dotset_value() failed"); +} + + +/** + * Serializes a JSON value into a string. + * + * @param key The key of the attribute as a dot path. + * + * @resurn A zero-terminated string containing the serialized JSON + * representation of the provided key. This string is allocated with malloc() + * and it is reponsability of the user to liberate the memory with free(). + * + * Any errors will abort the program. + */ +char * +ovni_attr_get_json(const char *key) +{ + JSON_Object *obj = get_thread_metadata(); + JSON_Value *val = json_object_dotget_value(obj, key); + if (val == NULL) + die("key not found: %s", key); + + char *str = json_serialize_to_string(val); + if (str == NULL) + die("json_serialize_to_string() failed"); + + return str; +} + + +/** + * Writes the metadata attributes to disk. + * Only used to ensure they are not lost in a crash. They are already written in + * ovni_thread_free(). + */ +void +ovni_attr_flush(void) +{ + if (rthread.finished) + die("thread already finished"); + + if (!rthread.ready) + die("thread not initialized"); + + thread_metadata_store(); +} diff --git a/test/emu/ovni/CMakeLists.txt b/test/emu/ovni/CMakeLists.txt index 1ccab87..1bb7718 100644 --- a/test/emu/ovni/CMakeLists.txt +++ b/test/emu/ovni/CMakeLists.txt @@ -28,3 +28,4 @@ test_emu(tmpdir-metadata.c MP DRIVER "tmpdir-metadata.driver.sh") test_emu(dummy.c NAME "ovniver" DRIVER "ovniver.driver.sh") test_emu(dummy.c NAME "match-doc-events" DRIVER "match-doc-events.sh") test_emu(dummy.c NAME "match-doc-version" DRIVER "match-doc-version.sh") +test_emu(libovni-attr.c) diff --git a/test/emu/ovni/libovni-attr.c b/test/emu/ovni/libovni-attr.c new file mode 100644 index 0000000..dd55135 --- /dev/null +++ b/test/emu/ovni/libovni-attr.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2024 Barcelona Supercomputing Center (BSC) + * SPDX-License-Identifier: GPL-3.0-or-later */ + +#include +#include "instr.h" +#include "ovni.h" + +/* Check stream attribute setting and getting. */ +int +main(void) +{ + instr_start(0, 1); + + ovni_attr_set_double("ovni.test.double", 123.5); + if (ovni_attr_get_double("ovni.test.double") != 123.5) + die("mismatch double"); + + ovni_attr_set_boolean("ovni.test.boolean", 1); + if (!ovni_attr_get_boolean("ovni.test.boolean")) + die("mismatch boolean"); + + ovni_attr_set_str("ovni.test.str", "foo"); + const char *str = ovni_attr_get_str("ovni.test.str"); + if (str == NULL || strcmp(str, "foo") != 0) + die("mismatch string"); + + const char *json = "{\"foo\":42}"; + ovni_attr_set_json("nosv.test.json", json); + + char *json2 = ovni_attr_get_json("nosv.test.json"); + if (strcmp(json, json2) != 0) + die("mismatch json: in '%s' out '%s'", json, json2); + + if (!ovni_attr_has("nosv.test.json.foo")) + die("missing attribute"); + + if (ovni_attr_get_double("nosv.test.json.foo") != 42.0) + die("mismatch double"); + + free(json2); + + instr_end(); + + return 0; +}