erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

257 lines
6.6 KiB

#include "epqueue.h"
#include "epqueue_item.h"
#include "epqueue_nif.h"
#include "priority_queue.h"
#include "critical_section.h"
#include "nif_utils.h"
#include "macros.h"
#include <string.h>
struct epqueue
{
CriticalSection* crit;
PriorityQueue* queue;
ERL_NIF_TERM owner_pid;
};
void nif_epqueue_free(ErlNifEnv* env, void* obj)
{
UNUSED(env);
epqueue* qinst = static_cast<epqueue*>(obj);
if(qinst->queue)
delete qinst->queue;
if(qinst->crit)
delete qinst->crit;
}
bool internal_insert(epqueue* q, queue_item* item)
{
CritScope ss(q->crit);
return q->queue->insert(item);
}
bool internal_remove(epqueue* q, queue_item* item)
{
CritScope ss(q->crit);
if(item->heap_index == -1)
return false;
return q->queue->remove(item, item->heap_index);
}
queue_item* internal_pop(epqueue* q)
{
CritScope ss(q->crit);
return static_cast<queue_item*>(q->queue->pop());
}
bool is_owner(ErlNifEnv* env, epqueue* q)
{
if(q->owner_pid == 0)
return true;
ErlNifPid self;
if(enif_self(env, &self) && !enif_is_identical(q->owner_pid, enif_make_pid(env, &self)))
return false;
return true;
}
ERL_NIF_TERM nif_epqueue_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
epqueue_data* data = static_cast<epqueue_data*>(enif_priv_data(env));
if(!enif_is_list(env, argv[0]))
return enif_make_badarg(env);
bool use_lock = false;
ERL_NIF_TERM settings_list = argv[0];
ERL_NIF_TERM head;
while(enif_get_list_cell(env, settings_list, &head, &settings_list))
{
const ERL_NIF_TERM *items;
int arity;
if(!enif_get_tuple(env, head, &arity, &items) || arity != 2)
return enif_make_badarg(env);
if(enif_is_identical(items[0], ATOMS.atomGlobalLock))
use_lock = enif_is_identical(items[1], ATOMS.atomTrue);
else
return enif_make_badarg(env);
}
epqueue* qinst = static_cast<epqueue*>(enif_alloc_resource(data->resPQueueInstance, sizeof(epqueue)));
if(qinst == NULL)
return make_error(env, "enif_alloc_resource failed");
memset(qinst, 0, sizeof(epqueue));
if(use_lock)
{
qinst->crit = new EnifCriticalSection();
qinst->owner_pid = 0;
}
else
{
qinst->crit = new NullCriticalSection();
ErlNifPid self;
enif_self(env, &self);
qinst->owner_pid = enif_make_pid(env, &self);
}
qinst->queue = new PriorityQueue(epqueue_item_less, epqueue_item_update_pos, enif_release_resource);
ERL_NIF_TERM term = enif_make_resource(env, qinst);
enif_release_resource(qinst);
return enif_make_tuple2(env, ATOMS.atomOk, term);
}
ERL_NIF_TERM nif_epqueue_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
epqueue_data* data = static_cast<epqueue_data*>(enif_priv_data(env));
epqueue* inst = NULL;
if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast<void**>(&inst)))
return enif_make_badarg(env);
if(!is_owner(env, inst))
return enif_make_badarg(env);
CritScope ss(inst->crit);
return enif_make_int(env, inst->queue->size());
}
ERL_NIF_TERM nif_epqueue_insert(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
epqueue_data* data = static_cast<epqueue_data*>(enif_priv_data(env));
epqueue* inst;
ErlNifBinary data_bin;
long priority;
if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast<void**>(&inst)))
return enif_make_badarg(env);
if(!is_owner(env, inst))
return enif_make_badarg(env);
if(!enif_get_long(env, argv[2], &priority))
return enif_make_badarg(env);
if(!enif_term_to_binary(env, argv[1], &data_bin))
return enif_make_badarg(env);
queue_item* item = epqueue_item_new(data, data_bin, priority);
if(item == NULL)
{
enif_release_binary(&data_bin);
return make_error(env, "failed to allocate a new item");
}
if(!internal_insert(inst, item))
{
enif_release_resource(&item);
return make_error(env, "failed to insert new item in the queue");
}
ERL_NIF_TERM ref = enif_make_resource(env, item);
return enif_make_tuple2(env, ATOMS.atomOk, ref);
}
ERL_NIF_TERM nif_epqueue_remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
epqueue_data* data = static_cast<epqueue_data*>(enif_priv_data(env));
epqueue* inst = NULL;
queue_item* item = NULL;
if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast<void**>(&inst)))
return enif_make_badarg(env);
if(!is_owner(env, inst))
return enif_make_badarg(env);
if(!enif_get_resource(env, argv[1], data->resPQueueItem, reinterpret_cast<void**>(&item)))
return enif_make_badarg(env);
if(!internal_remove(inst, item))
return ATOMS.atomFalse;
enif_release_resource(&item);
return ATOMS.atomTrue;
}
ERL_NIF_TERM nif_epqueue_pop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
epqueue_data* data = static_cast<epqueue_data*>(enif_priv_data(env));
epqueue* inst = NULL;
if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast<void**>(&inst)))
return enif_make_badarg(env);
if(!is_owner(env, inst))
return enif_make_badarg(env);
queue_item* item = internal_pop(inst);
if(item == NULL)
return ATOMS.atomUndefined;
ERL_NIF_TERM bin_term;
if(enif_binary_to_term(env, item->data.data, item->data.size, &bin_term, 0) == 0)
{
enif_release_resource(item);
return make_error(env, "failed to decode data");
}
enif_release_resource(item);
return enif_make_tuple3(env, ATOMS.atomOk, bin_term, enif_make_long(env, item->priority));
}
ERL_NIF_TERM nif_epqueue_peek(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
epqueue_data* data = static_cast<epqueue_data*>(enif_priv_data(env));
epqueue* inst = NULL;
if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast<void**>(&inst)))
return enif_make_badarg(env);
if(!is_owner(env, inst))
return enif_make_badarg(env);
CritScope ss(inst->crit);
queue_item* item = static_cast<queue_item*>(inst->queue->peek());
if(item == NULL)
return ATOMS.atomUndefined;
ERL_NIF_TERM bin_term;
if(enif_binary_to_term(env, item->data.data, item->data.size, &bin_term, 0) == 0)
return make_error(env, "failed to decode data");
return enif_make_tuple3(env, ATOMS.atomOk, bin_term, enif_make_int64(env, item->priority));
}