/** * Tests basic functionality for cuckoo_insert and cuckoo_get with small key/val. Checks that the * commands succeed and that the item returned is well-formed. */ void test_insert_basic(uint32_t policy, bool cas) { #define KEY "key" #define VAL "value" struct bstring key, testval; struct val val; rstatus_i status; struct item *it; test_reset(policy, cas); key.data = KEY; key.len = sizeof(KEY) - 1; val.type = VAL_TYPE_STR; val.vstr.data = VAL; val.vstr.len = sizeof(VAL) - 1; time_update(); status = cuckoo_insert(&key, &val, UINT32_MAX - 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); it = cuckoo_get(&key); ck_assert_msg(it != NULL, "cuckoo_get returned NULL"); ck_assert_int_eq(it->vlen, sizeof(VAL) - 1); ck_assert_int_eq(it->klen, sizeof(KEY) - 1); item_value_str(&testval, it); ck_assert_int_eq(it->vlen, testval.len); ck_assert_int_eq(cc_memcmp(testval.data, VAL, testval.len), 0); #undef KEY #undef VAL }
/* * test_load_factor -- calculates the average load factor of the hash table * when inserting <0, 1M> elements in random order. * * The factor itself isn't really that important because the implementation * is optimized for lookup speed, but it should be reasonable. */ static void test_load_factor() { struct cuckoo *c = cuckoo_new(); UT_ASSERT(c != NULL); /* * The seed is intentionally constant so that the test result is * consistent (at least on the same platform). */ srand(INITIAL_SEED); float avg_load = 0.f; rand64(); int inserted = 0; for (int i = 0; ; ++i) { if (cuckoo_insert(c, rand64() % NVALUES, TEST_VALUE) == 0) { inserted++; avg_load += (float)inserted / cuckoo_get_size(c); if (inserted == NVALUES) break; } } avg_load /= inserted; UT_ASSERT(avg_load >= 0.4f); cuckoo_delete(c); }
/** * Tests basic functionality for cuckoo_insert and cuckoo_get with small key/val. * Checks that the commands succeed and that the item returned is well-formed. */ void test_insert_basic(uint32_t policy, bool cas) { #define KEY "key" #define VAL "value" struct bstring key; struct val val; struct item *it; test_reset(policy, cas, CUCKOO_MAX_TTL, 0); bstring_set_literal(&key, KEY); val.type = VAL_TYPE_STR; bstring_set_literal(&val.vstr, VAL); time_update(); it = cuckoo_insert(&key, &val, INT32_MAX); ck_assert_msg(it != NULL, "cuckoo_insert not OK"); test_assert_entry_exists(&key, &val); test_reset(policy, cas, CUCKOO_MAX_TTL, 0); test_assert_entry_exists(&key, &val); #undef KEY #undef VAL }
END_TEST START_TEST(test_insert_insert_expire_swap) { #define NOW 12345678 struct bstring key; struct val val; rstatus_i status; char keystring[30]; uint64_t i; int hits = 0; metrics = (cuckoo_metrics_st) { CUCKOO_METRIC(METRIC_INIT) }; test_reset(CUCKOO_POLICY_EXPIRE, false); now = NOW; for (i = 0; metrics.item_curr.counter < CUCKOO_NITEM; i++) { key.len = sprintf(keystring, "%"PRIu64, i); key.data = keystring; val.type = VAL_TYPE_INT; val.vint = i; status = cuckoo_insert(&key, &val, now + i); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); } key.len = sprintf(keystring, "%"PRIu64, i); key.data = keystring; val.type = VAL_TYPE_INT; val.vint = i; status = cuckoo_insert(&key, &val, now + i); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); for (;i > 0 && hits < CUCKOO_NITEM;i--) { if (cuckoo_get(&key) != NULL) { hits++; } } ck_assert_msg(hits == CUCKOO_NITEM, "expected %d hits, got %d", CUCKOO_NITEM, hits); #undef NOW }
/* * pmemobj_runtime_init -- (internal) initialize runtime part of the pool header */ static int pmemobj_runtime_init(PMEMobjpool *pop, int rdonly, int boot) { LOG(3, "pop %p rdonly %d boot %d", pop, rdonly, boot); if (pop->replica != NULL) { /* switch to functions that replicate data */ pop->persist = obj_rep_persist; pop->flush = obj_rep_flush; pop->drain = obj_rep_drain; pop->memcpy_persist = obj_rep_memcpy_persist; pop->memset_persist = obj_rep_memset_persist; } /* run_id is made unique by incrementing the previous value */ pop->run_id += 2; if (pop->run_id == 0) pop->run_id += 2; pop->persist(pop, &pop->run_id, sizeof (pop->run_id)); /* * Use some of the memory pool area for run-time info. This * run-time state is never loaded from the file, it is always * created here, so no need to worry about byte-order. */ pop->rdonly = rdonly; pop->lanes = NULL; pop->uuid_lo = pmemobj_get_uuid_lo(pop); pop->store = (struct object_store *) ((uintptr_t)pop + pop->obj_store_offset); if (boot) { if ((errno = pmemobj_boot(pop)) != 0) return -1; if ((errno = cuckoo_insert(pools_ht, pop->uuid_lo, pop)) != 0) { ERR("!cuckoo_insert"); return -1; } if ((errno = ctree_insert(pools_tree, (uint64_t)pop, pop->size)) != 0) { ERR("!ctree_insert"); return -1; } } /* * If possible, turn off all permissions on the pool header page. * * The prototype PMFS doesn't allow this when large pages are in * use. It is not considered an error if this fails. */ util_range_none(pop->addr, sizeof (struct pool_hdr)); return 0; }
END_TEST START_TEST(test_insert_replace_expired) { #define NOW 12345678 struct bstring key; struct val val; rstatus_i status; char keystring[30]; uint64_t i; metrics = (cuckoo_metrics_st) { CUCKOO_METRIC(METRIC_INIT) }; test_reset(CUCKOO_POLICY_EXPIRE, true); now = NOW; for (i = 0; metrics.item_curr.counter < CUCKOO_NITEM; i++) { key.len = sprintf(keystring, "%"PRIu64, i); key.data = keystring; val.type = VAL_TYPE_INT; val.vint = i; status = cuckoo_insert(&key, &val, now + 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); } // dict is full, all items will expire in now + 1 now += 2; key.len = sprintf(keystring, "%"PRIu64, i); key.data = keystring; val.type = VAL_TYPE_INT; val.vint = i; status = cuckoo_insert(&key, &val, now + 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); ck_assert_int_eq(metrics.item_expire.counter, 1); #undef NOW }
void test_cas(uint32_t policy) { #define KEY "key" #define VAL "value" #define VAL2 "value2" struct bstring key; struct val val; rstatus_i status; struct item *it; uint64_t cas1, cas2; test_reset(policy, true); key.data = KEY; key.len = sizeof(KEY) - 1; val.type = VAL_TYPE_STR; val.vstr.data = VAL; val.vstr.len = sizeof(VAL) - 1; time_update(); status = cuckoo_insert(&key, &val, UINT32_MAX - 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); it = cuckoo_get(&key); cas1 = item_cas(it); ck_assert_uint_ne(cas1, 0); val.vstr.data = VAL2; val.vstr.len = sizeof(VAL2) - 1; status = cuckoo_update(it, &val, UINT32_MAX - 1); ck_assert_msg(status == CC_OK, "cuckoo_update not OK - return status %d", status); it = cuckoo_get(&key); cas2 = item_cas(it); ck_assert_uint_ne(cas2, 0); ck_assert_uint_ne(cas1, cas2); #undef KEY #undef VAL #undef VAL2 }
void test_insert_collision(uint32_t policy, bool cas) { struct bstring key; struct val val; rstatus_i status; struct item *it; int hits = 0; char keystring[CC_UINTMAX_MAXLEN]; uint64_t i, testval; test_reset(policy, cas); time_update(); for (i = 0; i < CUCKOO_NITEM + 1; i++) { key.len = sprintf(keystring, "%"PRIu64, i); key.data = keystring; val.type = VAL_TYPE_INT; val.vint = i; status = cuckoo_insert(&key, &val, UINT32_MAX - 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); } for (i = 0; i < CUCKOO_NITEM + 1; i++) { key.len = sprintf(keystring, "%"PRIu64, i); key.data = keystring; it = cuckoo_get(&key); if (it == NULL) { continue; } hits++; ck_assert_int_eq(it->klen, key.len); testval = item_value_int(it); ck_assert_int_eq(testval, i); } ck_assert_msg(hits > (double)CUCKOO_NITEM * 9 / 10, "hit rate is lower than expected when hash collision occurs"); ck_assert_msg(hits <= CUCKOO_NITEM, "hit rate is too high, expected more evicted values"); }
void test_delete_basic(uint32_t policy, bool cas) { #define KEY "key" #define VAL "value" struct bstring key; struct val val; rstatus_i status; struct item *it; bool deleted; test_reset(policy, cas); key.data = KEY; key.len = sizeof(KEY) - 1; val.type = VAL_TYPE_STR; val.vstr.data = VAL; val.vstr.len = sizeof(VAL) - 1; time_update(); status = cuckoo_insert(&key, &val, UINT32_MAX - 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); it = cuckoo_get(&key); ck_assert_msg(it != NULL, "cuckoo_get returned NULL"); deleted = cuckoo_delete(&key); ck_assert_msg(deleted, "cuckoo_delete return false"); it = cuckoo_get(&key); ck_assert_msg(it == NULL, "cuckoo_get returned not NULL"); deleted = cuckoo_delete(&key); ck_assert_msg(!deleted, "cuckoo_delete return true"); #undef KEY #undef VAL }
/* * get_lane_info_record -- (internal) get lane record attached to memory pool * or first free */ static inline struct lane_info * get_lane_info_record(PMEMobjpool *pop) { if (likely(Lane_info_cache != NULL && Lane_info_cache->pop_uuid_lo == pop->uuid_lo)) { return Lane_info_cache; } if (unlikely(Lane_info_ht == NULL)) { lane_info_ht_boot(); } struct lane_info *info = cuckoo_get(Lane_info_ht, pop->uuid_lo); if (unlikely(info == NULL)) { info = Malloc(sizeof(struct lane_info)); if (unlikely(info == NULL)) { FATAL("Malloc"); } info->pop_uuid_lo = pop->uuid_lo; info->lane_idx = UINT64_MAX; info->nest_count = 0; info->next = Lane_info_records; info->prev = NULL; if (Lane_info_records) { Lane_info_records->prev = info; } Lane_info_records = info; if (unlikely(cuckoo_insert( Lane_info_ht, pop->uuid_lo, info) != 0)) { FATAL("cuckoo_insert"); } } Lane_info_cache = info; return info; }
static void test_insert_get_remove() { struct cuckoo *c = cuckoo_new(); ASSERT(c != NULL); for (int i = 0; i < TEST_INSERTS; ++i) ASSERT(cuckoo_insert(c, i, TEST_VAL(i)) == 0); for (int i = 0; i < TEST_INSERTS; ++i) ASSERT(cuckoo_get(c, i) == TEST_VAL(i)); for (int i = 0; i < TEST_INSERTS; ++i) ASSERT(cuckoo_remove(c, i) == TEST_VAL(i)); for (int i = 0; i < TEST_INSERTS; ++i) ASSERT(cuckoo_remove(c, i) == NULL); for (int i = 0; i < TEST_INSERTS; ++i) ASSERT(cuckoo_get(c, i) == NULL); cuckoo_delete(c); }
static void *insert_thread(void *arg) { cuckoo_status st; KeyType key; ValType val; thread_arg_t* th = (thread_arg_t*) arg; th->ops = 0; th->failures = 0; th->num_read = 0; th->num_written = 0; while (keep_writing) { size_t i, task; task = task_assign(); if (task >= task_num) break; for (i = task * task_size + 1; i <= (task + 1) * task_size; i ++) { th->ops ++; key = (KeyType) i; val = (ValType) VALUE(i); st = cuckoo_insert(table, (const char*) &key, (const char*) &val); if (st == ok) { th->num_written ++; } else { printf("[writer%d] unknown error for key %zu (%d)\n", th->id, i, st); th->failures ++; } } task_complete(task); } pthread_exit(NULL); }
/* * alloc_class_register -- registers an allocation classes in the collection */ struct alloc_class * alloc_class_register(struct alloc_class_collection *ac, struct alloc_class *c) { struct alloc_class *nc = Malloc(sizeof(*nc)); if (nc == NULL) goto error_class_alloc; *nc = *c; if (c->type == CLASS_RUN) { size_t map_idx = SIZE_TO_CLASS_MAP_INDEX(nc->unit_size, ac->granularity); ASSERT(map_idx <= UINT32_MAX); uint32_t map_idx_s = (uint32_t)map_idx; ASSERT(nc->run.size_idx <= UINT16_MAX); uint16_t size_idx_s = (uint16_t)nc->run.size_idx; uint16_t header_type_s = (uint16_t)nc->header_type; uint64_t k = RUN_CLASS_KEY_PACK(map_idx_s, header_type_s, size_idx_s); if (cuckoo_insert(ac->class_map_by_unit_size, k, nc) != 0) { ERR("unable to register allocation class"); goto error_map_insert; } } ac->aclasses[nc->id] = nc; return nc; error_map_insert: Free(nc); error_class_alloc: alloc_class_reservation_clear(ac, c->id); return NULL; }
void test_expire_basic(uint32_t policy, bool cas) { #define KEY "key" #define VAL "value" #define NOW 12345678 struct bstring key; struct val val; rstatus_i status; struct item *it; test_reset(policy, cas); key.data = KEY; key.len = sizeof(KEY) - 1; val.type = VAL_TYPE_STR; val.vstr.data = VAL; val.vstr.len = sizeof(VAL) - 1; now = NOW; status = cuckoo_insert(&key, &val, NOW + 1); ck_assert_msg(status == CC_OK, "cuckoo_insert not OK - return status %d", status); it = cuckoo_get(&key); ck_assert_msg(it != NULL, "cuckoo_get returned NULL"); now += 2; it = cuckoo_get(&key); ck_assert_msg(it == NULL, "cuckoo_get returned not NULL after expiration"); #undef NOW #undef KEY #undef VAL }
/* * alloc_class_new -- creates a new allocation class */ struct alloc_class * alloc_class_new(int id, struct alloc_class_collection *ac, enum alloc_class_type type, enum header_type htype, size_t unit_size, size_t alignment, uint32_t size_idx) { LOG(10, NULL); struct alloc_class *c = Malloc(sizeof(*c)); if (c == NULL) goto error_class_alloc; c->unit_size = unit_size; c->header_type = htype; c->type = type; c->flags = (uint16_t) (header_type_to_flag[c->header_type] | (alignment ? CHUNK_FLAG_ALIGNED : 0)); switch (type) { case CLASS_HUGE: id = DEFAULT_ALLOC_CLASS_ID; break; case CLASS_RUN: alloc_class_generate_run_proto(&c->run, unit_size, size_idx, alignment); uint8_t slot = (uint8_t)id; if (id < 0 && alloc_class_find_first_free_slot(ac, &slot) != 0) goto error_class_alloc; id = slot; size_t map_idx = SIZE_TO_CLASS_MAP_INDEX(c->unit_size, ac->granularity); ASSERT(map_idx <= UINT32_MAX); uint32_t map_idx_s = (uint32_t)map_idx; ASSERT(c->run.size_idx <= UINT16_MAX); uint16_t size_idx_s = (uint16_t)c->run.size_idx; uint16_t flags_s = (uint16_t)c->flags; uint64_t k = RUN_CLASS_KEY_PACK(map_idx_s, flags_s, size_idx_s); if (cuckoo_insert(ac->class_map_by_unit_size, k, c) != 0) { ERR("unable to register allocation class"); goto error_map_insert; } break; default: ASSERT(0); } c->id = (uint8_t)id; ac->aclasses[c->id] = c; return c; error_map_insert: Free(c); error_class_alloc: if (id >= 0) alloc_class_reservation_clear(ac, id); return NULL; }
static int dtread_readline(const char *line, struct cuckoo_ctx *cu, unsigned int *maxid) { struct dtread_data *dr, *drp; struct dt_dentry *d; unsigned int pid, id, fid, items; unsigned long long size; const char *s; char *name; switch (line[0]) { case '0': if ((dr = dtread_data_alloc()) == NULL) return 0; if ((dr->de->id = atoi(&line[2])) == 0 ) { LOG_ERR("parsing failed for line %s\n", line); dtread_data_free(dr); return 0; } if (!cuckoo_insert(cu, dr->de->id, (void *) dr)) { LOG_ERR("cuckoo_insert() failed at line %s\n", line); dtread_data_free(dr); return 0; } if (dr->de->id > *maxid) *maxid = dr->de->id; break; case '1': s = strchr(line, ' '); if ((s == NULL) || (sscanf(++s, "%u", &pid) < 1 )) { LOG_ERR("parsing pid failed for line %s\n", line); return 0; } if (pid != 0) { drp = (struct dtread_data *) cuckoo_lookup(cu, pid); if (drp == NULL) { LOG_ERR("cuckoo_lookup() returned null for id %u line %s\n", pid, line); return 0; } } s = strchr(s, ' '); if ((s == NULL) || (sscanf(++s, "%u", &fid) < 1)) { LOG_ERR("Parsing fid failed for line %s\n", line); return 0; } s = strchr(s, ' '); if ((s == NULL) || (sscanf(++s, "%llu", &size) < 1)) { LOG_ERR("Parsing size failed for line %s\n", line); return 0; } s = strchr(s, ' '); if ((s == NULL) || (sscanf(++s, "%u", &id) < 1)) { LOG_ERR("Parsing id failed for line %s\n", line); return 0; } s = strchr(s, ' '); if ((s == NULL) || (sscanf(++s, "%u", &items) < 1)) { LOG_ERR("Parsing items failed for line %s\n", line); return 0; } s = strchr(s, ' '); if ((s == NULL) || ((name = strdup(++s))== NULL)) { LOG_ERR("Parsing name failed for line %s\n", line); return 0; } if (id == 0) { /* file */ LOG_ASSERT(pid != 0, "File with parent id=0 " "(only root dir has parent id=0). Line %s\n", line); if ((d = dt_alloc()) == NULL) { free(name); return 0; } d->type = DT_FILE; d->fid = fid; d->size = size; d->name = name; d->parent = drp->de; if (drp->file_child) drp->file_child->sibling = d; else drp->de->file_child = d; drp->file_child = d; } else { /* directory */ dr = (struct dtread_data *) cuckoo_lookup(cu, id); if (dr == NULL) { LOG_ERR("cuckoo_lookup() returned null for id %u\n", id); return 0; } d = dr->de; d->type = DT_DIR; d->fid = fid; d->size = size; d->name = name; d->items = items; if (pid != 0){ if (drp->child) drp->child->sibling = d; else drp->de->child = d; drp->child = d; d->parent = drp->de; cuckoo_delete(cu, id); free(dr); } } break; case '+': case '-': case '*': return 1; default: LOG_ERR("Unknown line format: %s\n", line); return 0; } return 1; }