Skip to content

Instantly share code, notes, and snippets.

@hwatkins
Created August 22, 2010 03:47
Show Gist options
  • Save hwatkins/543258 to your computer and use it in GitHub Desktop.
Save hwatkins/543258 to your computer and use it in GitHub Desktop.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% this module models a typical manager/worker pattern, where manager may spawn a large number of workers
%% required behaviour:
%%
%% 1) manager must log unexpected worker exits
%%
%% - manager must trap exits in init function -> 'process_flag(trap_exit, true)'
%% - workers must be spawned with spawn_link
%% - worker exit messages are handled by manager handle_info handler [where they can be logged etc]
%% - see worker_exit/1 function below
%%
%% 2) workers must exit gracefully on manager shutdown
%%
%% - manager must persist all worker Pids in State
%% - manager must implement a handle_cast(stop) handler which returns {stop, normal, State}, which causes terminate handler to be called on stop
%% - workers must implement receive -> die handler which causes graceful exit
%% - terminate function must send die message to all workers
%% - test stop/0 function below
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(worker_manager_test).
-behaviour(gen_server).
-export([start_link/0,
stop/0,
spawn_worker/0,
poke_worker/0,
kill_worker/0,
worker_exit/0]).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {worker_pid}).
%% functions
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
stop()->
gen_server:cast(?SERVER, stop).
spawn_worker()->
gen_server:call(?SERVER, spawn_worker).
poke_worker()-> %% tests to see if worker exists
gen_server:call(?SERVER, poke_worker).
kill_worker()-> %% kills worker gracefully
gen_server:call(?SERVER, kill_worker).
worker_exit()-> %% kills worker with exit
gen_server:call(?SERVER, worker_exit).
%% gen_server callbacks
init([]) ->
io:format("Manager starting~n"),
process_flag(trap_exit, true),
{ok, #state{}}.
handle_call(spawn_worker, _From, State) ->
case State#state.worker_pid of
undefined ->
io:format("Spawning worker~n"),
Pid=spawn_link(fun() -> worker_loop() end),
{reply, ok, State#state{worker_pid=Pid}};
_ ->
io:format("Worker already spawned~n"),
{reply, ok, State}
end;
handle_call(poke_worker, _From, State)->
case State#state.worker_pid of
undefined ->
io:format("Worker not spawned~n");
Pid ->
Pid ! poke
end,
{reply, ok, State};
handle_call(kill_worker, _From, State)->
case State#state.worker_pid of
undefined ->
io:format("Worker not spawned~n"),
{reply, ok, State};
Pid ->
Pid ! die,
{reply, ok, State#state{worker_pid=undefined}}
end;
handle_call(worker_exit, _From, State)->
case State#state.worker_pid of
undefined ->
io:format("Worker not spawned~n"),
{reply, ok, State};
Pid ->
Pid ! exit,
{reply, ok, State#state{worker_pid=undefined}}
end;
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(stop, State) ->
io:format("Manager stopping~n"),
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(Info, State)->
io:format("Manager received ~p~n", [Info]),
{noreply, State}.
terminate(_Reason, State) ->
io:format("Manager terminated~n"),
case State#state.worker_pid of
undefined ->
void;
Pid ->
Pid ! die
end,
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% worker
worker_loop()->
receive
poke ->
io:format("Hello World from worker!~n"),
worker_loop();
die ->
io:format("Worker dying~n");
exit ->
exit("Worker exit");
Anything ->
io:format("Worker received ~p", [Anything]),
worker_loop()
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment