How to implement global locks?

I have been thinking about implementing a global lock in erlang to solve some scenario requirements, so I wrote the following code.


-define(EtsGLockKey, '$EtsGLockKey').
-define(LockTimeOut, 5000).
-define(ReTryTime, 10).

start() ->
	ets:new(?EtsGLockKey, [named_table, set, public, {write_concurrency, auto}, {read_concurrency, true}]).

-spec lockApplys(KeyOrKeys :: tuple() | [tuple()], MFAOrFun :: {M :: atom(), F :: atom(), Args :: list()} | {Fun :: function(), Args :: list()}, TimeOut :: integer() | infinity, FirstTime :: integer()) -> term().
lockApplys(Keys, MFAOrFun, TimeOut, FirstTime) ->
	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(?EtsGLockKey, element(1, OneKey)) || OneKey <- Keys],
				ok
			end;
		_ ->
			loopTrys(Keys, MFAOrFun, TimeOut, FirstTime)
	end.

loopTrys(Keys, MFAOrFun, TimeOut, FirstTime) ->
	receive
	after ?ReTryTime ->
		[Key | _] = Keys,
		case ets:lookup(?EtsGLockKey, element(1, Key)) of
			[] ->
				lockApplys(Keys, MFAOrFun, TimeOut, FirstTime);
			_ ->
				case TimeOut of
					infinity ->
						loopTrys(Keys, MFAOrFun, TimeOut, FirstTime);
					_ ->
						LTimeOut = TimeOut - abs(erlang:system_time(millisecond) - FirstTime),
						case LTimeOut =< 0 of
							true ->
								{error, {lock_timeout, Keys}};
							_ ->
								loopTrys(Keys, MFAOrFun, TimeOut, FirstTime)
						end
				end
		end
	end.

doApply({M, F, A}) ->
	apply(M, F, A);
doApply({Fun, Args}) ->
	apply(Fun, Args).

This code appears to be fine, but if the process is killed while executing a function inside the lock, the lock will not be released. I think if there’s an erlang:be_monitor(mgrpid) function that lets some administrative process monitor the state of that process, and if that process state is unexpected then the administrative process can help with some things but there’s no erlang:be_monitor(mgrpid), This function can be supported? Or is there a better way to implement global locks.

I’m not quite sure what you want to achieve here. This global lock seems to be node-local. Have you looked at the global module? That implements actual global locks. If you want to serialize access to a shared resource, the usual way is to wrap it in a gen_server.

i have see it. In fact, I don’t want to encapsulate a single node global lock implementation through gen_server. But I’ve already solved that problem with link. thanks

If someone is feeling Can take a look at this:eGLock

Yes, or the locks library. GitHub - uwiger/locks: A scalable, deadlock-resolving resource locker

Global locks are hard to get right, so it’s a good idea to reuse something that already works.
Global works fine unless you have scenarios that may deadlock. Then, locks can help.

2 Likes