This is a different approach from async2, instead of a single queue

and many workers this has a queue per scheduler and a few workers per
queue.
This commit is contained in:
Gregory Burd 2013-04-14 08:44:54 -04:00
parent 456129e7f3
commit 8fe9dc9bad
5 changed files with 242 additions and 223 deletions

View file

@ -28,7 +28,7 @@ extern "C" {
#include "queue.h" #include "queue.h"
#define ASYNC_NIF_MAX_WORKERS 32 #define ASYNC_NIF_MAX_WORKERS 128
struct async_nif_req_entry { struct async_nif_req_entry {
ERL_NIF_TERM ref, *argv; ERL_NIF_TERM ref, *argv;
@ -40,25 +40,27 @@ struct async_nif_req_entry {
STAILQ_ENTRY(async_nif_req_entry) entries; STAILQ_ENTRY(async_nif_req_entry) entries;
}; };
struct async_nif_work_queue {
ErlNifMutex *reqs_mutex;
ErlNifCond *reqs_cnd;
STAILQ_HEAD(reqs, async_nif_req_entry) reqs;
};
struct async_nif_worker_entry { struct async_nif_worker_entry {
ErlNifTid tid; ErlNifTid tid;
LIST_ENTRY(async_nif_worker_entry) entries; unsigned int worker_id;
struct async_nif_state *async_nif;
struct async_nif_work_queue *q;
}; };
struct async_nif_state { struct async_nif_state {
volatile unsigned int req_count; unsigned int req_count;
volatile unsigned int shutdown; unsigned int shutdown;
ErlNifMutex *req_mutex;
ErlNifCond *cnd;
STAILQ_HEAD(reqs, async_nif_req_entry) reqs;
unsigned int num_workers; unsigned int num_workers;
struct async_nif_worker_entry worker_entries[ASYNC_NIF_MAX_WORKERS]; struct async_nif_worker_entry worker_entries[ASYNC_NIF_MAX_WORKERS];
}; unsigned int num_queues;
unsigned int next_q;
struct async_nif_worker_info { struct async_nif_work_queue queues[ASYNC_NIF_MAX_WORKERS];
struct async_nif_state *async_nif;
struct async_nif_worker_entry *worker;
unsigned int worker_id;
}; };
#define ASYNC_NIF_DECL(decl, frame, pre_block, work_block, post_block) \ #define ASYNC_NIF_DECL(decl, frame, pre_block, work_block, post_block) \
@ -72,10 +74,12 @@ struct async_nif_worker_info {
struct decl ## _args *args = &on_stack_args; \ struct decl ## _args *args = &on_stack_args; \
struct decl ## _args *copy_of_args; \ struct decl ## _args *copy_of_args; \
struct async_nif_req_entry *req = NULL; \ struct async_nif_req_entry *req = NULL; \
int scheduler_id = 0; \
ErlNifEnv *new_env = NULL; \ ErlNifEnv *new_env = NULL; \
/* 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; \ /* argv[1] is the current Erlang (scheduler_id - 1) */ \
argc--; \ const ERL_NIF_TERM *argv = argv_in + 2; \
argc -= 2; \
struct async_nif_state *async_nif = (struct async_nif_state*)enif_priv_data(env); \ struct async_nif_state *async_nif = (struct async_nif_state*)enif_priv_data(env); \
if (async_nif->shutdown) \ if (async_nif->shutdown) \
return enif_make_tuple2(env, enif_make_atom(env, "error"), \ return enif_make_tuple2(env, enif_make_atom(env, "error"), \
@ -107,7 +111,8 @@ struct async_nif_worker_info {
req->args = (void*)copy_of_args; \ req->args = (void*)copy_of_args; \
req->fn_work = (void (*)(ErlNifEnv *, ERL_NIF_TERM, ErlNifPid*, unsigned int, void *))fn_work_ ## decl ; \ req->fn_work = (void (*)(ErlNifEnv *, ERL_NIF_TERM, ErlNifPid*, unsigned int, void *))fn_work_ ## decl ; \
req->fn_post = (void (*)(void *))fn_post_ ## decl; \ req->fn_post = (void (*)(void *))fn_post_ ## decl; \
return async_nif_enqueue_req(async_nif, req); \ enif_get_int(env, argv_in[1], &scheduler_id); \
return async_nif_enqueue_req(async_nif, req, scheduler_id); \
} }
#define ASYNC_NIF_INIT(name) \ #define ASYNC_NIF_INIT(name) \
@ -143,7 +148,7 @@ struct async_nif_worker_info {
#define ASYNC_NIF_REPLY(msg) enif_send(NULL, pid, env, enif_make_tuple2(env, ref, msg)) #define ASYNC_NIF_REPLY(msg) enif_send(NULL, pid, env, enif_make_tuple2(env, ref, msg))
static ERL_NIF_TERM static ERL_NIF_TERM
async_nif_enqueue_req(struct async_nif_state* async_nif, struct async_nif_req_entry *req) async_nif_enqueue_req(struct async_nif_state* async_nif, struct async_nif_req_entry *req, int scheduler_id)
{ {
/* If we're shutting down return an error term and ignore the request. */ /* If we're shutting down return an error term and ignore the request. */
if (async_nif->shutdown) { if (async_nif->shutdown) {
@ -151,51 +156,62 @@ async_nif_enqueue_req(struct async_nif_state* async_nif, struct async_nif_req_en
enif_make_atom(req->env, "shutdown")); enif_make_atom(req->env, "shutdown"));
} }
/* We manage one request queue per-scheduler thread running in the Erlang VM.
Each request is placed onto the queue based on which schdeuler thread
was processing the request. Work queues are balanced only if requests
arrive from a sufficiently random distribution of Erlang scheduler
threads. */
unsigned int qid = async_nif->next_q; // Keep a local to avoid the race.
struct async_nif_work_queue *q = &async_nif->queues[qid];
async_nif->next_q = (qid + 1) % async_nif->num_queues;
/* Otherwise, add the request to the work queue. */ /* Otherwise, add the request to the work queue. */
enif_mutex_lock(async_nif->req_mutex); enif_mutex_lock(q->reqs_mutex);
STAILQ_INSERT_TAIL(&async_nif->reqs, req, entries); STAILQ_INSERT_TAIL(&q->reqs, req, entries);
async_nif->req_count++; async_nif->req_count++;
//fprintf(stderr, "enqueued %d (%d)\r\n", qid, async_nif->req_count); fflush(stderr);
/* Build the term before releasing the lock so as not to race on the use of /* Build the term before releasing the lock so as not to race on the use of
the req pointer. */ the req pointer. */
ERL_NIF_TERM reply = enif_make_tuple2(req->env, enif_make_atom(req->env, "ok"), ERL_NIF_TERM reply = enif_make_tuple2(req->env, enif_make_atom(req->env, "ok"),
enif_make_tuple2(req->env, enif_make_atom(req->env, "enqueued"), enif_make_tuple2(req->env, enif_make_atom(req->env, "enqueued"),
enif_make_int(req->env, async_nif->req_count))); enif_make_int(req->env, async_nif->req_count)));
enif_mutex_unlock(async_nif->req_mutex); enif_mutex_unlock(q->reqs_mutex);
enif_cond_broadcast(async_nif->cnd); enif_cond_broadcast(q->reqs_cnd);
return reply; return reply;
} }
static void * static void *
async_nif_worker_fn(void *arg) async_nif_worker_fn(void *arg)
{ {
struct async_nif_worker_info *wi = (struct async_nif_worker_info *)arg; struct async_nif_worker_entry *we = (struct async_nif_worker_entry *)arg;
struct async_nif_state *async_nif = wi->async_nif; unsigned int worker_id = we->worker_id;
unsigned int worker_id = wi->worker_id; struct async_nif_state *async_nif = we->async_nif;
struct async_nif_work_queue *q = we->q;
enif_free(arg); // Allocated when starting the thread, now no longer needed.
for(;;) { for(;;) {
struct async_nif_req_entry *req = NULL; struct async_nif_req_entry *req = NULL;
/* 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(q->reqs_mutex);
check_again_for_work: check_again_for_work:
if (async_nif->shutdown) { if (async_nif->shutdown) {
enif_mutex_unlock(async_nif->req_mutex); enif_mutex_unlock(q->reqs_mutex);
break; break;
} }
if ((req = STAILQ_FIRST(&async_nif->reqs)) == NULL) { if ((req = STAILQ_FIRST(&q->reqs)) == NULL) {
/* Queue is empty, wait for work */ /* Queue is empty, wait for work */
enif_cond_wait(async_nif->cnd, async_nif->req_mutex); enif_cond_wait(q->reqs_cnd, q->reqs_mutex);
goto check_again_for_work; goto check_again_for_work;
} else { } else {
/* `req` is our work request and we hold the req_mutex lock. */ /* At this point, `req` is ours to execute and we hold the reqs_mutex lock. */
do { do {
/* Take the request off the queue. */ /* Take the request off the queue. */
STAILQ_REMOVE(&async_nif->reqs, req, async_nif_req_entry, entries); //fprintf(stderr, "worker %d queue %d performing req (%d)\r\n", worker_id, (worker_id % async_nif->num_queues), async_nif->req_count); fflush(stderr);
STAILQ_REMOVE(&q->reqs, req, async_nif_req_entry, entries);
async_nif->req_count--; async_nif->req_count--;
enif_mutex_unlock(async_nif->req_mutex); enif_mutex_unlock(q->reqs_mutex);
/* Finally, do the work. */ /* Finally, do the work. */
req->fn_work(req->env, req->ref, &req->pid, worker_id, req->args); req->fn_work(req->env, req->ref, &req->pid, worker_id, req->args);
@ -204,14 +220,14 @@ async_nif_worker_fn(void *arg)
enif_free_env(req->env); enif_free_env(req->env);
enif_free(req); enif_free(req);
/* Review the work queue, start more worker threads if they are needed. */
// TODO: if queue_depth > last_depth && num_workers < MAX, start one up
/* Continue working if more requests are in the queue, otherwise wait /* Continue working if more requests are in the queue, otherwise wait
for new work to arrive. */ for new work to arrive. */
enif_mutex_lock(async_nif->req_mutex); if (STAILQ_EMPTY(&q->reqs)) {
if ((req = STAILQ_FIRST(&async_nif->reqs)) == NULL) { req = NULL;
enif_mutex_unlock(async_nif->req_mutex); } else {
enif_cond_broadcast(q->reqs_cnd);
enif_mutex_lock(q->reqs_mutex);
req = STAILQ_FIRST(&q->reqs);
} }
} while(req); } while(req);
@ -228,10 +244,13 @@ async_nif_unload(ErlNifEnv *env)
struct async_nif_state *async_nif = (struct async_nif_state*)enif_priv_data(env); struct async_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);
async_nif->shutdown = 1; async_nif->shutdown = 1;
enif_cond_broadcast(async_nif->cnd);
enif_mutex_unlock(async_nif->req_mutex); /* Wake up any waiting worker threads. */
for (i = 0; i < async_nif->num_queues; i++) {
struct async_nif_work_queue *q = &async_nif->queues[i];
enif_cond_broadcast(q->reqs_cnd);
}
/* Join for the now exiting worker threads. */ /* Join for the now exiting worker threads. */
for (i = 0; i < async_nif->num_workers; ++i) { for (i = 0; i < async_nif->num_workers; ++i) {
@ -239,30 +258,26 @@ async_nif_unload(ErlNifEnv *env)
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. /* Cleanup requests, mutexes and conditions in each work queue. */
Patch things up, and carry on. */ for (i = 0; i < async_nif->num_queues; i++) {
enif_mutex_lock(async_nif->req_mutex); struct async_nif_work_queue *q = &async_nif->queues[i];
enif_mutex_destroy(q->reqs_mutex);
enif_cond_destroy(q->reqs_cnd);
/* 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, &q->reqs, entries) {
STAILQ_REMOVE(&async_nif->reqs, STAILQ_LAST(&async_nif->reqs, async_nif_req_entry, entries), STAILQ_REMOVE(&q->reqs, STAILQ_LAST(&q->reqs, async_nif_req_entry, entries),
async_nif_req_entry, entries); async_nif_req_entry, entries);
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")));
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);
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_mutex_destroy(async_nif->req_mutex);
async_nif->req_mutex = NULL;
bzero(async_nif, sizeof(struct async_nif_state)); bzero(async_nif, sizeof(struct async_nif_state));
enif_free(async_nif); enif_free(async_nif);
} }
@ -271,7 +286,7 @@ static void *
async_nif_load(void) async_nif_load(void)
{ {
static int has_init = 0; static int has_init = 0;
int i, num_schedulers; unsigned int i, j;
ErlNifSysInfo info; ErlNifSysInfo info;
struct async_nif_state *async_nif; struct async_nif_state *async_nif;
@ -281,53 +296,59 @@ async_nif_load(void)
/* Find out how many schedulers there are. */ /* Find out how many schedulers there are. */
enif_system_info(&info, sizeof(ErlNifSysInfo)); enif_system_info(&info, sizeof(ErlNifSysInfo));
num_schedulers = info.scheduler_threads;
/* Init our portion of priv_data's module-specific state. */ /* Init our portion of priv_data's module-specific state. */
async_nif = enif_alloc(sizeof(struct async_nif_state)); async_nif = enif_alloc(sizeof(struct async_nif_state));
if (!async_nif) if (!async_nif)
return NULL; return NULL;
bzero(async_nif, sizeof(struct async_nif_state)); bzero(async_nif, sizeof(struct async_nif_state));
STAILQ_INIT(&(async_nif->reqs));
async_nif->num_queues = info.scheduler_threads;
async_nif->next_q = 0;
async_nif->req_count = 0;
async_nif->shutdown = 0; async_nif->shutdown = 0;
async_nif->req_mutex = enif_mutex_create(NULL); for (i = 0; i < async_nif->num_queues; i++) {
async_nif->cnd = enif_cond_create(NULL); struct async_nif_work_queue *q = &async_nif->queues[i];
STAILQ_INIT(&q->reqs);
/* Setup the requests management. */ q->reqs_mutex = enif_mutex_create(NULL);
async_nif->req_count = 0; q->reqs_cnd = enif_cond_create(NULL);
}
/* Setup the thread pool management. */ /* Setup the thread pool management. */
bzero(async_nif->worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS); bzero(async_nif->worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS);
/* Start the minimum of max workers allowed or number of scheduler threads running. */ /* Start the worker threads. */
unsigned int num_worker_threads = ASYNC_NIF_MAX_WORKERS; //unsigned int num_workers = ASYNC_NIF_MAX_WORKERS - (ASYNC_NIF_MAX_WORKERS % async_nif->num_queues);
if (num_schedulers < ASYNC_NIF_MAX_WORKERS) unsigned int num_workers = async_nif->num_queues;
num_worker_threads = num_schedulers; //unsigned int allocation = 1;
if (num_worker_threads < 1) //if (num_workers > async_nif->num_queues) {
num_worker_threads = 1; // allocation = num_workers / async_nif->num_queues;
num_worker_threads = ASYNC_NIF_MAX_WORKERS; // TODO: make this dynamic at some point //}
for (i = 0; i < num_worker_threads; i++) { for (i = 0; i < num_workers; i++) {
struct async_nif_worker_info *wi; struct async_nif_worker_entry *we = &async_nif->worker_entries[i];
wi = enif_alloc(sizeof(struct async_nif_worker_info)); // TODO: check we->async_nif = async_nif;
bzero(wi, sizeof(struct async_nif_worker_info)); we->worker_id = i;
wi->async_nif = async_nif; we->q = &async_nif->queues[i % async_nif->num_queues];
wi->worker = &async_nif->worker_entries[i]; //fprintf(stderr, "%d:%d:%d | allocating worker_id %d to queue %d\r\n", num_workers, async_nif->num_queues, allocation, i, i % async_nif->num_queues); fflush(stderr);
wi->worker_id = i;
if (enif_thread_create(NULL, &async_nif->worker_entries[i].tid, if (enif_thread_create(NULL, &async_nif->worker_entries[i].tid,
&async_nif_worker_fn, (void*)wi, NULL) != 0) { &async_nif_worker_fn, (void*)we, NULL) != 0) {
async_nif->shutdown = 1; async_nif->shutdown = 1;
enif_cond_broadcast(async_nif->cnd);
for (j = 0; j < async_nif->num_queues; j++) {
struct async_nif_work_queue *q = &async_nif->queues[j];
enif_cond_broadcast(q->reqs_cnd);
enif_mutex_destroy(q->reqs_mutex);
enif_cond_destroy(q->reqs_cnd);
}
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);
} }
bzero(async_nif->worker_entries, sizeof(struct async_nif_worker_entry) * ASYNC_NIF_MAX_WORKERS); 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_mutex_destroy(async_nif->req_mutex);
async_nif->req_mutex = NULL;
return NULL; return NULL;
} }
} }

View file

@ -30,17 +30,13 @@
#ifdef DEBUG #ifdef DEBUG
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
void debugf(const char *fmt, ...) #define dprint(s, ...) do { \
{ fprintf(stderr, s, ##__VA_ARGS__); \
va_list ap; fprintf(stderr, "\r\n"); \
va_start(ap, fmt); fflush(stderr); \
vfprintf(stderr, fmt, ap); } while(0);
fprintf(stderr, "\r\n");
fflush(stderr);
va_end(ap);
}
#else #else
# define debugf(X, ...) {} # define dprint(s, ...) {}
#endif #endif
static ErlNifResourceType *wterl_conn_RESOURCE; static ErlNifResourceType *wterl_conn_RESOURCE;
@ -282,7 +278,7 @@ ASYNC_NIF_DECL(
ASYNC_NIF_REPLY(enif_make_badarg(env)); ASYNC_NIF_REPLY(enif_make_badarg(env));
return; return;
} }
//debugf("c: %d // %s\ns: %d // %s", config.size, (char *)config.data, (char *)session_config.data, session_config.size); //dprint("c: %d // %s\ns: %d // %s", config.size, (char *)config.data, (char *)session_config.data, session_config.size);
int rc = wiredtiger_open(args->homedir, NULL, config.data[0] != 0 ? (const char*)config.data : NULL, &conn); int rc = wiredtiger_open(args->homedir, NULL, config.data[0] != 0 ? (const char*)config.data : NULL, &conn);
if (rc == 0) { if (rc == 0) {
WterlConnHandle *conn_handle = enif_alloc_resource(wterl_conn_RESOURCE, sizeof(WterlConnHandle)); WterlConnHandle *conn_handle = enif_alloc_resource(wterl_conn_RESOURCE, sizeof(WterlConnHandle));
@ -1203,7 +1199,7 @@ ASYNC_NIF_DECL(
/* We create a separate session here to ensure that operations are thread safe. */ /* We create a separate session here to ensure that operations are thread safe. */
WT_CONNECTION *conn = args->conn_handle->conn; WT_CONNECTION *conn = args->conn_handle->conn;
WT_SESSION *session = NULL; WT_SESSION *session = NULL;
//debugf("cursor open: %s", (char *)args->conn_handle->session_config); //dprint("cursor open: %s", (char *)args->conn_handle->session_config);
int rc = conn->open_session(conn, NULL, args->conn_handle->session_config, &session); int rc = conn->open_session(conn, NULL, args->conn_handle->session_config, &session);
if (rc != 0) { if (rc != 0) {
ASYNC_NIF_REPLY(__strerror_term(env, rc)); ASYNC_NIF_REPLY(__strerror_term(env, rc));
@ -1823,36 +1819,36 @@ on_upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM
static ErlNifFunc nif_funcs[] = static ErlNifFunc nif_funcs[] =
{ {
{"checkpoint_nif", 3, wterl_checkpoint}, {"checkpoint_nif", 4, wterl_checkpoint},
{"conn_close_nif", 2, wterl_conn_close}, {"conn_close_nif", 3, wterl_conn_close},
{"conn_open_nif", 4, wterl_conn_open}, {"conn_open_nif", 5, wterl_conn_open},
{"create_nif", 4, wterl_create}, {"create_nif", 5, wterl_create},
{"delete_nif", 4, wterl_delete}, {"delete_nif", 5, wterl_delete},
{"drop_nif", 4, wterl_drop}, {"drop_nif", 5, wterl_drop},
{"get_nif", 4, wterl_get}, {"get_nif", 5, wterl_get},
{"put_nif", 5, wterl_put}, {"put_nif", 6, wterl_put},
{"rename_nif", 5, wterl_rename}, {"rename_nif", 6, wterl_rename},
{"salvage_nif", 4, wterl_salvage}, {"salvage_nif", 5, wterl_salvage},
// TODO: {"txn_begin", 3, wterl_txn_begin}, // TODO: {"txn_begin", 4, wterl_txn_begin},
// TODO: {"txn_commit", 3, wterl_txn_commit}, // TODO: {"txn_commit", 4, wterl_txn_commit},
// TODO: {"txn_abort", 3, wterl_txn_abort}, // TODO: {"txn_abort", 4, wterl_txn_abort},
{"truncate_nif", 6, wterl_truncate}, {"truncate_nif", 7, wterl_truncate},
{"upgrade_nif", 4, wterl_upgrade}, {"upgrade_nif", 5, wterl_upgrade},
{"verify_nif", 4, wterl_verify}, {"verify_nif", 5, wterl_verify},
{"cursor_close_nif", 2, wterl_cursor_close}, {"cursor_close_nif", 3, wterl_cursor_close},
{"cursor_insert_nif", 4, wterl_cursor_insert}, {"cursor_insert_nif", 5, wterl_cursor_insert},
{"cursor_next_key_nif", 2, wterl_cursor_next_key}, {"cursor_next_key_nif", 3, wterl_cursor_next_key},
{"cursor_next_nif", 2, wterl_cursor_next}, {"cursor_next_nif", 3, wterl_cursor_next},
{"cursor_next_value_nif", 2, wterl_cursor_next_value}, {"cursor_next_value_nif", 3, wterl_cursor_next_value},
{"cursor_open_nif", 4, wterl_cursor_open}, {"cursor_open_nif", 5, wterl_cursor_open},
{"cursor_prev_key_nif", 2, wterl_cursor_prev_key}, {"cursor_prev_key_nif", 3, wterl_cursor_prev_key},
{"cursor_prev_nif", 2, wterl_cursor_prev}, {"cursor_prev_nif", 3, wterl_cursor_prev},
{"cursor_prev_value_nif", 2, wterl_cursor_prev_value}, {"cursor_prev_value_nif", 3, wterl_cursor_prev_value},
{"cursor_remove_nif", 3, wterl_cursor_remove}, {"cursor_remove_nif", 4, wterl_cursor_remove},
{"cursor_reset_nif", 2, wterl_cursor_reset}, {"cursor_reset_nif", 3, wterl_cursor_reset},
{"cursor_search_near_nif", 3, wterl_cursor_search_near}, {"cursor_search_near_nif", 4, wterl_cursor_search_near},
{"cursor_search_nif", 3, wterl_cursor_search}, {"cursor_search_nif", 4, wterl_cursor_search},
{"cursor_update_nif", 4, wterl_cursor_update}, {"cursor_update_nif", 5, wterl_cursor_update},
}; };
ERL_NIF_INIT(wterl, nif_funcs, &on_load, &on_reload, &on_upgrade, &on_unload); ERL_NIF_INIT(wterl, nif_funcs, &on_load, &on_reload, &on_upgrade, &on_unload);

View file

@ -24,7 +24,8 @@
-define(ASYNC_NIF_CALL(Fun, Args), -define(ASYNC_NIF_CALL(Fun, Args),
begin begin
NIFRef = erlang:make_ref(), NIFRef = erlang:make_ref(),
case erlang:apply(Fun, [NIFRef|Args]) of Id = erlang:system_info(scheduler_id) - 1,
case erlang:apply(Fun, [NIFRef|[Id|Args]]) of
{ok, {enqueued, _QDepth}} -> {ok, {enqueued, _QDepth}} ->
receive receive
{NIFRef, {error, shutdown}=Error} -> {NIFRef, {error, shutdown}=Error} ->

View file

@ -321,13 +321,14 @@ callback(_Ref, _Msg, State) ->
%% =================================================================== %% ===================================================================
%% @private %% @private
max_sessions(Config) -> max_sessions(_Config) -> % TODO:
RingSize = 8192.
case app_helper:get_prop_or_env(ring_creation_size, Config, riak_core) of %% RingSize =
undefined -> 1024; %% case app_helper:get_prop_or_env(ring_creation_size, Config, riak_core) of
Size -> Size %% undefined -> 1024;
end, %% Size -> Size
2 * (RingSize * erlang:system_info(schedulers)). %% end,
%% 2 * (RingSize * erlang:system_info(schedulers)).
%% @private %% @private
establish_utility_cursors(Connection, Table) -> establish_utility_cursors(Connection, Table) ->

View file

@ -115,20 +115,20 @@ connection_open(HomeDir, ConnectionConfig, SessionConfig) ->
-spec conn_open(string(), config_list(), config_list()) -> {ok, connection()} | {error, term()}. -spec conn_open(string(), config_list(), config_list()) -> {ok, connection()} | {error, term()}.
conn_open(HomeDir, ConnectionConfig, SessionConfig) -> conn_open(HomeDir, ConnectionConfig, SessionConfig) ->
?ASYNC_NIF_CALL(fun conn_open_nif/4, [HomeDir, ?ASYNC_NIF_CALL(fun conn_open_nif/5, [HomeDir,
config_to_bin(ConnectionConfig), config_to_bin(ConnectionConfig),
config_to_bin(SessionConfig)]). config_to_bin(SessionConfig)]).
-spec conn_open_nif(reference(), string(), config(), config()) -> {ok, connection()} | {error, term()}. -spec conn_open_nif(reference(), non_neg_integer(), string(), config(), config()) -> {ok, connection()} | {error, term()}.
conn_open_nif(_AsyncRef, _HomeDir, _ConnectionConfig, _SessionConfig) -> conn_open_nif(_AsyncRef, _SchedulerId, _HomeDir, _ConnectionConfig, _SessionConfig) ->
?nif_stub. ?nif_stub.
-spec connection_close(connection()) -> ok | {error, term()}. -spec connection_close(connection()) -> ok | {error, term()}.
connection_close(ConnRef) -> connection_close(ConnRef) ->
?ASYNC_NIF_CALL(fun conn_close_nif/2, [ConnRef]). ?ASYNC_NIF_CALL(fun conn_close_nif/3, [ConnRef]).
-spec conn_close_nif(reference(), connection()) -> ok | {error, term()}. -spec conn_close_nif(reference(), non_neg_integer(), connection()) -> ok | {error, term()}.
conn_close_nif(_AsyncRef, _ConnRef) -> conn_close_nif(_AsyncRef, _SchedulerId, _ConnRef) ->
?nif_stub. ?nif_stub.
-spec create(connection(), string()) -> ok | {error, term()}. -spec create(connection(), string()) -> ok | {error, term()}.
@ -136,10 +136,10 @@ conn_close_nif(_AsyncRef, _ConnRef) ->
create(Ref, Name) -> create(Ref, Name) ->
create(Ref, Name, []). create(Ref, Name, []).
create(Ref, Name, Config) -> create(Ref, Name, Config) ->
?ASYNC_NIF_CALL(fun create_nif/4, [Ref, Name, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun create_nif/5, [Ref, Name, config_to_bin(Config)]).
-spec create_nif(reference(), connection(), string(), config()) -> ok | {error, term()}. -spec create_nif(reference(), non_neg_integer(), connection(), string(), config()) -> ok | {error, term()}.
create_nif(_AsyncNif, _Ref, _Name, _Config) -> create_nif(_AsyncNif, _SchedulerId, _Ref, _Name, _Config) ->
?nif_stub. ?nif_stub.
-spec drop(connection(), string()) -> ok | {error, term()}. -spec drop(connection(), string()) -> ok | {error, term()}.
@ -147,34 +147,34 @@ create_nif(_AsyncNif, _Ref, _Name, _Config) ->
drop(Ref, Name) -> drop(Ref, Name) ->
drop(Ref, Name, []). drop(Ref, Name, []).
drop(Ref, Name, Config) -> drop(Ref, Name, Config) ->
?ASYNC_NIF_CALL(fun drop_nif/4, [Ref, Name, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun drop_nif/5, [Ref, Name, config_to_bin(Config)]).
-spec drop_nif(reference(), connection(), string(), config()) -> ok | {error, term()}. -spec drop_nif(reference(), non_neg_integer(), connection(), string(), config()) -> ok | {error, term()}.
drop_nif(_AsyncRef, _Ref, _Name, _Config) -> drop_nif(_AsyncRef, _SchedulerId, _Ref, _Name, _Config) ->
?nif_stub. ?nif_stub.
-spec delete(connection(), string(), key()) -> ok | {error, term()}. -spec delete(connection(), string(), key()) -> ok | {error, term()}.
delete(Ref, Table, Key) -> delete(Ref, Table, Key) ->
?ASYNC_NIF_CALL(fun delete_nif/4, [Ref, Table, Key]). ?ASYNC_NIF_CALL(fun delete_nif/5, [Ref, Table, Key]).
-spec delete_nif(reference(), connection(), string(), key()) -> ok | {error, term()}. -spec delete_nif(reference(), non_neg_integer(), connection(), string(), key()) -> ok | {error, term()}.
delete_nif(_AsyncRef, _Ref, _Table, _Key) -> delete_nif(_AsyncRef, _SchedulerId, _Ref, _Table, _Key) ->
?nif_stub. ?nif_stub.
-spec get(connection(), string(), key()) -> {ok, value()} | not_found | {error, term()}. -spec get(connection(), string(), key()) -> {ok, value()} | not_found | {error, term()}.
get(Ref, Table, Key) -> get(Ref, Table, Key) ->
?ASYNC_NIF_CALL(fun get_nif/4, [Ref, Table, Key]). ?ASYNC_NIF_CALL(fun get_nif/5, [Ref, Table, Key]).
-spec get_nif(reference(), connection(), string(), key()) -> {ok, value()} | not_found | {error, term()}. -spec get_nif(reference(), non_neg_integer(), connection(), string(), key()) -> {ok, value()} | not_found | {error, term()}.
get_nif(_AsyncRef, _Ref, _Table, _Key) -> get_nif(_AsyncRef, _SchedulerId, _Ref, _Table, _Key) ->
?nif_stub. ?nif_stub.
-spec put(connection(), string(), key(), value()) -> ok | {error, term()}. -spec put(connection(), string(), key(), value()) -> ok | {error, term()}.
put(Ref, Table, Key, Value) -> put(Ref, Table, Key, Value) ->
?ASYNC_NIF_CALL(fun put_nif/5, [Ref, Table, Key, Value]). ?ASYNC_NIF_CALL(fun put_nif/6, [Ref, Table, Key, Value]).
-spec put_nif(reference(), connection(), string(), key(), value()) -> ok | {error, term()}. -spec put_nif(reference(), non_neg_integer(), connection(), string(), key(), value()) -> ok | {error, term()}.
put_nif(_AsyncRef, _Ref, _Table, _Key, _Value) -> put_nif(_AsyncRef, _SchedulerId, _Ref, _Table, _Key, _Value) ->
?nif_stub. ?nif_stub.
-spec rename(connection(), string(), string()) -> ok | {error, term()}. -spec rename(connection(), string(), string()) -> ok | {error, term()}.
@ -182,10 +182,10 @@ put_nif(_AsyncRef, _Ref, _Table, _Key, _Value) ->
rename(Ref, OldName, NewName) -> rename(Ref, OldName, NewName) ->
rename(Ref, OldName, NewName, []). rename(Ref, OldName, NewName, []).
rename(Ref, OldName, NewName, Config) -> rename(Ref, OldName, NewName, Config) ->
?ASYNC_NIF_CALL(fun rename_nif/5, [Ref, OldName, NewName, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun rename_nif/6, [Ref, OldName, NewName, config_to_bin(Config)]).
-spec rename_nif(reference(), connection(), string(), string(), config()) -> ok | {error, term()}. -spec rename_nif(reference(), non_neg_integer(), connection(), string(), string(), config()) -> ok | {error, term()}.
rename_nif(_AsyncRef, _Ref, _OldName, _NewName, _Config) -> rename_nif(_AsyncRef, _SchedulerId, _Ref, _OldName, _NewName, _Config) ->
?nif_stub. ?nif_stub.
-spec salvage(connection(), string()) -> ok | {error, term()}. -spec salvage(connection(), string()) -> ok | {error, term()}.
@ -193,10 +193,10 @@ rename_nif(_AsyncRef, _Ref, _OldName, _NewName, _Config) ->
salvage(Ref, Name) -> salvage(Ref, Name) ->
salvage(Ref, Name, []). salvage(Ref, Name, []).
salvage(Ref, Name, Config) -> salvage(Ref, Name, Config) ->
?ASYNC_NIF_CALL(fun salvage_nif/4, [Ref, Name, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun salvage_nif/5, [Ref, Name, config_to_bin(Config)]).
-spec salvage_nif(reference(), connection(), string(), config()) -> ok | {error, term()}. -spec salvage_nif(reference(), non_neg_integer(), connection(), string(), config()) -> ok | {error, term()}.
salvage_nif(_AsyncRef, _Ref, _Name, _Config) -> salvage_nif(_AsyncRef, _SchedulerId, _Ref, _Name, _Config) ->
?nif_stub. ?nif_stub.
-spec checkpoint(connection()) -> ok | {error, term()}. -spec checkpoint(connection()) -> ok | {error, term()}.
@ -204,10 +204,10 @@ salvage_nif(_AsyncRef, _Ref, _Name, _Config) ->
checkpoint(_Ref) -> checkpoint(_Ref) ->
checkpoint(_Ref, []). checkpoint(_Ref, []).
checkpoint(Ref, Config) -> checkpoint(Ref, Config) ->
?ASYNC_NIF_CALL(fun checkpoint_nif/3, [Ref, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun checkpoint_nif/4, [Ref, config_to_bin(Config)]).
-spec checkpoint_nif(reference(), connection(), config()) -> ok | {error, term()}. -spec checkpoint_nif(reference(), non_neg_integer(), connection(), config()) -> ok | {error, term()}.
checkpoint_nif(_AsyncRef, _Ref, _Config) -> checkpoint_nif(_AsyncRef, _SchedulerId, _Ref, _Config) ->
?nif_stub. ?nif_stub.
-spec truncate(connection(), string()) -> ok | {error, term()}. -spec truncate(connection(), string()) -> ok | {error, term()}.
@ -221,10 +221,10 @@ truncate(Ref, Name, Config) ->
truncate(Ref, Name, Start, Stop) -> truncate(Ref, Name, Start, Stop) ->
truncate(Ref, Name, Start, Stop, []). truncate(Ref, Name, Start, Stop, []).
truncate(Ref, Name, Start, Stop, Config) -> truncate(Ref, Name, Start, Stop, Config) ->
?ASYNC_NIF_CALL(fun truncate_nif/6, [Ref, Name, Start, Stop, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun truncate_nif/7, [Ref, Name, Start, Stop, config_to_bin(Config)]).
-spec truncate_nif(reference(), connection(), string(), cursor() | first, cursor() | last, config()) -> ok | {error, term()}. -spec truncate_nif(reference(), non_neg_integer(), connection(), string(), cursor() | first, cursor() | last, config()) -> ok | {error, term()}.
truncate_nif(_AsyncRef, _Ref, _Name, _Start, _Stop, _Config) -> truncate_nif(_AsyncRef, _SchedulerId, _Ref, _Name, _Start, _Stop, _Config) ->
?nif_stub. ?nif_stub.
-spec upgrade(connection(), string()) -> ok | {error, term()}. -spec upgrade(connection(), string()) -> ok | {error, term()}.
@ -232,10 +232,10 @@ truncate_nif(_AsyncRef, _Ref, _Name, _Start, _Stop, _Config) ->
upgrade(Ref, Name) -> upgrade(Ref, Name) ->
upgrade(Ref, Name, []). upgrade(Ref, Name, []).
upgrade(Ref, Name, Config) -> upgrade(Ref, Name, Config) ->
?ASYNC_NIF_CALL(fun upgrade_nif/4, [Ref, Name, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun upgrade_nif/5, [Ref, Name, config_to_bin(Config)]).
-spec upgrade_nif(reference(), connection(), string(), config()) -> ok | {error, term()}. -spec upgrade_nif(reference(), non_neg_integer(), connection(), string(), config()) -> ok | {error, term()}.
upgrade_nif(_AsyncRef, _Ref, _Name, _Config) -> upgrade_nif(_AsyncRef, _SchedulerId, _Ref, _Name, _Config) ->
?nif_stub. ?nif_stub.
-spec verify(connection(), string()) -> ok | {error, term()}. -spec verify(connection(), string()) -> ok | {error, term()}.
@ -243,10 +243,10 @@ upgrade_nif(_AsyncRef, _Ref, _Name, _Config) ->
verify(Ref, Name) -> verify(Ref, Name) ->
verify(Ref, Name, []). verify(Ref, Name, []).
verify(Ref, Name, Config) -> verify(Ref, Name, Config) ->
?ASYNC_NIF_CALL(fun verify_nif/4, [Ref, Name, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun verify_nif/5, [Ref, Name, config_to_bin(Config)]).
-spec verify_nif(reference(), connection(), string(), config()) -> ok | {error, term()}. -spec verify_nif(reference(), non_neg_integer(), connection(), string(), config()) -> ok | {error, term()}.
verify_nif(_AsyncRef, _Ref, _Name, _Config) -> verify_nif(_AsyncRef, _SchedulerId, _Ref, _Name, _Config) ->
?nif_stub. ?nif_stub.
-spec cursor_open(connection(), string()) -> {ok, cursor()} | {error, term()}. -spec cursor_open(connection(), string()) -> {ok, cursor()} | {error, term()}.
@ -254,114 +254,114 @@ verify_nif(_AsyncRef, _Ref, _Name, _Config) ->
cursor_open(Ref, Table) -> cursor_open(Ref, Table) ->
cursor_open(Ref, Table, []). cursor_open(Ref, Table, []).
cursor_open(Ref, Table, Config) -> cursor_open(Ref, Table, Config) ->
?ASYNC_NIF_CALL(fun cursor_open_nif/4, [Ref, Table, config_to_bin(Config)]). ?ASYNC_NIF_CALL(fun cursor_open_nif/5, [Ref, Table, config_to_bin(Config)]).
-spec cursor_open_nif(reference(), connection(), string(), config()) -> {ok, cursor()} | {error, term()}. -spec cursor_open_nif(reference(), non_neg_integer(), connection(), string(), config()) -> {ok, cursor()} | {error, term()}.
cursor_open_nif(_AsyncRef, _Ref, _Table, _Config) -> cursor_open_nif(_AsyncRef, _SchedulerId, _Ref, _Table, _Config) ->
?nif_stub. ?nif_stub.
-spec cursor_close(cursor()) -> ok | {error, term()}. -spec cursor_close(cursor()) -> ok | {error, term()}.
cursor_close(Cursor) -> cursor_close(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_close_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_close_nif/3, [Cursor]).
-spec cursor_close_nif(reference(), cursor()) -> ok | {error, term()}. -spec cursor_close_nif(reference(), non_neg_integer(), cursor()) -> ok | {error, term()}.
cursor_close_nif(_AsyncRef, _Cursor) -> cursor_close_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_next(cursor()) -> {ok, key(), value()} | not_found | {error, term()}. -spec cursor_next(cursor()) -> {ok, key(), value()} | not_found | {error, term()}.
cursor_next(Cursor) -> cursor_next(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_next_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_next_nif/3, [Cursor]).
-spec cursor_next_nif(reference(), cursor()) -> {ok, key(), value()} | not_found | {error, term()}. -spec cursor_next_nif(reference(), non_neg_integer(), cursor()) -> {ok, key(), value()} | not_found | {error, term()}.
cursor_next_nif(_AsyncRef, _Cursor) -> cursor_next_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_next_key(cursor()) -> {ok, key()} | not_found | {error, term()}. -spec cursor_next_key(cursor()) -> {ok, key()} | not_found | {error, term()}.
cursor_next_key(Cursor) -> cursor_next_key(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_next_key_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_next_key_nif/3, [Cursor]).
-spec cursor_next_key_nif(reference(), cursor()) -> {ok, key()} | not_found | {error, term()}. -spec cursor_next_key_nif(reference(), non_neg_integer(), cursor()) -> {ok, key()} | not_found | {error, term()}.
cursor_next_key_nif(_AsyncRef, _Cursor) -> cursor_next_key_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_next_value(cursor()) -> {ok, value()} | not_found | {error, term()}. -spec cursor_next_value(cursor()) -> {ok, value()} | not_found | {error, term()}.
cursor_next_value(Cursor) -> cursor_next_value(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_next_value_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_next_value_nif/3, [Cursor]).
-spec cursor_next_value_nif(reference(), cursor()) -> {ok, value()} | not_found | {error, term()}. -spec cursor_next_value_nif(reference(), non_neg_integer(), cursor()) -> {ok, value()} | not_found | {error, term()}.
cursor_next_value_nif(_AsyncRef, _Cursor) -> cursor_next_value_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_prev(cursor()) -> {ok, key(), value()} | not_found | {error, term()}. -spec cursor_prev(cursor()) -> {ok, key(), value()} | not_found | {error, term()}.
cursor_prev(Cursor) -> cursor_prev(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_prev_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_prev_nif/3, [Cursor]).
-spec cursor_prev_nif(reference(), cursor()) -> {ok, key(), value()} | not_found | {error, term()}. -spec cursor_prev_nif(reference(), non_neg_integer(), cursor()) -> {ok, key(), value()} | not_found | {error, term()}.
cursor_prev_nif(_AsyncRef, _Cursor) -> cursor_prev_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_prev_key(cursor()) -> {ok, key()} | not_found | {error, term()}. -spec cursor_prev_key(cursor()) -> {ok, key()} | not_found | {error, term()}.
cursor_prev_key(Cursor) -> cursor_prev_key(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_prev_key_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_prev_key_nif/3, [Cursor]).
-spec cursor_prev_key_nif(reference(), cursor()) -> {ok, key()} | not_found | {error, term()}. -spec cursor_prev_key_nif(reference(), non_neg_integer(), cursor()) -> {ok, key()} | not_found | {error, term()}.
cursor_prev_key_nif(_AsyncRef, _Cursor) -> cursor_prev_key_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_prev_value(cursor()) -> {ok, value()} | not_found | {error, term()}. -spec cursor_prev_value(cursor()) -> {ok, value()} | not_found | {error, term()}.
cursor_prev_value(Cursor) -> cursor_prev_value(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_prev_value_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_prev_value_nif/3, [Cursor]).
-spec cursor_prev_value_nif(reference(), cursor()) -> {ok, value()} | not_found | {error, term()}. -spec cursor_prev_value_nif(reference(), non_neg_integer(), cursor()) -> {ok, value()} | not_found | {error, term()}.
cursor_prev_value_nif(_AsyncRef, _Cursor) -> cursor_prev_value_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_search(cursor(), key()) -> {ok, value()} | {error, term()}. -spec cursor_search(cursor(), key()) -> {ok, value()} | {error, term()}.
cursor_search(Cursor, Key) -> cursor_search(Cursor, Key) ->
?ASYNC_NIF_CALL(fun cursor_search_nif/3, [Cursor, Key]). ?ASYNC_NIF_CALL(fun cursor_search_nif/4, [Cursor, Key]).
-spec cursor_search_nif(reference(), cursor(), key()) -> {ok, value()} | {error, term()}. -spec cursor_search_nif(reference(), non_neg_integer(), cursor(), key()) -> {ok, value()} | {error, term()}.
cursor_search_nif(_AsyncRef, _Cursor, _Key) -> cursor_search_nif(_AsyncRef, _SchedulerId, _Cursor, _Key) ->
?nif_stub. ?nif_stub.
-spec cursor_search_near(cursor(), key()) -> {ok, value()} | {error, term()}. -spec cursor_search_near(cursor(), key()) -> {ok, value()} | {error, term()}.
cursor_search_near(Cursor, Key) -> cursor_search_near(Cursor, Key) ->
?ASYNC_NIF_CALL(fun cursor_search_near_nif/3, [Cursor, Key]). ?ASYNC_NIF_CALL(fun cursor_search_near_nif/4, [Cursor, Key]).
-spec cursor_search_near_nif(reference(), cursor(), key()) -> {ok, value()} | {error, term()}. -spec cursor_search_near_nif(reference(), non_neg_integer(), cursor(), key()) -> {ok, value()} | {error, term()}.
cursor_search_near_nif(_AsyncRef, _Cursor, _Key) -> cursor_search_near_nif(_AsyncRef, _SchedulerId, _Cursor, _Key) ->
?nif_stub. ?nif_stub.
-spec cursor_reset(cursor()) -> ok | {error, term()}. -spec cursor_reset(cursor()) -> ok | {error, term()}.
cursor_reset(Cursor) -> cursor_reset(Cursor) ->
?ASYNC_NIF_CALL(fun cursor_reset_nif/2, [Cursor]). ?ASYNC_NIF_CALL(fun cursor_reset_nif/3, [Cursor]).
-spec cursor_reset_nif(reference(), cursor()) -> ok | {error, term()}. -spec cursor_reset_nif(reference(), non_neg_integer(), cursor()) -> ok | {error, term()}.
cursor_reset_nif(_AsyncRef, _Cursor) -> cursor_reset_nif(_AsyncRef, _SchedulerId, _Cursor) ->
?nif_stub. ?nif_stub.
-spec cursor_insert(cursor(), key(), value()) -> ok | {error, term()}. -spec cursor_insert(cursor(), key(), value()) -> ok | {error, term()}.
cursor_insert(Cursor, Key, Value) -> cursor_insert(Cursor, Key, Value) ->
?ASYNC_NIF_CALL(fun cursor_insert_nif/4, [Cursor, Key, Value]). ?ASYNC_NIF_CALL(fun cursor_insert_nif/5, [Cursor, Key, Value]).
-spec cursor_insert_nif(reference(), cursor(), key(), value()) -> ok | {error, term()}. -spec cursor_insert_nif(reference(), non_neg_integer(), cursor(), key(), value()) -> ok | {error, term()}.
cursor_insert_nif(_AsyncRef, _Cursor, _Key, _Value) -> cursor_insert_nif(_AsyncRef, _SchedulerId, _Cursor, _Key, _Value) ->
?nif_stub. ?nif_stub.
-spec cursor_update(cursor(), key(), value()) -> ok | {error, term()}. -spec cursor_update(cursor(), key(), value()) -> ok | {error, term()}.
cursor_update(Cursor, Key, Value) -> cursor_update(Cursor, Key, Value) ->
?ASYNC_NIF_CALL(fun cursor_update_nif/4, [Cursor, Key, Value]). ?ASYNC_NIF_CALL(fun cursor_update_nif/5, [Cursor, Key, Value]).
-spec cursor_update_nif(reference(), cursor(), key(), value()) -> ok | {error, term()}. -spec cursor_update_nif(reference(), non_neg_integer(), cursor(), key(), value()) -> ok | {error, term()}.
cursor_update_nif(_AsyncRef, _Cursor, _Key, _Value) -> cursor_update_nif(_AsyncRef, _SchedulerId, _Cursor, _Key, _Value) ->
?nif_stub. ?nif_stub.
-spec cursor_remove(cursor(), key()) -> ok | {error, term()}. -spec cursor_remove(cursor(), key()) -> ok | {error, term()}.
cursor_remove(Cursor, Key) -> cursor_remove(Cursor, Key) ->
?ASYNC_NIF_CALL(fun cursor_remove_nif/3, [Cursor, Key]). ?ASYNC_NIF_CALL(fun cursor_remove_nif/4, [Cursor, Key]).
-spec cursor_remove_nif(reference(), cursor(), key()) -> ok | {error, term()}. -spec cursor_remove_nif(reference(), non_neg_integer(), cursor(), key()) -> ok | {error, term()}.
cursor_remove_nif(_AsyncRef, _Cursor, _Key) -> cursor_remove_nif(_AsyncRef, _SchedulerId, _Cursor, _Key) ->
?nif_stub. ?nif_stub.
-type fold_keys_fun() :: fun((Key::binary(), any()) -> any()). -type fold_keys_fun() :: fun((Key::binary(), any()) -> any()).