WIP-- Not Yet Compiling or Functional -- WIP
* No longer expose WT_SESSION into Erlang at all as WT's model is to maintain one WT_SESSION per-thread and we don't know anything about threads in Erlang. * async_nif worker threads don't pull both mutexes on every loop when processing requests, only one * async_nif provides a worker_id (int, 0 - MAX_WORKERS) within the work block scope which we use to find our per-worker WT_SESSIONs * async_nif maintained a number of globals which I'm moving into the NIF's priv_data so that on upgrade/reload we have a fighting chance to "Do the Right Thing(TM)". * NIF Upgrades/Reloads started to plumb this in. * Use a khash to manage the cache of URI->WT_CURSORs per WT_SESSION. * Added start/stop positions into truncate call to allow for truncating sub-ranges data. * Lots of other details I'm sure I've forgotten and more left undone. Search for "TODO:" or try to compile to see what's left, and then there is a need for a lot more tests given all this new complexity.
This commit is contained in:
parent
163a5073cb
commit
19268b7c77
6 changed files with 1843 additions and 800 deletions
|
@ -1,5 +1,4 @@
|
||||||
/*
|
/*
|
||||||
*
|
|
||||||
* async_nif: An async thread-pool layer for Erlang's NIF API
|
* async_nif: An async thread-pool layer for Erlang's NIF API
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Basho Technologies, Inc. All Rights Reserved.
|
* Copyright (c) 2012 Basho Technologies, Inc. All Rights Reserved.
|
||||||
|
@ -18,7 +17,6 @@
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __ASYNC_NIF_H__
|
#ifndef __ASYNC_NIF_H__
|
||||||
|
@ -28,49 +26,42 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Redefine this in your NIF implementation before including this file to
|
|
||||||
change the thread pool size. The maximum number of threads might be
|
|
||||||
bounded on your OS. For instance, to allow 1,000,000 threads on a Linux
|
|
||||||
system you must do the following before launching the process.
|
|
||||||
echo 1000000 > /proc/sys/kernel/threads-max
|
|
||||||
and for all UNIX systems there will be ulimit maximums. */
|
|
||||||
#ifndef ASYNC_NIF_MAX_WORKERS
|
|
||||||
#define ASYNC_NIF_MAX_WORKERS 16
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
|
#define ASYNC_NIF_MAX_WORKERS 32
|
||||||
|
|
||||||
struct async_nif_req_entry {
|
struct async_nif_req_entry {
|
||||||
ERL_NIF_TERM ref, *argv;
|
ERL_NIF_TERM ref, *argv;
|
||||||
ErlNifEnv *env;
|
ErlNifEnv *env;
|
||||||
ErlNifPid pid;
|
ErlNifPid pid;
|
||||||
void *args;
|
void *args;
|
||||||
void *priv_data;
|
void (*fn_work)(ErlNifEnv*, ERL_NIF_TERM, ErlNifPid*, unsigned int, void *);
|
||||||
void (*fn_work)(ErlNifEnv*, ERL_NIF_TERM, void *, ErlNifPid*, void *);
|
|
||||||
void (*fn_post)(void *);
|
void (*fn_post)(void *);
|
||||||
STAILQ_ENTRY(async_nif_req_entry) entries;
|
STAILQ_ENTRY(async_nif_req_entry) entries;
|
||||||
};
|
};
|
||||||
STAILQ_HEAD(reqs, async_nif_req_entry) async_nif_reqs = STAILQ_HEAD_INITIALIZER(async_nif_reqs);
|
|
||||||
|
|
||||||
struct async_nif_worker_entry {
|
struct async_nif_worker_entry {
|
||||||
ErlNifTid tid;
|
ErlNifTid tid;
|
||||||
LIST_ENTRY(async_nif_worker_entry) entries;
|
LIST_ENTRY(async_nif_worker_entry) entries;
|
||||||
};
|
};
|
||||||
LIST_HEAD(idle_workers, async_nif_worker_entry) async_nif_idle_workers = LIST_HEAD_INITIALIZER(async_nif_worker);
|
|
||||||
|
|
||||||
static volatile unsigned int async_nif_req_count = 0;
|
|
||||||
static volatile unsigned int async_nif_shutdown = 0;
|
|
||||||
static ErlNifMutex *async_nif_req_mutex = NULL;
|
|
||||||
static ErlNifMutex *async_nif_worker_mutex = NULL;
|
|
||||||
static ErlNifCond *async_nif_cnd = NULL;
|
|
||||||
static struct async_nif_worker_entry async_nif_worker_entries[ASYNC_NIF_MAX_WORKERS];
|
|
||||||
|
|
||||||
|
struct async_nif_state {
|
||||||
|
volatile unsigned int req_count;
|
||||||
|
volatile unsigned int shutdown;
|
||||||
|
ErlNifMutex *req_mutex;
|
||||||
|
ErlNifMutex *worker_mutex;
|
||||||
|
ErlNifCond *cnd;
|
||||||
|
STAILQ_HEAD(reqs, async_nif_req_entry) reqs;
|
||||||
|
LIST_HEAD(workers, async_nif_worker_entry) workers;
|
||||||
|
unsigned int num_workers;
|
||||||
|
struct async_nif_worker_entry worker_entries[ASYNC_NIF_MAX_WORKERS];
|
||||||
|
};
|
||||||
|
|
||||||
#define ASYNC_NIF_DECL(decl, frame, pre_block, work_block, post_block) \
|
#define ASYNC_NIF_DECL(decl, frame, pre_block, work_block, post_block) \
|
||||||
struct decl ## _args frame; \
|
struct decl ## _args frame; \
|
||||||
static void fn_work_ ## decl (ErlNifEnv *env, ERL_NIF_TERM ref, void *priv_data, ErlNifPid *pid, struct decl ## _args *args) work_block \
|
static void fn_work_ ## decl (ErlNifEnv *env, ERL_NIF_TERM ref, ErlNifPid *pid, unsigned int worker_id, struct decl ## _args *args) work_block \
|
||||||
static void fn_post_ ## decl (struct decl ## _args *args) { \
|
static void fn_post_ ## decl (struct decl ## _args *args) { \
|
||||||
do post_block while(0); \
|
do post_block while(0); \
|
||||||
} \
|
} \
|
||||||
static ERL_NIF_TERM decl(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv_in[]) { \
|
static ERL_NIF_TERM decl(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv_in[]) { \
|
||||||
struct decl ## _args on_stack_args; \
|
struct decl ## _args on_stack_args; \
|
||||||
|
@ -81,7 +72,8 @@ static struct async_nif_worker_entry async_nif_worker_entries[ASYNC_NIF_MAX_WORK
|
||||||
/* argv[0] is a ref used for selective recv */ \
|
/* argv[0] is a ref used for selective recv */ \
|
||||||
const ERL_NIF_TERM *argv = argv_in + 1; \
|
const ERL_NIF_TERM *argv = argv_in + 1; \
|
||||||
argc--; \
|
argc--; \
|
||||||
if (async_nif_shutdown) \
|
async_nif = (struct async_nif_state*)enif_priv_data(env); \
|
||||||
|
if (async_nif->shutdown) \
|
||||||
return enif_make_tuple2(env, enif_make_atom(env, "error"), \
|
return enif_make_tuple2(env, enif_make_atom(env, "error"), \
|
||||||
enif_make_atom(env, "shutdown")); \
|
enif_make_atom(env, "shutdown")); \
|
||||||
if (!(new_env = enif_alloc_env())) { \
|
if (!(new_env = enif_alloc_env())) { \
|
||||||
|
@ -108,17 +100,14 @@ static struct async_nif_worker_entry async_nif_worker_entries[ASYNC_NIF_MAX_WORK
|
||||||
req->ref = enif_make_copy(new_env, argv_in[0]); \
|
req->ref = enif_make_copy(new_env, argv_in[0]); \
|
||||||
enif_self(env, &req->pid); \
|
enif_self(env, &req->pid); \
|
||||||
req->args = (void*)copy_of_args; \
|
req->args = (void*)copy_of_args; \
|
||||||
req->priv_data = enif_priv_data(env); \
|
req->fn_work = (void (*)(ErlNifEnv *, ERL_NIF_TERM, ErlNifPid*, unsigned int, void *))fn_work_ ## decl ; \
|
||||||
req->fn_work = (void (*)(ErlNifEnv *, ERL_NIF_TERM, void*, ErlNifPid*, void *))fn_work_ ## decl ; \
|
|
||||||
req->fn_post = (void (*)(void *))fn_post_ ## decl; \
|
req->fn_post = (void (*)(void *))fn_post_ ## decl; \
|
||||||
async_nif_enqueue_req(req); \
|
return async_nif_enqueue_req(async_nif, req); \
|
||||||
return enif_make_tuple2(env, enif_make_atom(env, "ok"), \
|
|
||||||
enif_make_tuple2(env, enif_make_atom(env, "enqueued"), \
|
|
||||||
enif_make_int(env, async_nif_req_count))); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ASYNC_NIF_LOAD() if (async_nif_init() != 0) return -1;
|
#define ASYNC_NIF_LOAD() async_nif_load();
|
||||||
#define ASYNC_NIF_UNLOAD() async_nif_unload();
|
#define ASYNC_NIF_UNLOAD() async_nif_unload();
|
||||||
|
//define ASYNC_NIF_RELOAD()
|
||||||
#define ASYNC_NIF_UPGRADE() async_nif_unload();
|
#define ASYNC_NIF_UPGRADE() async_nif_unload();
|
||||||
|
|
||||||
#define ASYNC_NIF_RETURN_BADARG() return enif_make_badarg(env);
|
#define ASYNC_NIF_RETURN_BADARG() return enif_make_badarg(env);
|
||||||
|
@ -130,14 +119,25 @@ static struct async_nif_worker_entry async_nif_worker_entries[ASYNC_NIF_MAX_WORK
|
||||||
#define ASYNC_NIF_REPLY(msg) PULSE_SEND(NULL, pid, env, enif_make_tuple2(env, ref, msg))
|
#define ASYNC_NIF_REPLY(msg) PULSE_SEND(NULL, pid, env, enif_make_tuple2(env, ref, msg))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void async_nif_enqueue_req(struct async_nif_req_entry *r)
|
static ERL_NIF_TERM
|
||||||
|
async_nif_enqueue_req(struct async_nif_state* async_nif, struct async_nif_req_entry *req)
|
||||||
{
|
{
|
||||||
/* Add the request to the work queue. */
|
/* If we're shutting down return an error term and ignore the request. */
|
||||||
enif_mutex_lock(async_nif_req_mutex);
|
if (async_nif->shutdown) {
|
||||||
STAILQ_INSERT_TAIL(&async_nif_reqs, r, entries);
|
return enif_make_tuple2(req->env, enif_make_atom(req->env, "error"),
|
||||||
async_nif_req_count++;
|
enif_make_atom(req->env, "shutdown")));
|
||||||
enif_mutex_unlock(async_nif_req_mutex);
|
}
|
||||||
enif_cond_broadcast(async_nif_cnd);
|
|
||||||
|
/* Otherwise, add the request to the work queue. */
|
||||||
|
enif_mutex_lock(async_nif->req_mutex);
|
||||||
|
STAILQ_INSERT_TAIL(&async_nif->reqs, req, entries);
|
||||||
|
async_nif->req_count++;
|
||||||
|
enif_mutex_unlock(async_nif->req_mutex);
|
||||||
|
enif_cond_broadcast(async_nif->cnd);
|
||||||
|
|
||||||
|
return enif_make_tuple2(env, enif_make_atom(env, "ok"),
|
||||||
|
enif_make_tuple2(env, enif_make_atom(env, "enqueued"),
|
||||||
|
enif_make_int(env, async_nif->req_count))); \
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *async_nif_worker_fn(void *arg)
|
static void *async_nif_worker_fn(void *arg)
|
||||||
|
@ -151,128 +151,164 @@ static void *async_nif_worker_fn(void *arg)
|
||||||
*/
|
*/
|
||||||
for(;;) {
|
for(;;) {
|
||||||
/* Examine the request queue, are there things to be done? */
|
/* Examine the request queue, are there things to be done? */
|
||||||
enif_mutex_lock(async_nif_req_mutex);
|
enif_mutex_lock(async_nif->req_mutex);
|
||||||
enif_mutex_lock(async_nif_worker_mutex);
|
enif_mutex_lock(async_nif->worker_mutex);
|
||||||
LIST_INSERT_HEAD(&async_nif_idle_workers, worker, entries);
|
LIST_INSERT_HEAD(&async_nif->workers, worker, entries);
|
||||||
enif_mutex_unlock(async_nif_worker_mutex);
|
enif_mutex_unlock(async_nif->worker_mutex);
|
||||||
check_again_for_work:
|
check_again_for_work:
|
||||||
if (async_nif_shutdown) { enif_mutex_unlock(async_nif_req_mutex); break; }
|
if (async_nif->shutdown) { enif_mutex_unlock(async_nif->req_mutex); break; }
|
||||||
if ((req = STAILQ_FIRST(&async_nif_reqs)) == NULL) {
|
if ((req = STAILQ_FIRST(&async_nif->reqs)) == NULL) {
|
||||||
/* Queue is empty, join the list of idle workers and wait for work */
|
/* Queue is empty, join the list of idle workers and wait for work */
|
||||||
enif_cond_wait(async_nif_cnd, async_nif_req_mutex);
|
enif_cond_wait(async_nif->cnd, async_nif->req_mutex);
|
||||||
goto check_again_for_work;
|
goto check_again_for_work;
|
||||||
} else {
|
} else {
|
||||||
/* `req` is our work request and we hold the lock. */
|
/* `req` is our work request and we hold the req_mutex lock. */
|
||||||
enif_cond_broadcast(async_nif_cnd);
|
// TODO: do we need this? enif_cond_broadcast(async_nif->cnd);
|
||||||
|
|
||||||
/* Take the request off the queue. */
|
/* Remove this thread from the list of idle threads. */
|
||||||
STAILQ_REMOVE(&async_nif_reqs, req, async_nif_req_entry, entries); async_nif_req_count--;
|
enif_mutex_lock(async_nif->worker_mutex);
|
||||||
|
|
||||||
/* Now we need to remove this thread from the list of idle threads. */
|
|
||||||
enif_mutex_lock(async_nif_worker_mutex);
|
|
||||||
LIST_REMOVE(worker, entries);
|
LIST_REMOVE(worker, entries);
|
||||||
|
enif_mutex_unlock(async_nif->worker_mutex);
|
||||||
|
|
||||||
/* Release the locks in reverse order that we acquired them,
|
do {
|
||||||
so as not to self-deadlock. */
|
/* Take the request off the queue. */
|
||||||
enif_mutex_unlock(async_nif_worker_mutex);
|
STAILQ_REMOVE(&async_nif->reqs, req, async_nif->req_entry, entries);
|
||||||
enif_mutex_unlock(async_nif_req_mutex);
|
async_nif->req_count--;
|
||||||
|
enif_mutex_unlock(async_nif->req_mutex);
|
||||||
|
|
||||||
/* Finally, let's do the work! :) */
|
/* Finally, do the work. */
|
||||||
req->fn_work(req->env, req->ref, req->priv_data, &req->pid, req->args);
|
unsigned int worker_id = (unsigned int)(worker - worker_entries);
|
||||||
req->fn_post(req->args);
|
req->fn_work(req->env, req->ref, &req->pid, worker_id, req->args);
|
||||||
enif_free(req->args);
|
req->fn_post(req->args);
|
||||||
enif_free_env(req->env);
|
enif_free(req->args);
|
||||||
enif_free(req);
|
enif_free_env(req->env);
|
||||||
|
enif_free(req);
|
||||||
|
|
||||||
|
/* Finally, check the request queue for more work before switching
|
||||||
|
into idle mode. */
|
||||||
|
enif_mutex_lock(async_nif->req_mutex);
|
||||||
|
if ((req = STAILQ_FIRST(&async_nif->reqs)) == NULL) {
|
||||||
|
enif_mutex_unlock(async_nif->req_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take a second to see if we need to adjust the number of active
|
||||||
|
worker threads up or down. */
|
||||||
|
// TODO: if queue_depth > last_depth && num_workers < MAX, start one up
|
||||||
|
|
||||||
|
} while(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enif_thread_exit(0);
|
enif_thread_exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void async_nif_unload(void)
|
static void async_nif_unload(ErlNifEnv *env)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
struct_nif_state *async_nif = (struct async_nif_state*)enif_priv_data(env);
|
||||||
|
|
||||||
/* Signal the worker threads, stop what you're doing and exit. */
|
/* Signal the worker threads, stop what you're doing and exit. */
|
||||||
enif_mutex_lock(async_nif_req_mutex);
|
enif_mutex_lock(async_nif->req_mutex);
|
||||||
async_nif_shutdown = 1;
|
async_nif->shutdown = 1;
|
||||||
enif_cond_broadcast(async_nif_cnd);
|
enif_cond_broadcast(async_nif->cnd);
|
||||||
enif_mutex_unlock(async_nif_req_mutex);
|
enif_mutex_unlock(async_nif->req_mutex);
|
||||||
|
|
||||||
/* Join for the now exiting worker threads. */
|
/* Join for the now exiting worker threads. */
|
||||||
for (i = 0; i < ASYNC_NIF_MAX_WORKERS; ++i) {
|
for (i = 0; i < ASYNC_NIF_MAX_WORKERS; ++i) {
|
||||||
void *exit_value = 0; /* Ignore this. */
|
void *exit_value = 0; /* Ignore this. */
|
||||||
enif_thread_join(async_nif_worker_entries[i].tid, &exit_value);
|
enif_thread_join(async_nif->worker_entries[i].tid, &exit_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We won't get here until all threads have exited.
|
/* We won't get here until all threads have exited.
|
||||||
Patch things up, and carry on. */
|
Patch things up, and carry on. */
|
||||||
enif_mutex_lock(async_nif_req_mutex);
|
enif_mutex_lock(async_nif->req_mutex);
|
||||||
|
|
||||||
/* Worker threads are stopped, now toss anything left in the queue. */
|
/* Worker threads are stopped, now toss anything left in the queue. */
|
||||||
struct async_nif_req_entry *req = NULL;
|
struct async_nif_req_entry *req = NULL;
|
||||||
STAILQ_FOREACH(req, &async_nif_reqs, entries) {
|
STAILQ_FOREACH(req, &async_nif->reqs, entries) {
|
||||||
STAILQ_REMOVE(&async_nif_reqs, STAILQ_LAST(&async_nif_reqs, async_nif_req_entry, entries),
|
STAILQ_REMOVE(&async_nif->reqs, STAILQ_LAST(&async_nif->reqs, async_nif_req_entry, entries),
|
||||||
async_nif_req_entry, entries);
|
async_nif_req_entry, entries);
|
||||||
#ifdef PULSE
|
|
||||||
PULSE_SEND(NULL, &req->pid, req->env,
|
|
||||||
enif_make_tuple2(req->env, enif_make_atom(req->env, "error"),
|
|
||||||
enif_make_atom(req->env, "shutdown")));
|
|
||||||
#else
|
|
||||||
enif_send(NULL, &req->pid, req->env,
|
enif_send(NULL, &req->pid, req->env,
|
||||||
enif_make_tuple2(req->env, enif_make_atom(req->env, "error"),
|
enif_make_tuple2(req->env, enif_make_atom(req->env, "error"),
|
||||||
enif_make_atom(req->env, "shutdown")));
|
enif_make_atom(req->env, "shutdown")));
|
||||||
#endif
|
|
||||||
req->fn_post(req->args);
|
req->fn_post(req->args);
|
||||||
enif_free(req->args);
|
enif_free(req->args);
|
||||||
enif_free(req);
|
enif_free(req);
|
||||||
async_nif_req_count--;
|
async_nif->req_count--;
|
||||||
}
|
}
|
||||||
enif_mutex_unlock(async_nif_req_mutex);
|
enif_mutex_unlock(async_nif->req_mutex);
|
||||||
|
|
||||||
memset(async_nif_worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS, 0);
|
bzero(async_nif->worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS);
|
||||||
enif_cond_destroy(async_nif_cnd); async_nif_cnd = NULL;
|
enif_cond_destroy(async_nif->cnd); async_nif->cnd = NULL;
|
||||||
enif_mutex_destroy(async_nif_req_mutex); async_nif_req_mutex = NULL;
|
enif_mutex_destroy(async_nif->req_mutex); async_nif->req_mutex = NULL;
|
||||||
enif_mutex_destroy(async_nif_worker_mutex); async_nif_worker_mutex = NULL;
|
enif_mutex_destroy(async_nif->worker_mutex); async_nif->worker_mutex = NULL;
|
||||||
|
bzero(async_nif, sizeof(struct async_nif_state));
|
||||||
|
free(async_nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int async_nif_init(void)
|
static void *
|
||||||
|
async_nif_load(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i, num_schedulers;
|
||||||
|
ErlDrvSysInfo info;
|
||||||
|
struct async_nif_state *async_nif;
|
||||||
|
|
||||||
/* Don't init more than once. */
|
/* Don't init more than once. */
|
||||||
if (async_nif_req_mutex) return 0;
|
if (async_nif_req_mutex) return 0;
|
||||||
|
|
||||||
async_nif_req_mutex = enif_mutex_create(NULL);
|
/* Find out how many schedulers there are. */
|
||||||
async_nif_worker_mutex = enif_mutex_create(NULL);
|
erl_drv_sys_info(&info, sizeof(ErlDrvSysInfo));
|
||||||
async_nif_cnd = enif_cond_create(NULL);
|
num_schedulers = info->scheduler_threads;
|
||||||
|
|
||||||
|
/* Init our portion of priv_data's module-specific state. */
|
||||||
|
async_nif = malloc(sizeof(struct async_nif_state));
|
||||||
|
if (!async_nif)
|
||||||
|
return NULL;
|
||||||
|
STAILQ_INIT(async_nif->reqs);
|
||||||
|
LIST_INIT(async_nif->workers);
|
||||||
|
async_nif->shutdown = 0;
|
||||||
|
|
||||||
|
async_nif->req_mutex = enif_mutex_create(NULL);
|
||||||
|
async_nif->worker_mutex = enif_mutex_create(NULL);
|
||||||
|
async_nif->cnd = enif_cond_create(NULL);
|
||||||
|
|
||||||
/* Setup the requests management. */
|
/* Setup the requests management. */
|
||||||
async_nif_req_count = 0;
|
async_nif->req_count = 0;
|
||||||
|
|
||||||
/* Setup the thread pool management. */
|
/* Setup the thread pool management. */
|
||||||
enif_mutex_lock(async_nif_worker_mutex);
|
enif_mutex_lock(async_nif->worker_mutex);
|
||||||
memset(async_nif_worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS, 0);
|
bzero(async_nif->worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS);
|
||||||
|
|
||||||
for (i = 0; i < ASYNC_NIF_MAX_WORKERS; i++) {
|
/* Start the minimum of max workers allowed or number of scheduler threads running. */
|
||||||
if (enif_thread_create(NULL, &async_nif_worker_entries[i].tid,
|
unsigned int num_worker_threads = ASYNC_NIF_MAX_WORKERS;
|
||||||
&async_nif_worker_fn, (void*)&async_nif_worker_entries[i], NULL) != 0) {
|
if (num_schedulers < ASYNC_NIF_MAX_WORKERS)
|
||||||
async_nif_shutdown = 1;
|
num_worker_threads = num_schedulers;
|
||||||
enif_cond_broadcast(async_nif_cnd);
|
if (num_worker_threads < 1)
|
||||||
enif_mutex_unlock(async_nif_worker_mutex);
|
num_worker_threads = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < num_worker_threads; i++) {
|
||||||
|
if (enif_thread_create(NULL, &async_nif->worker_entries[i].tid,
|
||||||
|
&async_nif_worker_fn, (void*)&async_nif->worker_entries[i], NULL) != 0) {
|
||||||
|
async_nif->shutdown = 1;
|
||||||
|
enif_cond_broadcast(async_nif->cnd);
|
||||||
|
enif_mutex_unlock(async_nif->worker_mutex);
|
||||||
while(i-- > 0) {
|
while(i-- > 0) {
|
||||||
void *exit_value = 0; /* Ignore this. */
|
void *exit_value = 0; /* Ignore this. */
|
||||||
enif_thread_join(async_nif_worker_entries[i].tid, &exit_value);
|
enif_thread_join(async_nif->worker_entries[i].tid, &exit_value);
|
||||||
}
|
}
|
||||||
memset(async_nif_worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS, 0);
|
bzero(async_nif->worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS);
|
||||||
enif_cond_destroy(async_nif_cnd); async_nif_cnd = NULL;
|
enif_cond_destroy(async_nif->cnd);
|
||||||
enif_mutex_destroy(async_nif_req_mutex); async_nif_req_mutex = NULL;
|
async_nif->cnd = NULL;
|
||||||
enif_mutex_destroy(async_nif_worker_mutex); async_nif_worker_mutex = NULL;
|
enif_mutex_destroy(async_nif->req_mutex);
|
||||||
return -1;
|
async_nif->req_mutex = NULL;
|
||||||
|
enif_mutex_destroy(async_nif->worker_mutex);
|
||||||
|
async_nif->worker_mutex = NULL;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enif_mutex_unlock(async_nif_worker_mutex);
|
async_nif->num_workers = num_worker_threads;
|
||||||
return 0;
|
enif_mutex_unlock(async_nif->worker_mutex);
|
||||||
|
return async_nif;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
|
|
610
c_src/khash.h
Normal file
610
c_src/khash.h
Normal file
|
@ -0,0 +1,610 @@
|
||||||
|
/* The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
An example:
|
||||||
|
|
||||||
|
#include "khash.h"
|
||||||
|
KHASH_MAP_INIT_INT(32, char)
|
||||||
|
int main() {
|
||||||
|
int ret, is_missing;
|
||||||
|
khiter_t k;
|
||||||
|
khash_t(32) *h = kh_init(32);
|
||||||
|
k = kh_put(32, h, 5, &ret);
|
||||||
|
kh_value(h, k) = 10;
|
||||||
|
k = kh_get(32, h, 10);
|
||||||
|
is_missing = (k == kh_end(h));
|
||||||
|
k = kh_get(32, h, 5);
|
||||||
|
kh_del(32, h, k);
|
||||||
|
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||||
|
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||||
|
kh_destroy(32, h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
2011-12-29 (0.2.7):
|
||||||
|
|
||||||
|
* Minor code clean up; no actual effect.
|
||||||
|
|
||||||
|
2011-09-16 (0.2.6):
|
||||||
|
|
||||||
|
* The capacity is a power of 2. This seems to dramatically improve the
|
||||||
|
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||||
|
|
||||||
|
- http://code.google.com/p/ulib/
|
||||||
|
- http://nothings.org/computer/judy/
|
||||||
|
|
||||||
|
* Allow to optionally use linear probing which usually has better
|
||||||
|
performance for random input. Double hashing is still the default as it
|
||||||
|
is more robust to certain non-random input.
|
||||||
|
|
||||||
|
* Added Wang's integer hash function (not used by default). This hash
|
||||||
|
function is more robust to certain non-random input.
|
||||||
|
|
||||||
|
2011-02-14 (0.2.5):
|
||||||
|
|
||||||
|
* Allow to declare global functions.
|
||||||
|
|
||||||
|
2009-09-26 (0.2.4):
|
||||||
|
|
||||||
|
* Improve portability
|
||||||
|
|
||||||
|
2008-09-19 (0.2.3):
|
||||||
|
|
||||||
|
* Corrected the example
|
||||||
|
* Improved interfaces
|
||||||
|
|
||||||
|
2008-09-11 (0.2.2):
|
||||||
|
|
||||||
|
* Improved speed a little in kh_put()
|
||||||
|
|
||||||
|
2008-09-10 (0.2.1):
|
||||||
|
|
||||||
|
* Added kh_clear()
|
||||||
|
* Fixed a compiling error
|
||||||
|
|
||||||
|
2008-09-02 (0.2.0):
|
||||||
|
|
||||||
|
* Changed to token concatenation which increases flexibility.
|
||||||
|
|
||||||
|
2008-08-31 (0.1.2):
|
||||||
|
|
||||||
|
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||||
|
|
||||||
|
2008-08-31 (0.1.1):
|
||||||
|
|
||||||
|
* Added destructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __AC_KHASH_H
|
||||||
|
#define __AC_KHASH_H
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
|
||||||
|
Generic hash table library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AC_VERSION_KHASH_H "0.2.6"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* compiler specific configuration */
|
||||||
|
|
||||||
|
#if UINT_MAX == 0xffffffffu
|
||||||
|
typedef unsigned int khint32_t;
|
||||||
|
#elif ULONG_MAX == 0xffffffffu
|
||||||
|
typedef unsigned long khint32_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ULONG_MAX == ULLONG_MAX
|
||||||
|
typedef unsigned long khint64_t;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long khint64_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define kh_inline __inline
|
||||||
|
#else
|
||||||
|
#define kh_inline inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef khint32_t khint_t;
|
||||||
|
typedef khint_t khiter_t;
|
||||||
|
|
||||||
|
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||||
|
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||||
|
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||||
|
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||||
|
|
||||||
|
#ifdef KHASH_LINEAR
|
||||||
|
#define __ac_inc(k, m) 1
|
||||||
|
#else
|
||||||
|
#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||||
|
|
||||||
|
#ifndef kroundup32
|
||||||
|
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef kcalloc
|
||||||
|
#define kcalloc(N,Z) calloc(N,Z)
|
||||||
|
#endif
|
||||||
|
#ifndef kmalloc
|
||||||
|
#define kmalloc(Z) malloc(Z)
|
||||||
|
#endif
|
||||||
|
#ifndef krealloc
|
||||||
|
#define krealloc(P,Z) realloc(P,Z)
|
||||||
|
#endif
|
||||||
|
#ifndef kfree
|
||||||
|
#define kfree(P) free(P)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const double __ac_HASH_UPPER = 0.77;
|
||||||
|
|
||||||
|
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
typedef struct { \
|
||||||
|
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||||
|
khint32_t *flags; \
|
||||||
|
khkey_t *keys; \
|
||||||
|
khval_t *vals; \
|
||||||
|
} kh_##name##_t;
|
||||||
|
|
||||||
|
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||||
|
extern kh_##name##_t *kh_init_##name(void); \
|
||||||
|
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||||
|
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||||
|
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||||
|
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||||
|
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||||
|
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||||
|
|
||||||
|
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||||
|
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||||
|
{ \
|
||||||
|
if (h) { \
|
||||||
|
kfree((void *)h->keys); kfree(h->flags); \
|
||||||
|
kfree((void *)h->vals); \
|
||||||
|
kfree(h); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||||
|
{ \
|
||||||
|
if (h && h->flags) { \
|
||||||
|
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||||
|
h->size = h->n_occupied = 0; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||||
|
{ \
|
||||||
|
if (h->n_buckets) { \
|
||||||
|
khint_t inc, k, i, last, mask; \
|
||||||
|
mask = h->n_buckets - 1; \
|
||||||
|
k = __hash_func(key); i = k & mask; \
|
||||||
|
inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
|
||||||
|
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||||
|
i = (i + inc) & mask; \
|
||||||
|
if (i == last) return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||||
|
} else return 0; \
|
||||||
|
} \
|
||||||
|
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||||
|
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||||
|
khint32_t *new_flags = 0; \
|
||||||
|
khint_t j = 1; \
|
||||||
|
{ \
|
||||||
|
kroundup32(new_n_buckets); \
|
||||||
|
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||||
|
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||||
|
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||||
|
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||||
|
if (!new_flags) return -1; \
|
||||||
|
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||||
|
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||||
|
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||||
|
if (!new_keys) return -1; \
|
||||||
|
h->keys = new_keys; \
|
||||||
|
if (kh_is_map) { \
|
||||||
|
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||||
|
if (!new_vals) return -1; \
|
||||||
|
h->vals = new_vals; \
|
||||||
|
} \
|
||||||
|
} /* otherwise shrink */ \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (j) { /* rehashing is needed */ \
|
||||||
|
for (j = 0; j != h->n_buckets; ++j) { \
|
||||||
|
if (__ac_iseither(h->flags, j) == 0) { \
|
||||||
|
khkey_t key = h->keys[j]; \
|
||||||
|
khval_t val; \
|
||||||
|
khint_t new_mask; \
|
||||||
|
new_mask = new_n_buckets - 1; \
|
||||||
|
if (kh_is_map) val = h->vals[j]; \
|
||||||
|
__ac_set_isdel_true(h->flags, j); \
|
||||||
|
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||||
|
khint_t inc, k, i; \
|
||||||
|
k = __hash_func(key); \
|
||||||
|
i = k & new_mask; \
|
||||||
|
inc = __ac_inc(k, new_mask); \
|
||||||
|
while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
|
||||||
|
__ac_set_isempty_false(new_flags, i); \
|
||||||
|
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||||
|
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||||
|
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||||
|
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||||
|
} else { /* write the element and jump out of the loop */ \
|
||||||
|
h->keys[i] = key; \
|
||||||
|
if (kh_is_map) h->vals[i] = val; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||||
|
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||||
|
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||||
|
} \
|
||||||
|
kfree(h->flags); /* free the working space */ \
|
||||||
|
h->flags = new_flags; \
|
||||||
|
h->n_buckets = new_n_buckets; \
|
||||||
|
h->n_occupied = h->size; \
|
||||||
|
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||||
|
{ \
|
||||||
|
khint_t x; \
|
||||||
|
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||||
|
if (h->n_buckets > (h->size<<1)) { \
|
||||||
|
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||||
|
*ret = -1; return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||||
|
*ret = -1; return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||||
|
{ \
|
||||||
|
khint_t inc, k, i, site, last, mask = h->n_buckets - 1; \
|
||||||
|
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||||
|
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||||
|
else { \
|
||||||
|
inc = __ac_inc(k, mask); last = i; \
|
||||||
|
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||||
|
if (__ac_isdel(h->flags, i)) site = i; \
|
||||||
|
i = (i + inc) & mask; \
|
||||||
|
if (i == last) { x = site; break; } \
|
||||||
|
} \
|
||||||
|
if (x == h->n_buckets) { \
|
||||||
|
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||||
|
else x = i; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||||
|
h->keys[x] = key; \
|
||||||
|
__ac_set_isboth_false(h->flags, x); \
|
||||||
|
++h->size; ++h->n_occupied; \
|
||||||
|
*ret = 1; \
|
||||||
|
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||||
|
h->keys[x] = key; \
|
||||||
|
__ac_set_isboth_false(h->flags, x); \
|
||||||
|
++h->size; \
|
||||||
|
*ret = 2; \
|
||||||
|
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||||
|
return x; \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||||
|
{ \
|
||||||
|
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||||
|
__ac_set_isdel_true(h->flags, x); \
|
||||||
|
--h->size; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||||
|
|
||||||
|
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||||
|
|
||||||
|
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||||
|
|
||||||
|
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Integer hash function
|
||||||
|
@param key The integer [khint32_t]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||||
|
/*! @function
|
||||||
|
@abstract Integer comparison function
|
||||||
|
*/
|
||||||
|
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||||
|
/*! @function
|
||||||
|
@abstract 64-bit integer hash function
|
||||||
|
@param key The integer [khint64_t]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||||
|
/*! @function
|
||||||
|
@abstract 64-bit integer comparison function
|
||||||
|
*/
|
||||||
|
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||||
|
/*! @function
|
||||||
|
@abstract const char* hash function
|
||||||
|
@param s Pointer to a null terminated string
|
||||||
|
@return The hash value
|
||||||
|
*/
|
||||||
|
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||||
|
{
|
||||||
|
khint_t h = (khint_t)*s;
|
||||||
|
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
/*! @function
|
||||||
|
@abstract Another interface to const char* hash function
|
||||||
|
@param key Pointer to a null terminated string [const char*]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||||
|
/*! @function
|
||||||
|
@abstract Const char* comparison function
|
||||||
|
*/
|
||||||
|
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||||
|
|
||||||
|
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||||
|
{
|
||||||
|
key += ~(key << 15);
|
||||||
|
key ^= (key >> 10);
|
||||||
|
key += (key << 3);
|
||||||
|
key ^= (key >> 6);
|
||||||
|
key += ~(key << 11);
|
||||||
|
key ^= (key >> 16);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
|
||||||
|
|
||||||
|
/* --- END OF HASH FUNCTIONS --- */
|
||||||
|
|
||||||
|
/* Other convenient macros... */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@abstract Type of the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define khash_t(name) kh_##name##_t
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Initiate a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@return Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_init(name) kh_init_##name()
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Destroy a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Reset a hash table without deallocating memory.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_clear(name, h) kh_clear_##name(h)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Resize a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param s New size [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Insert a key to the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Key [type of keys]
|
||||||
|
@param r Extra return code: 0 if the key is present in the hash table;
|
||||||
|
1 if the bucket is empty (never used); 2 if the element in
|
||||||
|
the bucket has been deleted [int*]
|
||||||
|
@return Iterator to the inserted element [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Retrieve a key from the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Key [type of keys]
|
||||||
|
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Remove a key from the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Iterator to the element to be deleted [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Test whether a bucket contains data.
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return 1 if containing data; 0 otherwise [int]
|
||||||
|
*/
|
||||||
|
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get key given an iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return Key [type of keys]
|
||||||
|
*/
|
||||||
|
#define kh_key(h, x) ((h)->keys[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get value given an iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return Value [type of values]
|
||||||
|
@discussion For hash sets, calling this results in segfault.
|
||||||
|
*/
|
||||||
|
#define kh_val(h, x) ((h)->vals[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Alias of kh_val()
|
||||||
|
*/
|
||||||
|
#define kh_value(h, x) ((h)->vals[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the start iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return The start iterator [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_begin(h) (khint_t)(0)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the end iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return The end iterator [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_end(h) ((h)->n_buckets)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the number of elements in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return Number of elements in the hash table [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_size(h) ((h)->size)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the number of buckets in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return Number of buckets in the hash table [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Iterate over the entries in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param kvar Variable to which key will be assigned
|
||||||
|
@param vvar Variable to which value will be assigned
|
||||||
|
@param code Block of code to execute
|
||||||
|
*/
|
||||||
|
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||||
|
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||||
|
if (!kh_exist(h,__i)) continue; \
|
||||||
|
(kvar) = kh_key(h,__i); \
|
||||||
|
(vvar) = kh_val(h,__i); \
|
||||||
|
code; \
|
||||||
|
} }
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Iterate over the values in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param vvar Variable to which value will be assigned
|
||||||
|
@param code Block of code to execute
|
||||||
|
*/
|
||||||
|
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||||
|
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||||
|
if (!kh_exist(h,__i)) continue; \
|
||||||
|
(vvar) = kh_val(h,__i); \
|
||||||
|
code; \
|
||||||
|
} }
|
||||||
|
|
||||||
|
/* More conenient interfaces */
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash set containing integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_INT(name) \
|
||||||
|
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||||
|
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_INT64(name) \
|
||||||
|
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||||
|
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||||
|
|
||||||
|
typedef const char *kh_cstr_t;
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing const char* keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_STR(name) \
|
||||||
|
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing const char* keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||||
|
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
|
#endif /* __AC_KHASH_H */
|
1390
c_src/wterl.c
1390
c_src/wterl.c
File diff suppressed because it is too large
Load diff
|
@ -97,6 +97,8 @@ start(Partition, Config) ->
|
||||||
end,
|
end,
|
||||||
case AppStart of
|
case AppStart of
|
||||||
ok ->
|
ok ->
|
||||||
|
%% TODO: on failure to open a table try to verify, and then salvage it
|
||||||
|
%% if the cluster size > the n value
|
||||||
Table = "lsm:wt" ++ integer_to_list(Partition),
|
Table = "lsm:wt" ++ integer_to_list(Partition),
|
||||||
{ok, Connection} = establish_connection(Config),
|
{ok, Connection} = establish_connection(Config),
|
||||||
Passes = establish_passes(erlang:system_info(schedulers), Connection, Table),
|
Passes = establish_passes(erlang:system_info(schedulers), Connection, Table),
|
||||||
|
|
382
src/wterl.erl
382
src/wterl.erl
|
@ -28,6 +28,7 @@
|
||||||
cursor_next_key/1,
|
cursor_next_key/1,
|
||||||
cursor_next_value/1,
|
cursor_next_value/1,
|
||||||
cursor_open/2,
|
cursor_open/2,
|
||||||
|
cursor_open/3,
|
||||||
cursor_prev/1,
|
cursor_prev/1,
|
||||||
cursor_prev_key/1,
|
cursor_prev_key/1,
|
||||||
cursor_prev_value/1,
|
cursor_prev_value/1,
|
||||||
|
@ -36,28 +37,26 @@
|
||||||
cursor_search/2,
|
cursor_search/2,
|
||||||
cursor_search_near/2,
|
cursor_search_near/2,
|
||||||
cursor_update/3,
|
cursor_update/3,
|
||||||
session_checkpoint/1,
|
checkpoint/1,
|
||||||
session_checkpoint/2,
|
checkpoint/2,
|
||||||
session_close/1,
|
create/2,
|
||||||
session_create/2,
|
create/4,
|
||||||
session_create/3,
|
delete/3,
|
||||||
session_delete/3,
|
drop/2,
|
||||||
session_drop/2,
|
drop/3,
|
||||||
session_drop/3,
|
get/3,
|
||||||
session_get/3,
|
put/4,
|
||||||
session_open/1,
|
rename/3,
|
||||||
session_open/2,
|
rename/4,
|
||||||
session_put/4,
|
salvage/2,
|
||||||
session_rename/3,
|
salvage/3,
|
||||||
session_rename/4,
|
truncate/2,
|
||||||
session_salvage/2,
|
truncate/3,
|
||||||
session_salvage/3,
|
truncate/5,
|
||||||
session_truncate/2,
|
upgrade/2,
|
||||||
session_truncate/3,
|
upgrade/3,
|
||||||
session_upgrade/2,
|
verify/2,
|
||||||
session_upgrade/3,
|
verify/3,
|
||||||
session_verify/2,
|
|
||||||
session_verify/3,
|
|
||||||
config_value/3,
|
config_value/3,
|
||||||
config_to_bin/1,
|
config_to_bin/1,
|
||||||
priv_dir/0,
|
priv_dir/0,
|
||||||
|
@ -77,12 +76,11 @@
|
||||||
-type config() :: binary().
|
-type config() :: binary().
|
||||||
-type config_list() :: [{atom(), any()}].
|
-type config_list() :: [{atom(), any()}].
|
||||||
-opaque connection() :: reference().
|
-opaque connection() :: reference().
|
||||||
-opaque session() :: reference().
|
|
||||||
-opaque cursor() :: reference().
|
-opaque cursor() :: reference().
|
||||||
-type key() :: binary().
|
-type key() :: binary().
|
||||||
-type value() :: binary().
|
-type value() :: binary().
|
||||||
|
|
||||||
-export_type([connection/0, session/0, cursor/0]).
|
-export_type([connection/0, cursor/0]).
|
||||||
|
|
||||||
-on_load(init/0).
|
-on_load(init/0).
|
||||||
|
|
||||||
|
@ -94,7 +92,9 @@ nif_stub_error(Line) ->
|
||||||
|
|
||||||
-spec init() -> ok | {error, any()}.
|
-spec init() -> ok | {error, any()}.
|
||||||
init() ->
|
init() ->
|
||||||
erlang:load_nif(filename:join(priv_dir(), atom_to_list(?MODULE)), 0).
|
erlang:load_nif(filename:join(priv_dir(), atom_to_list(?MODULE)),
|
||||||
|
[{wterl, "163a5073cb85db2a270ebe904e788bd8d478ea1c"},
|
||||||
|
{wiredtiger, "e9a607b1b78ffa528631519b5cb6ac944468991e"}]).
|
||||||
|
|
||||||
-spec connection_open(string(), config()) -> {ok, connection()} | {error, term()}.
|
-spec connection_open(string(), config()) -> {ok, connection()} | {error, term()}.
|
||||||
connection_open(HomeDir, Config) ->
|
connection_open(HomeDir, Config) ->
|
||||||
|
@ -127,143 +127,130 @@ connection_close(ConnRef) ->
|
||||||
conn_close_nif(_AsyncRef, _ConnRef) ->
|
conn_close_nif(_AsyncRef, _ConnRef) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_open(connection()) -> {ok, session()} | {error, term()}.
|
-spec create(connection(), string()) -> ok | {error, term()}.
|
||||||
-spec session_open(connection(), config()) -> {ok, session()} | {error, term()}.
|
-spec create(connection(), string(), config(), config()) -> ok | {error, term()}.
|
||||||
session_open(ConnRef) ->
|
create(Ref, Name) ->
|
||||||
session_open(ConnRef, ?EMPTY_CONFIG).
|
create(Ref, Name, ?EMPTY_CONFIG, ?EMPTY_CONFIG).
|
||||||
session_open(ConnRef, Config) ->
|
create(Ref, Name, Config, SessionConfig) ->
|
||||||
?ASYNC_NIF_CALL(fun session_open_nif/3, [ConnRef, Config]).
|
?ASYNC_NIF_CALL(fun create_nif/5, [Ref, Name, Config, SessionConfig]).
|
||||||
|
|
||||||
-spec session_open_nif(reference(), connection(), config()) -> {ok, session()} | {error, term()}.
|
-spec create_nif(reference(), connection(), string(), config(), config()) -> ok | {error, term()}.
|
||||||
session_open_nif(_AsyncRef, _ConnRef, _Config) ->
|
create_nif(_AsyncNif, _Ref, _Name, _Config, _SessionConfig) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_close(session()) -> ok | {error, term()}.
|
-spec drop(connection(), string()) -> ok | {error, term()}.
|
||||||
session_close(Ref) ->
|
-spec drop(connection(), string(), config()) -> ok | {error, term()}.
|
||||||
?ASYNC_NIF_CALL(fun session_close_nif/2, [Ref]).
|
drop(Ref, Name) ->
|
||||||
|
drop(Ref, Name, ?EMPTY_CONFIG).
|
||||||
|
drop(Ref, Name, Config) ->
|
||||||
|
?ASYNC_NIF_CALL(fun drop_nif/4, [Ref, Name, Config]).
|
||||||
|
|
||||||
-spec session_close_nif(reference(), session()) -> ok | {error, term()}.
|
-spec drop_nif(reference(), connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_close_nif(_AsyncRef, _Ref) ->
|
drop_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_create(session(), string()) -> ok | {error, term()}.
|
-spec delete(connection(), string(), key()) -> ok | {error, term()}.
|
||||||
-spec session_create(session(), string(), config()) -> ok | {error, term()}.
|
delete(Ref, Table, Key) ->
|
||||||
session_create(Ref, Name) ->
|
?ASYNC_NIF_CALL(fun delete_nif/4, [Ref, Table, Key]).
|
||||||
session_create(Ref, Name, ?EMPTY_CONFIG).
|
|
||||||
session_create(Ref, Name, Config) ->
|
|
||||||
?ASYNC_NIF_CALL(fun session_create_nif/4, [Ref, Name, Config]).
|
|
||||||
|
|
||||||
-spec session_create_nif(reference(), session(), string(), config()) -> ok | {error, term()}.
|
-spec delete_nif(reference(), connection(), string(), key()) -> ok | {error, term()}.
|
||||||
session_create_nif(_AsyncNif, _Ref, _Name, _Config) ->
|
delete_nif(_AsyncRef, _Ref, _Table, _Key) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_drop(session(), string()) -> ok | {error, term()}.
|
-spec get(connection(), string(), key()) -> {ok, value()} | not_found | {error, term()}.
|
||||||
-spec session_drop(session(), string(), config()) -> ok | {error, term()}.
|
get(Ref, Table, Key) ->
|
||||||
session_drop(Ref, Name) ->
|
?ASYNC_NIF_CALL(fun get_nif/4, [Ref, Table, Key]).
|
||||||
session_drop(Ref, Name, ?EMPTY_CONFIG).
|
|
||||||
session_drop(Ref, Name, Config) ->
|
|
||||||
?ASYNC_NIF_CALL(fun session_drop_nif/4, [Ref, Name, Config]).
|
|
||||||
|
|
||||||
-spec session_drop_nif(reference(), session(), string(), config()) -> ok | {error, term()}.
|
-spec get_nif(reference(), connection(), string(), key()) -> {ok, value()} | not_found | {error, term()}.
|
||||||
session_drop_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
get_nif(_AsyncRef, _Ref, _Table, _Key) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_delete(session(), string(), key()) -> ok | {error, term()}.
|
-spec put(connection(), string(), key(), value()) -> ok | {error, term()}.
|
||||||
session_delete(Ref, Table, Key) ->
|
put(Ref, Table, Key, Value) ->
|
||||||
?ASYNC_NIF_CALL(fun session_delete_nif/4, [Ref, Table, Key]).
|
?ASYNC_NIF_CALL(fun put_nif/5, [Ref, Table, Key, Value]).
|
||||||
|
|
||||||
-spec session_delete_nif(reference(), session(), string(), key()) -> ok | {error, term()}.
|
-spec put_nif(reference(), connection(), string(), key(), value()) -> ok | {error, term()}.
|
||||||
session_delete_nif(_AsyncRef, _Ref, _Table, _Key) ->
|
put_nif(_AsyncRef, _Ref, _Table, _Key, _Value) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_get(session(), string(), key()) -> {ok, value()} | not_found | {error, term()}.
|
-spec rename(connection(), string(), string()) -> ok | {error, term()}.
|
||||||
session_get(Ref, Table, Key) ->
|
-spec rename(connection(), string(), string(), config()) -> ok | {error, term()}.
|
||||||
?ASYNC_NIF_CALL(fun session_get_nif/4, [Ref, Table, Key]).
|
rename(Ref, OldName, NewName) ->
|
||||||
|
rename(Ref, OldName, NewName, ?EMPTY_CONFIG).
|
||||||
|
rename(Ref, OldName, NewName, Config) ->
|
||||||
|
?ASYNC_NIF_CALL(fun rename_nif/5, [Ref, OldName, NewName, Config]).
|
||||||
|
|
||||||
-spec session_get_nif(reference(), session(), string(), key()) -> {ok, value()} | not_found | {error, term()}.
|
-spec rename_nif(reference(), connection(), string(), string(), config()) -> ok | {error, term()}.
|
||||||
session_get_nif(_AsyncRef, _Ref, _Table, _Key) ->
|
rename_nif(_AsyncRef, _Ref, _OldName, _NewName, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_put(session(), string(), key(), value()) -> ok | {error, term()}.
|
-spec salvage(connection(), string()) -> ok | {error, term()}.
|
||||||
session_put(Ref, Table, Key, Value) ->
|
-spec salvage(connection(), string(), config()) -> ok | {error, term()}.
|
||||||
?ASYNC_NIF_CALL(fun session_put_nif/5, [Ref, Table, Key, Value]).
|
salvage(Ref, Name) ->
|
||||||
|
salvage(Ref, Name, ?EMPTY_CONFIG).
|
||||||
|
salvage(Ref, Name, Config) ->
|
||||||
|
?ASYNC_NIF_CALL(fun salvage_nif/4, [Ref, Name, Config]).
|
||||||
|
|
||||||
-spec session_put_nif(reference(), session(), string(), key(), value()) -> ok | {error, term()}.
|
-spec salvage_nif(reference(), connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_put_nif(_AsyncRef, _Ref, _Table, _Key, _Value) ->
|
salvage_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_rename(session(), string(), string()) -> ok | {error, term()}.
|
-spec checkpoint(connection()) -> ok | {error, term()}.
|
||||||
-spec session_rename(session(), string(), string(), config()) -> ok | {error, term()}.
|
-spec checkpoint(connection(), config()) -> ok | {error, term()}.
|
||||||
session_rename(Ref, OldName, NewName) ->
|
checkpoint(_Ref) ->
|
||||||
session_rename(Ref, OldName, NewName, ?EMPTY_CONFIG).
|
checkpoint(_Ref, ?EMPTY_CONFIG).
|
||||||
session_rename(Ref, OldName, NewName, Config) ->
|
checkpoint(Ref, Config) ->
|
||||||
?ASYNC_NIF_CALL(fun session_rename_nif/5, [Ref, OldName, NewName, Config]).
|
?ASYNC_NIF_CALL(fun checkpoint_nif/3, [Ref, Config]).
|
||||||
|
|
||||||
-spec session_rename_nif(reference(), session(), string(), string(), config()) -> ok | {error, term()}.
|
-spec checkpoint_nif(reference(), connection(), config()) -> ok | {error, term()}.
|
||||||
session_rename_nif(_AsyncRef, _Ref, _OldName, _NewName, _Config) ->
|
checkpoint_nif(_AsyncRef, _Ref, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_salvage(session(), string()) -> ok | {error, term()}.
|
-spec truncate(connection(), string()) -> ok | {error, term()}.
|
||||||
-spec session_salvage(session(), string(), config()) -> ok | {error, term()}.
|
-spec truncate(connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_salvage(Ref, Name) ->
|
truncate(Ref, Name, Config) ->
|
||||||
session_salvage(Ref, Name, ?EMPTY_CONFIG).
|
truncate(Ref, Name, 0, 0, Config).
|
||||||
session_salvage(Ref, Name, Config) ->
|
-spec truncate(connection(), string(), cursor() | 0, cursor() | 0, config()) -> ok | {error, term()}.
|
||||||
?ASYNC_NIF_CALL(fun session_salvage_nif/4, [Ref, Name, Config]).
|
truncate(Ref, Name) ->
|
||||||
|
truncate(Ref, Name, 0, 0, ?EMPTY_CONFIG).
|
||||||
|
truncate(Ref, Name, Start, Stop, Config) ->
|
||||||
|
?ASYNC_NIF_CALL(fun truncate_nif/6, [Ref, Name, Start, Stop, Config]).
|
||||||
|
|
||||||
-spec session_salvage_nif(reference(), session(), string(), config()) -> ok | {error, term()}.
|
-spec truncate_nif(reference(), connection(), string(), cursor() | 0, cursor() | 0, config()) -> ok | {error, term()}.
|
||||||
session_salvage_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
truncate_nif(_AsyncRef, _Ref, _Name, _Start, _Stop, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_checkpoint(session()) -> ok | {error, term()}.
|
-spec upgrade(connection(), string()) -> ok | {error, term()}.
|
||||||
-spec session_checkpoint(session(), config()) -> ok | {error, term()}.
|
-spec upgrade(connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_checkpoint(_Ref) ->
|
upgrade(Ref, Name) ->
|
||||||
session_checkpoint(_Ref, ?EMPTY_CONFIG).
|
upgrade(Ref, Name, ?EMPTY_CONFIG).
|
||||||
session_checkpoint(Ref, Config) ->
|
upgrade(Ref, Name, Config) ->
|
||||||
?ASYNC_NIF_CALL(fun session_checkpoint_nif/3, [Ref, Config]).
|
?ASYNC_NIF_CALL(fun upgrade_nif/4, [Ref, Name, Config]).
|
||||||
|
|
||||||
-spec session_checkpoint_nif(reference(), session(), config()) -> ok | {error, term()}.
|
-spec upgrade_nif(reference(), connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_checkpoint_nif(_AsyncRef, _Ref, _Config) ->
|
upgrade_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_truncate(session(), string()) -> ok | {error, term()}.
|
-spec verify(connection(), string()) -> ok | {error, term()}.
|
||||||
-spec session_truncate(session(), string(), config()) -> ok | {error, term()}.
|
-spec verify(connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_truncate(Ref, Name) ->
|
verify(Ref, Name) ->
|
||||||
session_truncate(Ref, Name, ?EMPTY_CONFIG).
|
verify(Ref, Name, ?EMPTY_CONFIG).
|
||||||
session_truncate(Ref, Name, Config) ->
|
verify(Ref, Name, Config) ->
|
||||||
?ASYNC_NIF_CALL(fun session_truncate_nif/4, [Ref, Name, Config]).
|
?ASYNC_NIF_CALL(fun verify_nif/4, [Ref, Name, Config]).
|
||||||
|
|
||||||
-spec session_truncate_nif(reference(), session(), string(), config()) -> ok | {error, term()}.
|
-spec verify_nif(reference(), connection(), string(), config()) -> ok | {error, term()}.
|
||||||
session_truncate_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
verify_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec session_upgrade(session(), string()) -> ok | {error, term()}.
|
-spec cursor_open(connection(), string()) -> {ok, cursor()} | {error, term()}.
|
||||||
-spec session_upgrade(session(), string(), config()) -> ok | {error, term()}.
|
-spec cursor_open(connection(), string(), config() | 0) -> {ok, cursor()} | {error, term()}.
|
||||||
session_upgrade(Ref, Name) ->
|
|
||||||
session_upgrade(Ref, Name, ?EMPTY_CONFIG).
|
|
||||||
session_upgrade(Ref, Name, Config) ->
|
|
||||||
?ASYNC_NIF_CALL(fun session_upgrade_nif/4, [Ref, Name, Config]).
|
|
||||||
|
|
||||||
-spec session_upgrade_nif(reference(), session(), string(), config()) -> ok | {error, term()}.
|
|
||||||
session_upgrade_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
|
||||||
?nif_stub.
|
|
||||||
|
|
||||||
-spec session_verify(session(), string()) -> ok | {error, term()}.
|
|
||||||
-spec session_verify(session(), string(), config()) -> ok | {error, term()}.
|
|
||||||
session_verify(Ref, Name) ->
|
|
||||||
session_verify(Ref, Name, ?EMPTY_CONFIG).
|
|
||||||
session_verify(Ref, Name, Config) ->
|
|
||||||
?ASYNC_NIF_CALL(fun session_verify_nif/4, [Ref, Name, Config]).
|
|
||||||
|
|
||||||
-spec session_verify_nif(reference(), session(), string(), config()) -> ok | {error, term()}.
|
|
||||||
session_verify_nif(_AsyncRef, _Ref, _Name, _Config) ->
|
|
||||||
?nif_stub.
|
|
||||||
|
|
||||||
-spec cursor_open(session(), string()) -> {ok, cursor()} | {error, term()}.
|
|
||||||
cursor_open(Ref, Table) ->
|
cursor_open(Ref, Table) ->
|
||||||
?ASYNC_NIF_CALL(fun cursor_open_nif/3, [Ref, Table]).
|
cursor_open(Ref, Table, 0).
|
||||||
|
cursor_open(Ref, Table, Config) ->
|
||||||
|
?ASYNC_NIF_CALL(fun cursor_open_nif/4, [Ref, Table, Config]).
|
||||||
|
|
||||||
-spec cursor_open_nif(reference(), session(), string()) -> {ok, cursor()} | {error, term()}.
|
-spec cursor_open_nif(reference(), connection(), string(), config() | 0) -> {ok, cursor()} | {error, term()}.
|
||||||
cursor_open_nif(_AsyncRef, _Ref, _Table) ->
|
cursor_open_nif(_AsyncRef, _Ref, _Table, _Config) ->
|
||||||
?nif_stub.
|
?nif_stub.
|
||||||
|
|
||||||
-spec cursor_close(cursor()) -> ok | {error, term()}.
|
-spec cursor_close(cursor()) -> ok | {error, term()}.
|
||||||
|
@ -518,11 +505,10 @@ open_test_conn(DataDir) ->
|
||||||
{ok, ConnRef} = connection_open(DataDir, OpenConfig),
|
{ok, ConnRef} = connection_open(DataDir, OpenConfig),
|
||||||
ConnRef.
|
ConnRef.
|
||||||
|
|
||||||
open_test_session(ConnRef) ->
|
open_test_table(ConnRef) ->
|
||||||
{ok, SRef} = session_open(ConnRef),
|
?assertMatch(ok, drop(ConnRef, "table:test", config_to_bin([{force,true}]))),
|
||||||
?assertMatch(ok, session_drop(SRef, "table:test", config_to_bin([{force,true}]))),
|
?assertMatch(ok, create(ConnRef, "table:test", config_to_bin([{block_compressor, "snappy"}]))),
|
||||||
?assertMatch(ok, session_create(SRef, "table:test", config_to_bin([{block_compressor, "snappy"}]))),
|
ConnRef.
|
||||||
SRef.
|
|
||||||
|
|
||||||
conn_test() ->
|
conn_test() ->
|
||||||
ConnRef = open_test_conn(?TEST_DATA_DIR),
|
ConnRef = open_test_conn(?TEST_DATA_DIR),
|
||||||
|
@ -538,110 +524,102 @@ session_test_() ->
|
||||||
end,
|
end,
|
||||||
fun(ConnRef) ->
|
fun(ConnRef) ->
|
||||||
{inorder,
|
{inorder,
|
||||||
[{"open/close a session",
|
[{"create and drop a table",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, SRef} = session_open(ConnRef),
|
ConnRef = open_test_table(ConnRef),
|
||||||
?assertMatch(ok, session_close(SRef))
|
?assertMatch(ok, drop(ConnRef, "table:test"))
|
||||||
end},
|
|
||||||
{"create and drop a table",
|
|
||||||
fun() ->
|
|
||||||
SRef = open_test_session(ConnRef),
|
|
||||||
?assertMatch(ok, session_drop(SRef, "table:test")),
|
|
||||||
?assertMatch(ok, session_close(SRef))
|
|
||||||
end}]}
|
end}]}
|
||||||
end}.
|
end}.
|
||||||
|
|
||||||
insert_delete_test() ->
|
insert_delete_test() ->
|
||||||
ConnRef = open_test_conn(?TEST_DATA_DIR),
|
ConnRef = open_test_conn(?TEST_DATA_DIR),
|
||||||
SRef = open_test_session(ConnRef),
|
ConnRef = open_test_table(ConnRef),
|
||||||
?assertMatch(ok, session_put(SRef, "table:test", <<"a">>, <<"apple">>)),
|
?assertMatch(ok, put(ConnRef, "table:test", <<"a">>, <<"apple">>)),
|
||||||
?assertMatch({ok, <<"apple">>}, session_get(SRef, "table:test", <<"a">>)),
|
?assertMatch({ok, <<"apple">>}, get(ConnRef, "table:test", <<"a">>)),
|
||||||
?assertMatch(ok, session_delete(SRef, "table:test", <<"a">>)),
|
?assertMatch(ok, delete(ConnRef, "table:test", <<"a">>)),
|
||||||
?assertMatch(not_found, session_get(SRef, "table:test", <<"a">>)),
|
?assertMatch(not_found, get(ConnRef, "table:test", <<"a">>)),
|
||||||
ok = session_close(SRef),
|
|
||||||
ok = connection_close(ConnRef).
|
ok = connection_close(ConnRef).
|
||||||
|
|
||||||
init_test_table() ->
|
init_test_table() ->
|
||||||
ConnRef = open_test_conn(?TEST_DATA_DIR),
|
ConnRef = open_test_conn(?TEST_DATA_DIR),
|
||||||
SRef = open_test_session(ConnRef),
|
ConnRef = open_test_table(ConnRef),
|
||||||
?assertMatch(ok, session_put(SRef, "table:test", <<"a">>, <<"apple">>)),
|
?assertMatch(ok, put(ConnRef, "table:test", <<"a">>, <<"apple">>)),
|
||||||
?assertMatch(ok, session_put(SRef, "table:test", <<"b">>, <<"banana">>)),
|
?assertMatch(ok, put(ConnRef, "table:test", <<"b">>, <<"banana">>)),
|
||||||
?assertMatch(ok, session_put(SRef, "table:test", <<"c">>, <<"cherry">>)),
|
?assertMatch(ok, put(ConnRef, "table:test", <<"c">>, <<"cherry">>)),
|
||||||
?assertMatch(ok, session_put(SRef, "table:test", <<"d">>, <<"date">>)),
|
?assertMatch(ok, put(ConnRef, "table:test", <<"d">>, <<"date">>)),
|
||||||
?assertMatch(ok, session_put(SRef, "table:test", <<"g">>, <<"gooseberry">>)),
|
?assertMatch(ok, put(ConnRef, "table:test", <<"g">>, <<"gooseberry">>)),
|
||||||
{ConnRef, SRef}.
|
ConnRef.
|
||||||
|
|
||||||
stop_test_table({ConnRef, SRef}) ->
|
stop_test_table(ConnRef) ->
|
||||||
?assertMatch(ok, session_close(SRef)),
|
|
||||||
?assertMatch(ok, connection_close(ConnRef)).
|
?assertMatch(ok, connection_close(ConnRef)).
|
||||||
|
|
||||||
various_session_test_() ->
|
various_session_test_() ->
|
||||||
{setup,
|
{setup,
|
||||||
fun init_test_table/0,
|
fun init_test_table/0,
|
||||||
fun stop_test_table/1,
|
fun stop_test_table/1,
|
||||||
fun({_, SRef}) ->
|
fun(ConnRef) ->
|
||||||
{inorder,
|
{inorder,
|
||||||
[{"session verify",
|
[{"session verify",
|
||||||
fun() ->
|
fun() ->
|
||||||
?assertMatch(ok, session_verify(SRef, "table:test")),
|
?assertMatch(ok, verify(ConnRef, "table:test")),
|
||||||
?assertMatch({ok, <<"apple">>},
|
?assertMatch({ok, <<"apple">>},
|
||||||
session_get(SRef, "table:test", <<"a">>))
|
get(ConnRef, "table:test", <<"a">>))
|
||||||
end},
|
end},
|
||||||
{"session checkpoint",
|
{"session checkpoint",
|
||||||
fun() ->
|
fun() ->
|
||||||
Cfg = wterl:config_to_bin([{target, ["\"table:test\""]}]),
|
Cfg = wterl:config_to_bin([{target, ["\"table:test\""]}]),
|
||||||
?assertMatch(ok, session_checkpoint(SRef, Cfg)),
|
?assertMatch(ok, checkpoint(ConnRef, Cfg)),
|
||||||
?assertMatch({ok, <<"apple">>},
|
?assertMatch({ok, <<"apple">>},
|
||||||
session_get(SRef, "table:test", <<"a">>))
|
get(ConnRef, "table:test", <<"a">>))
|
||||||
end},
|
end},
|
||||||
{"session salvage",
|
{"session salvage",
|
||||||
fun() ->
|
fun() ->
|
||||||
ok = session_salvage(SRef, "table:test"),
|
ok = salvage(ConnRef, "table:test"),
|
||||||
{ok, <<"apple">>} = session_get(SRef, "table:test", <<"a">>)
|
{ok, <<"apple">>} = get(ConnRef, "table:test", <<"a">>)
|
||||||
end},
|
end},
|
||||||
{"session upgrade",
|
{"session upgrade",
|
||||||
fun() ->
|
fun() ->
|
||||||
?assertMatch(ok, session_upgrade(SRef, "table:test")),
|
?assertMatch(ok, upgrade(ConnRef, "table:test")),
|
||||||
?assertMatch({ok, <<"apple">>},
|
?assertMatch({ok, <<"apple">>},
|
||||||
session_get(SRef, "table:test", <<"a">>))
|
get(ConnRef, "table:test", <<"a">>))
|
||||||
end},
|
end},
|
||||||
{"session rename",
|
{"session rename",
|
||||||
fun() ->
|
fun() ->
|
||||||
?assertMatch(ok,
|
?assertMatch(ok,
|
||||||
session_rename(SRef, "table:test", "table:new")),
|
rename(ConnRef, "table:test", "table:new")),
|
||||||
?assertMatch({ok, <<"apple">>},
|
?assertMatch({ok, <<"apple">>},
|
||||||
session_get(SRef, "table:new", <<"a">>)),
|
get(ConnRef, "table:new", <<"a">>)),
|
||||||
?assertMatch(ok,
|
?assertMatch(ok,
|
||||||
session_rename(SRef, "table:new", "table:test")),
|
rename(ConnRef, "table:new", "table:test")),
|
||||||
?assertMatch({ok, <<"apple">>},
|
?assertMatch({ok, <<"apple">>},
|
||||||
session_get(SRef, "table:test", <<"a">>))
|
get(ConnRef, "table:test", <<"a">>))
|
||||||
end},
|
end},
|
||||||
{"session truncate",
|
{"session truncate",
|
||||||
fun() ->
|
fun() ->
|
||||||
?assertMatch(ok, session_truncate(SRef, "table:test")),
|
?assertMatch(ok, truncate(ConnRef, "table:test")),
|
||||||
?assertMatch(not_found, session_get(SRef, "table:test", <<"a">>))
|
?assertMatch(not_found, get(ConnRef, "table:test", <<"a">>))
|
||||||
end}]}
|
end}]}
|
||||||
end}.
|
end}.
|
||||||
|
|
||||||
cursor_open_close_test() ->
|
cursor_open_close_test() ->
|
||||||
{ConnRef, SRef} = init_test_table(),
|
ConnRef = init_test_table(),
|
||||||
{ok, Cursor1} = cursor_open(SRef, "table:test"),
|
{ok, Cursor1} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"a">>, <<"apple">>}, cursor_next(Cursor1)),
|
?assertMatch({ok, <<"a">>, <<"apple">>}, cursor_next(Cursor1)),
|
||||||
?assertMatch(ok, cursor_close(Cursor1)),
|
?assertMatch(ok, cursor_close(Cursor1)),
|
||||||
{ok, Cursor2} = cursor_open(SRef, "table:test"),
|
{ok, Cursor2} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"g">>, <<"gooseberry">>}, cursor_prev(Cursor2)),
|
?assertMatch({ok, <<"g">>, <<"gooseberry">>}, cursor_prev(Cursor2)),
|
||||||
?assertMatch(ok, cursor_close(Cursor2)),
|
?assertMatch(ok, cursor_close(Cursor2)),
|
||||||
stop_test_table({ConnRef, SRef}).
|
stop_test_table(ConnRef).
|
||||||
|
|
||||||
various_cursor_test_() ->
|
various_cursor_test_() ->
|
||||||
{setup,
|
{setup,
|
||||||
fun init_test_table/0,
|
fun init_test_table/0,
|
||||||
fun stop_test_table/1,
|
fun stop_test_table/1,
|
||||||
fun({_, SRef}) ->
|
fun(ConnRef) ->
|
||||||
{inorder,
|
{inorder,
|
||||||
[{"move a cursor back and forth, getting key",
|
[{"move a cursor back and forth, getting key",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"a">>}, cursor_next_key(Cursor)),
|
?assertMatch({ok, <<"a">>}, cursor_next_key(Cursor)),
|
||||||
?assertMatch({ok, <<"b">>}, cursor_next_key(Cursor)),
|
?assertMatch({ok, <<"b">>}, cursor_next_key(Cursor)),
|
||||||
?assertMatch({ok, <<"c">>}, cursor_next_key(Cursor)),
|
?assertMatch({ok, <<"c">>}, cursor_next_key(Cursor)),
|
||||||
|
@ -654,7 +632,7 @@ various_cursor_test_() ->
|
||||||
end},
|
end},
|
||||||
{"move a cursor back and forth, getting value",
|
{"move a cursor back and forth, getting value",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"apple">>}, cursor_next_value(Cursor)),
|
?assertMatch({ok, <<"apple">>}, cursor_next_value(Cursor)),
|
||||||
?assertMatch({ok, <<"banana">>}, cursor_next_value(Cursor)),
|
?assertMatch({ok, <<"banana">>}, cursor_next_value(Cursor)),
|
||||||
?assertMatch({ok, <<"cherry">>}, cursor_next_value(Cursor)),
|
?assertMatch({ok, <<"cherry">>}, cursor_next_value(Cursor)),
|
||||||
|
@ -667,7 +645,7 @@ various_cursor_test_() ->
|
||||||
end},
|
end},
|
||||||
{"move a cursor back and forth, getting key and value",
|
{"move a cursor back and forth, getting key and value",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"a">>, <<"apple">>}, cursor_next(Cursor)),
|
?assertMatch({ok, <<"a">>, <<"apple">>}, cursor_next(Cursor)),
|
||||||
?assertMatch({ok, <<"b">>, <<"banana">>}, cursor_next(Cursor)),
|
?assertMatch({ok, <<"b">>, <<"banana">>}, cursor_next(Cursor)),
|
||||||
?assertMatch({ok, <<"c">>, <<"cherry">>}, cursor_next(Cursor)),
|
?assertMatch({ok, <<"c">>, <<"cherry">>}, cursor_next(Cursor)),
|
||||||
|
@ -680,27 +658,27 @@ various_cursor_test_() ->
|
||||||
end},
|
end},
|
||||||
{"fold keys",
|
{"fold keys",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch([<<"g">>, <<"d">>, <<"c">>, <<"b">>, <<"a">>],
|
?assertMatch([<<"g">>, <<"d">>, <<"c">>, <<"b">>, <<"a">>],
|
||||||
fold_keys(Cursor, fun(Key, Acc) -> [Key | Acc] end, [])),
|
fold_keys(Cursor, fun(Key, Acc) -> [Key | Acc] end, [])),
|
||||||
?assertMatch(ok, cursor_close(Cursor))
|
?assertMatch(ok, cursor_close(Cursor))
|
||||||
end},
|
end},
|
||||||
{"search for an item",
|
{"search for an item",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"banana">>}, cursor_search(Cursor, <<"b">>)),
|
?assertMatch({ok, <<"banana">>}, cursor_search(Cursor, <<"b">>)),
|
||||||
?assertMatch(ok, cursor_close(Cursor))
|
?assertMatch(ok, cursor_close(Cursor))
|
||||||
end},
|
end},
|
||||||
{"range search for an item",
|
{"range search for an item",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"gooseberry">>},
|
?assertMatch({ok, <<"gooseberry">>},
|
||||||
cursor_search_near(Cursor, <<"z">>)),
|
cursor_search_near(Cursor, <<"z">>)),
|
||||||
?assertMatch(ok, cursor_close(Cursor))
|
?assertMatch(ok, cursor_close(Cursor))
|
||||||
end},
|
end},
|
||||||
{"check cursor reset",
|
{"check cursor reset",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch({ok, <<"apple">>}, cursor_next_value(Cursor)),
|
?assertMatch({ok, <<"apple">>}, cursor_next_value(Cursor)),
|
||||||
?assertMatch(ok, cursor_reset(Cursor)),
|
?assertMatch(ok, cursor_reset(Cursor)),
|
||||||
?assertMatch({ok, <<"apple">>}, cursor_next_value(Cursor)),
|
?assertMatch({ok, <<"apple">>}, cursor_next_value(Cursor)),
|
||||||
|
@ -708,7 +686,7 @@ various_cursor_test_() ->
|
||||||
end},
|
end},
|
||||||
{"insert/overwrite an item using a cursor",
|
{"insert/overwrite an item using a cursor",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch(ok,
|
?assertMatch(ok,
|
||||||
cursor_insert(Cursor, <<"h">>, <<"huckleberry">>)),
|
cursor_insert(Cursor, <<"h">>, <<"huckleberry">>)),
|
||||||
?assertMatch({ok, <<"huckleberry">>},
|
?assertMatch({ok, <<"huckleberry">>},
|
||||||
|
@ -719,28 +697,28 @@ various_cursor_test_() ->
|
||||||
cursor_search(Cursor, <<"g">>)),
|
cursor_search(Cursor, <<"g">>)),
|
||||||
?assertMatch(ok, cursor_close(Cursor)),
|
?assertMatch(ok, cursor_close(Cursor)),
|
||||||
?assertMatch({ok, <<"grapefruit">>},
|
?assertMatch({ok, <<"grapefruit">>},
|
||||||
session_get(SRef, "table:test", <<"g">>)),
|
get(ConnRef, "table:test", <<"g">>)),
|
||||||
?assertMatch({ok, <<"huckleberry">>},
|
?assertMatch({ok, <<"huckleberry">>},
|
||||||
session_get(SRef, "table:test", <<"h">>))
|
get(ConnRef, "table:test", <<"h">>))
|
||||||
end},
|
end},
|
||||||
{"update an item using a cursor",
|
{"update an item using a cursor",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch(ok,
|
?assertMatch(ok,
|
||||||
cursor_update(Cursor, <<"g">>, <<"goji berries">>)),
|
cursor_update(Cursor, <<"g">>, <<"goji berries">>)),
|
||||||
?assertMatch(not_found,
|
?assertMatch(not_found,
|
||||||
cursor_update(Cursor, <<"k">>, <<"kumquat">>)),
|
cursor_update(Cursor, <<"k">>, <<"kumquat">>)),
|
||||||
?assertMatch(ok, cursor_close(Cursor)),
|
?assertMatch(ok, cursor_close(Cursor)),
|
||||||
?assertMatch({ok, <<"goji berries">>},
|
?assertMatch({ok, <<"goji berries">>},
|
||||||
session_get(SRef, "table:test", <<"g">>))
|
get(ConnRef, "table:test", <<"g">>))
|
||||||
end},
|
end},
|
||||||
{"remove an item using a cursor",
|
{"remove an item using a cursor",
|
||||||
fun() ->
|
fun() ->
|
||||||
{ok, Cursor} = cursor_open(SRef, "table:test"),
|
{ok, Cursor} = cursor_open(ConnRef, "table:test"),
|
||||||
?assertMatch(ok, cursor_remove(Cursor, <<"g">>)),
|
?assertMatch(ok, cursor_remove(Cursor, <<"g">>)),
|
||||||
?assertMatch(not_found, cursor_remove(Cursor, <<"l">>)),
|
?assertMatch(not_found, cursor_remove(Cursor, <<"l">>)),
|
||||||
?assertMatch(ok, cursor_close(Cursor)),
|
?assertMatch(ok, cursor_close(Cursor)),
|
||||||
?assertMatch(not_found, session_get(SRef, "table:test", <<"g">>))
|
?assertMatch(not_found, get(ConnRef, "table:test", <<"g">>))
|
||||||
end}]}
|
end}]}
|
||||||
end}.
|
end}.
|
||||||
|
|
||||||
|
@ -758,13 +736,13 @@ values() ->
|
||||||
ops(Keys, Values) ->
|
ops(Keys, Values) ->
|
||||||
{oneof([put, delete]), oneof(Keys), oneof(Values)}.
|
{oneof([put, delete]), oneof(Keys), oneof(Values)}.
|
||||||
|
|
||||||
apply_kv_ops([], _SRef, _Tbl, Acc0) ->
|
apply_kv_ops([], _ConnRef, _Tbl, Acc0) ->
|
||||||
Acc0;
|
Acc0;
|
||||||
apply_kv_ops([{put, K, V} | Rest], SRef, Tbl, Acc0) ->
|
apply_kv_ops([{put, K, V} | Rest], ConnRef, Tbl, Acc0) ->
|
||||||
ok = wterl:session_put(SRef, Tbl, K, V),
|
ok = wterl:put(ConnRef, Tbl, K, V),
|
||||||
apply_kv_ops(Rest, SRef, Tbl, orddict:store(K, V, Acc0));
|
apply_kv_ops(Rest, ConnRef, Tbl, orddict:store(K, V, Acc0));
|
||||||
apply_kv_ops([{delete, K, _} | Rest], SRef, Tbl, Acc0) ->
|
apply_kv_ops([{delete, K, _} | Rest], ConnRef, Tbl, Acc0) ->
|
||||||
ok = case wterl:session_delete(SRef, Tbl, K) of
|
ok = case wterl:delete(ConnRef, Tbl, K) of
|
||||||
ok ->
|
ok ->
|
||||||
ok;
|
ok;
|
||||||
not_found ->
|
not_found ->
|
||||||
|
@ -772,7 +750,7 @@ apply_kv_ops([{delete, K, _} | Rest], SRef, Tbl, Acc0) ->
|
||||||
Else ->
|
Else ->
|
||||||
Else
|
Else
|
||||||
end,
|
end,
|
||||||
apply_kv_ops(Rest, SRef, Tbl, orddict:store(K, deleted, Acc0)).
|
apply_kv_ops(Rest, ConnRef, Tbl, orddict:store(K, deleted, Acc0)).
|
||||||
|
|
||||||
prop_put_delete() ->
|
prop_put_delete() ->
|
||||||
?LET({Keys, Values}, {keys(), values()},
|
?LET({Keys, Values}, {keys(), values()},
|
||||||
|
@ -786,21 +764,19 @@ prop_put_delete() ->
|
||||||
ok = filelib:ensure_dir(filename:join(DataDir, "x")),
|
ok = filelib:ensure_dir(filename:join(DataDir, "x")),
|
||||||
Cfg = wterl:config_to_bin([{create,true}]),
|
Cfg = wterl:config_to_bin([{create,true}]),
|
||||||
{ok, Conn} = wterl:connection_open(DataDir, Cfg),
|
{ok, Conn} = wterl:connection_open(DataDir, Cfg),
|
||||||
{ok, SRef} = wterl:session_open(Conn),
|
|
||||||
try
|
try
|
||||||
wterl:session_create(SRef, Table),
|
wterl:create(ConnRef, Table),
|
||||||
Model = apply_kv_ops(Ops, SRef, Table, []),
|
Model = apply_kv_ops(Ops, ConnRef, Table, []),
|
||||||
|
|
||||||
%% Validate that all deleted values return not_found
|
%% Validate that all deleted values return not_found
|
||||||
F = fun({K, deleted}) ->
|
F = fun({K, deleted}) ->
|
||||||
?assertEqual(not_found, wterl:session_get(SRef, Table, K));
|
?assertEqual(not_found, wterl:get(ConnRef, Table, K));
|
||||||
({K, V}) ->
|
({K, V}) ->
|
||||||
?assertEqual({ok, V}, wterl:session_get(SRef, Table, K))
|
?assertEqual({ok, V}, wterl:get(ConnRef, Table, K))
|
||||||
end,
|
end,
|
||||||
lists:map(F, Model),
|
lists:map(F, Model),
|
||||||
true
|
true
|
||||||
after
|
after
|
||||||
wterl:session_close(SRef),
|
|
||||||
wterl:connection_close(Conn)
|
wterl:connection_close(Conn)
|
||||||
end
|
end
|
||||||
end)).
|
end)).
|
||||||
|
|
9
update-version.sh
Executable file
9
update-version.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh -
|
||||||
|
|
||||||
|
wterl=`git log -n 1 --pretty=format:"%H"`
|
||||||
|
wiredtiger0=`(cd c_src/wiredtiger && git log -n 1 --pretty=format:"%H")`
|
||||||
|
wiredtiger=`echo $wiredtiger0 | awk '{print $2}'`
|
||||||
|
|
||||||
|
echo $wterl
|
||||||
|
echo $wiredtiger
|
||||||
|
|
Loading…
Reference in a new issue