1 %%%-------------------------------------------------------------------
2 %%% File : /Users/dfayram/Projects/concilium/elibs/resource_manager.erl
3 %%% Author : David Fayram
4 %%%-------------------------------------------------------------------
5 -module(resource_manager
).
6 -behaviour(gen_server
).
9 -export([start_link
/3, start
/3,nodes/0,nodecount
/0,change_nodecount
/1,cycle
/0,cycle
/1]).
11 %% gen_server callback exports
12 -export([init
/1, handle_call
/3, handle_cast
/2, handle_info
/2,
13 terminate
/2, code_change
/3]).
15 %% Erlang records are ugly.
16 -record(state
, {generator
= fun() -> undefined
end,
17 terminator
= fun(_
) -> undefined
end,
20 term_hook
= fun(_
) -> undefined
end
23 %% External call functions
25 % Note the local server, one of these should run on every
26 % node serving up rails responders.
27 start_link(Generator
, Terminator
, NumNodes
) ->
28 gen_server:start_link({local
, ?MODULE
}, ?MODULE
, [Generator
, Terminator
, NumNodes
], []).
29 start(Generator
, Terminator
, NumNodes
) ->
30 gen_server:start({local
, ?MODULE
}, ?MODULE
, [Generator
, Terminator
, NumNodes
], []).
32 nodes() -> gen_server:call(?MODULE
, nodes).
33 nodecount() -> gen_server:call(?MODULE
, nodecount
).
34 change_nodecount(NewNodecount
) -> gen_server:cast(?MODULE
, {change_nodecount
, NewNodecount
}).
35 cycle() -> gen_server:cast(?MODULE
, cycle
).
36 cycle(Node
) -> gen_server:cast({?MODULE
, Node
}, cycle
).
38 %% GEN_SERVER callbacks.
39 init([Generator
, Terminator
, NumNodes
]) ->
40 process_flag(trap_exit
, true
),
41 Nodes
= spawn_nodes(Generator
, NumNodes
),
42 {ok
, #state
{generator
= Generator
, nodecount
= NumNodes
,
43 nodes = Nodes
, terminator
= Terminator
}}.
45 handle_call(term_hook
, _From
, State
) ->
46 {reply
, State#state
.term_hook
, State
};
47 handle_call({term_hook
, Hook
}, _From
, State
) when is_function(Hook
, 1) ->
48 {reply
, State#state
.term_hook
, State#state
{term_hook
= Hook
}};
49 handle_call(nodecount
,_From
,State
) ->
50 {reply
, State#state
.nodecount
, State
};
51 handle_call(nodes, _From
, State
) ->
52 {reply
, State#state
.nodes, State
}.
54 handle_cast(cycle
, State
) ->
55 drop_nodes(State#state
.terminator
, State#state
.nodes),
56 {noreply
, State#state
{nodes=spawn_nodes(State#state
.generator
, State#state
.nodecount
)}};
57 handle_cast({change_nodecount
, NewCount
}, S
) when is_number(NewCount
) ->
58 Count
= S#state
.nodecount
,
62 S#state
{nodecount
= NewCount
,
63 nodes = spawn_nodes(S#state
.generator
, NewCount
- Count
) ++ S#state
.nodes}};
65 {ToKill
, ToKeep
} = lists:split(NewCount
- Count
, S#state
.nodes),
66 drop_nodes(S#state
.terminator
, ToKill
),
67 {noreply
, S#state
{nodecount
=NewCount
, nodes=ToKeep
}};
72 handle_info({'EXIT', Pid
, _Reason
}, S
) ->
73 Term
= S#state
.terminator
,
74 Membership
= lists:any(fun(X
) -> X
=:= Pid
end, S#state
.nodes),
78 Res
= lists:delete(Pid
, S#state
.nodes),
79 NewNode
= spawn_linked_node(S#state
.generator
),
80 {noreply
, S#state
{nodes=[NewNode
|Res
]}};
85 io:format("Got INFO ~p~n", [Any
]),
88 terminate(_Reason
, _State
) ->
91 code_change(_OldVsn
, State
, _Extra
) ->
96 spawn_linked_node(Generator
) ->
101 spawn_nodes(Generator
,NumNodes
) ->
102 spawn_nodes(Generator
,NumNodes
,[]).
104 spawn_nodes(_Generator
,0,Acc
) -> Acc
;
105 spawn_nodes(Generator
,NumNodes
,Acc
) -> spawn_nodes(Generator
,NumNodes
- 1, [spawn_linked_node(Generator
)|Acc
]).
107 drop_nodes(Terminator
, Nodes
) ->
108 Killer
= fun(Node
) -> unlink(Node
), Terminator(Node
) end,
109 lists:map(Killer
, Nodes
).