Skip to content

Commit

Permalink
use an asset pool to manage multiple handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
mojombo committed Jul 9, 2009
1 parent 1740f7a commit 4e5b644
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 13 deletions.
98 changes: 98 additions & 0 deletions elib/asset_pool.erl
@@ -0,0 +1,98 @@
-module(asset_pool).
-behaviour(gen_server).

%% api
-export([start_link/1, start/1, lease_asset/0, return_asset/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).

-record(state, {assets = undefined,
handler = undefined}).

%%====================================================================
%% API
%%====================================================================

start_link(Args) ->
gen_server:start_link({global, ?MODULE}, ?MODULE, Args, []).

start(Args) ->
gen_server:start({global, ?MODULE}, ?MODULE, Args, []).

lease_asset() ->
gen_server:call({global, ?MODULE}, {lease_asset}).

return_asset(Asset) ->
gen_server:call({global, ?MODULE}, {return_asset, Asset}).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Count, Handler]) ->
process_flag(trap_exit, true),
error_logger:info_msg("~p starting~n", [?MODULE]),
Assets = start_handlers(Count, Handler),
{ok, #state{assets = Assets, handler = Handler}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({lease_asset}, _From, State) ->
{{value, Asset}, Assets2} = queue:out(State#state.assets),
{reply, Asset, State#state{assets = Assets2}};
handle_call({return_asset, Asset}, _From, State) ->
Assets2 = queue:in(Asset, State#state.assets),
{reply, ok, State#state{assets = Assets2}};
handle_call(_Request, _From, State) ->
{reply, ok, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) -> {noreply, State}.

handle_info({'EXIT', _Pid, _Error}, State) ->
error_logger:error_msg("Port closed, restarting port...~n", []),
Handler = State#state.handler,
Asset = port_wrapper:wrap_link("ruby " ++ Handler),
Assets = queue:in(Asset, State#state.assets),
{noreply, State#state{assets = Assets}};
handle_info(Msg, State) ->
error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
{noreply, State}.

terminate(_Reason, _State) -> ok.
code_change(_OldVersion, State, _Extra) -> {ok, State}.

%%====================================================================
%% Internal
%%====================================================================

start_handlers(Count, Handler) ->
start_handlers(queue:new(), Count, Handler).

start_handlers(Assets, 0, _Handler) ->
Assets;
start_handlers(Assets, Count, Handler) ->
Asset = port_wrapper:wrap_link("ruby " ++ Handler),
Assets2 = queue:in(Asset, Assets),
start_handlers(Assets2, Count - 1, Handler).
13 changes: 13 additions & 0 deletions elib/asset_pool_sup.erl
@@ -0,0 +1,13 @@
-module(asset_pool_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).

start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
{ok, Handler} = application:get_env(ernie_server_app, handler),
io:format("Using handler ~p~n", [Handler]),
{ok, {{one_for_one, 1, 60},
[{asset_pool, {asset_pool, start_link, [[2, Handler]]},
permanent, brutal_kill, worker, [asset_pool]}]}}.
19 changes: 10 additions & 9 deletions elib/ernie_server.erl
Expand Up @@ -33,13 +33,12 @@ start(Args) ->
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Port, Handler]) ->
init([Port]) ->
process_flag(trap_exit, true),
error_logger:info_msg("~p starting~n", [?MODULE]),
Ducky = port_wrapper:wrap_link("ruby " ++ Handler),
{ok, LSock} = try_listen(Port, 500),
spawn(fun() -> loop(LSock, Ducky) end),
{ok, #state{lsock = LSock, handler = Handler, ducky = Ducky}}.
spawn(fun() -> loop(LSock) end),
{ok, #state{lsock = LSock}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
Expand Down Expand Up @@ -92,17 +91,19 @@ try_listen(Port, Times) ->
try_listen(Port, Times - 1)
end.

loop(LSock, Ducky) ->
loop(LSock) ->
{ok, Sock} = gen_tcp:accept(LSock),
spawn(fun() -> handle_method(Sock, Ducky) end),
loop(LSock, Ducky).
spawn(fun() -> handle_method(Sock) end),
loop(LSock).

handle_method(Sock, Ducky) ->
handle_method(Sock) ->
case gen_tcp:recv(Sock, 0) of
{ok, BinaryTerm} ->
Asset = asset_pool:lease_asset(),
% error_logger:info_msg("From Internet: ~p~n", [BinaryTerm]),
{ok, Data} = port_wrapper:rpc(Ducky, BinaryTerm),
{ok, Data} = port_wrapper:rpc(Asset, BinaryTerm),
% error_logger:info_msg("From Port: ~p~n", [Data]),
asset_pool:return_asset(Asset),
gen_tcp:send(Sock, Data),
ok = gen_tcp:close(Sock);
{error, closed} ->
Expand Down
3 changes: 2 additions & 1 deletion elib/ernie_server_app.erl
Expand Up @@ -7,7 +7,8 @@ boot() ->
application:start(ernie_server_app).

start(_Type, _Args) ->
ernie_server_sup:start_link().
ernie_server_sup:start_link(),
asset_pool_sup:start_link().

stop(_State) ->
ok.
4 changes: 1 addition & 3 deletions elib/ernie_server_sup.erl
Expand Up @@ -8,14 +8,12 @@ start_link() ->
init([]) ->
{ok, Port} = application:get_env(ernie_server_app, port),
io:format("Using port ~p~n", [Port]),
{ok, Handler} = application:get_env(ernie_server_app, handler),
io:format("Using handler ~p~n", [Handler]),
case application:get_env(ernie_server_app, pidfile) of
{ok, Location} ->
Pid = os:getpid(),
ok = file:write_file(Location, list_to_binary(Pid));
undefined -> ok
end,
{ok, {{one_for_one, 1, 60},
[{ernie_server, {ernie_server, start_link, [[Port, Handler]]},
[{ernie_server, {ernie_server, start_link, [[Port]]},
permanent, brutal_kill, worker, [ernie_server]}]}}.
8 changes: 8 additions & 0 deletions examples/calc.rb
@@ -0,0 +1,8 @@
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'ernie'

mod(:calc) do
fun(:add) do |a, b|
a + b
end
end

0 comments on commit 4e5b644

Please sign in to comment.