erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

442 lines
11 KiB

#define _GNU_SOURCE
#include "erl_nif.h"
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
// #include "fifo.h"
#include "lifo.h"
typedef struct {
ERL_NIF_TERM ok;
ERL_NIF_TERM error;
ERL_NIF_TERM fifo;
ERL_NIF_TERM lifo;
ERL_NIF_TERM ttl;
ERL_NIF_TERM max_size;
} atoms_t;
typedef struct {
ErlNifResourceType *queue;
atoms_t atoms;
} priv_t;
typedef struct {
union {
fifo_handle_t fifo;
lifo_handle_t lifo;
} handle;
ErlNifBinary data;
struct timespec added;
} item_t;
typedef enum {
QTYPE_FIFO = 0,
QTYPE_LIFO
} queue_type_t;
typedef struct queue {
union {
fifo_t fifo;
lifo_t lifo;
} queue;
uint64_t ttl;
uint64_t max_size;
void (*push) (struct queue *inst, item_t *item);
item_t* (*pop) (struct queue *inst);
void (*free) (struct queue *inst);
uint64_t (*size) (struct queue *inst);
void (*cleanup) (struct queue *inst);
} queue_t;
// returns tuple {error, atom()}
static inline ERL_NIF_TERM
make_error(ErlNifEnv* env, const char *error) {
priv_t *priv = (priv_t *) enif_priv_data(env);
return enif_make_tuple2(env, priv->atoms.error, enif_make_atom(env, error));
}
// returns time diff in milliseconds
static inline int64_t
tdiff(struct timespec *t2, struct timespec *t1) {
return (t2->tv_sec * 1000 + t2->tv_nsec / 1000000UL) -
(t1->tv_sec * 1000 + t1->tv_nsec / 1000000UL);
}
static inline void
gettime(struct timespec *tp) {
int rc = clock_gettime(CLOCK_MONOTONIC_RAW, tp);
assert(rc == 0);
}
/******************************************************************************/
/* FIFO callbacks */
/******************************************************************************/
static void
cleanup_fifo(queue_t *inst) {
struct timespec now;
gettime(&now);
for (;;) {
item_t *item = NULL;
__fifo_peak(&inst->queue.fifo, item, handle.fifo);
if (item == NULL)
return;
int64_t diff = tdiff(&now, &item->added);
if (diff < inst->ttl) {
return;
} else {
__fifo_pop(&inst->queue.fifo, item, handle.fifo);
enif_release_binary(&item->data);
enif_free(item);
}
}
}
static void
push_fifo(queue_t *inst, item_t *item) {
__fifo_push(&inst->queue.fifo, item, handle.fifo);
}
static item_t *
pop_fifo(queue_t *inst) {
item_t *item = NULL;
if (inst->ttl > 0) {
struct timespec now;
gettime(&now);
for (;;) {
__fifo_pop(&inst->queue.fifo, item, handle.fifo);
if (item == NULL)
return NULL;
int64_t diff = tdiff(&now, &item->added);
if (diff < inst->ttl) {
return item;
} else {
enif_release_binary(&item->data);
enif_free(item);
}
}
} else {
__fifo_pop(&inst->queue.fifo, item, handle.fifo);
}
return item;
}
static void
free_fifo(queue_t *inst) {
item_t *item;
for(;;) {
__fifo_pop(&inst->queue.fifo, item, handle.fifo);
if (item == NULL)
return;
enif_release_binary(&item->data);
enif_free(item);
}
}
static uint64_t
size_fifo(queue_t *inst) {
return fifo_length(&inst->queue.fifo);
}
/******************************************************************************/
/* LIFO callbacks */
/******************************************************************************/
static void
cleanup_lifo(queue_t *inst) {
struct timespec now;
gettime(&now);
for(;;) {
item_t *item = inst->queue.lifo.tail;
if (item == NULL)
return;
int64_t diff = tdiff(&now, &item->added);
if (diff < inst->ttl) {
return;
} else {
item_t *prev = item->handle.lifo.prev;
if (prev != NULL)
prev->handle.lifo.next = NULL;
inst->queue.lifo.tail = prev;
enif_release_binary(&item->data);
enif_free(item);
}
}
}
static void
push_lifo(queue_t *inst, item_t *item) {
__lifo_push(&inst->queue.lifo, item, handle.lifo);
}
static item_t *
pop_lifo(queue_t *inst) {
item_t *item = NULL;
if (inst->ttl > 0)
cleanup_lifo(inst);
__lifo_pop(&inst->queue.lifo, item, handle.lifo);
return item;
}
static void
free_lifo(queue_t *inst) {
item_t *item;
for(;;) {
__lifo_pop(&inst->queue.lifo, item, handle.lifo);
if (item == NULL)
return;
enif_release_binary(&item->data);
enif_free(item);
}
}
static uint64_t
size_lifo(queue_t *inst) {
return lifo_length(&inst->queue.lifo);
}
/******************************************************************************
** NIFs
*******************************************************************************/
static ERL_NIF_TERM
new_queue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
if (!enif_is_list(env, argv[0]))
return enif_make_badarg(env);
priv_t *priv = (priv_t *) enif_priv_data(env);
queue_type_t qtype = QTYPE_FIFO;
unsigned long ttl = 0;
unsigned long max_size = 0;
ERL_NIF_TERM settings_list = argv[0];
ERL_NIF_TERM head;
// parses proplist [fifo, lifo, {ttl, non_neg_integer()}, {max_size, non_neg_integer()}]
while(enif_get_list_cell(env, settings_list, &head, &settings_list))
{
const ERL_NIF_TERM *items;
int arity;
if (enif_is_atom(env, head)) {
if (enif_is_identical(head, priv->atoms.fifo)) {
qtype = QTYPE_FIFO;
} else if (enif_is_identical(head, priv->atoms.lifo)) {
qtype = QTYPE_LIFO;
} else {
return enif_make_badarg(env);
}
} else if (enif_get_tuple(env, head, &arity, &items) && arity == 2) {
if (enif_is_identical(items[0], priv->atoms.ttl)) {
if (!enif_get_ulong(env, items[1], &ttl)) {
return enif_make_badarg(env);
}
} else if (enif_is_identical(items[0], priv->atoms.max_size)) {
if (!enif_get_ulong(env, items[1], &max_size)) {
return enif_make_badarg(env);
}
} else {
return enif_make_badarg(env);
}
} else {
return enif_make_badarg(env);
}
}
queue_t *inst = (queue_t *) enif_alloc_resource(priv->queue, sizeof(*inst));
if (inst == NULL)
return make_error(env, "enif_alloc_resource");
inst->ttl = ttl;
inst->max_size = max_size;
switch (qtype) {
case QTYPE_FIFO:
fifo_init(&inst->queue.fifo);
inst->push = &push_fifo;
inst->pop = &pop_fifo;
inst->free = &free_fifo;
inst->size = &size_fifo;
inst->cleanup = &cleanup_fifo;
break;
case QTYPE_LIFO:
lifo_init(&inst->queue.lifo);
inst->push = &push_lifo;
inst->pop = &pop_lifo;
inst->free = &free_lifo;
inst->size = &size_lifo;
inst->cleanup = &cleanup_lifo;
break;
}
ERL_NIF_TERM result = enif_make_resource(env, inst);
enif_release_resource(inst);
return enif_make_tuple2(env, priv->atoms.ok, result);
}
static ERL_NIF_TERM
push_item(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
priv_t *priv = (priv_t *) enif_priv_data(env);
queue_t *inst;
if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst))
return enif_make_badarg(env);
// todo: check an owner of the queue
ErlNifBinary bin;
if (!enif_inspect_binary(env, argv[1], &bin))
return enif_make_badarg(env);
if (inst->ttl > 0) {
inst->cleanup(inst);
}
if (inst->max_size > 0 && inst->size(inst) >= inst->max_size) {
return enif_make_tuple2(env, priv->atoms.error, priv->atoms.max_size);
}
item_t *item = (item_t *) enif_alloc(sizeof(*item));
if (item == NULL)
return make_error(env, "enif_alloc");
if (!enif_alloc_binary(bin.size, &item->data)) {
enif_free(item);
return make_error(env, "enif_alloc_binary");
}
memcpy(item->data.data, bin.data, bin.size);
if (inst->ttl > 0) {
gettime(&item->added);
}
inst->push(inst, item);
return priv->atoms.ok;
}
static ERL_NIF_TERM
pop_item(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
priv_t *priv = (priv_t *) enif_priv_data(env);
queue_t *inst;
item_t *item;
if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst))
return enif_make_badarg(env);
// todo: check an owner of the queue
item = inst->pop(inst);
if (item == NULL)
return enif_make_list(env, 0);
ERL_NIF_TERM result = enif_make_binary(env, &item->data);
enif_free(item);
return enif_make_list1(env, result);
}
static ERL_NIF_TERM
queue_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
priv_t *priv = (priv_t *) enif_priv_data(env);
queue_t *inst;
if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst))
return enif_make_badarg(env);
return enif_make_uint64(env, inst->size(inst));
}
/******************************************************************************
** NIF initialization
*******************************************************************************/
static void
enq_queue_free(ErlNifEnv* env, void* obj) {
queue_t *inst = obj;
inst->free(inst);
}
static priv_t *
make_priv(ErlNifEnv *env) {
priv_t *priv = enif_alloc(sizeof(*priv));
if (priv == NULL)
return NULL;
ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
priv->queue = enif_open_resource_type(env, NULL, "enq_queue", enq_queue_free, flags, NULL);
priv->atoms.ok = enif_make_atom(env, "ok");
priv->atoms.error = enif_make_atom(env, "error");
priv->atoms.fifo = enif_make_atom(env, "fifo");
priv->atoms.lifo = enif_make_atom(env, "lifo");
priv->atoms.ttl = enif_make_atom(env, "ttl");
priv->atoms.max_size = enif_make_atom(env, "max_size");
return priv;
}
static int
enq_nif_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
*priv_data = make_priv(env);
return 0;
}
static int
enq_nif_upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM load_info) {
*priv_data = make_priv(env);
return 0;
}
static ErlNifFunc enq_nif_funcs[] = {
{"new", 1, new_queue},
{"push", 2, push_item},
{"pop", 1, pop_item},
{"size", 1, queue_size},
};
ERL_NIF_INIT(enq_nif, enq_nif_funcs, enq_nif_load, NULL, enq_nif_upgrade, NULL)