@ -0,0 +1,29 @@ | |||||
.eunit | |||||
*.o | |||||
*.beam | |||||
*.plt | |||||
erl_crash.dump | |||||
.concrete/DEV_MODE | |||||
# rebar 2.x | |||||
.rebar | |||||
rel/example_project | |||||
ebin/* | |||||
deps | |||||
# rebar 3 | |||||
.rebar3 | |||||
_build/ | |||||
_checkouts/ | |||||
rebar.lock | |||||
# idea | |||||
.idea | |||||
*.iml | |||||
cmake-build* | |||||
CMakeLists.txt | |||||
# nif compile temp file | |||||
*.pdb | |||||
*.d | |||||
compile_commands.json |
@ -0,0 +1,21 @@ | |||||
MIT License | |||||
Copyright (c) 2024 AICells | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. |
@ -0,0 +1,8 @@ | |||||
eGLock | |||||
===== | |||||
erlang的全局锁,不可重入。写这个的目的是考虑slg大地图场景的战斗会存在大乱斗,各个战斗目标血量处理需要全局锁处理。 | |||||
Build | |||||
----- | |||||
$ rebar3 compile |
@ -0,0 +1,10 @@ | |||||
%% 锁key | |||||
-define(EtsGLockKey, '$EtsGLockKey'). | |||||
%% 锁等待pid | |||||
-define(EtsGLockPid, '$EtsGLockPid'). | |||||
%% 锁等待pid | |||||
-define(ReTryLockApply, '$ReTryLockApply'). | |||||
%% 默认超时时间 | |||||
-define(LockTimeOut, 5000). |
@ -0,0 +1,8 @@ | |||||
{erl_opts, [no_debug_info, deterministic, {i, "include"}, nowarn_unused_function, nowarn_unused_vars, nowarn_export_all]}. | |||||
{deps, []}. | |||||
{shell, [ | |||||
% {config, "config/sys.config"}, | |||||
{apps, [eGLock]} | |||||
]}. |
@ -0,0 +1,11 @@ | |||||
{application, eGLock, | |||||
[{description, "An OTP application"}, | |||||
{vsn, "0.1.0"}, | |||||
{registered, []}, | |||||
{mod, {eGLock_app, []}}, | |||||
{applications, [kernel, stdlib]}, | |||||
{env,[]}, | |||||
{modules, []}, | |||||
{licenses, ["MIT License"]}, | |||||
{links, []} | |||||
]}. |
@ -0,0 +1,107 @@ | |||||
-module(eGLock). | |||||
-include("eGLock.hrl"). | |||||
-export([ | |||||
lockApply/2 | |||||
, lockApply/3 | |||||
]). | |||||
-spec lockApply(KeyOrKeys :: tuple() |[tuple()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}) -> term(). | |||||
lockApply(KeyOrKeys, MFAOrFun) -> | |||||
lockApply(KeyOrKeys, MFAOrFun, ?LockTimeOut). | |||||
-spec lockApply(KeyOrKeys :: tuple() |[tuple()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list(), TimeOut :: integer() | infinity) -> term(). | |||||
lockApply(KeyOrKeys, MFAOrFun, TimeOut) -> | |||||
case KeyOrKeys of | |||||
{_, _} -> | |||||
lockApply(KeyOrKeys, MFAOrFun, TimeOut, erlang:system_time(microsecond)); | |||||
_ -> | |||||
lockApplys(KeyOrKeys, MFAOrFun, TimeOut, erlang:system_time(microsecond)) | |||||
end. | |||||
lockApply(Key, MFAOrFun, TimeOut, LastTime) -> | |||||
SelfPid = self(), | |||||
PidInfo = {Key, SelfPid}, | |||||
ets:insert(?EtsGLockPid, PidInfo), | |||||
case ets:insert_new(?EtsGLockKey, Key) of | |||||
true -> | |||||
try doApply(MFAOrFun) | |||||
catch C:R:S -> | |||||
{error, {lock_apply_error, {C, R, S}}} | |||||
after | |||||
ets:delete_object(?EtsGLockPid, PidInfo), | |||||
ets:delete(?EtsGLockKey, element(1, Key)), | |||||
WaitLockList = ets:lookup(?EtsGLockPid, Key), | |||||
[WaitPid ! ?ReTryLockApply || {_Key, WaitPid} <- WaitLockList], | |||||
ok | |||||
end; | |||||
_ -> | |||||
receive | |||||
?ReTryLockApply -> | |||||
case TimeOut of | |||||
infinity -> | |||||
lockApply(Key, MFAOrFun, TimeOut, LastTime); | |||||
_ -> | |||||
CurTime = erlang:system_time(microsecond), | |||||
NewTimeOut = TimeOut - max(CurTime - LastTime, 0), | |||||
lockApply(Key, MFAOrFun, NewTimeOut, CurTime) | |||||
end | |||||
after TimeOut -> | |||||
ets:delete_object(?EtsGLockPid, PidInfo), | |||||
{error, {lock_timeout, Key}} | |||||
end | |||||
end. | |||||
lockApplys(Keys, MFAOrFun, TimeOut, LastTime) -> | |||||
SelfPid = self(), | |||||
AllPidInfo = [{OneKey, SelfPid} || OneKey <- Keys], | |||||
ets:insert(?EtsGLockPid, AllPidInfo), | |||||
case ets:insert_new(?EtsGLockKey, Keys) of | |||||
true -> | |||||
try doApply(MFAOrFun) | |||||
catch C:R:S -> | |||||
{error, {lock_apply_error, {C, R, S}}} | |||||
after | |||||
[ets:delete_object(?EtsGLockPid, OnePidInfo) || OnePidInfo <- AllPidInfo], | |||||
[ets:delete(?EtsGLockKey, element(1, OneKey)) || OneKey <- Keys], | |||||
notifyKeys(Keys, #{}), | |||||
ok | |||||
end; | |||||
_ -> | |||||
receive | |||||
?ReTryLockApply -> | |||||
case TimeOut of | |||||
infinity -> | |||||
lockApplys(Keys, MFAOrFun, TimeOut, LastTime); | |||||
_ -> | |||||
CurTime = erlang:system_time(microsecond), | |||||
NewTimeOut = TimeOut - max(CurTime - LastTime, 0), | |||||
lockApplys(Keys, MFAOrFun, NewTimeOut, CurTime) | |||||
end | |||||
after TimeOut -> | |||||
[ets:delete_object(?EtsGLockPid, OnePidInfo) || OnePidInfo <- AllPidInfo], | |||||
{error, {lock_timeout, Keys}} | |||||
end | |||||
end. | |||||
doApply({M, F, A}) -> | |||||
apply(M, F, A); | |||||
doApply({Fun, Args}) -> | |||||
apply(Fun, Args). | |||||
notifyKeys([], _AccMap) -> ok; | |||||
notifyKeys([OneKey | Keys], AccMap) -> | |||||
WaitLockList = ets:lookup(?EtsGLockPid, OneKey), | |||||
NewAccMap = notifyToPid(WaitLockList, AccMap), | |||||
notifyKeys(Keys, NewAccMap). | |||||
notifyToPid([], AccMap) -> AccMap; | |||||
notifyToPid([{_OneKey, WaitPid} | WaitLockList], AccMap) -> | |||||
case maps:is_key(WaitPid, AccMap) of | |||||
true -> | |||||
notifyToPid(WaitLockList, AccMap); | |||||
_ -> | |||||
WaitPid ! ?ReTryLockApply, | |||||
notifyToPid(WaitLockList, AccMap#{WaitPid => true}) | |||||
end. |
@ -0,0 +1,11 @@ | |||||
-module(eGLock_app). | |||||
-behaviour(application). | |||||
-export([start/2, stop/1]). | |||||
start(_StartType, _StartArgs) -> | |||||
eGLock_sup:start_link(). | |||||
stop(_State) -> | |||||
ok. |
@ -0,0 +1,18 @@ | |||||
-module(eGLock_sup). | |||||
-behaviour(supervisor). | |||||
-include("eGLock.hrl"). | |||||
-export([start_link/0]). | |||||
-export([init/1]). | |||||
-define(SERVER, ?MODULE). | |||||
start_link() -> | |||||
supervisor:start_link({local, ?SERVER}, ?MODULE, []). | |||||
init([]) -> | |||||
SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, | |||||
ets:new(?EtsGLockKey, [named_table, set, public, {write_concurrency, true}]), | |||||
ets:new(?EtsGLockPid, [named_table, bag, public, {write_concurrency, auto}, {read_concurrency, true}]), | |||||
{ok, {SupFlags, []}}. |