Quellcode durchsuchen

ft: 添加clock

master
SisMaker vor 1 Jahr
Ursprung
Commit
27ea5a120f
6 geänderte Dateien mit 297 neuen und 0 gelöschten Zeilen
  1. +20
    -0
      c_src/eCLock/eCLock.c
  2. +7
    -0
      c_src/eCLock/rebar.config
  3. +83
    -0
      c_src/eNifLock/eNifLock.c
  4. +7
    -0
      c_src/eNifLock/rebar.config
  5. +148
    -0
      src/eCLock.erl
  6. +32
    -0
      src/eNifLock.erl

+ 20
- 0
c_src/eCLock/eCLock.c Datei anzeigen

@ -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);

+ 7
- 0
c_src/eCLock/rebar.config Datei anzeigen

@ -0,0 +1,7 @@
{port_specs, [
{"../../priv/eGPidInt.so", ["*.c"]}
]}.

+ 83
- 0
c_src/eNifLock/eNifLock.c Datei anzeigen

@ -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)

+ 7
- 0
c_src/eNifLock/rebar.config Datei anzeigen

@ -0,0 +1,7 @@
{port_specs, [
{"../../priv/eNifLock.so", ["*.c"]}
]}.

+ 148
- 0
src/eCLock.erl Datei anzeigen

@ -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).

+ 32
- 0
src/eNifLock.erl Datei anzeigen

@ -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}]}).

Laden…
Abbrechen
Speichern