Created
August 22, 2010 03:47
-
-
Save hwatkins/543258 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
%% | |
%% 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