|
|
@ -10,6 +10,9 @@ |
|
|
|
%% 数组数量 |
|
|
|
-define(eGLockSize, 2097152). |
|
|
|
|
|
|
|
%% 没有ets 表的key |
|
|
|
-define(undefTab, undefTab). |
|
|
|
|
|
|
|
-export([ |
|
|
|
tryLock/1 |
|
|
|
, tryLock/2 |
|
|
@ -19,13 +22,11 @@ |
|
|
|
, lockApply/3 |
|
|
|
]). |
|
|
|
|
|
|
|
|
|
|
|
-export([ |
|
|
|
lockGet/1 |
|
|
|
, lockGet/2 |
|
|
|
, transaction/2 |
|
|
|
, transaction/3 |
|
|
|
|
|
|
|
]). |
|
|
|
|
|
|
|
-spec tryLock(KeyOrKeys :: term() | [term()]) -> true | lockTimeout. |
|
|
@ -162,6 +163,21 @@ lockGet(KeyOrKeys) -> |
|
|
|
lockGet(KeyOrKeys, ?LockTimeOut). |
|
|
|
|
|
|
|
-spec lockGet(KeyOrKeys :: term() | [term()], TimeOut :: integer() | infinity) -> ok. |
|
|
|
lockGet({?undefTab, Key = GetKey}, TimeOut) -> |
|
|
|
KeyIx = erlang:phash2(GetKey, ?eGLockSize), |
|
|
|
case doTryLock(KeyIx, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
|
#{{?undefTab, GetKey} => undefined} |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lockGetError, {?undefTab, Key}, {C, R, S}}} |
|
|
|
after |
|
|
|
eNifLock:releaseLock(KeyIx), |
|
|
|
ok |
|
|
|
end; |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end; |
|
|
|
lockGet({EtsTab, Key} = GetKey, TimeOut) -> |
|
|
|
KeyIx = erlang:phash2(GetKey, ?eGLockSize), |
|
|
|
case doTryLock(KeyIx, TimeOut) of |
|
|
@ -177,6 +193,21 @@ lockGet({EtsTab, Key} = GetKey, TimeOut) -> |
|
|
|
lockTimeout -> |
|
|
|
{error, lockTimeout} |
|
|
|
end; |
|
|
|
lockGet({?undefTab, Key = GetKey, DefValue}, TimeOut) -> |
|
|
|
KeyIx = erlang:phash2(GetKey, ?eGLockSize), |
|
|
|
case doTryLock(KeyIx, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
|
#{{?undefTab, GetKey} => getDefValue(DefValue)} |
|
|
|
catch C:R:S -> |
|
|
|
{error, {lockGetError, {?undefTab, Key, DefValue}, {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), |
|
|
@ -194,9 +225,7 @@ lockGet({EtsTab, Key, DefValue}, TimeOut) -> |
|
|
|
{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, []), |
|
|
|
{KeyIxs, KeysMap} = getKeyIxAndMaps(EtsTabKeys, [], #{}), |
|
|
|
case doTryLocks(KeyIxs, TimeOut) of |
|
|
|
true -> |
|
|
|
try |
|
|
@ -211,6 +240,28 @@ lockGet(EtsTabKeys, TimeOut) -> |
|
|
|
{error, lockTimeout} |
|
|
|
end. |
|
|
|
|
|
|
|
getKeyIxAndMaps([], IxAcc, KeysMap) -> {IxAcc, KeysMap}; |
|
|
|
getKeyIxAndMaps([Key | Keys], IxAcc, KeysMap) -> |
|
|
|
case Key of |
|
|
|
{?undefTab, JustKey} -> |
|
|
|
GetKey = Key, |
|
|
|
LDefValue = undefined, |
|
|
|
KeyIx = erlang:phash2(Key, ?eGLockSize); |
|
|
|
{?undefTab, JustKey, DefValue} -> |
|
|
|
GetKey = {?undefTab, JustKey}, |
|
|
|
LDefValue = DefValue, |
|
|
|
KeyIx = erlang:phash2(Key, ?eGLockSize); |
|
|
|
{EtsTab, TabKey} -> |
|
|
|
GetKey = Key, |
|
|
|
LDefValue = undefined, |
|
|
|
KeyIx = erlang:phash2(Key, ?eGLockSize); |
|
|
|
{EtsTab, TabKey, DefValue} -> |
|
|
|
GetKey = {EtsTab, TabKey}, |
|
|
|
LDefValue = DefValue, |
|
|
|
KeyIx = erlang:phash2(Key, ?eGLockSize) |
|
|
|
end, |
|
|
|
getKeyIxAndMaps(Keys, ?CASE(lists:member(KeyIx, IxAcc), IxAcc, [KeyIx | IxAcc]), KeysMap#{GetKey => LDefValue}). |
|
|
|
|
|
|
|
transactionApply({M, F, Args}, EtsTabValue) -> |
|
|
|
apply(M, F, [EtsTabValue | Args]); |
|
|
|
transactionApply({Fun, Args}, EtsTabValue) -> |
|
|
@ -222,22 +273,22 @@ transaction(EtsTabKeys, MFAOrFun) -> |
|
|
|
|
|
|
|
-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, []), |
|
|
|
{KeyIxs, KeysMap} = getKeyIxAndMaps(EtsTabKeys, [], #{}), |
|
|
|
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} -> |
|
|
|
{ok, Ret, ChangeEtsTab} -> |
|
|
|
[changeEtsTabValue(OneEtsTab, OneKey, ChangeValue) || {OneEtsTab, OneKey} := ChangeValue <- ChangeEtsTab], |
|
|
|
Ret; |
|
|
|
Ret -> |
|
|
|
Ret |
|
|
|
{ok, Ret} -> |
|
|
|
Ret; |
|
|
|
{error, Err} -> |
|
|
|
Err |
|
|
|
end |
|
|
|
catch |
|
|
|
throw:Throw-> Throw; |
|
|
|
throw:Throw -> Throw; |
|
|
|
C:R:S -> |
|
|
|
{error, {lockTransactionError, {MFAOrFun, EtsTabKeys}, {C, R, S}}} |
|
|
|
after |
|
|
@ -248,22 +299,23 @@ transaction(EtsTabKeys, MFAOrFun, TimeOut) -> |
|
|
|
{error, lockTimeout} |
|
|
|
end. |
|
|
|
|
|
|
|
getEtsTabValue(undefined, _Key, DefValue) -> |
|
|
|
DefValue; |
|
|
|
getDefValue(undefined) -> undefined; |
|
|
|
getDefValue({DefFun, Args}) when is_function(DefFun) -> erlang:apply(DefFun, Args); |
|
|
|
getDefValue(DefValue) -> DefValue. |
|
|
|
|
|
|
|
getEtsTabValue(?undefTab, _Key, DefValue) -> |
|
|
|
getDefValue(DefValue); |
|
|
|
getEtsTabValue(Ets, Key, DefValue) -> |
|
|
|
case ets:lookup(Ets, Key) of |
|
|
|
[] -> |
|
|
|
DefValue; |
|
|
|
getDefValue(DefValue); |
|
|
|
[OneValue] -> |
|
|
|
OneValue |
|
|
|
end. |
|
|
|
|
|
|
|
changeEtsTabValue(undefined, _Key, _Value) -> |
|
|
|
changeEtsTabValue(?undefTab, _Key, _Value) -> |
|
|
|
ok; |
|
|
|
changeEtsTabValue(Ets, Key, delete) -> |
|
|
|
ets:delete(Ets, Key); |
|
|
|
changeEtsTabValue(Ets, _Key, Value) -> |
|
|
|
ets:insert(Ets, Value). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ets:insert(Ets, Value). |