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