diff --git a/src/modules/kvs/cache.c b/src/modules/kvs/cache.c index fe0eeb1f97b6..1122d2779f74 100644 --- a/src/modules/kvs/cache.c +++ b/src/modules/kvs/cache.c @@ -53,6 +53,8 @@ struct cache_entry { waitqueue_t *waitlist_valid; void *data; /* value object/data */ int len; + bool data_valid; /* flag indicating if data set, don't use + * data == NULL as test */ cache_data_type_t type; /* what does data point to */ int lastuse_epoch; /* time of last use for cache expiry */ uint8_t dirty:1; @@ -62,25 +64,38 @@ struct cache { zhash_t *zh; }; -struct cache_entry *cache_entry_create (void) +struct cache_entry *cache_entry_create (cache_data_type_t t) { - struct cache_entry *hp = calloc (1, sizeof (*hp)); - if (!hp) { + struct cache_entry *hp; + + if (t != CACHE_DATA_TYPE_NONE + && t != CACHE_DATA_TYPE_JSON + && t != CACHE_DATA_TYPE_RAW) { + errno = EINVAL; + return NULL; + } + + if (!(hp = calloc (1, sizeof (*hp)))) { errno = ENOMEM; return NULL; } - hp->type = CACHE_DATA_TYPE_NONE; + hp->type = t; return hp; } struct cache_entry *cache_entry_create_json (json_t *o) { - struct cache_entry *hp = cache_entry_create (); - if (!hp) + struct cache_entry *hp; + + if (!o) { + errno = EINVAL; return NULL; - if (o) - hp->data = o; - hp->type = CACHE_DATA_TYPE_JSON; + } + + if (!(hp = cache_entry_create (CACHE_DATA_TYPE_JSON))) + return NULL; + hp->data = o; + hp->data_valid = true; return hp; } @@ -93,13 +108,15 @@ struct cache_entry *cache_entry_create_raw (void *data, int len) return NULL; } - if (!(hp = cache_entry_create ())) + if (!(hp = cache_entry_create (CACHE_DATA_TYPE_RAW))) return NULL; + if (data) { hp->data = data; hp->len = len; } - hp->type = CACHE_DATA_TYPE_RAW; + /* true even if data == NULL */ + hp->data_valid = true; return hp; } @@ -125,17 +142,17 @@ bool cache_entry_is_type_raw (struct cache_entry *hp) bool cache_entry_get_valid (struct cache_entry *hp) { - return (hp && hp->data != NULL); + return (hp && hp->data_valid); } bool cache_entry_get_dirty (struct cache_entry *hp) { - return (hp && hp->data && hp->dirty); + return (hp && hp->data_valid && hp->dirty); } int cache_entry_set_dirty (struct cache_entry *hp, bool val) { - if (hp && hp->data) { + if (hp && hp->data_valid) { if ((val && hp->dirty) || (!val && !hp->dirty)) ; /* no-op */ else if (val && !hp->dirty) @@ -157,7 +174,7 @@ int cache_entry_set_dirty (struct cache_entry *hp, bool val) int cache_entry_clear_dirty (struct cache_entry *hp) { - if (hp && hp->data) { + if (hp && hp->data_valid) { if (hp->dirty && (!hp->waitlist_notdirty || !wait_queue_length (hp->waitlist_notdirty))) @@ -169,7 +186,7 @@ int cache_entry_clear_dirty (struct cache_entry *hp) int cache_entry_force_clear_dirty (struct cache_entry *hp) { - if (hp && hp->data) { + if (hp && hp->data_valid) { if (hp->dirty) { if (hp->waitlist_notdirty) { wait_queue_destroy (hp->waitlist_notdirty); @@ -184,30 +201,34 @@ int cache_entry_force_clear_dirty (struct cache_entry *hp) json_t *cache_entry_get_json (struct cache_entry *hp) { - if (!hp || !hp->data || hp->type != CACHE_DATA_TYPE_JSON) + if (!hp || !hp->data_valid || hp->type != CACHE_DATA_TYPE_JSON) return NULL; + /* should be non-NULL for json */ + assert (hp->data); return hp->data; } int cache_entry_set_json (struct cache_entry *hp, json_t *o) { if (hp + && o && (hp->type == CACHE_DATA_TYPE_NONE || hp->type == CACHE_DATA_TYPE_JSON)) { - if ((o && hp->data) || (!o && !hp->data)) { + if (hp->data_valid) { + assert (hp->data); json_decref (o); /* no-op, 'o' is assumed identical to hp->data */ - } else if (o && !hp->data) { + } else { + assert (!hp->data); hp->data = o; + hp->data_valid = true; if (hp->waitlist_valid) { if (wait_runqueue (hp->waitlist_valid) < 0) { /* set back to orig */ hp->data = NULL; + hp->data_valid = false; return -1; } } - } else if (!o && hp->data) { - json_decref (hp->data); - hp->data = NULL; } hp->type = CACHE_DATA_TYPE_JSON; return 0; @@ -215,13 +236,15 @@ int cache_entry_set_json (struct cache_entry *hp, json_t *o) return -1; } -void *cache_entry_get_raw (struct cache_entry *hp, int *len) +int cache_entry_get_raw (struct cache_entry *hp, void **data, int *len) { - if (!hp || !hp->data || hp->type != CACHE_DATA_TYPE_RAW) - return NULL; + if (!hp || !hp->data_valid || hp->type != CACHE_DATA_TYPE_RAW) + return -1; + if (data) + (*data) = hp->data; if (len) (*len) = hp->len; - return hp->data; + return 0; } int cache_entry_set_raw (struct cache_entry *hp, void *data, int len) @@ -234,23 +257,30 @@ int cache_entry_set_raw (struct cache_entry *hp, void *data, int len) if (hp && (hp->type == CACHE_DATA_TYPE_NONE || hp->type == CACHE_DATA_TYPE_RAW)) { - if ((data && hp->data) || (!data && !hp->data)) { - free (data); /* no-op, 'data' is assumed identical to hp->data */ - } else if (data && !hp->data) { + if (hp->data_valid) { + if ((data && hp->data) || (!data && !hp->data)) + free (data); /* no-op, 'data' is assumed identical to hp->data */ + else { + /* attempt to change already valid cache entry, + * cannot, must call cache_entry_clear_data() */ + errno = EBADE; + return -1; + } + } + else { hp->data = data; hp->len = len; + hp->data_valid = true; + if (hp->waitlist_valid) { if (wait_runqueue (hp->waitlist_valid) < 0) { /* set back to orig */ hp->data = NULL; hp->len = 0; + hp->data_valid = false; return -1; } } - } else if (!data && hp->data) { - free (hp->data); - hp->data = NULL; - hp->len = 0; } hp->type = CACHE_DATA_TYPE_RAW; return 0; @@ -258,11 +288,28 @@ int cache_entry_set_raw (struct cache_entry *hp, void *data, int len) return -1; } +int cache_entry_clear_data (struct cache_entry *hp) +{ + if (hp) { + if (hp->data) { + if (hp->type == CACHE_DATA_TYPE_JSON) + json_decref (hp->data); + else if (hp->type == CACHE_DATA_TYPE_RAW) + free (hp->data); + } + hp->data = NULL; + hp->len = 0; + hp->data_valid = false; + return 0; + } + return -1; +} + void cache_entry_destroy (void *arg) { struct cache_entry *hp = arg; if (hp) { - if (hp->data) { + if (hp->data_valid) { if (hp->type == CACHE_DATA_TYPE_JSON) json_decref (hp->data); else if (hp->type == CACHE_DATA_TYPE_RAW) diff --git a/src/modules/kvs/cache.h b/src/modules/kvs/cache.h index 46b86bc9d9e7..3ddce30213c2 100644 --- a/src/modules/kvs/cache.h +++ b/src/modules/kvs/cache.h @@ -19,21 +19,28 @@ struct cache; /* Create/destroy cache entry. * * cache_entry_create() creates an entry, setting the cache entry type - * to CACHE_DATA_TYPE_NONE. + * to specified type. CACHE_DATA_TYPE_NONE indicates user is not yet + * sure of the type of data to be stored, and it will be determined + * later when cache_entry_set_X() function is called. + * cache_entry_get_valid() will return false after + * cache_entry_create() is initially called, regardless of the type + * passed in. * * cache_entry_create_json() creates an entry, setting the cache entry * type to CACHE_DATA_TYPE_JSON. The create transfers ownership of * 'o' to the cache entry. On destroy, json_decref() will be called - * on 'o'. If 'o' is NULL, no data is set, but the type is still set - * to CACHE_DATA_TYPE_JSON and only json can be used for the entry. + * on 'o'. 'o' cannot be NULL. * * cache_entry_create_raw() creates an entry, setting the cache entry * type to CACHE_DATA_TYPE_RAW. The create transfers ownership of * 'data' to the cache entry. On destroy, free() will be called on - * 'data'. If 'data' is NULL, no data is set, but the type is still - * set to CACHE_DATA_TYPE_RAW and only raw can be used for the entry. + * 'data'. If 'data' is NULL, 'len' must be zero. If 'data' is + * non-NULL, 'len' must be > 0. + * + * cache_entry_get_valid() will return true on entries when + * cache_entry_create_json() and cache_entry_get_raw() return success. */ -struct cache_entry *cache_entry_create (void); +struct cache_entry *cache_entry_create (cache_data_type_t t); struct cache_entry *cache_entry_create_json (json_t *o); struct cache_entry *cache_entry_create_raw (void *data, int len); void cache_entry_destroy (void *arg); @@ -89,26 +96,35 @@ int cache_entry_force_clear_dirty (struct cache_entry *hp); * * json set accessor must have type of CACHE_DATA_TYPE_NONE or * CACHE_DATA_TYPE_JSON to set json object. After setting, the type - * is converted to CACHE_DATA_TYPE_JSON. If non-NULL, set transfers - * ownership of 'o' to the cache entry. + * is converted to CACHE_DATA_TYPE_JSON. 'o' must be non-NULL. Set + * transfers ownership of 'o' to the cache entry. * * raw set accessor must have type of CACHE_DATA_TYPE_NONE or * CACHE_DATA_TYPE_RAW to set raw data. After setting, the type is - * converted to CACHE_DATA_TYPE_RAW. If non-NULL, set transfers - * ownership of 'data' to the cache entry. + * converted to CACHE_DATA_TYPE_RAW. If 'data' is NULL, 'len' must be + * zero. If 'data' is non-NULL, 'len' must be > 0. If non-NULL, set + * transfers ownership of 'data' to the cache entry. + * + * cache_entry_clear_data () will clear any data in the entry. * * An invalid->valid transition runs the entry's wait queue, if any in * both set accessors. * - * cache_entry_set_json() & cache_entry_set_raw() returns -1 on error, - * 0 on success + * Generally speaking, a cache entry can only bet set once. If you + * wish to set it again, you must run cache_entry_clear_data() before + * doing so. + * + * cache_entry_set_json() & cache_entry_set_raw() & + * cache_entry_clear_data() returns -1 on error, 0 on success */ json_t *cache_entry_get_json (struct cache_entry *hp); int cache_entry_set_json (struct cache_entry *hp, json_t *o); -void *cache_entry_get_raw (struct cache_entry *hp, int *len); +int cache_entry_get_raw (struct cache_entry *hp, void **data, int *len); int cache_entry_set_raw (struct cache_entry *hp, void *data, int len); +int cache_entry_clear_data (struct cache_entry *hp); + /* Arrange for message handler represented by 'wait' to be restarted * once cache entry becomes valid or not dirty at completion of a * load or store RPC. diff --git a/src/modules/kvs/commit.c b/src/modules/kvs/commit.c index 2049752f0bec..d3ddaaaad83d 100644 --- a/src/modules/kvs/commit.c +++ b/src/modules/kvs/commit.c @@ -222,7 +222,7 @@ static int store_cache (commit_t *c, int current_epoch, json_t *o, } } if (!(hp = cache_lookup (c->cm->cache, ref, current_epoch))) { - if (!(hp = cache_entry_create ())) { + if (!(hp = cache_entry_create (CACHE_DATA_TYPE_NONE))) { saved_errno = ENOMEM; goto done; } diff --git a/src/modules/kvs/kvs.c b/src/modules/kvs/kvs.c index 4fd623e19c38..1dbaec0fc08d 100644 --- a/src/modules/kvs/kvs.c +++ b/src/modules/kvs/kvs.c @@ -203,13 +203,15 @@ static void content_load_completion (flux_future_t *f, void *arg) * this error scenario appropriately. */ if (cache_entry_is_type_raw (hp)) { - char *datacpy; + char *datacpy = NULL; - if (!(datacpy = malloc (size))) { - flux_log_error (ctx->h, "%s: malloc", __FUNCTION__); - goto done; + if (size) { + if (!(datacpy = malloc (size))) { + flux_log_error (ctx->h, "%s: malloc", __FUNCTION__); + goto done; + } + memcpy (datacpy, data, size); } - memcpy (datacpy, data, size); if (cache_entry_set_raw (hp, datacpy, size) < 0) { flux_log_error (ctx->h, "%s: cache_entry_set_raw", __FUNCTION__); @@ -276,15 +278,15 @@ static int load (kvs_ctx_t *ctx, const href_t ref, bool is_raw, wait_t *wait, */ if (!hp) { if (is_raw) { - if (!(hp = cache_entry_create_raw (NULL, 0))) { - flux_log_error (ctx->h, "%s: cache_entry_create_raw", + if (!(hp = cache_entry_create (CACHE_DATA_TYPE_RAW))) { + flux_log_error (ctx->h, "%s: cache_entry_create", __FUNCTION__); return -1; } } else { - if (!(hp = cache_entry_create_json (NULL))) { - flux_log_error (ctx->h, "%s: cache_entry_create_json", + if (!(hp = cache_entry_create (CACHE_DATA_TYPE_JSON))) { + flux_log_error (ctx->h, "%s: cache_entry_create", __FUNCTION__); return -1; } @@ -481,7 +483,7 @@ static int commit_cache_cb (commit_t *c, struct cache_entry *hp, void *data) is_raw = cache_entry_is_type_raw (hp); if (is_raw) - storedata = cache_entry_get_raw (hp, &storedatalen); + cache_entry_get_raw (hp, &storedata, &storedatalen); else storedata = cache_entry_get_json (hp); @@ -1628,14 +1630,16 @@ static int store_initial_rootdir (kvs_ctx_t *ctx, json_t *o, href_t ref) goto decref_done; } if (!(hp = cache_lookup (ctx->cache, ref, ctx->epoch))) { - if (!(hp = cache_entry_create ())) { + if (!(hp = cache_entry_create (CACHE_DATA_TYPE_JSON))) { saved_errno = errno; - flux_log_error (ctx->h, "%s: cache_entry_create", __FUNCTION__); + flux_log_error (ctx->h, "%s: cache_entry_create_json_empty", + __FUNCTION__); goto decref_done; } cache_insert (ctx->cache, ref, hp); } if (!cache_entry_get_valid (hp)) { + assert (o); if (cache_entry_set_json (hp, o) < 0) { saved_errno = errno; flux_log_error (ctx->h, "%s: cache_entry_set_json", diff --git a/src/modules/kvs/lookup.c b/src/modules/kvs/lookup.c index f249ad1e29c8..647db873a163 100644 --- a/src/modules/kvs/lookup.c +++ b/src/modules/kvs/lookup.c @@ -724,7 +724,7 @@ bool lookup (lookup_t *lh) lh->missing_ref_raw = true; goto stall; } - if (!(valdata = cache_entry_get_raw (hp, &len))) { + if (cache_entry_get_raw (hp, &valdata, &len) < 0) { flux_log (lh->h, LOG_ERR, "valref points to non-raw data"); lh->errnum = EPERM; goto done; diff --git a/src/modules/kvs/test/cache.c b/src/modules/kvs/test/cache.c index 729b8d602b15..ce5e8dab082f 100644 --- a/src/modules/kvs/test/cache.c +++ b/src/modules/kvs/test/cache.c @@ -45,24 +45,32 @@ void cache_entry_basic_tests (void) cache_data_type_t t; /* corner case tests */ + ok (cache_entry_create (447) == NULL, + "cache_entry_create fails with bad input"); + ok (cache_entry_create_json (NULL) == NULL, + "cache_entry_create_json fails with bad input"); ok (cache_entry_create_raw (NULL, 5) == NULL, "cache_entry_create_raw fails with bad input"); ok (cache_entry_type (NULL, NULL) < 0, "cache_entry_type fails with bad input"); ok (cache_entry_set_json (NULL, NULL) < 0, "cache_entry_set_json fails with bad input"); + ok (cache_entry_clear_data (NULL) < 0, + "cache_entry_clear_data fails with bad input"); cache_entry_destroy (NULL); diag ("cache_entry_destroy accept NULL arg"); ok ((e = cache_entry_create_raw (NULL, 0)) != NULL, "cache_entry_create_raw success"); ok (cache_entry_set_raw (e, "abcd", -1) < 0, "cache_entry_set_raw fails with bad input"); + ok (cache_entry_set_raw (e, NULL, 5) < 0, + "cache_entry_set_raw fails with bad input"); cache_entry_destroy (e); e = NULL; /* test empty cache entry created by cache_entry_create() */ - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create works"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); @@ -80,8 +88,8 @@ void cache_entry_basic_tests (void) "cache_entry_set_dirty fails b/c entry non-valid"); ok ((otmp = cache_entry_get_json (e)) == NULL, "cache_entry_get_json returns NULL, no json set"); - ok (cache_entry_get_raw (e, NULL) == NULL, - "cache_entry_get_raw returns NULL, no data set"); + ok (cache_entry_get_raw (e, NULL, NULL) < 0, + "cache_entry_get_raw fails, no data set"); cache_entry_destroy (e); e = NULL; } @@ -96,10 +104,11 @@ void cache_entry_json_tests (void) * N.B.: json ref is NOT incremented by create or get_json. */ - /* test empty cache entry created by cache_entry_create_json() */ + /* test empty cache entry with json type created by + * cache_entry_create() */ - ok ((e = cache_entry_create_json (NULL)) != NULL, - "cache_entry_create_json w/ NULL input works"); + ok ((e = cache_entry_create (CACHE_DATA_TYPE_JSON)) != NULL, + "cache_entry_create works"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); ok (t == CACHE_DATA_TYPE_JSON, @@ -119,13 +128,15 @@ void cache_entry_json_tests (void) cache_entry_destroy (e); e = NULL; - /* test empty cache entry, later filled with json. + /* test empty cache entry with none type, later filled with json. */ o1 = json_object (); json_object_set_new (o1, "foo", json_integer (42)); + o2 = json_object (); + json_object_set_new (o1, "foo", json_integer (42)); - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create works"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); @@ -145,6 +156,8 @@ void cache_entry_json_tests (void) "cache_entry_get_json returns NULL, no json set"); ok (cache_entry_set_json (e, o1) == 0, "cache_entry_set_json success"); + ok (cache_entry_set_json (e, o2) == 0, + "cache_entry_set_json again, silent success"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); ok (t == CACHE_DATA_TYPE_JSON, @@ -158,6 +171,44 @@ void cache_entry_json_tests (void) cache_entry_destroy (e); /* destroys o1 */ e = NULL; + /* test empty json cache entry with type json, later filled with + * data. + */ + + o1 = json_object (); + json_object_set_new (o1, "foo", json_integer (42)); + + ok ((e = cache_entry_create (CACHE_DATA_TYPE_JSON)) != NULL, + "cache_entry_create works"); + ok (cache_entry_type (e, &t) == 0, + "cache_entry_type success"); + ok (t == CACHE_DATA_TYPE_JSON, + "cache_entry_type returns JSON"); + ok (cache_entry_is_type_json (e) == true, + "cache_entry_is_type_json returns true"); + ok (cache_entry_get_valid (e) == false, + "cache entry initially non-valid"); + ok (cache_entry_get_dirty (e) == false, + "cache entry initially not dirty"); + ok (cache_entry_set_dirty (e, true) < 0, + "cache_entry_set_dirty fails b/c entry non-valid"); + ok (cache_entry_get_dirty (e) == false, + "cache entry does not set dirty, b/c no data"); + ok (cache_entry_set_raw (e, "abcd", 4) < 0, + "cache_entry_set_raw fails on cache entry with type json"); + ok ((otmp = cache_entry_get_json (e)) == NULL, + "cache_entry_get_json returns NULL, no json set"); + ok (cache_entry_set_json (e, o1) == 0, + "cache_entry_set_json success"); + ok (cache_entry_get_valid (e) == true, + "cache entry now valid after cache_entry_set_json call"); + ok (cache_entry_clear_data (e) == 0, + "cache_entry_clear_data success"); + ok (cache_entry_get_valid (e) == false, + "cache entry invalid after clear"); + cache_entry_destroy (e); /* destroys o1 */ + e = NULL; + /* test cache entry filled with json initially */ o1 = json_object (); @@ -201,8 +252,8 @@ void cache_entry_json_tests (void) ok (json_integer_value (otmp) == 42, "expected json object found"); - ok (cache_entry_set_json (e, NULL) == 0, - "cache_entry_set_json success"); + ok (cache_entry_clear_data (e) == 0, + "cache_entry_clear_data success"); ok (cache_entry_get_json (e) == NULL, "cache entry no longer has json object"); @@ -215,13 +266,14 @@ void cache_entry_raw_tests (void) struct cache_entry *e; json_t *o1; cache_data_type_t t; - char *data; + char *data, *data2; int len; - /* test empty cache entry created by cache_entry_create_raw() */ + /* test empty cache entry with raw type created by + * cache_entry_create() */ - ok ((e = cache_entry_create_raw (NULL, 0)) != NULL, - "cache_entry_create_raw w/ NULL input works"); + ok ((e = cache_entry_create (CACHE_DATA_TYPE_RAW)) != NULL, + "cache_entry_create works"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); ok (t == CACHE_DATA_TYPE_RAW, @@ -236,17 +288,19 @@ void cache_entry_raw_tests (void) "cache_entry_set_dirty fails b/c entry non-valid"); ok (cache_entry_get_dirty (e) == false, "cache entry does not set dirty, b/c no data"); - ok ((data = cache_entry_get_raw (e, NULL)) == NULL, - "cache_entry_get_raw returns NULL, no data set"); + ok (cache_entry_get_raw (e, (void **)&data, NULL) < 0, + "cache_entry_get_raw fails, no data set"); cache_entry_destroy (e); e = NULL; - /* test empty cache entry, later filled with raw data. + /* test empty cache entry with none type, later filled with raw + * data. */ data = strdup ("abcd"); + data2 = strdup ("abcd"); - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create works"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); @@ -262,10 +316,14 @@ void cache_entry_raw_tests (void) "cache_entry_set_dirty fails b/c entry non-valid"); ok (cache_entry_get_dirty (e) == false, "cache entry does not set dirty, b/c no data"); - ok (cache_entry_get_raw (e, NULL) == NULL, - "cache_entry_get_raw returns NULL, no data set"); + ok (cache_entry_get_raw (e, NULL, NULL) < 0, + "cache_entry_get_raw fails, no data set"); ok (cache_entry_set_raw (e, data, strlen (data) + 1) == 0, "cache_entry_set_raw success"); + ok (cache_entry_set_raw (e, data2, strlen (data) + 1) == 0, + "cache_entry_set_raw again, silent success"); + ok (cache_entry_set_raw (e, NULL, 0) < 0 && errno == EBADE, + "cache_entry_set_raw fails with EBADE, changing validity type"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); ok (t == CACHE_DATA_TYPE_RAW, @@ -282,12 +340,90 @@ void cache_entry_raw_tests (void) cache_entry_destroy (e); /* destroys data */ e = NULL; + /* test empty cache entry with raw type, later filled with raw + * data. + */ + + data = strdup ("abcd"); + + ok ((e = cache_entry_create (CACHE_DATA_TYPE_RAW)) != NULL, + "cache_entry_create works"); + ok (cache_entry_type (e, &t) == 0, + "cache_entry_type success"); + ok (t == CACHE_DATA_TYPE_RAW, + "cache_entry_type returns RAW"); + ok (cache_entry_is_type_raw (e) == true, + "cache_entry_is_type_raw returns true"); + ok (cache_entry_get_valid (e) == false, + "cache entry initially non-valid"); + ok (cache_entry_get_dirty (e) == false, + "cache entry initially not dirty"); + ok (cache_entry_set_dirty (e, true) < 0, + "cache_entry_set_dirty fails b/c entry non-valid"); + ok (cache_entry_get_dirty (e) == false, + "cache entry does not set dirty, b/c no data"); + o1 = json_object (); + json_object_set_new (o1, "foo", json_integer (42)); + ok (cache_entry_set_json (e, o1) < 0, + "cache_entry_set_json fails on cache entry with type raw"); + json_decref (o1); + ok (cache_entry_get_raw (e, NULL, NULL) < 0, + "cache_entry_get_raw fails, no data set"); + ok (cache_entry_set_raw (e, data, strlen (data) + 1) == 0, + "cache_entry_set_raw success"); + ok (cache_entry_get_valid (e) == true, + "cache entry now valid after cache_entry_set_raw call"); + ok (cache_entry_clear_data (e) == 0, + "cache_entry_clear_data success"); + ok (cache_entry_get_valid (e) == false, + "cache entry invalid after clear"); + cache_entry_destroy (e); /* destroys data */ + e = NULL; + + /* test empty cache entry w/ raw type, later filled with zero-byte + * raw data. + */ + + ok ((e = cache_entry_create (CACHE_DATA_TYPE_RAW)) != NULL, + "cache_entry_create works"); + ok (cache_entry_type (e, &t) == 0, + "cache_entry_type success"); + ok (t == CACHE_DATA_TYPE_RAW, + "cache_entry_type returns RAW"); + ok (cache_entry_is_type_raw (e) == true, + "cache_entry_is_type_raw returns true"); + ok (cache_entry_get_valid (e) == false, + "cache entry initially non-valid"); + ok (cache_entry_get_dirty (e) == false, + "cache entry initially not dirty"); + ok (cache_entry_set_dirty (e, true) < 0, + "cache_entry_set_dirty fails b/c entry non-valid"); + ok (cache_entry_get_dirty (e) == false, + "cache entry does not set dirty, b/c no data"); + o1 = json_object (); + json_object_set_new (o1, "foo", json_integer (42)); + ok (cache_entry_set_json (e, o1) < 0, + "cache_entry_set_json fails on cache entry with type raw"); + json_decref (o1); + ok (cache_entry_get_raw (e, NULL, NULL) < 0, + "cache_entry_get_raw fails, no data set"); + ok (cache_entry_set_raw (e, NULL, 0) == 0, + "cache_entry_set_raw success"); + ok (cache_entry_get_valid (e) == true, + "cache entry now valid after cache_entry_set_raw call"); + ok (cache_entry_clear_data (e) == 0, + "cache_entry_clear_data success"); + ok (cache_entry_get_valid (e) == false, + "cache entry invalid after clear"); + cache_entry_destroy (e); /* destroys data */ + e = NULL; + /* test cache entry filled with raw data initially */ data = strdup ("abcd"); ok ((e = cache_entry_create_raw (data, strlen (data) + 1)) != NULL, - "cache_entry_create_json w/ non-NULL input works"); + "cache_entry_create_raw w/ non-NULL input works"); ok (cache_entry_type (e, &t) == 0, "cache_entry_type success"); ok (t == CACHE_DATA_TYPE_RAW, @@ -317,15 +453,16 @@ void cache_entry_raw_tests (void) ok (cache_entry_get_dirty (e) == false, "cache entry succcessfully now not dirty"); - ok ((data = cache_entry_get_raw (e, &len)) != NULL, + ok (cache_entry_get_raw (e, (void **)&data, &len) == 0, "raw data retrieved from cache entry"); - ok (strcmp (data, "abcd") == 0, + ok (data && strcmp (data, "abcd") == 0, "raw data matches expected string"); - ok (len == strlen (data) + 1, + ok (data && (len == strlen (data) + 1), "raw data length matches expected length"); - ok (cache_entry_set_raw (e, NULL, 0) == 0, - "cache_entry_set_raw success"); - ok (cache_entry_get_raw (e, NULL) == NULL, + + ok (cache_entry_clear_data (e) == 0, + "cache_entry_clear_data success"); + ok (cache_entry_get_raw (e, (void **)&data, &len) < 0, "cache entry no longer has data object"); cache_entry_destroy (e); /* destroys data */ @@ -345,7 +482,7 @@ void waiter_json_tests (void) count = 0; ok ((w = wait_create (wait_cb, &count)) != NULL, "wait_create works"); - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create created empty object"); ok (cache_entry_get_valid (e) == false, "cache entry invalid, adding waiter"); @@ -414,7 +551,7 @@ void waiter_raw_tests (void) count = 0; ok ((w = wait_create (wait_cb, &count)) != NULL, "wait_create works"); - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create created empty object"); ok (cache_entry_get_valid (e) == false, "cache entry invalid, adding waiter"); @@ -432,6 +569,24 @@ void waiter_raw_tests (void) ok (count == 1, "waiter callback ran"); + /* set cache entry to zero-data, should also call get valid + * waiter */ + count = 0; + ok ((w = wait_create (wait_cb, &count)) != NULL, + "wait_create works"); + ok (cache_entry_clear_data (e) == 0, + "cache_entry_clear_data success"); + ok (cache_entry_get_valid (e) == false, + "cache entry invalid, adding waiter"); + ok (cache_entry_wait_valid (e, w) == 0, + "cache_entry_wait_valid success"); + ok (cache_entry_set_raw (e, NULL, 0) == 0, + "cache_entry_set_raw success"); + ok (cache_entry_get_valid (e) == true, + "cache entry set valid with one waiter"); + ok (count == 1, + "waiter callback ran"); + count = 0; ok ((w = wait_create (wait_cb, &count)) != NULL, "wait_create works"); @@ -480,7 +635,7 @@ void cache_remove_entry_tests (void) ok ((cache = cache_create ()) != NULL, "cache_create works"); - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create works"); cache_insert (cache, "remove-ref", e); ok (cache_lookup (cache, "remove-ref", 0) != NULL, @@ -495,7 +650,7 @@ void cache_remove_entry_tests (void) count = 0; ok ((w = wait_create (wait_cb, &count)) != NULL, "wait_create works"); - ok ((e = cache_entry_create ()) != NULL, + ok ((e = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create created empty object"); cache_insert (cache, "remove-ref", e); ok (cache_lookup (cache, "remove-ref", 0) != NULL, @@ -566,7 +721,7 @@ void cache_expiration_tests (void) "cache contains 0 entries"); /* first test w/ entry w/o json object */ - ok ((e1 = cache_entry_create ()) != NULL, + ok ((e1 = cache_entry_create (CACHE_DATA_TYPE_NONE)) != NULL, "cache_entry_create works"); cache_insert (cache, "xxx1", e1); ok (cache_count_entries (cache) == 1, diff --git a/t/t1002-kvs-extra.t b/t/t1002-kvs-extra.t index a8f82ae31667..e7cdfd972aae 100755 --- a/t/t1002-kvs-extra.t +++ b/t/t1002-kvs-extra.t @@ -297,6 +297,13 @@ test_expect_success 'kvs: valref that points to content store data can be read' flux kvs get $TEST.largeval2 | grep $largeval ' +test_expect_success 'kvs: valref that points to zero size content store data can be read' ' + flux kvs unlink -Rf $TEST && + hashval=`flux content store