diff options
author | Calvin Morrison <calvin@pobox.com> | 2025-09-03 21:15:36 -0400 |
---|---|---|
committer | Calvin Morrison <calvin@pobox.com> | 2025-09-03 21:15:36 -0400 |
commit | 49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch) | |
tree | 61d86a7705dacc9fddccc29fa79d075d83ab8059 /server/_build/default/lib/bcrypt/src/bcrypt_pool.erl |
Diffstat (limited to 'server/_build/default/lib/bcrypt/src/bcrypt_pool.erl')
-rw-r--r-- | server/_build/default/lib/bcrypt/src/bcrypt_pool.erl | 140 |
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). |