Packages

otpbp

4.72.2
7.18.0 7.16.1 7.15.0 7.14.0 7.13.0 7.12.0 7.11.0 7.10.2 7.9.3 7.9.2 7.9.1 7.8.7 7.8.6 7.8.5 7.8.4 7.7.1 7.7.0 7.6.3 7.6.2 7.6.1 7.6.0 7.5.1 7.5.0 7.4.0 7.3.4 7.3.3 7.3.2 7.3.1 7.3.0 7.2.0 7.1.1 7.1.0 7.0.4 7.0.3 7.0.2 7.0.1 6.26.0 6.25.0 6.24.0 6.23.0 6.22.2 6.21.3 6.21.2 6.21.1 6.20.2 6.19.1 6.19.0 6.18.3 6.18.2 6.18.1 6.18.0 6.17.1 6.17.0 6.16.0 6.15.4 6.15.3 6.15.2 6.15.1 6.15.0 6.14.0 6.13.5 6.13.4 6.13.3 6.13.2 6.13.1 6.13.0 6.12.0 6.11.2 6.11.1 6.10.0 6.9.2 6.9.1 6.9.0 6.8.0 6.7.1 6.7.0 6.6.4 6.6.3 6.6.2 6.6.1 6.6.0 6.5.0 6.4.0 6.3.3 6.3.2 6.3.1 6.3.0 6.2.1 6.2.0 6.1.0 6.0.0 5.39.0 5.38.0 5.37.0 5.36.0 5.35.2 5.34.3 5.34.2 5.34.1 5.33.1 5.32.1 5.32.0 5.31.3 5.31.2 5.31.1 5.31.0 5.30.1 5.30.0 5.29.0 5.28.4 5.28.3 5.28.2 5.28.1 5.28.0 5.27.0 5.26.5 5.26.4 5.26.3 5.26.2 5.26.1 5.26.0 5.25.0 5.24.2 5.24.1 5.23.0 5.22.2 5.22.1 5.22.0 5.21.0 5.20.1 5.20.0 5.19.4 5.19.3 5.19.2 5.19.1 5.19.0 5.18.0 5.17.0 5.16.3 5.16.1 5.16.0 5.15.1 5.15.0 5.14.0 5.13.1 5.13.0 5.12.1 5.12.0 5.11.2 5.11.1 5.10.1 5.10.0 5.9.0 5.8.0 5.7.0 5.6.1 5.6.0 5.5.0 5.4.2 5.4.1 5.4.0 5.3.0 5.2.2 5.2.1 5.2.0 5.1.2 5.1.1 4.79.0 4.78.0 4.77.0 4.76.2 4.75.3 4.75.2 4.75.1 4.74.0 4.73.1 4.73.0 4.72.3 4.72.2 4.72.1 4.72.0 4.71.1 4.71.0 4.70.0 4.69.4 4.69.3 4.69.2 4.69.1 4.69.0 4.68.0 4.67.5 4.67.4 4.67.3 4.67.2 4.67.0 4.66.0 4.65.2 4.65.1 4.64.0 4.63.2 4.63.1 4.63.0 4.62.0 4.61.1 4.61.0 4.60.4 4.60.3 4.60.2 4.60.1 4.60.0 4.59.0 4.58.0 4.57.2 4.57.1 4.57.0 4.56.2 4.56.1 4.56.0 4.55.0 4.54.0 4.53.0 4.52.1 4.52.0 4.51.3 4.51.2 4.51.0 4.50.0 4.49.3 4.49.2

OTP backports

Current section

Files

Jump to
otpbp src otpbp_pg2.erl
Raw

src/otpbp_pg2.erl

-module(otpbp_pg2).
-ifndef(HAVE_pg2__create_1).
% OTP < 24.0
-export([create/1]).
-endif.
-ifndef(HAVE_pg2__delete_1).
% OTP < 24.0
-export([delete/1]).
-endif.
-ifndef(HAVE_pg2__get_closest_pid_1).
% OTP < 24.0
-export([get_closest_pid/1]).
-endif.
-ifndef(HAVE_pg2__get_local_members_1).
% OTP < 24.0
-export([get_local_members/1]).
-endif.
-ifndef(HAVE_pg2__get_members_1).
% OTP < 24.0
-export([get_members/1]).
-endif.
-ifndef(HAVE_pg2__join_2).
% OTP < 24.0
-export([join/2]).
-endif.
-ifndef(HAVE_pg2__leave_2).
% OTP < 24.0
-export([leave/2]).
-endif.
-ifndef(HAVE_pg2__start_0).
% OTP < 24.0
-export([start/0]).
-endif.
-ifndef(HAVE_pg2__start_link_0).
% OTP < 24.0
-export([start_link/0]).
-endif.
-ifndef(HAVE_pg2__which_groups_0).
% OTP < 24.0
-export([which_groups/0]).
-endif.
-ifndef(HAVE_pg2__create_1).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__delete_1).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__get_closest_pid_1).
-ifdef(HAVE_pg2__get_local_members_1).
-import(pg2, [get_local_members/1]).
-endif.
-ifdef(HAVE_pg2__get_members_1).
-import(pg2, [get_members/1]).
-endif.
-endif.
-ifndef(HAVE_pg2__get_local_members_1).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__get_members_1).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__join_2).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__leave_2).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__which_groups_0).
-ifdef(HAVE_pg2__start_0).
-import(pg2, [start/0]).
-endif.
-endif.
-ifndef(HAVE_pg2__init_1).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
-behaviour(gen_server).
-define(GEN_SERVER_MODULE, ?MODULE).
-ifndef(NEED_record__local_member).
-define(NEED_record__local_member, true).
-endif.
-else.
-define(GEN_SERVER_MODULE, pg2).
-endif.
-define(SERVICE, pg2).
-ifndef(HAVE_pg2__get_local_members_1).
-ifndef(NEED_record__local_member).
-define(NEED_record__local_member, true).
-endif.
-endif.
-ifdef(NEED_record__local_member).
-record(local_member, {name, pid}).
-endif.
-ifndef(HAVE_pg2__create_1).
create(Name) ->
start(),
ets:member(pg2_table, {group, Name}) orelse
global:trans({{?SERVICE, Name}, self()}, fun() -> gen_server:multi_call(?SERVICE, {create, Name}) end),
ok.
-endif.
-ifndef(HAVE_pg2__delete_1).
delete(Name) ->
start(),
global:trans({{?SERVICE, Name}, self()}, fun() -> gen_server:multi_call(?SERVICE, {delete, Name}) end),
ok.
-endif.
-ifndef(HAVE_pg2__get_closest_pid_1).
get_closest_pid(Name) ->
case get_local_members(Name) of
[Pid] -> Pid;
[] ->
case get_members(Name) of
[] -> {error, {no_process, Name}};
Members -> random_element(Members)
end;
Members when is_list(Members) -> random_element(Members);
Else -> Else
end.
random_element(List) -> lists:nth(abs(erlang:monotonic_time() bxor erlang:unique_integer()) rem length(List) + 1, List).
-endif.
-ifndef(HAVE_pg2__get_local_members_1).
get_local_members(Name) ->
start(),
case ets:member(pg2_table, {group, Name}) of
true -> local_group_members(Name);
_false -> {error, {no_such_group, Name}}
end.
-compile({inline, local_group_members/1}).
local_group_members(Name) ->
[P || [Pid] <- ets:match(pg2_table, {#local_member{name = Name, pid = '$1'}}), P <- member_in_group(Pid, Name)].
-ifndef(NEED_member_in_group_2).
-define(NEED_member_in_group_2, true).
-endif.
-endif.
-ifndef(HAVE_pg2__get_members_1).
get_members(Name) ->
start(),
case ets:member(pg2_table, {group, Name}) of
true -> group_members(Name);
_false -> {error, {no_such_group, Name}}
end.
-ifndef(NEED_group_members_1).
-define(NEED_group_members_1, true).
-endif.
-endif.
-ifndef(HAVE_pg2__join_2).
join(Name, Pid) -> act(Name, Pid, join).
-ifndef(NEED_act_3).
-define(NEED_act_3, true).
-endif.
-endif.
-ifndef(HAVE_pg2__leave_2).
leave(Name, Pid) -> act(Name, Pid, leave).
-ifndef(NEED_act_3).
-define(NEED_act_3, true).
-endif.
-endif.
-ifndef(HAVE_pg2__start_0).
start() ->
case whereis(?SERVICE) of
undefined ->
supervisor:start_child(kernel_safe_sup,
#{id => pg2, start => {?GEN_SERVER_MODULE, start_link, []}, shutdown => 1000});
Pid -> {ok, Pid}
end.
-endif.
-ifndef(HAVE_pg2__start_link_0).
start_link() -> gen_server:start_link({local, ?SERVICE}, ?GEN_SERVER_MODULE, [], []).
-endif.
-ifndef(HAVE_pg2__which_groups_0).
which_groups() ->
start(),
all_groups().
-ifndef(NEED_all_groups_0).
-define(NEED_all_groups_0, true).
-endif.
-endif.
-ifndef(HAVE_pg2__init_1).
-record(state, {}).
-record(group, {name}).
-record(ref, {v}).
init([]) ->
ok = net_kernel:monitor_nodes(true),
lists:foreach(fun(N) ->
{?SERVICE, N} ! {new_pg2, node()},
self() ! {nodeup, N}
end, nodes()),
pg2_table = ets:new(pg2_table, [ordered_set, protected, named_table]),
{ok, #state{}}.
handle_call({create, Name}, _From, S) ->
assure_group(Name),
{reply, ok, S};
handle_call({join, Name, Pid}, _From, S) ->
ets:member(pg2_table, {group, Name}) andalso join_group(Name, Pid),
{reply, ok, S};
handle_call({leave, Name, Pid}, _From, S) ->
ets:member(pg2_table, {group, Name}) andalso leave_group(Name, Pid),
{reply, ok, S};
handle_call({delete, Name}, _From, S) ->
lists:foreach(fun(Pid) -> leave_group(Name, Pid) end, group_members(Name)),
true = ets:delete(pg2_table, #group{name = Name}),
{reply, ok, S};
handle_call(Request, From, S) ->
error_logger:warning_msg("The pg2 server received an unexpected message:\nhandle_call(~tp, ~tp, _)\n",
[Request, From]),
{noreply, S}.
handle_cast({exchange, _Node, Names}, S) ->
lists:foreach(fun([Name, Members]) ->
assure_group(Name) andalso lists:foreach(fun(P) -> join_group(Name, P) end,
Members -- group_members(Name));
(_) -> ok
end,
Names),
{noreply, S};
handle_cast(_, S) -> {noreply, S}.
-record(pid, {pid, name}).
handle_info({'DOWN', MonitorRef, process, _Pid, _Info}, S) ->
[{_, Pid}] = ets:lookup(pg2_table, #ref{v = MonitorRef}),
Names = all_matches({#pid{pid = Pid, name = '$1'}}),
_ = [leave_group(Name, P) || Name <- Names, P <- member_in_group(Pid, Name)],
lists:foreach(fun(Name) -> gen_server:abcast(nodes(), ?SERVICE, {del_member, Name, Pid}) end, Names),
{noreply, S};
handle_info({T, Node}, S) when T =:= nodeup; T =:= new_pg2 ->
gen_server:cast({?SERVICE, Node}, {exchange, node(), [[G, group_members(G)] || G <- all_groups()]}),
{noreply, S};
handle_info(_, S) -> {noreply, S}.
terminate(_Reason, _S) -> ets:delete(pg2_table).
assure_group(Name) ->
Key = #group{name = Name},
ets:member(pg2_table, Key) orelse true =:= ets:insert(pg2_table, {Key}).
-record(member, {name, pid}).
join_group(Name, Pid) ->
Ref_Pid = #ref{v = Pid},
try ets:update_counter(pg2_table, Ref_Pid, {4, 1}) of
_ -> true
catch
_:_ ->
{RPid, Ref} = case lists:member(node(Pid), nodes([this, visible])) of
true -> {Pid, monitor(process, Pid)};
_false ->
spawn_monitor(fun() ->
Ref = monitor(process, Pid),
receive
{'DOWN', Ref, process, Pid, _Info} -> exit(normal)
end
end)
end,
true = ets:insert(pg2_table, [{Ref_Pid, RPid, Ref, 1}, {#ref{v = Ref}, Pid}])
end,
Member = #member{name = Name, pid = Pid},
try
ets:update_counter(pg2_table, Member, {2, 1})
catch _:_ ->
true = ets:insert(pg2_table, [{Member, 1}, {#pid{pid = Pid, name = Name}}]),
node(Pid) =:= node() andalso ets:insert(pg2_table, {#local_member{name = Name, pid = Pid}})
end.
leave_group(Name, Pid) ->
Member = #member{name = Name, pid = Pid},
try ets:update_counter(pg2_table, Member, {2, -1}) of
N ->
N =:= 0 orelse
begin
true = ets:delete(pg2_table, #pid{pid = Pid, name = Name}),
node(Pid) =:= node() andalso ets:delete(pg2_table, #local_member{name = Name, pid = Pid}),
true = ets:delete(pg2_table, Member)
end,
Ref_Pid = #ref{v = Pid},
case ets:update_counter(pg2_table, Ref_Pid, {4, -1}) of
0 ->
[{#ref{}, RPid, Ref, 0}] = ets:lookup(pg2_table, Ref_Pid),
true = ets:delete(pg2_table, #ref{v = Ref}),
true = ets:delete(pg2_table, Ref_Pid),
true = demonitor(Ref, [flush]),
RPid =:= Pid orelse exit(RPid, kill);
_ -> ok
end
catch _:_ -> ok
end.
-ifndef(NEED_member_in_group_2).
-define(NEED_member_in_group_2, true).
-endif.
-ifndef(NEED_all_matches_1).
-define(NEED_all_matches_1, true).
-endif.
-ifndef(NEED_all_groups_0).
-define(NEED_all_groups_0, true).
-endif.
-ifndef(NEED_group_members_1).
-define(NEED_group_members_1, true).
-endif.
-endif.
-ifdef(NEED_act_3).
act(Name, Pid, Action) when is_pid(Pid) ->
start(),
case ets:member(pg2_table, {group, Name}) of
false -> {error, {no_such_group, Name}};
true ->
global:trans({{?SERVICE, Name}, self()},
fun() -> gen_server:multi_call(?SERVICE, {Action, Name, Pid}) end),
ok
end.
-endif.
-ifdef(NEED_group_members_1).
group_members(Name) ->
[P || [P, N] <- ets:match(pg2_table, {#member{name = Name, pid = '$1'}, '$2'}), _ <- lists:seq(1, N)].
-endif.
-ifdef(NEED_member_in_group_2).
member_in_group(Pid, Name) ->
case ets:lookup(pg2_table, #member{name = Name, pid = Pid}) of
[] -> [];
[{#member{}, N}] -> lists:duplicate(N, Pid)
end.
-endif.
-ifdef(NEED_all_groups_0).
all_groups() -> all_matches({#group{name = '$1'}}).
-ifndef(NEED_all_matches_1).
-define(NEED_all_matches_1, true).
-endif.
-endif.
-ifdef(NEED_all_matches_1).
all_matches(Spec) -> [N || [N] <- ets:match(pg2_table, Spec)].
-endif.