erlang's global lock
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 

188 righe
4.6 KiB

#include "erl_nif.h"
#include <atomic>
using namespace std;
const int LockSize = 2097152;
const int HashSalt = 786234121;
atomic<uint64_t> 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)