Skip to content

Commit

Permalink
Making locks_leader pass locks-test
Browse files Browse the repository at this point in the history
Ref. https://github.com/ten0s/locks-test

Fixes include adding an election ref value, to sync with cands.
This value is kept when the leader enters the safe loop.
Also, an 'assert_leader' message is introduced, to ensure that
candidates are kicked out of the safe loop when a leader acquires
a new lock and remains the leader.

Also, a locks_agent:async_await_all_locks/1 function was added
to ensure that the leader gets told that it has all locks in a
contested scenario.
  • Loading branch information
uwiger committed Dec 11, 2017
1 parent bc108d7 commit 8ce6222
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 67 deletions.
37 changes: 32 additions & 5 deletions src/locks_agent.erl
Expand Up @@ -48,6 +48,7 @@
lock_objects/2,
surrender_nowait/4,
await_all_locks/1,
async_await_all_locks/1,
monitor_nodes/2,
change_flag/3,
lock_info/1,
Expand Down Expand Up @@ -102,14 +103,17 @@
claim_no = 0,
require = all}).

-type monitored_nodes() :: [{node(), reference()}].
-type down_nodes() :: [node()].

-record(state, {
locks :: ets:tab(),
agents :: ets:tab(),
interesting = [] :: [lock_id()],
claim_no = 0 :: integer(),
requests :: ets:tab(),
down = [] :: [node()],
monitored = [] :: [{node(), reference()}],
down = [] :: down_nodes(),
monitored = [] :: monitored_nodes(),
await_nodes = false :: boolean(),
monitor_nodes = false :: boolean(),
pending :: ets:tab(),
Expand Down Expand Up @@ -332,6 +336,9 @@ begin_transaction(Objects, Opts) ->
await_all_locks(Agent) ->
call(Agent, await_all_locks,infinity).

async_await_all_locks(Agent) ->
cast(Agent, {await_all_locks, self()}).

-spec monitor_nodes(agent(), boolean()) -> boolean().
%% @doc Toggles monitoring of nodes, like net_kernel:monitor_nodes/1.
%%
Expand Down Expand Up @@ -391,6 +398,9 @@ change_flag(Agent, Option, Bool)
is_boolean(Bool), Option == notify ->
gen_server:cast(Agent, {option, Option, Bool}).

cast(Agent, Msg) ->
gen_server:cast(Agent, Msg).

cast(Agent, Msg, Ref) ->
gen_server:cast(Agent, Msg),
await_reply(Ref).
Expand Down Expand Up @@ -541,6 +551,15 @@ handle_cast({lock, Object, Mode, Nodes, Require, Wait, Client, Tag} = _Req,
{true, S1} ->
{noreply, check_if_done(S1)}
end;
handle_cast({await_all_locks, Pid},
#state{status = Status, awaiting_all = Aw} = State) ->
case Status of
{have_all_locks, _} ->
{noreply, notify_have_all(
State#state{awaiting_all = [{Pid,async}|Aw]})};
_ ->
{noreply, check_if_done(add_waiter(wait, Pid, async, State))}
end;
handle_cast({surrender, O, ToAgent, Nodes} = _Req, S) ->
?event(_Req, S),
case lists:all(
Expand Down Expand Up @@ -926,9 +945,14 @@ have_all(#state{have_all = Prev, claim_no = Cl} = State) ->
notify_have_all(State#state{have_all = true, claim_no = Cl1}).

notify_have_all(#state{awaiting_all = Aw, status = Status} = S) ->
[gen_server:reply(W, Status) || W <- Aw],
[reply_await_(W, Status) || W <- Aw],
S#state{awaiting_all = []}.

reply_await_({Pid, notify}, Status) ->
notify_(Pid, Status);
reply_await_(From, Status) ->
gen_server:reply(From, Status).

abort_on_deadlock(OID, State) ->
notify({abort, Reason = {deadlock, OID}}, State),
error(Reason).
Expand All @@ -939,11 +963,14 @@ notify_msgs([M|Ms], S) ->
notify_msgs([], S) ->
S.


notify(Msg, #state{notify = Notify} = State) ->
[P ! {?MODULE, self(), Msg} || P <- Notify],
[notify_(P, Msg) || P <- Notify],
State.

notify_(P, Msg) ->
P ! {?MODULE, self(), Msg}.


handle_locks(#state{have_all = true} = State) ->
%% If we have all locks we've asked for, no need to search for potential
%% deadlocks - reasonably, we cannot be involved in one.
Expand Down

0 comments on commit 8ce6222

Please sign in to comment.