#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));
|
|
}
|