Permalink
Browse files

Automatic checkin when parents die

  • Loading branch information...
1 parent a773135 commit d862db61f0db446a8eb89135153529951dc04fd0 Devin Torres committed Feb 17, 2011
Showing with 99 additions and 56 deletions.
  1. +0 −13 LICENSE
  2. +2 −14 README.md
  3. +24 −0 UNLICENSE
  4. +1 −1 ebin/poolboy.app
  5. BIN rebar
  6. +71 −27 src/poolboy.erl
  7. +1 −1 src/poolboy_sup.erl
View
@@ -1,13 +0,0 @@
-Copyright (c) 2010, Devin Torres <devin@devintorres.com>
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
View
@@ -86,22 +86,21 @@ Poolboy - A hunky Erlang worker pool factory
-export([start_link/1, stop/0, init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
- -record(state, {pool, conn, monitor}).
+ -record(state, {conn}).
start_link(Args) -> gen_server:start_link(?MODULE, Args, []).
stop() -> gen_server:cast(?MODULE, stop).
init(Args) ->
process_flag(trap_exit, true),
- Pool = proplists:get_value(pool, Args),
Hostname = proplists:get_value(hostname, Args),
Database = proplists:get_value(database, Args),
Username = proplists:get_value(username, Args),
Password = proplists:get_value(password, Args),
{ok, Conn} = pgsql:connect(Hostname, Username, Password, [
{database, Database}
]),
- {ok, #state{pool=Pool, conn=Conn}}.
+ {ok, #state{conn=Conn}}.
handle_call({squery, Sql}, _From, #state{conn=Conn}=State) ->
{reply, pgsql:squery(Conn, Sql), State};
@@ -110,22 +109,11 @@ Poolboy - A hunky Erlang worker pool factory
handle_call(_Request, _From, State) ->
{reply, ok, State}.
- handle_cast({monitor, Pid}, State) ->
- MonitorRef = monitor(process, Pid),
- {noreply, State#state{monitor=MonitorRef}};
- handle_cast(demonitor, #state{monitor=null}=State) ->
- {noreply, State};
- handle_cast(demonitor, #state{monitor=MonitorRef}=State) ->
- demonitor(MonitorRef),
- {noreply, State#state{monitor=null}};
handle_cast(stop, State) ->
{stop, shutdown, State};
handle_cast(_Msg, State) ->
{noreply, State}.
- handle_info({'DOWN', _, _, _, _}, #state{pool=Pool}=State) ->
- gen_fsm:send_event(Pool, {checkin, self()}),
- {noreply, State};
handle_info({'EXIT', _, _}, State) ->
{stop, shutdown, State};
handle_info(_Info, State) ->
View
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
View
@@ -1,6 +1,6 @@
{application, poolboy, [
{description, "A hunky Erlang worker pool factory"},
- {vsn, "0.1"},
+ {vsn, "0.2"},
{applications, [kernel, stdlib]},
{modules, [poolboy, poolboy_sup]},
{registered, []}
View
Binary file not shown.
View
@@ -1,4 +1,4 @@
-% Copyright (c) 2010, Devin Torres <devin@devintorres.com>
+% Poolboy by Devin Torres <devin@devintorres.com>
-module(poolboy).
-behaviour(gen_fsm).
@@ -7,7 +7,7 @@
overflow/2, overflow/3, full/2, full/3, handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
--record(state, {workers, worker_sup, waiting=queue:new(),
+-record(state, {workers, worker_sup, waiting=queue:new(), monitors=[],
size=5, overflow=0, max_overflow=10}).
checkin(Pool, Worker) ->
@@ -25,8 +25,7 @@ init(Args) ->
init(Args, #state{}).
init([{worker_module, Mod} | Rest], State) ->
- Args = [{pool, self()} | Rest],
- {ok, Sup} = poolboy_sup:start_link(Mod, Args),
+ {ok, Sup} = poolboy_sup:start_link(Mod, Rest),
init(Rest, State#state{worker_sup=Sup});
init([{size, PoolSize} | Rest], State) ->
init(Rest, State#state{size=PoolSize});
@@ -38,20 +37,34 @@ init([], #state{size=Size, worker_sup=Sup}=State) ->
Workers = prepopulate(Size, Sup),
{ok, ready, State#state{workers=Workers}}.
-ready({checkin, Pid}, #state{workers=Workers}=State) ->
- gen_server:cast(Pid, demonitor),
- {next_state, ready, State#state{workers=queue:in(Pid, Workers)}};
+ready({checkin, Pid}, State) ->
+ Workers = queue:in(Pid, State#state.workers),
+ Monitors = case lists:keytake(Pid, 1, State#state.monitors) of
+ {value, {_, Ref}, Left} -> erlang:demonitor(Ref), Left;
+ false -> []
+ end,
+ {next_state, ready, State#state{workers=Workers, monitors=Monitors}};
ready(_Event, State) ->
{next_state, ready, State}.
-ready(checkout, {Pid, _}, #state{workers=Workers, worker_sup=Sup}=State) ->
+ready(checkout, {From, _}, #state{workers=Workers, worker_sup=Sup,
+ max_overflow=MaxOverflow}=State) ->
case queue:out(Workers) of
- {{value, Reply}, Remaining} ->
- gen_server:cast(Reply, {monitor, Pid}),
- {reply, Reply, ready, State#state{workers=Remaining}};
+ {{value, Pid}, Left} ->
+ Ref = erlang:monitor(process, From),
+ Monitors = [{Pid, Ref} | State#state.monitors],
+ {reply, Pid, ready, State#state{workers=Left,
+ monitors=Monitors}};
+ {empty, Empty} when MaxOverflow > 0 ->
+ {Pid, Ref} = new_worker(Sup, From),
+ Monitors = [{Pid, Ref} | State#state.monitors],
+ {reply, Pid, overflow, State#state{workers=Empty,
+ monitors=Monitors,
+ overflow=1}};
{empty, Empty} ->
- Reply = new_worker(Sup, Pid),
- {reply, Reply, overflow, State#state{workers=Empty, overflow=1}}
+ Waiting = State#state.waiting,
+ {next_state, full, State#state{workers=Empty,
+ waiting=queue:in(From, Waiting)}}
end;
ready(_Event, _From, State) ->
{reply, ok, ready, State}.
@@ -69,17 +82,23 @@ overflow(checkout, From, #state{overflow=Overflow,
max_overflow=MaxOverflow}=State) when Overflow >= MaxOverflow ->
Waiting = State#state.waiting,
{next_state, full, State#state{waiting=queue:in(From, Waiting)}};
-overflow(checkout, {Pid, _}, #state{overflow=Overflow, worker_sup=Sup}=State) ->
- {reply, new_worker(Sup, Pid), overflow, State#state{overflow=Overflow+1}};
+overflow(checkout, {From, _}, #state{worker_sup=Sup,
+ overflow=Overflow}=State) ->
+ {Pid, Ref} = new_worker(Sup, From),
+ Monitors = [{Pid, Ref} | State#state.monitors],
+ {reply, Pid, overflow, State#state{monitors=Monitors,
+ overflow=Overflow+1}};
overflow(_Event, _From, State) ->
{reply, ok, overflow, State}.
full({checkin, Pid}, #state{waiting=Waiting}=State) ->
case queue:out(Waiting) of
- {{value, FromPid}, Remaining} ->
- gen_server:cast(Pid, {monitor, FromPid}),
- gen_fsm:reply(FromPid, Pid),
- {next_state, full, State#state{waiting=Remaining}};
+ {{value, From}, Left} ->
+ Ref = erlang:monitor(process, From),
+ Monitors = [{Pid, Ref} | State#state.monitors],
+ gen_fsm:reply(From, Pid),
+ {next_state, full, State#state{waiting=Left,
+ monitors=Monitors}};
{empty, Empty} ->
dismiss_worker(Pid),
{next_state, overflow, State#state{waiting=Empty}}
@@ -99,11 +118,36 @@ handle_sync_event(_Event, _From, StateName, State) ->
Reply = {error, invalid_message},
{reply, Reply, StateName, State}.
-handle_info({'EXIT', Pid, _}, ready, #state{worker_sup=Sup}=State) ->
- Workers = queue:filter(fun (W) -> W =/= Pid end, State#state.workers),
- {next_state, ready, State#state{workers=queue:in(new_worker(Sup), Workers)}};
-handle_info({'EXIT', Sup, Msg}, _, #state{worker_sup=Sup}=State) ->
- {stop, Msg, State};
+handle_info({'DOWN', Ref, _, _, _}, StateName, State) ->
+ case lists:keytake(Ref, 2, State#state.monitors) of
+ {value, {Pid, _}, _} -> dismiss_worker(Pid);
+ false -> false
+ end,
+ {next_state, StateName, State};
+handle_info({'EXIT', Pid, _}, StateName, #state{worker_sup=Sup,
+ overflow=Overflow,
+ max_overflow=MaxOverflow}=State) ->
+ Monitors = case lists:keytake(Pid, 1, State#state.monitors) of
+ {value, {_, Ref}, Left} -> erlang:demonitor(Ref), Left;
+ false -> []
+ end,
+ case StateName of
+ ready ->
+ W = queue:filter(fun (P) -> P =/= Pid end, State#state.workers),
+ {next_state, ready, State#state{workers=queue:in(new_worker(Sup), W),
+ monitors=Monitors}};
+ overflow when Overflow =< 1 ->
+ {next_state, ready, State#state{monitors=Monitors, overflow=0}};
+ overflow ->
+ {next_state, overflow, State#state{monitors=Monitors,
+ overflow=Overflow-1}};
+ full when Overflow =< MaxOverflow ->
+ {next_state, overflow, State#state{monitors=Monitors,
+ overflow=Overflow-1}};
+ full ->
+ {next_state, full, State#state{monitors=Monitors,
+ overflow=Overflow-1}}
+ end;
handle_info(_Info, StateName, State) ->
{next_state, StateName, State}.
@@ -117,10 +161,10 @@ new_worker(Sup) ->
link(Pid),
Pid.
-new_worker(Sup, Owner) ->
+new_worker(Sup, From) ->
Pid = new_worker(Sup),
- gen_server:cast(Pid, {monitor, Owner}),
- Pid.
+ Ref = erlang:monitor(process, From),
+ {Pid, Ref}.
dismiss_worker(Pid) -> gen_server:cast(Pid, stop).
View
@@ -1,4 +1,4 @@
-% Copyright (c) 2010, Devin Torres <devin@devintorres.com>
+% Poolboy by Devin Torres <devin@devintorres.com>
-module(poolboy_sup).
-behaviour(supervisor).

0 comments on commit d862db6

Please sign in to comment.