|
|
@ -1,6 +1,7 @@ |
|
|
|
-module(eGLock). |
|
|
|
|
|
|
|
-define(CASE(Cond, Then, That), case Cond of true -> Then; _ -> That end). |
|
|
|
-define(CASE(Expr, Expect, Then, ExprRet, That), case Expr of Expect -> Then; ExprRet -> That end). |
|
|
|
|
|
|
|
%% 默认超时时间单位:Ms |
|
|
|
-define(LockTimeOut, 5000). |
|
|
@ -18,7 +19,16 @@ |
|
|
|
, lockApply/3 |
|
|
|
]). |
|
|
|
|
|
|
|
-spec tryLock(KeyOrKeys :: term() | [term()]) -> true | ltimeout. |
|
|
|
|
|
|
|
-export([ |
|
|
|
lockGet/1 |
|
|
|
, lockGet/2 |
|
|
|
, transaction/2 |
|
|
|
, transaction/3 |
|
|
|
|
|
|
|
]). |
|
|
|
|
|
|
|
-spec tryLock(KeyOrKeys :: term() | [term()]) -> true | lockTimeout. |
|
|
|
tryLock(KeyOrKeys) -> |
|
|
|
tryLock(KeyOrKeys, ?LockTimeOut). |
|
|
|
|
|
|
@ -52,7 +62,7 @@ loopLock(KeyIx, TimeOut) -> |
|
|
|
loopLock(KeyIx, LTimeOut) |
|
|
|
end; |
|
|
|
_ -> |
|
|
|
ltimeout |
|
|
|
lockTimeout |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
@ -77,7 +87,7 @@ loopLocks(KeyIxs, TimeOut) -> |
|
|
|
loopLocks(KeyIxs, LTimeOut) |
|
|
|
end; |
|
|
|
_ -> |
|
|
|
ltimeout |
|
|
|
lockTimeout |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
@ -100,7 +110,7 @@ getLockPid(KeyOrKeys) -> |
|
|
|
{KeyOrKeys, eNifLock:getLockPid(erlang:phash2(KeyOrKeys, ?eGLockSize))} |
|
|
|
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()}}. |
|
|
|
-spec lockApply(KeyOrKeys :: term() | [term()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}) -> term() | {error, lockTimeout} | {error, {lockApplyError, term()}}. |
|
|
|
lockApply(KeyOrKeys, MFAOrFun) -> |
|
|
|
lockApply(KeyOrKeys, MFAOrFun, ?LockTimeOut). |
|
|
|
|
|
|
@ -113,13 +123,13 @@ lockApply(KeyOrKeys, MFAOrFun, TimeOut) -> |
|
|
|
true -> |
|
|
|
try doApply(MFAOrFun) |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lock_apply_error, {C, R, S}}} |
|
|
|
{error, {lockApplyError, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLocks(KeyIxs), |
|
|
|
ok |
|
|
|
end; |
|
|
|
ltimeout -> |
|
|
|
{error, ltimeout} |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end; |
|
|
|
_ -> |
|
|
|
KeyIx = erlang:phash2(KeyOrKeys, ?eGLockSize), |
|
|
@ -127,13 +137,13 @@ lockApply(KeyOrKeys, MFAOrFun, TimeOut) -> |
|
|
|
true -> |
|
|
|
try doApply(MFAOrFun) |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lock_apply_error, {C, R, S}}} |
|
|
|
{error, {lockApplyError, {KeyOrKeys, MFAOrFun}, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLock(KeyIx), |
|
|
|
ok |
|
|
|
end; |
|
|
|
ltimeout -> |
|
|
|
{error, ltimeout} |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
@ -147,5 +157,113 @@ doApply({M, F, Args}) -> |
|
|
|
doApply({Fun, Args}) -> |
|
|
|
apply(Fun, Args). |
|
|
|
|
|
|
|
-spec lockGet(KeyOrKeys :: term() | [term()]) -> ok. |
|
|
|
lockGet(KeyOrKeys) -> |
|
|
|
lockGet(KeyOrKeys, ?LockTimeOut). |
|
|
|
|
|
|
|
-spec lockGet(KeyOrKeys :: term() | [term()], TimeOut :: integer() | infinity) -> ok. |
|
|
|
lockGet({EtsTab, Key} = GetKey, TimeOut) -> |
|
|
|
KeyIx = erlang:phash2(GetKey, ?eGLockSize), |
|
|
|
case doTryLock(KeyIx, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
|
#{GetKey => getEtsTabValue(EtsTab, Key, undefined)} |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lockGetError, GetKey, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLock(KeyIx), |
|
|
|
ok |
|
|
|
end; |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end; |
|
|
|
lockGet({EtsTab, Key, DefValue}, TimeOut) -> |
|
|
|
GetKey = {EtsTab, Key}, |
|
|
|
KeyIx = erlang:phash2(GetKey, ?eGLockSize), |
|
|
|
case doTryLock(KeyIx, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
|
#{GetKey => getEtsTabValue(EtsTab, Key, DefValue)} |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lockGetError, {EtsTab, Key, DefValue}, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLock(KeyIx), |
|
|
|
ok |
|
|
|
end; |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end; |
|
|
|
lockGet(EtsTabKeys, TimeOut) -> |
|
|
|
KeysMap = #{{EtsTab, Key} => DefValue || OneEtsTabKey <- EtsTabKeys, case OneEtsTabKey of {EtsTab, Key} -> DefValue = undefined, true; {EtsTab, Key, DefValue} -> true end}, |
|
|
|
KeyOrKeys = maps:keys(KeysMap), |
|
|
|
KeyIxs = getKexIxs(KeyOrKeys, []), |
|
|
|
case doTryLocks(KeyIxs, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
|
#{OneGetKey => getEtsTabValue(OneEtsTab, OneKey, OneDefValue) || {OneEtsTab, OneKey} = OneGetKey := OneDefValue <- KeysMap} |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lockGetError, EtsTabKeys, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLocks(KeyIxs), |
|
|
|
ok |
|
|
|
end; |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end. |
|
|
|
|
|
|
|
transactionApply({M, F, Args}, EtsTabValue) -> |
|
|
|
apply(M, F, [EtsTabValue | Args]); |
|
|
|
transactionApply({Fun, Args}, EtsTabValue) -> |
|
|
|
apply(Fun, [EtsTabValue | Args]). |
|
|
|
|
|
|
|
-spec transaction(KeyOrKeys :: term() | [term()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}) -> term(). |
|
|
|
transaction(EtsTabKeys, MFAOrFun) -> |
|
|
|
transaction(EtsTabKeys, MFAOrFun, ?LockTimeOut). |
|
|
|
|
|
|
|
-spec transaction(KeyOrKeys :: term() | [term()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}, TimeOut :: integer() | infinity) -> term(). |
|
|
|
transaction(EtsTabKeys, MFAOrFun, TimeOut) -> |
|
|
|
KeysMap = #{{EtsTab, Key} => DefValue || OneEtsTabKey <- EtsTabKeys, case OneEtsTabKey of {EtsTab, Key} -> DefValue = undefined, true; {EtsTab, Key, DefValue} -> true end}, |
|
|
|
KeyOrKeys = maps:keys(KeysMap), |
|
|
|
KeyIxs = getKexIxs(KeyOrKeys, []), |
|
|
|
case doTryLocks(KeyIxs, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
|
EtsTabValue = #{OneGetKey => getEtsTabValue(OneEtsTab, OneKey, OneDefValue) || {OneEtsTab, OneKey} = OneGetKey := OneDefValue <- KeysMap}, |
|
|
|
case transactionApply(MFAOrFun, EtsTabValue) of |
|
|
|
{Ret, ChangeEtsTab} -> |
|
|
|
[changeEtsTabValue(OneEtsTab, OneKey, ChangeValue) || {OneEtsTab, OneKey} := ChangeValue <- ChangeEtsTab], |
|
|
|
Ret; |
|
|
|
Ret -> |
|
|
|
Ret |
|
|
|
end |
|
|
|
catch |
|
|
|
throw:Ret -> Ret; |
|
|
|
C:R:S -> |
|
|
|
{error, {lockTransactionError, {MFAOrFun, EtsTabKeys}, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLocks(KeyIxs), |
|
|
|
ok |
|
|
|
end; |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end. |
|
|
|
|
|
|
|
getEtsTabValue(undefined, _Key, DefValue) -> |
|
|
|
DefValue; |
|
|
|
getEtsTabValue(Ets, Key, DefValue) -> |
|
|
|
case ets:lookup(Ets, Key) of |
|
|
|
[] -> |
|
|
|
DefValue; |
|
|
|
[OneValue] -> |
|
|
|
OneValue |
|
|
|
end. |
|
|
|
|
|
|
|
changeEtsTabValue(undefined, _Key, _Value) -> |
|
|
|
ok; |
|
|
|
changeEtsTabValue(Ets, Key, delete) -> |
|
|
|
ets:delete(Ets, Key); |
|
|
|
changeEtsTabValue(Ets, _Key, Value) -> |
|
|
|
ets:insert(Ets, Value). |
|
|
|
|
|
|
|
|
|
|
|
|