update group leaders when restarting user process

ensure any processes with a reference to an old user process as their
group leader are updated to use the new user process. this introduces a
slight delay at startup as the system must wait for the new processes
to be registered. there is a max wait period of three seconds (before
the shell command gives up and throws a timeout error)

fixes #314 ("rebar shell" somehow blocks using io:format in gen_server
handle_call)
This commit is contained in:
alisdair sullivan 2014-07-07 21:40:32 +00:00
parent 0d1ae7a468
commit 68089c6297

View file

@ -41,10 +41,20 @@
shell(_Config, _AppFile) -> shell(_Config, _AppFile) ->
true = code:add_pathz(rebar_utils:ebin_dir()), true = code:add_pathz(rebar_utils:ebin_dir()),
%% scan all processes for any with references to the old user and save them to
%% update later
NeedsUpdate = [Pid || Pid <- erlang:processes(),
proplists:get_value(group_leader, erlang:process_info(Pid)) == whereis(user)
],
%% terminate the current user %% terminate the current user
ok = supervisor:terminate_child(kernel_sup, user), ok = supervisor:terminate_child(kernel_sup, user),
%% start a new shell (this also starts a new user under the correct group) %% start a new shell (this also starts a new user under the correct group)
user_drv:start(), _ = user_drv:start(),
%% wait until user_drv and user have been registered (max 3 seconds)
ok = wait_until_user_started(3000),
%% set any process that had a reference to the old user's group leader to the
%% new user process
_ = [erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate],
%% enable error_logger's tty output %% enable error_logger's tty output
ok = error_logger:swap_handler(tty), ok = error_logger:swap_handler(tty),
%% disable the simple error_logger (which may have been added multiple %% disable the simple error_logger (which may have been added multiple
@ -56,9 +66,10 @@ shell(_Config, _AppFile) ->
info(help, shell) -> info(help, shell) ->
?CONSOLE( ?CONSOLE(
"Start a shell with project and deps preloaded similar to~n" "Start a shell with project and deps preloaded similar to~n"
"'erl -pa ebin -pa deps/*/ebin'.~n", "'erl -pa ebin -pa deps/*/ebin'.~n",
[]). []
).
remove_error_handler(0) -> remove_error_handler(0) ->
?WARN("Unable to remove simple error_logger handler~n", []); ?WARN("Unable to remove simple error_logger handler~n", []);
@ -66,4 +77,15 @@ remove_error_handler(N) ->
case gen_event:delete_handler(error_logger, error_logger, []) of case gen_event:delete_handler(error_logger, error_logger, []) of
{error, module_not_found} -> ok; {error, module_not_found} -> ok;
{error_logger, _} -> remove_error_handler(N-1) {error_logger, _} -> remove_error_handler(N-1)
end.
%% Timeout is a period to wait before giving up
wait_until_user_started(0) ->
?ABORT("Timeout exceeded waiting for `user` to register itself~n", []),
erlang:error(timeout);
wait_until_user_started(Timeout) ->
case whereis(user) of
%% if user is not yet registered wait a tenth of a second and try again
undefined -> timer:sleep(100), wait_until_user_started(Timeout - 100);
_ -> ok
end. end.