@ -0,0 +1,20 @@ | |||||
#include "erl_nif.h" | |||||
static ERL_NIF_TERM pidToInt(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { | |||||
ErlNifUInt64 TermInt = (ErlNifUInt64)argv[0]; | |||||
return enif_make_uint64(env, TermInt); | |||||
} | |||||
static ERL_NIF_TERM intToPid(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { | |||||
ErlNifUInt64 Pid; | |||||
if (!enif_get_uint64(env, argv[0], &Pid)) | |||||
return enif_make_badarg(env); | |||||
return (ERL_NIF_TERM)Pid; | |||||
} | |||||
static ErlNifFunc nif_funcs[] = { | |||||
{"pidToInt", 1, pidToInt}, | |||||
{"intToPid", 1, intToPid} | |||||
}; | |||||
ERL_NIF_INIT(eGPidInt, nif_funcs, NULL, NULL, NULL, NULL); |
@ -0,0 +1,7 @@ | |||||
{port_specs, [ | |||||
{"../../priv/eGPidInt.so", ["*.c"]} | |||||
]}. | |||||
@ -0,0 +1,83 @@ | |||||
#include "erl_nif.h" | |||||
#include <stdatomic.h> | |||||
atomic_ullong LockSlot[1048576]; | |||||
ERL_NIF_TERM atomTrue; | |||||
ERL_NIF_TERM atomFalse; | |||||
ERL_NIF_TERM atomUndefined; | |||||
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; | |||||
} | |||||
ERL_NIF_TERM tryLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){ | |||||
unsigned int KeyIx; | |||||
if (!enif_get_uint(env, argv[0], &KeyIx)){ | |||||
return enif_make_badarg(env); | |||||
} | |||||
ErlNifPid ThePid; | |||||
enif_self(env, &ThePid); | |||||
atomic_ullong Expected = 0; | |||||
atomic_ullong Val = (atomic_ullong)(ThePid.pid); | |||||
if (atomic_compare_exchange_strong(&(LockSlot[KeyIx]), &Expected, Val)){ | |||||
return atomTrue; | |||||
}else{ | |||||
ThePid.pid = (ERL_NIF_TERM)Expected; | |||||
if (enif_is_process_alive(env, &ThePid)){ | |||||
return atomFalse; | |||||
}else{ | |||||
if (atomic_compare_exchange_strong(&LockSlot[KeyIx], &Expected, Val)) { | |||||
return atomTrue; | |||||
}else{ | |||||
return atomFalse; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
ERL_NIF_TERM releaseLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){ | |||||
unsigned int KeyIx; | |||||
if (!enif_get_uint(env, argv[0], &KeyIx)){ | |||||
return enif_make_badarg(env); | |||||
} | |||||
ErlNifPid ThePid; | |||||
enif_self(env, &ThePid); | |||||
atomic_ullong Expected = (atomic_ullong)(ThePid.pid); | |||||
atomic_ullong Val = 0; | |||||
if (atomic_compare_exchange_strong(&LockSlot[KeyIx], &Expected, Val)){ | |||||
return atomTrue; | |||||
}else{ | |||||
return atomFalse; | |||||
} | |||||
} | |||||
ERL_NIF_TERM getLockPid(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){ | |||||
unsigned int KeyIx; | |||||
if (!enif_get_uint(env, argv[0], &KeyIx)){ | |||||
return enif_make_badarg(env); | |||||
} | |||||
ErlNifPid ThePid; | |||||
atomic_ullong Var = atomic_load(&(LockSlot[KeyIx])); | |||||
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) |
@ -0,0 +1,7 @@ | |||||
{port_specs, [ | |||||
{"../../priv/eNifLock.so", ["*.c"]} | |||||
]}. | |||||
@ -0,0 +1,148 @@ | |||||
-module(eCLock). | |||||
-include("eGLock.hrl"). | |||||
-define(CASE(Cond, Then, That), case Cond of true -> Then; _ -> That end). | |||||
-export([ | |||||
tryLock/1 | |||||
, tryLock/2 | |||||
, releaseLock/1 | |||||
, lockApply/2 | |||||
, lockApply/3 | |||||
]). | |||||
-spec tryLock(KeyOrKeys :: term() | [term()]) -> true | ltimeout. | |||||
tryLock(KeyOrKeys) -> | |||||
tryLock(KeyOrKeys, ?LockTimeOut). | |||||
tryLock(KeyOrKeys, TimeOut) -> | |||||
case is_list(KeyOrKeys) of | |||||
true -> | |||||
KeyIxs = getKexIxs(KeyOrKeys, []), | |||||
doTryLocks(KeyIxs, TimeOut); | |||||
_ -> | |||||
doTryLock(erlang:phash2(KeyOrKeys, ?eGLockSize), TimeOut) | |||||
end. | |||||
doTryLock(KeyIx, TimeOut) -> | |||||
case eNifLock:tryLock(KeyIx) of | |||||
true -> | |||||
true; | |||||
_ -> | |||||
loopLock(KeyIx, TimeOut) | |||||
end. | |||||
loopLock(KeyIx, TimeOut) -> | |||||
receive | |||||
after ?ReTryTime -> | |||||
LTimeOut = ?CASE(TimeOut == infinity, TimeOut, TimeOut - ?ReTryTime), | |||||
case LTimeOut >= 0 of | |||||
true -> | |||||
case eNifLock:tryLock(KeyIx) of | |||||
true -> | |||||
true; | |||||
_ -> | |||||
loopLock(KeyIx, LTimeOut) | |||||
end; | |||||
_ -> | |||||
ltimeout | |||||
end | |||||
end. | |||||
doTryLocks(KeyIxs, TimeOut) -> | |||||
case tryLockAll(KeyIxs, []) of | |||||
true -> | |||||
true; | |||||
_ -> | |||||
loopLocks(KeyIxs, TimeOut) | |||||
end. | |||||
loopLocks(KeyIxs, TimeOut) -> | |||||
receive | |||||
after ?ReTryTime -> | |||||
LTimeOut = ?CASE(TimeOut == infinity, TimeOut, TimeOut - ?ReTryTime), | |||||
case LTimeOut >= 0 of | |||||
true -> | |||||
case tryLockAll(KeyIxs, []) of | |||||
true -> | |||||
true; | |||||
_ -> | |||||
loopLocks(KeyIxs, LTimeOut) | |||||
end; | |||||
_ -> | |||||
ltimeout | |||||
end | |||||
end. | |||||
-spec releaseLock(KeyOrKeys :: term() | [term()]) -> ok. | |||||
releaseLock(KeyOrKeys) -> | |||||
case is_list(KeyOrKeys) of | |||||
true -> | |||||
KeyIxs = getKexIxs(KeyOrKeys, []), | |||||
[eNifLock:releaseLock(OneKeyIx) || OneKeyIx <- KeyIxs], | |||||
ok; | |||||
_ -> | |||||
eNifLock:releaseLock(erlang:phash2(KeyOrKeys, ?eGLockSize)), | |||||
ok | |||||
end. | |||||
-spec lockApply(KeyOrKeys :: term() | [term()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}) -> term() | {error, ltimeout} | {error, {lock_apply_error, term()}}. | |||||
lockApply(KeyOrKeys, MFAOrFun) -> | |||||
lockApply(KeyOrKeys, MFAOrFun, ?LockTimeOut). | |||||
-spec lockApply(KeyOrKeys :: term() | [term()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}, TimeOut :: integer() | infinity) -> term(). | |||||
lockApply(KeyOrKeys, MFAOrFun, TimeOut) -> | |||||
case is_list(KeyOrKeys) of | |||||
true -> | |||||
KeyIxs = getKexIxs(KeyOrKeys, []), | |||||
case doTryLocks(KeyIxs, TimeOut) of | |||||
true -> | |||||
try doApply(MFAOrFun) | |||||
catch C:R:S -> | |||||
{error, {lock_apply_error, {C, R, S}}} | |||||
after | |||||
[eNifLock:releaseLock(OneKeyIx) || OneKeyIx <- KeyIxs], | |||||
ok | |||||
end; | |||||
ltimeout -> | |||||
{error, ltimeout} | |||||
end; | |||||
_ -> | |||||
KeyIx = erlang:phash2(KeyOrKeys, ?eGLockSize), | |||||
case doTryLock(KeyIx, TimeOut) of | |||||
true -> | |||||
try doApply(MFAOrFun) | |||||
catch C:R:S -> | |||||
{error, {lock_apply_error, {C, R, S}}} | |||||
after | |||||
eNifLock:releaseLock(KeyIx), | |||||
ok | |||||
end; | |||||
ltimeout -> | |||||
{error, ltimeout} | |||||
end | |||||
end. | |||||
getKexIxs([], IxAcc) -> IxAcc; | |||||
getKexIxs([Key | Keys], IxAcc) -> | |||||
KeyIx = erlang:phash2(Key, ?eGLockSize), | |||||
getKexIxs(Keys, ?CASE(lists:member(KeyIx, IxAcc), IxAcc, [KeyIx | IxAcc])). | |||||
tryLockAll([], _LockAcc) -> | |||||
true; | |||||
tryLockAll([KeyIx | KeyIxs], LockAcc) -> | |||||
case eNifLock:tryLock(KeyIx) of | |||||
true -> | |||||
tryLockAll(KeyIxs, [KeyIx | LockAcc]); | |||||
_ -> | |||||
[eNifLock:releaseLock(OneLock) || OneLock <- LockAcc], | |||||
false | |||||
end. | |||||
doApply({M, F, A}) -> | |||||
apply(M, F, A); | |||||
doApply({Fun, Args}) -> | |||||
apply(Fun, Args). | |||||
@ -0,0 +1,32 @@ | |||||
-module(eNifLock). | |||||
-export([tryLock/1, releaseLock/1, getLockPid/1]). | |||||
-on_load(init/0). | |||||
init() -> | |||||
SoName = | |||||
case code:priv_dir(?MODULE) of | |||||
{error, _} -> | |||||
case code:which(?MODULE) of | |||||
Filename when is_list(Filename) -> | |||||
filename:join([filename:dirname(Filename), "../priv", "eNifLock"]); | |||||
_ -> | |||||
filename:join("../priv", "eNifLock") | |||||
end; | |||||
Dir -> | |||||
filename:join(Dir, "eNifLock") | |||||
end, | |||||
erlang:load_nif(SoName, 0). | |||||
-spec tryLock(KeyIx :: non_neg_integer()) -> true | false. | |||||
tryLock(_KeyIx) -> | |||||
erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, ?LINE}]}). | |||||
-spec releaseLock(KeyIx :: non_neg_integer()) -> true | false. | |||||
releaseLock(_KeyIx) -> | |||||
erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, ?LINE}]}). | |||||
-spec getLockPid(KeyIx :: non_neg_integer()) -> pid() | undefined. | |||||
getLockPid(_KeyIx) -> | |||||
erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, ?LINE}]}). |