#include "erl_nif.h" #include using namespace std; const int LockSize = 2097152; const int HashSalt = 786234121; atomic LockSlot[LockSize]; ERL_NIF_TERM atomTrue; ERL_NIF_TERM atomFalse; ERL_NIF_TERM atomUndefined; typedef struct KeyNode_r{ int KeyIx; struct KeyNode_r *next; } KeyNode; bool isNotLocked(KeyNode *LockedHead, int KeyIx){ KeyNode *temp = LockedHead; while (temp != NULL){ if (temp->KeyIx == KeyIx) return false; temp = temp->next; } return true; } int nifLoad(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM){ atomTrue = enif_make_atom(env, "true"); atomFalse = enif_make_atom(env, "false"); atomUndefined = enif_make_atom(env, "undefined"); return 0; } bool lockOne(ErlNifEnv *env, ErlNifPid *ThePid, int KeyIx, uint64_t Val){ uint64_t Expected = 0; if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val)){ return true; }else{ ThePid->pid = (ERL_NIF_TERM)Expected; if (enif_is_process_alive(env, ThePid)){ return false; }else{ if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val)){ return true; }else{ return false; } } } } ERL_NIF_TERM tryLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){ if (enif_is_list(env, argv[0])){ ERL_NIF_TERM allList = argv[0]; ERL_NIF_TERM head; ErlNifPid ThePid; enif_self(env, &ThePid); uint64_t Val = (uint64_t)(ThePid.pid); int KeyIx; KeyNode *LockedHead = NULL; while (enif_get_list_cell(env, allList, &head, &allList)){ KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, head, HashSalt) % LockSize; KeyNode OneKeyNode = {KeyIx, LockedHead}; if (isNotLocked(LockedHead, KeyIx)){ if (lockOne(env, &ThePid, KeyIx, Val)){ LockedHead = &OneKeyNode; }else{ uint64_t RExpected; KeyNode *temp = LockedHead; while (temp != NULL){ RExpected = Val; LockSlot[temp->KeyIx].compare_exchange_strong(RExpected, 0); temp = temp->next; } } } } return atomTrue; }else{ int KeyIx; KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize; ErlNifPid ThePid; enif_self(env, &ThePid); uint64_t Val = (uint64_t)(ThePid.pid); if (lockOne(env, &ThePid, KeyIx, Val)){ return atomTrue; }else{ return atomFalse; } } } ERL_NIF_TERM releaseLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){ if (enif_is_list(env, argv[0])){ ERL_NIF_TERM allList = argv[0]; ERL_NIF_TERM head; ErlNifPid ThePid; enif_self(env, &ThePid); uint64_t Expected = (uint64_t)(ThePid.pid); uint64_t RExpected; int KeyIx; int isAllOk = 1; while (enif_get_list_cell(env, allList, &head, &allList)){ KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, head, HashSalt) % LockSize; RExpected = Expected; if (!LockSlot[KeyIx].compare_exchange_strong(RExpected, 0)){ isAllOk = 0; } } return isAllOk > 0 ? atomTrue : atomFalse; }else{ int KeyIx; KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize; ErlNifPid ThePid; enif_self(env, &ThePid); uint64_t Expected = (uint64_t)(ThePid.pid); if (LockSlot[KeyIx].compare_exchange_strong(Expected, 0)){ return atomTrue; }else{ return atomFalse; } } } ERL_NIF_TERM getLockPid(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){ int KeyIx; KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize; ErlNifPid ThePid; uint64_t Var = LockSlot[KeyIx].load(); if (Var > 0){ ThePid.pid = (ERL_NIF_TERM)Var; return enif_make_pid(env, &ThePid); }else{ return atomUndefined; } } static ErlNifFunc nifFuncs[] = { {"tryLock", 1, tryLock}, {"releaseLock", 1, releaseLock}, {"getLockPid", 1, getLockPid} }; ERL_NIF_INIT(eNifLock, nifFuncs, &nifLoad, NULL, NULL, NULL)