aboutsummaryrefslogtreecommitdiff
path: root/server/_build/default/lib/bcrypt/src/bcrypt_pool.erl
diff options
context:
space:
mode:
Diffstat (limited to 'server/_build/default/lib/bcrypt/src/bcrypt_pool.erl')
-rw-r--r--server/_build/default/lib/bcrypt/src/bcrypt_pool.erl140
1 files changed, 140 insertions, 0 deletions
diff --git a/server/_build/default/lib/bcrypt/src/bcrypt_pool.erl b/server/_build/default/lib/bcrypt/src/bcrypt_pool.erl
new file mode 100644
index 0000000..7b6ea91
--- /dev/null
+++ b/server/_build/default/lib/bcrypt/src/bcrypt_pool.erl
@@ -0,0 +1,140 @@
+%% @copyright 2011 Hunter Morris
+%% @doc Implementation of `gen_server' behaviour.
+%% @end
+%% Distributed under the MIT license; see LICENSE for details.
+-module(bcrypt_pool).
+-author('Hunter Morris <huntermorris@gmail.com>').
+
+-behaviour(gen_server).
+
+-export([start_link/0, available/1]).
+-export([gen_salt/0, gen_salt/1]).
+-export([hashpw/2]).
+
+%% gen_server
+-export([init/1, code_change/3, terminate/2,
+ handle_call/3, handle_cast/2, handle_info/2]).
+
+-record(state, {
+ size = 0,
+ busy = 0,
+ requests = queue:new(),
+ ports = queue:new()
+ }).
+
+-record(req, {mon :: reference(), from :: {pid(), atom()}}).
+
+-type state() :: #state{size :: 0, busy :: 0, requests :: queue:queue(), ports :: queue:queue()}.
+
+%% @doc Creates a `gen_server' process as part of a supervision tree.
+
+-spec start_link() -> Result when
+ Result :: {ok,Pid} | ignore | {error,Error},
+ Pid :: pid(),
+ Error :: {already_started,Pid} | term().
+start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+%% @doc Asynchronosly check if `Pid' in `#state:requests' queue or not.
+
+-spec available(Pid) -> Result when
+ Pid :: pid(),
+ Result :: ok.
+available(Pid) -> gen_server:cast(?MODULE, {available, Pid}).
+
+%% @doc Generate a random text salt.
+
+-spec gen_salt() -> Result when
+ Result :: {ok, Salt},
+ Salt :: [byte()].
+gen_salt() -> do_call(fun bcrypt_port:gen_salt/1, []).
+
+%% @doc Generate a random text salt. Rounds defines the complexity of
+%% the hashing, increasing the cost as 2^log_rounds.
+
+-spec gen_salt(Rounds) -> Result when
+ Rounds :: bcrypt:rounds(),
+ Result :: {ok, Salt},
+ Salt :: [byte()].
+gen_salt(Rounds) -> do_call(fun bcrypt_port:gen_salt/2, [Rounds]).
+
+%% @doc Hash the specified password and the salt.
+
+hashpw(Password, Salt) -> do_call(fun bcrypt_port:hashpw/3, [Password, Salt]).
+
+%% @private
+
+-spec init([]) -> Result when
+ Result :: {ok, state()}.
+init([]) ->
+ {ok, Size} = application:get_env(bcrypt, pool_size),
+ {ok, #state{size = Size}}.
+
+%% @private
+
+terminate(shutdown, _) -> ok.
+
+%% @private
+
+-spec handle_call(Request, From, State) -> Result when
+ Request :: request,
+ From :: {RPid, atom()},
+ RPid :: pid(),
+ State :: state(),
+ Result :: {noreply, state()} | {reply, {ok, pid()}, state()}.
+handle_call(request, {RPid, _} = From, #state{ports = P} = State) ->
+ case queue:out(P) of
+ {empty, P} ->
+ #state{size = Size, busy = B, requests = R} = State,
+ B1 =
+ if Size > B ->
+ {ok, _} = bcrypt_port_sup:start_child(),
+ B + 1;
+ true ->
+ B
+ end,
+ RRef = erlang:monitor(process, RPid),
+ R1 = queue:in(#req{mon = RRef, from = From}, R),
+ {noreply, State#state{requests = R1,
+ busy = B1}};
+ {{value, PPid}, P1} ->
+ #state{busy = B} = State,
+ {reply, {ok, PPid}, State#state{busy = B + 1, ports = P1}}
+ end;
+handle_call(Msg, _, _) -> exit({unknown_call, Msg}).
+
+%% @private
+
+-spec handle_cast({available, Pid}, state()) -> Result when
+ Pid :: pid(),
+ Result :: {noreply, state()}.
+handle_cast(
+ {available, Pid},
+ #state{requests = R, ports = P, busy = B} = S) ->
+ case queue:out(R) of
+ {empty, R} ->
+ {noreply, S#state{ports = queue:in(Pid, P), busy = B - 1}};
+ {{value, #req{mon = Mon, from = F}}, R1} ->
+ true = erlang:demonitor(Mon, [flush]),
+ gen_server:reply(F, {ok, Pid}),
+ {noreply, S#state{requests = R1}}
+ end;
+handle_cast(Msg, _) -> exit({unknown_cast, Msg}).
+
+%% @private
+
+handle_info({'DOWN', Ref, process, _Pid, _Reason}, #state{requests = R} = State) ->
+ R1 = queue:from_list(lists:keydelete(Ref, #req.mon, queue:to_list(R))),
+ {noreply, State#state{requests = R1}};
+
+%% @private
+
+handle_info(Msg, _) -> exit({unknown_info, Msg}).
+
+%% @private
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+
+do_call(F, Args0) ->
+ {ok, Pid} = gen_server:call(?MODULE, request, infinity),
+ Args = [Pid|Args0],
+ apply(F, Args).