diff --git a/.gitignore b/.gitignore index 80a5baa..0ad44f1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ deps _build/ _checkouts/ rebar.lock -priv # idea .idea diff --git a/c_src/epqueue/critical_section.h b/c_src/epqueue/critical_section.h new file mode 100644 index 0000000..334da0b --- /dev/null +++ b/c_src/epqueue/critical_section.h @@ -0,0 +1,62 @@ +#ifndef C_SRC_CRITICAL_SECTION_H_ +#define C_SRC_CRITICAL_SECTION_H_ + +#include "erl_nif.h" +#include "macros.h" + +class CriticalSection +{ +public: + + CriticalSection() { } + virtual ~CriticalSection() {} + + virtual void Enter() = 0; + virtual void Leave() = 0; +}; + +class NullCriticalSection : public CriticalSection +{ +public: + + NullCriticalSection() {} + ~NullCriticalSection() {} + + void Enter() override {} + void Leave() override {} + +private: + + DISALLOW_COPY_AND_ASSIGN(NullCriticalSection); +}; + +class EnifCriticalSection : public CriticalSection +{ +public: + + EnifCriticalSection() { mutex_ = enif_mutex_create(NULL);} + ~EnifCriticalSection() {enif_mutex_destroy(mutex_);} + + void Enter() override {enif_mutex_lock(mutex_);} + void Leave() override {enif_mutex_unlock(mutex_);} + +private: + + ErlNifMutex *mutex_; + DISALLOW_COPY_AND_ASSIGN(EnifCriticalSection); +}; + +class CritScope +{ +public: + + explicit CritScope(CriticalSection *pp) : pcrit_(pp) { pcrit_->Enter();} + ~CritScope() {pcrit_->Leave();} + +private: + + CriticalSection *pcrit_; + DISALLOW_COPY_AND_ASSIGN(CritScope); +}; + +#endif // C_SRC_CRITICAL_SECTION_H_ diff --git a/c_src/epqueue/epqueue.cc b/c_src/epqueue/epqueue.cc new file mode 100644 index 0000000..d7fe536 --- /dev/null +++ b/c_src/epqueue/epqueue.cc @@ -0,0 +1,257 @@ +#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 + +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(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(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(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(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(enif_priv_data(env)); + + epqueue* inst = NULL; + + if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast(&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(enif_priv_data(env)); + + epqueue* inst; + ErlNifBinary data_bin; + long priority; + + if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast(&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(enif_priv_data(env)); + epqueue* inst = NULL; + queue_item* item = NULL; + + if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast(&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(&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(enif_priv_data(env)); + epqueue* inst = NULL; + + if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast(&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(enif_priv_data(env)); + epqueue* inst = NULL; + + if(!enif_get_resource(env, argv[0], data->resPQueueInstance, reinterpret_cast(&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(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)); +} diff --git a/c_src/epqueue/epqueue.h b/c_src/epqueue/epqueue.h new file mode 100644 index 0000000..8efce7b --- /dev/null +++ b/c_src/epqueue/epqueue.h @@ -0,0 +1,14 @@ +#ifndef C_SRC_EPQUEUE_H_ +#define C_SRC_EPQUEUE_H_ + +#include "erl_nif.h" + +void nif_epqueue_free(ErlNifEnv* env, void* obj); +ERL_NIF_TERM nif_epqueue_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM nif_epqueue_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM nif_epqueue_insert(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM nif_epqueue_remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM nif_epqueue_pop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM nif_epqueue_peek(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif // C_SRC_EPQUEUE_H_ diff --git a/c_src/epqueue/epqueue_item.cc b/c_src/epqueue/epqueue_item.cc new file mode 100644 index 0000000..614cc35 --- /dev/null +++ b/c_src/epqueue/epqueue_item.cc @@ -0,0 +1,36 @@ +#include "epqueue_item.h" +#include "epqueue_nif.h" +#include "macros.h" + +bool epqueue_item_less(void* ax, void* bx) +{ + queue_item* a = static_cast(ax); + queue_item* b = static_cast(bx); + return a->priority < b->priority; +} + +void epqueue_item_update_pos(void* ax, int32_t pos) +{ + queue_item* a = static_cast(ax); + a->heap_index = pos; +} + +void epqueue_item_free(ErlNifEnv* env, void* obj) +{ + UNUSED(env); + queue_item* item = static_cast(obj); + enif_release_binary(&item->data); +} + +queue_item* epqueue_item_new(const epqueue_data* data, const ErlNifBinary& bin, uint64_t priority) +{ + queue_item* item = static_cast(enif_alloc_resource(data->resPQueueItem, sizeof(queue_item))); + + if(item == NULL) + return NULL; + + item->heap_index = -1; + item->priority = priority; + item->data = bin; + return item; +} diff --git a/c_src/epqueue/epqueue_item.h b/c_src/epqueue/epqueue_item.h new file mode 100644 index 0000000..86568af --- /dev/null +++ b/c_src/epqueue/epqueue_item.h @@ -0,0 +1,22 @@ +#ifndef C_SRC_EPQUEUE_ITEM_H_ +#define C_SRC_EPQUEUE_ITEM_H_ + +#include "erl_nif.h" +#include + +struct epqueue_data; + +struct queue_item +{ + int32_t heap_index; + uint64_t priority; + ErlNifBinary data; +}; + +void epqueue_item_update_pos(void* ax, int32_t pos); +bool epqueue_item_less(void* ax, void* bx); + +queue_item* epqueue_item_new(const epqueue_data* data, const ErlNifBinary& bin, uint64_t priority); +void epqueue_item_free(ErlNifEnv* env, void* obj); + +#endif // C_SRC_EPQUEUE_ITEM_H_ diff --git a/c_src/epqueue/epqueue_nif.cc b/c_src/epqueue/epqueue_nif.cc new file mode 100644 index 0000000..1220a21 --- /dev/null +++ b/c_src/epqueue/epqueue_nif.cc @@ -0,0 +1,74 @@ +#include /* memcmp,strlen */ +#include "epqueue_nif.h" +#include "epqueue.h" +#include "epqueue_item.h" +#include "nif_utils.h" +#include "macros.h" + +const char kAtomOk[] = "ok"; +const char kAtomError[] = "error"; +const char kAtomTrue[] = "true"; +const char kAtomFalse[] = "false"; +const char kAtomUndefined[] = "undefined"; +const char kAtomGlobalLock[] = "global_lock"; + +atoms ATOMS; + +void open_resources(ErlNifEnv* env, epqueue_data* data) +{ + ErlNifResourceFlags flags = static_cast(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); + data->resPQueueInstance = enif_open_resource_type(env, NULL, "pqueue_instance", nif_epqueue_free, flags, NULL); + data->resPQueueItem = enif_open_resource_type(env, NULL, "pqueue_item", epqueue_item_free, flags, NULL); +} + +int on_nif_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + UNUSED(load_info); + + ATOMS.atomOk = make_atom(env, kAtomOk); + ATOMS.atomError = make_atom(env, kAtomError); + ATOMS.atomTrue = make_atom(env, kAtomTrue); + ATOMS.atomFalse = make_atom(env, kAtomFalse); + ATOMS.atomUndefined = make_atom(env, kAtomUndefined); + + ATOMS.atomGlobalLock = make_atom(env, kAtomGlobalLock); + + epqueue_data* data = static_cast(enif_alloc(sizeof(epqueue_data))); + open_resources(env, data); + + *priv_data = data; + return 0; +} + +void on_nif_unload(ErlNifEnv* env, void* priv_data) +{ + UNUSED(env); + + epqueue_data* data = static_cast(priv_data); + enif_free(data); +} + +int on_nif_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) +{ + UNUSED(old_priv); + UNUSED(info); + + epqueue_data* data = static_cast(enif_alloc(sizeof(epqueue_data))); + open_resources(env, data); + + *priv = data; + return 0; +} + +static ErlNifFunc nif_funcs[] = +{ + {"new", 1, nif_epqueue_new}, + {"insert", 3, nif_epqueue_insert}, + {"remove", 2, nif_epqueue_remove}, + {"pop", 1, nif_epqueue_pop}, + {"peek", 1, nif_epqueue_peek}, + {"size", 1, nif_epqueue_size} +}; + +ERL_NIF_INIT(epqueue_nif, nif_funcs, on_nif_load, NULL, on_nif_upgrade, on_nif_unload) + diff --git a/c_src/epqueue/epqueue_nif.h b/c_src/epqueue/epqueue_nif.h new file mode 100644 index 0000000..202138a --- /dev/null +++ b/c_src/epqueue/epqueue_nif.h @@ -0,0 +1,25 @@ +#ifndef C_SRC_EPQUEUE_NIF_H_ +#define C_SRC_EPQUEUE_NIF_H_ + +#include "erl_nif.h" + +struct atoms +{ + ERL_NIF_TERM atomOk; + ERL_NIF_TERM atomError; + ERL_NIF_TERM atomTrue; + ERL_NIF_TERM atomFalse; + ERL_NIF_TERM atomUndefined; + + ERL_NIF_TERM atomGlobalLock; +}; + +struct epqueue_data +{ + ErlNifResourceType* resPQueueInstance; + ErlNifResourceType* resPQueueItem; +}; + +extern atoms ATOMS; + +#endif // C_SRC_EPQUEUE_NIF_H_ diff --git a/c_src/epqueue/macros.h b/c_src/epqueue/macros.h new file mode 100644 index 0000000..8576331 --- /dev/null +++ b/c_src/epqueue/macros.h @@ -0,0 +1,9 @@ +#ifndef C_SRC_MACROS_H_ +#define C_SRC_MACROS_H_ + +#define UNUSED(expr) do { (void)(expr); } while (0) + +#define DISALLOW_ASSIGN(TypeName) void operator=(const TypeName&) +#define DISALLOW_COPY_AND_ASSIGN(TypeName) TypeName(const TypeName&); DISALLOW_ASSIGN(TypeName) + +#endif // C_SRC_MACROS_H_ diff --git a/c_src/epqueue/nif_utils.cc b/c_src/epqueue/nif_utils.cc new file mode 100644 index 0000000..7581b96 --- /dev/null +++ b/c_src/epqueue/nif_utils.cc @@ -0,0 +1,27 @@ +#include "nif_utils.h" +#include "epqueue_nif.h" + +#include + +ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name) +{ + ERL_NIF_TERM ret; + + if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) + return ret; + + return enif_make_atom(env, name); +} + +ERL_NIF_TERM make_binary(ErlNifEnv* env, const char* buff, size_t length) +{ + ERL_NIF_TERM term; + unsigned char *destination_buffer = enif_make_new_binary(env, length, &term); + memcpy(destination_buffer, buff, length); + return term; +} + +ERL_NIF_TERM make_error(ErlNifEnv* env, const char* error) +{ + return enif_make_tuple2(env, ATOMS.atomError, make_binary(env, error, strlen(error))); +} diff --git a/c_src/epqueue/nif_utils.h b/c_src/epqueue/nif_utils.h new file mode 100644 index 0000000..541a3aa --- /dev/null +++ b/c_src/epqueue/nif_utils.h @@ -0,0 +1,10 @@ +#ifndef C_SRC_NIF_UTILS_H_ +#define C_SRC_NIF_UTILS_H_ + +#include "erl_nif.h" + +ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name); +ERL_NIF_TERM make_error(ErlNifEnv* env, const char* error); +ERL_NIF_TERM make_binary(ErlNifEnv* env, const char* buff, size_t length); + +#endif // C_SRC_NIF_UTILS_H_ diff --git a/c_src/epqueue/priority_queue.cc b/c_src/epqueue/priority_queue.cc new file mode 100644 index 0000000..e6c4e95 --- /dev/null +++ b/c_src/epqueue/priority_queue.cc @@ -0,0 +1,148 @@ +#include "priority_queue.h" +#include "erl_nif.h" +#include +#include + +// Algorithm described at : http://robin-thomas.github.io/min-heap/ + +#define left(x) 2 * x + 1 +#define right(x) 2 * x + 2 +#define parent(x) (x - 1) / 2 + +#define less(a, b) less_(heap_[a], heap_[b]) + +PriorityQueue::PriorityQueue(LessFun ls, UpdatePositionFun upd, DestroyElementFun dtor) : + capacity_(0), + length_(0), + heap_(NULL), + less_(ls), + update_pos_fun_(upd), + item_dtor_(dtor) { } + +PriorityQueue::~PriorityQueue() +{ + if(heap_) + { + for(int i = 0; i < length_; i++) + item_dtor_(heap_[i]); + + enif_free(heap_); + } +} + +bool PriorityQueue::insert(void* item) +{ + int pos; + + if (length_ == capacity_) + { + int new_capacity = (length_+1) * 2; + void** new_heap = reinterpret_cast(enif_alloc(sizeof(void*) * new_capacity)); + + if (!new_heap) + return false; + + memcpy(new_heap, heap_, sizeof(void*)*length_); + enif_free(heap_); + heap_ = new_heap; + capacity_ = new_capacity; + } + + pos = (length_)++; + set(pos, item); + bubble_down(pos); + return true; +} + +bool PriorityQueue::remove(void* item, int pos) +{ + if (pos >= length_) + return false; + + if(heap_[pos] != item) + return false; + + return remove(pos) == item; +} + +void* PriorityQueue::remove(int pos) +{ + if (pos >= length_) + return NULL; + + void* item = heap_[pos]; + length_--; + + int ls = less(pos, length_); + + set(pos, heap_[length_]); + + if(ls) + bubble_up(pos); + else + bubble_down(pos); + + // todo: resize down the heap in case we have a lot of empty slots + + update_pos_fun_(item, -1); + return item; +} + +void* PriorityQueue::peek() +{ + if(length_ == 0) + return NULL; + + return heap_[0]; +} + +// private + +void PriorityQueue::set(int pos, void* item) +{ + heap_[pos] = item; + update_pos_fun_(item, pos); +} + +void PriorityQueue::pos_swap(int pos1, int pos2) +{ + void* tmp = heap_[pos1]; + set(pos1, heap_[pos2]); + set(pos2, tmp); +} + +void PriorityQueue::bubble_down(int pos) +{ + while(true) + { + int parent = parent(pos); + + if (pos == 0 || less(parent, pos)) + return; + + pos_swap(pos, parent); + pos = parent; + } +} + +void PriorityQueue::bubble_up(int pos) +{ + while (true) + { + int left = left(pos); + int right = right(pos); + int smallest = pos; + + if (left < length_ && less(left, smallest)) + smallest = left; + + if (right < length_ && less(right, smallest)) + smallest = right; + + if (smallest == pos) + return; + + pos_swap(pos, smallest); + pos = smallest; + } +} diff --git a/c_src/epqueue/priority_queue.h b/c_src/epqueue/priority_queue.h new file mode 100644 index 0000000..c9ae375 --- /dev/null +++ b/c_src/epqueue/priority_queue.h @@ -0,0 +1,41 @@ +#ifndef C_SRC_PRIORITY_QUEUE_H_ +#define C_SRC_PRIORITY_QUEUE_H_ + +#include "macros.h" + +typedef bool(*LessFun)(void*, void*); +typedef void(*UpdatePositionFun)(void*, int); +typedef void(*DestroyElementFun)(void*); + +class PriorityQueue +{ +public: + + PriorityQueue(LessFun ls, UpdatePositionFun upd, DestroyElementFun dtor); + ~PriorityQueue(); + + bool insert(void* item); + bool remove(void* item, int pos); + void* remove(int pos); + void* pop() {return remove(0);} + void* peek(); + int size() const { return length_;} + +private: + + inline void set(int pos, void* item); + inline void pos_swap(int pos1, int pos2); + void bubble_down(int pos); + void bubble_up(int pos); + + int capacity_; + int length_; + void** heap_; + LessFun less_; + UpdatePositionFun update_pos_fun_; + DestroyElementFun item_dtor_; + + DISALLOW_COPY_AND_ASSIGN(PriorityQueue); +}; + +#endif // C_SRC_PRIORITY_QUEUE_H_ diff --git a/c_src/epqueue/rebar.config b/c_src/epqueue/rebar.config new file mode 100644 index 0000000..cccb5e8 --- /dev/null +++ b/c_src/epqueue/rebar.config @@ -0,0 +1,7 @@ +{port_specs, [ + {"../../priv/epqueue_nif.so", ["*.cc"]} +]}. + + + + diff --git a/c_src/erlNpc b/c_src/erlNpc index 369ccec..4dc4f03 100644 Binary files a/c_src/erlNpc and b/c_src/erlNpc differ diff --git a/priv/binary_tools.exp b/priv/binary_tools.exp new file mode 100644 index 0000000..4fdbf16 Binary files /dev/null and b/priv/binary_tools.exp differ diff --git a/priv/binary_tools.lib b/priv/binary_tools.lib new file mode 100644 index 0000000..a08bf38 Binary files /dev/null and b/priv/binary_tools.lib differ diff --git a/priv/binary_tools.so b/priv/binary_tools.so new file mode 100644 index 0000000..57345da Binary files /dev/null and b/priv/binary_tools.so differ diff --git a/priv/bitmap_filter.exp b/priv/bitmap_filter.exp new file mode 100644 index 0000000..daad718 Binary files /dev/null and b/priv/bitmap_filter.exp differ diff --git a/priv/bitmap_filter.lib b/priv/bitmap_filter.lib new file mode 100644 index 0000000..229476e Binary files /dev/null and b/priv/bitmap_filter.lib differ diff --git a/priv/bitmap_filter.so b/priv/bitmap_filter.so new file mode 100644 index 0000000..8d26c84 Binary files /dev/null and b/priv/bitmap_filter.so differ diff --git a/priv/bsn_ext.exp b/priv/bsn_ext.exp new file mode 100644 index 0000000..d68de84 Binary files /dev/null and b/priv/bsn_ext.exp differ diff --git a/priv/bsn_ext.lib b/priv/bsn_ext.lib new file mode 100644 index 0000000..08336a4 Binary files /dev/null and b/priv/bsn_ext.lib differ diff --git a/priv/bsn_ext.so b/priv/bsn_ext.so new file mode 100644 index 0000000..de2626f Binary files /dev/null and b/priv/bsn_ext.so differ diff --git a/priv/bsn_int.exp b/priv/bsn_int.exp new file mode 100644 index 0000000..dcee353 Binary files /dev/null and b/priv/bsn_int.exp differ diff --git a/priv/bsn_int.lib b/priv/bsn_int.lib new file mode 100644 index 0000000..62ef019 Binary files /dev/null and b/priv/bsn_int.lib differ diff --git a/priv/bsn_int.so b/priv/bsn_int.so new file mode 100644 index 0000000..cd11c9c Binary files /dev/null and b/priv/bsn_int.so differ diff --git a/priv/btreelru_nif.exp b/priv/btreelru_nif.exp new file mode 100644 index 0000000..bf278d4 Binary files /dev/null and b/priv/btreelru_nif.exp differ diff --git a/priv/btreelru_nif.lib b/priv/btreelru_nif.lib new file mode 100644 index 0000000..1376c73 Binary files /dev/null and b/priv/btreelru_nif.lib differ diff --git a/priv/btreelru_nif.so b/priv/btreelru_nif.so new file mode 100644 index 0000000..359bdc2 Binary files /dev/null and b/priv/btreelru_nif.so differ diff --git a/priv/cbase64.exp b/priv/cbase64.exp new file mode 100644 index 0000000..d5e0683 Binary files /dev/null and b/priv/cbase64.exp differ diff --git a/priv/cbase64.lib b/priv/cbase64.lib new file mode 100644 index 0000000..50fcf20 Binary files /dev/null and b/priv/cbase64.lib differ diff --git a/priv/cbase64.so b/priv/cbase64.so new file mode 100644 index 0000000..02c17f1 Binary files /dev/null and b/priv/cbase64.so differ diff --git a/priv/e2qc_nif.so b/priv/e2qc_nif.so new file mode 100644 index 0000000..411716c Binary files /dev/null and b/priv/e2qc_nif.so differ diff --git a/priv/enlfq.exp b/priv/enlfq.exp new file mode 100644 index 0000000..82ec3c9 Binary files /dev/null and b/priv/enlfq.exp differ diff --git a/priv/enlfq.lib b/priv/enlfq.lib new file mode 100644 index 0000000..3ed51cd Binary files /dev/null and b/priv/enlfq.lib differ diff --git a/priv/enlfq.so b/priv/enlfq.so new file mode 100644 index 0000000..9a2f05c Binary files /dev/null and b/priv/enlfq.so differ diff --git a/priv/epqueue_nif.exp b/priv/epqueue_nif.exp new file mode 100644 index 0000000..0faf3d4 Binary files /dev/null and b/priv/epqueue_nif.exp differ diff --git a/priv/epqueue_nif.lib b/priv/epqueue_nif.lib new file mode 100644 index 0000000..7f4f26f Binary files /dev/null and b/priv/epqueue_nif.lib differ diff --git a/priv/epqueue_nif.so b/priv/epqueue_nif.so new file mode 100644 index 0000000..f4b53d5 Binary files /dev/null and b/priv/epqueue_nif.so differ diff --git a/priv/etsq.exp b/priv/etsq.exp new file mode 100644 index 0000000..afe65ce Binary files /dev/null and b/priv/etsq.exp differ diff --git a/priv/etsq.lib b/priv/etsq.lib new file mode 100644 index 0000000..9872568 Binary files /dev/null and b/priv/etsq.lib differ diff --git a/priv/etsq.so b/priv/etsq.so new file mode 100644 index 0000000..638ce93 Binary files /dev/null and b/priv/etsq.so differ diff --git a/priv/granderl.so b/priv/granderl.so new file mode 100644 index 0000000..7848a46 Binary files /dev/null and b/priv/granderl.so differ diff --git a/priv/hqueue.exp b/priv/hqueue.exp new file mode 100644 index 0000000..15d1f8d Binary files /dev/null and b/priv/hqueue.exp differ diff --git a/priv/hqueue.lib b/priv/hqueue.lib new file mode 100644 index 0000000..43ac1f3 Binary files /dev/null and b/priv/hqueue.lib differ diff --git a/priv/hqueue.so b/priv/hqueue.so new file mode 100644 index 0000000..2e24f4e Binary files /dev/null and b/priv/hqueue.so differ diff --git a/priv/judy.exp b/priv/judy.exp new file mode 100644 index 0000000..2764169 Binary files /dev/null and b/priv/judy.exp differ diff --git a/priv/judy.lib b/priv/judy.lib new file mode 100644 index 0000000..bacb67b Binary files /dev/null and b/priv/judy.lib differ diff --git a/priv/judy.so b/priv/judy.so new file mode 100644 index 0000000..e2d57ef Binary files /dev/null and b/priv/judy.so differ diff --git a/priv/mqtree.so b/priv/mqtree.so new file mode 100644 index 0000000..8060295 Binary files /dev/null and b/priv/mqtree.so differ diff --git a/priv/native_array_nif.exp b/priv/native_array_nif.exp new file mode 100644 index 0000000..376e0bb Binary files /dev/null and b/priv/native_array_nif.exp differ diff --git a/priv/native_array_nif.lib b/priv/native_array_nif.lib new file mode 100644 index 0000000..e575a49 Binary files /dev/null and b/priv/native_array_nif.lib differ diff --git a/priv/native_array_nif.so b/priv/native_array_nif.so new file mode 100644 index 0000000..d65552a Binary files /dev/null and b/priv/native_array_nif.so differ diff --git a/priv/neural.exp b/priv/neural.exp new file mode 100644 index 0000000..8e8f5d5 Binary files /dev/null and b/priv/neural.exp differ diff --git a/priv/neural.lib b/priv/neural.lib new file mode 100644 index 0000000..a21f517 Binary files /dev/null and b/priv/neural.lib differ diff --git a/priv/neural.so b/priv/neural.so new file mode 100644 index 0000000..e0b19eb Binary files /dev/null and b/priv/neural.so differ diff --git a/priv/nif_skiplist.exp b/priv/nif_skiplist.exp new file mode 100644 index 0000000..91c30f7 Binary files /dev/null and b/priv/nif_skiplist.exp differ diff --git a/priv/nif_skiplist.lib b/priv/nif_skiplist.lib new file mode 100644 index 0000000..f84471f Binary files /dev/null and b/priv/nif_skiplist.lib differ diff --git a/priv/nif_skiplist.so b/priv/nif_skiplist.so new file mode 100644 index 0000000..b20619f Binary files /dev/null and b/priv/nif_skiplist.so differ diff --git a/priv/zset_nif.dll b/priv/zset_nif.dll new file mode 100644 index 0000000..f6e72db Binary files /dev/null and b/priv/zset_nif.dll differ diff --git a/priv/zset_nif.exp b/priv/zset_nif.exp new file mode 100644 index 0000000..0bd6e86 Binary files /dev/null and b/priv/zset_nif.exp differ diff --git a/priv/zset_nif.lib b/priv/zset_nif.lib new file mode 100644 index 0000000..c83c72d Binary files /dev/null and b/priv/zset_nif.lib differ diff --git a/src/nifSrc/epqueue/benchmarks/benchmark.erl b/src/nifSrc/epqueue/benchmarks/benchmark.erl new file mode 100644 index 0000000..f6682a7 --- /dev/null +++ b/src/nifSrc/epqueue/benchmarks/benchmark.erl @@ -0,0 +1,68 @@ +-module(benchmark). + +-export([ + benchmark_serial/3, + benchmark_concurrent/3 +]). + +benchmark_serial(Elements, MaxPriority, Lock) -> + rand:uniform(), %just to init the seed + {ok, Q} = epqueue:new([{global_lock, Lock}]), + + {T0, ok} = timer:tc(fun() -> insert_none(Elements, MaxPriority) end), + {T1, ok} = timer:tc(fun() -> insert_item(Elements, Q, MaxPriority) end), + {T2, ok} = timer:tc(fun() -> remove_item(Q) end), + + T0Ms = T0/1000, + T1Ms = T1/1000, + T2Ms = T2/1000, + + io:format(<<"insert overhead: ~p ms insert time: ~p ms pop time: ~p ms ~n">>, [T0Ms, T1Ms, T2Ms]). + +benchmark_concurrent(Procs, Elements, MaxPriority) -> + {ok, Q} = epqueue:new([{global_lock, true}]), + + ElsPerProcess = round(Elements/Procs), + + InsertNoneWorkFun = fun() -> + insert_none(ElsPerProcess, MaxPriority) + end, + + InsertWorkFun = fun() -> + insert_item(ElsPerProcess, Q, MaxPriority) + end, + + RemoveWorkFun = fun() -> + remove_item(Q) + end, + + {T0, _} = timer:tc(fun()-> multi_spawn:do_work(InsertNoneWorkFun, Procs) end), + {T1, _} = timer:tc(fun()-> multi_spawn:do_work(InsertWorkFun, Procs) end), + {T2, _} = timer:tc(fun()-> multi_spawn:do_work(RemoveWorkFun, Procs) end), + + T0Ms = T0/1000, + T1Ms = T1/1000, + T2Ms = T2/1000, + + io:format(<<"insert overhead: ~p ms insert time: ~p ms pop time: ~p ms ~n">>, [T0Ms, T1Ms, T2Ms]). + +insert_item(0, _Q, _Max) -> + ok; +insert_item(N, Q, Max) -> + El = rand:uniform(Max), + {ok, _} = epqueue:insert(Q, El, El), + insert_item(N-1, Q, Max). + +remove_item(Q) -> + case epqueue:pop(Q) of + undefined-> + ok; + {ok, _, _} -> + remove_item(Q) + end. + +insert_none(0, _Max) -> + ok; +insert_none(N, Max) -> + rand:uniform(Max), + insert_none(N-1, Max). \ No newline at end of file diff --git a/src/nifSrc/epqueue/benchmarks/multi_spawn.erl b/src/nifSrc/epqueue/benchmarks/multi_spawn.erl new file mode 100644 index 0000000..e1d712d --- /dev/null +++ b/src/nifSrc/epqueue/benchmarks/multi_spawn.erl @@ -0,0 +1,24 @@ +-module(multi_spawn). + +-export([ + do_work/2 +]). + +do_work(Fun, Count) -> + process_flag(trap_exit, true), + spawn_childrens(Fun, Count), + wait_responses(Count). + +spawn_childrens(_Fun, 0) -> + ok; +spawn_childrens(Fun, Count) -> + spawn_link(Fun), + spawn_childrens(Fun, Count -1). + +wait_responses(0) -> + ok; +wait_responses(Count) -> + receive + {'EXIT',_FromPid, _Reason} -> + wait_responses(Count -1) + end. \ No newline at end of file diff --git a/src/nifSrc/epqueue/src/epqueue.app.src b/src/nifSrc/epqueue/src/epqueue.app.src new file mode 100644 index 0000000..8ea9938 --- /dev/null +++ b/src/nifSrc/epqueue/src/epqueue.app.src @@ -0,0 +1,29 @@ +{application, epqueue, [ + {description, "Erlang Priority Queue"}, + {licenses, ["MIT"]}, + {links,[{"Github","https://github.com/silviucpp/epqueue"}]}, + {vsn, "1.2.1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {env, []}, + {files, [ + "LICENSE*", + "*.MD", + "Makefile", + "rebar.config", + "rebar.lock", + "src/*.erl", + "src/*.src", + "c_src/*.h", + "c_src/*.cc", + "c_src/Makefile", + "c_src/nif.mk", + "test/*.erl", + "test/cover.spec", + "benchmarks/*.erl" + ]} +]}. + diff --git a/src/nifSrc/epqueue/src/epqueue.erl b/src/nifSrc/epqueue/src/epqueue.erl new file mode 100644 index 0000000..39849b0 --- /dev/null +++ b/src/nifSrc/epqueue/src/epqueue.erl @@ -0,0 +1,60 @@ +-module(epqueue). + +-export([ + new/0, + new/1, + size/1, + insert/3, + remove/2, + pop/1, + peek/1 +]). + +-type error() :: badarg | {error, binary()}. +-type queue_option() :: {global_lock, boolean()}. +-type priority() :: non_neg_integer(). +-type data() :: any(). +-type queue_ref() :: reference(). +-type data_ref() :: reference(). + +-spec new() -> + {ok, queue_ref()} | error(). + +new() -> + epqueue_nif:new([]). + +-spec new([queue_option()]) -> + {ok, queue_ref()} | error(). + +new(Options) -> + epqueue_nif:new(Options). + +-spec size(queue_ref()) -> + non_neg_integer() | badarg. + +size(QueueRef) -> + epqueue_nif:size(QueueRef). + +-spec insert(queue_ref(), data(), priority()) -> + {ok, data_ref()} | error(). + +insert(QueueRef, Data, Priority) -> + epqueue_nif:insert(QueueRef, Data, Priority). + +-spec remove(queue_ref(), data_ref()) -> + boolean() | error(). + +remove(QueueRef, Ref) -> + epqueue_nif:remove(QueueRef, Ref). + +-spec pop(queue_ref()) -> + {ok, data(), priority()} | error(). + +pop(QueueRef) -> + epqueue_nif:pop(QueueRef). + +-spec peek(queue_ref()) -> + {ok, data(), priority()} | error(). + +peek(QueueRef) -> + epqueue_nif:peek(QueueRef). diff --git a/src/nifSrc/epqueue/src/epqueue_nif.erl b/src/nifSrc/epqueue/src/epqueue_nif.erl new file mode 100644 index 0000000..ba54d62 --- /dev/null +++ b/src/nifSrc/epqueue/src/epqueue_nif.erl @@ -0,0 +1,51 @@ +-module(epqueue_nif). + +-define(NOT_LOADED, not_loaded(?LINE)). + +-on_load(load_nif/0). + +-export([ + new/1, + size/1, + insert/3, + remove/2, + pop/1, + peek/1 +]). + +load_nif() -> + SoName = get_priv_path(?MODULE), + io:format(<<"Loading library: ~p ~n">>, [SoName]), + ok = erlang:load_nif(SoName, 0). + +new(_Options) -> + ?NOT_LOADED. + +size(_QueueRef) -> + ?NOT_LOADED. + +insert(_QueueRef, _Data, _Priority) -> + ?NOT_LOADED. + +remove(_QueueRef, _Ref) -> + ?NOT_LOADED. + +pop(_QueueRef) -> + ?NOT_LOADED. + +peek(_QueueRef) -> + ?NOT_LOADED. + +% internals + +get_priv_path(File) -> + case code:priv_dir(epqueue) of + {error, bad_name} -> + Ebin = filename:dirname(code:which(?MODULE)), + filename:join([filename:dirname(Ebin), "priv", File]); + Dir -> + filename:join(Dir, File) + end. + +not_loaded(Line) -> + erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}). diff --git a/src/nifSrc/epqueue/test/cover.spec b/src/nifSrc/epqueue/test/cover.spec new file mode 100644 index 0000000..ee41276 --- /dev/null +++ b/src/nifSrc/epqueue/test/cover.spec @@ -0,0 +1,8 @@ +{level, details}. +{incl_dirs, [ + "../_build/test/lib/epqueue/ebin" +]}. + +{excl_mods, [ + +]}. diff --git a/src/nifSrc/epqueue/test/integrity_test_SUITE.erl b/src/nifSrc/epqueue/test/integrity_test_SUITE.erl new file mode 100644 index 0000000..850dad0 --- /dev/null +++ b/src/nifSrc/epqueue/test/integrity_test_SUITE.erl @@ -0,0 +1,69 @@ +-module(integrity_test_SUITE). + +-compile(export_all). + +all() -> [ + {group, epqueue_group} +]. + +groups() -> [ + {epqueue_group, [sequence], [ + basic_ops, + empty_queue, + non_empty_queue, + test_remove, + test_pop + ]} +]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +basic_ops(_Config) -> + {ok, Q} = epqueue:new([]), + {ok, _Ref} = epqueue:insert(Q, 1, 1), + {ok, 1, 1} = epqueue:peek(Q), + 1 = epqueue:size(Q), + {ok, 1, 1} = epqueue:pop(Q), + 0 = epqueue:size(Q), + ok. + +empty_queue(_Config) -> + {ok, _} = epqueue:new([]), + ok. + +non_empty_queue(_Config) -> + {ok, Q1} = epqueue:new([]), + {ok, _} = epqueue:insert(Q1, 1, 1), + {ok, _} = epqueue:insert(Q1, 2, 2), + {ok, _} = epqueue:insert(Q1, 3, 3), + {ok, _} = epqueue:insert(Q1, 4, 4), + {ok, _} = epqueue:insert(Q1, 5, 5), + ok. + +test_remove(_Config) -> + {ok, Q1} = epqueue:new([]), + {ok, Q2} = epqueue:new([]), + {ok, Ref7} = epqueue:insert(Q2, 7, 7), + {ok, Ref3} = epqueue:insert(Q1, 3, 3), + {ok, Ref5} = epqueue:insert(Q1, 5, 5), + {ok, Ref1} = epqueue:insert(Q1, 1, 1), + false = epqueue:remove(Q1, Ref7), + true = epqueue:remove(Q1, Ref5), + true = epqueue:remove(Q1, Ref3), + true = epqueue:remove(Q1, Ref1), + true = epqueue:remove(Q2, Ref7), + ok. + +test_pop(_Config) -> + {ok, Q1} = epqueue:new([]), + epqueue:insert(Q1, 3, 3), + epqueue:insert(Q1, 5, 5), + epqueue:insert(Q1, 1, 1), + {ok, 1, 1} = epqueue:pop(Q1), + {ok, 3, 3} = epqueue:pop(Q1), + {ok, 5, 5} = epqueue:pop(Q1), + ok. \ No newline at end of file