From fcb03e7b4df4091b9cd4a285f5ac5555d73ea12f Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Thu, 11 Apr 2024 15:41:41 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E5=AE=8C=E6=88=90=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 29 ++++++++++++ LICENSE | 21 +++++++++ README.md | 8 ++++ include/eGLock.hrl | 10 +++++ rebar.config | 8 ++++ src/eGLock.app.src | 11 +++++ src/eGLock.erl | 107 +++++++++++++++++++++++++++++++++++++++++++++ src/eGLock_app.erl | 11 +++++ src/eGLock_sup.erl | 18 ++++++++ 9 files changed, 223 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 include/eGLock.hrl create mode 100644 rebar.config create mode 100644 src/eGLock.app.src create mode 100644 src/eGLock.erl create mode 100644 src/eGLock_app.erl create mode 100644 src/eGLock_sup.erl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ad44f1 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f5121a0 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..daa4496 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +eGLock +===== + erlang的全局锁,不可重入。写这个的目的是考虑slg大地图场景的战斗会存在大乱斗,各个战斗目标血量处理需要全局锁处理。 + +Build +----- + + $ rebar3 compile diff --git a/include/eGLock.hrl b/include/eGLock.hrl new file mode 100644 index 0000000..ff66498 --- /dev/null +++ b/include/eGLock.hrl @@ -0,0 +1,10 @@ +%% 锁key +-define(EtsGLockKey, '$EtsGLockKey'). +%% 锁等待pid +-define(EtsGLockPid, '$EtsGLockPid'). + +%% 锁等待pid +-define(ReTryLockApply, '$ReTryLockApply'). + +%% 默认超时时间 +-define(LockTimeOut, 5000). diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..fe4376c --- /dev/null +++ b/rebar.config @@ -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]} +]}. diff --git a/src/eGLock.app.src b/src/eGLock.app.src new file mode 100644 index 0000000..ad6037d --- /dev/null +++ b/src/eGLock.app.src @@ -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, []} + ]}. diff --git a/src/eGLock.erl b/src/eGLock.erl new file mode 100644 index 0000000..35cd763 --- /dev/null +++ b/src/eGLock.erl @@ -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. \ No newline at end of file diff --git a/src/eGLock_app.erl b/src/eGLock_app.erl new file mode 100644 index 0000000..3cafeb2 --- /dev/null +++ b/src/eGLock_app.erl @@ -0,0 +1,11 @@ +-module(eGLock_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + eGLock_sup:start_link(). + +stop(_State) -> + ok. \ No newline at end of file diff --git a/src/eGLock_sup.erl b/src/eGLock_sup.erl new file mode 100644 index 0000000..abae64c --- /dev/null +++ b/src/eGLock_sup.erl @@ -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, []}}.