diff --git a/.github/workflows/erlang.yml b/.github/workflows/erlang.yml
new file mode 100644
index 0000000..2509326
--- /dev/null
+++ b/.github/workflows/erlang.yml
@@ -0,0 +1,38 @@
+name: Erlang CI
+
+on: [push, pull_request]
+
+jobs:
+
+ build:
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ otp: ['23.3', '24.0']
+ rebar: ['3.16.1']
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: erlef/setup-beam@v1
+ id: setup-beam
+ with:
+ otp-version: ${{matrix.otp}}
+ rebar3-version: ${{matrix.rebar}}
+ - name: Restore _build
+ uses: actions/cache@v2
+ with:
+ path: _build
+ key: _build-cache-for-os-${{runner.os}}-otp-${{steps.setup-beam.outputs.otp-version}}-rebar3-${{steps.setup-beam.outputs.rebar3-version}}-hash-${{hashFiles('rebar.lock')}}
+ - name: Restore rebar3's cache
+ uses: actions/cache@v2
+ with:
+ path: ~/.cache/rebar3
+ key: rebar3-cache-for-os-${{runner.os}}-otp-${{steps.setup-beam.outputs.otp-version}}-rebar3-${{steps.setup-beam.outputs.rebar3-version}}-hash-${{hashFiles('rebar.lock')}}
+ - name: Compile
+ run: rebar3 compile
+ - name: Format check
+ run: rebar3 format --verify
+ - name: Run tests and verifications
+ run: rebar3 test
diff --git a/.gitignore b/.gitignore
index dee833d..4fbd7e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+rebar3.crashdump
+doc/
codecov.json
_build/
all.coverdata
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ad850e7..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-sudo: false
-language: erlang
-otp_release:
- - 21.0.2
-before_install:
- - ./ci before_install "${PWD:?}"/rebar3
-install:
- - ./ci install "${PWD:?}"/rebar3
- - pip install --user codecov
-script:
- - ./ci script "${PWD:?}"/rebar3
-cache:
- directories:
- - .plt
-after_success:
- - codecov
diff --git a/Emakefile b/Emakefile
deleted file mode 100644
index 51f591a..0000000
--- a/Emakefile
+++ /dev/null
@@ -1,2 +0,0 @@
-{"src/*", [warn_unused_vars, warn_export_all, warn_shadow_vars, warn_unused_import, warn_unused_function, warn_bif_clash, warn_unused_record, warn_deprecated_function, warn_obsolete_guard, strict_validation, report, warn_export_vars, warn_exported_vars, warn_missing_spec, warn_untyped_record, debug_info, {outdir, "ebin"}, {i, "include"}]}.
-{"test/*", [warn_unused_vars, warn_export_all, warn_shadow_vars, warn_unused_import, warn_unused_function, warn_bif_clash, warn_unused_record, warn_deprecated_function, warn_obsolete_guard, strict_validation, report, warn_export_vars, warn_exported_vars, warn_missing_spec, warn_untyped_record, debug_info, {outdir, "ebin"}, {i, "include"}]}.
diff --git a/README.md b/README.md
index 779066f..c892ded 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Worker Pool [![Build Status](https://travis-ci.org/inaka/worker_pool.svg?branch=master)](https://travis-ci.org/inaka/worker_pool)[![codecov](https://codecov.io/gh/inaka/worker_pool/branch/master/graph/badge.svg)](https://codecov.io/gh/inaka/worker_pool)
+# Worker Pool [![Build Status](https://travis-ci.org/inaka/worker_pool.svg?branch=main)](https://travis-ci.org/inaka/worker_pool)[![codecov](https://codecov.io/gh/inaka/worker_pool/branch/main/graph/badge.svg)](https://codecov.io/gh/inaka/worker_pool)
diff --git a/ci b/ci
deleted file mode 100755
index 792fc8f..0000000
--- a/ci
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-set -ev # Ref https://docs.travis-ci.com/user/customizing-the-build/#Implementing-Complex-Build-Steps
-
-case "${1:?}" in
- before_install)
- ## Travis CI does not support rebar3 yet. See https://github.com/travis-ci/travis-ci/issues/6506#issuecomment-275189490
- Rebar3="${2:?}"
- curl -f -L -o "${Rebar3:?}" https://github.com/erlang/rebar3/releases/download/3.6.1/rebar3
- chmod +x "${Rebar3:?}"
- ;;
- install)
- Rebar3="${2:?}"
- "${Rebar3:?}" deps
- "${Rebar3:?}" dialyzer -u true -s false
- ;;
- script)
- Rebar3="${2:?}"
- "${Rebar3:?}" ct
- ;;
-esac
diff --git a/elvis.config b/elvis.config
index 80b3506..ed17396 100644
--- a/elvis.config
+++ b/elvis.config
@@ -1,62 +1,20 @@
-[
- {
- elvis,
- [
- {config,
- [#{dirs => ["src"],
- filter => "*.erl",
- ruleset => erl_files,
- rules =>
- [ { elvis_style
- , invalid_dynamic_call
- , #{ignore => [ wpool_process
- , wpool_time_checker
- ]}
- }
- , { elvis_style
- , god_modules
- , #{limit => 30}
- }
- , { elvis_style
- , dont_repeat_yourself
- , #{ ignore => [wpool_SUITE]
- , min_complexity => 13
- }
- }
- , { elvis_style
- , line_length
- , #{limit => 100}
- }
- ]
- },
- #{dirs => ["test"],
- filter => "*.erl",
- ruleset => erl_files,
- rules =>
- [ { elvis_style
- , no_debug_call
- , disable
- }
- , { elvis_style
- , dont_repeat_yourself
- , #{min_complexity => 13}
- }
- , { elvis_style
- , line_length
- , #{limit => 100}
- }
- ]
- },
- #{dirs => ["."],
- filter => "Makefile",
- ruleset => makefiles
- },
- #{dirs => ["."],
- filter => "elvis.config",
- ruleset => elvis_config
- }
- ]
- }
- ]
- }
-].
+[{elvis,
+ [{config,
+ [#{dirs => ["src"],
+ filter => "*.erl",
+ ruleset => erl_files,
+ rules =>
+ [{elvis_style, invalid_dynamic_call, #{ignore => [wpool_process, wpool_time_checker]}},
+ {elvis_style, god_modules, #{limit => 30}},
+ {elvis_style, state_record_and_type, disable},
+ {elvis_style, dont_repeat_yourself, #{ignore => [wpool_SUITE], min_complexity => 13}}]},
+ #{dirs => ["test"],
+ filter => "*.erl",
+ ruleset => erl_files,
+ rules =>
+ [{elvis_style, no_debug_call, disable},
+ {elvis_style, state_record_and_type, disable},
+ {elvis_style, dont_repeat_yourself, #{min_complexity => 13}}]},
+ #{dirs => ["."],
+ filter => "elvis.config",
+ ruleset => elvis_config}]}]}].
diff --git a/rebar.config b/rebar.config
index 7bf5e15..7f1e955 100644
--- a/rebar.config
+++ b/rebar.config
@@ -2,75 +2,78 @@
%% ex: ts=4 sw=4 ft=erlang et
%% == Erlang Compiler ==
-{minimum_otp_vsn, "21.0"}.
+{minimum_otp_vsn, "23"}.
%% Erlang compiler options
-{erl_opts, [ warn_unused_vars
- , ewarn_export_all
- , warn_shadow_vars
- , warn_unused_import
- , warn_unused_function
- , warn_bif_clash
- , warn_unused_record
- , warn_deprecated_function
- , warn_obsolete_guard
- , strict_validation
- , warn_export_vars
- , warn_exported_vars
- , warn_missing_spec
- , warn_untyped_record
- , debug_info
- ]}.
+{erl_opts,
+ [warn_unused_vars,
+ ewarn_export_all,
+ warn_shadow_vars,
+ warn_unused_import,
+ warn_unused_function,
+ warn_bif_clash,
+ warn_unused_record,
+ warn_deprecated_function,
+ warn_obsolete_guard,
+ strict_validation,
+ warn_export_vars,
+ warn_exported_vars,
+ warn_missing_spec,
+ warn_untyped_record,
+ debug_info]}.
-{profiles, [{test, [{deps, [ {katana_test, "1.0.1"}
- , {katana, "0.4.0"}
- , {mixer, "1.1.0", {pkg, inaka_mixer}}
- , {meck, "0.8.13"}
- ]
- }]
- }]
-}.
+{profiles,
+ [{test,
+ [{deps, [{katana, "1.0.0"}, {mixer, "1.2.0", {pkg, inaka_mixer}}, {meck, "0.9.2"}]}]}]}.
-{ct_compile_opts, [ warn_unused_vars
- , warn_export_all
- , warn_shadow_vars
- , warn_unused_import
- , warn_unused_function
- , warn_bif_clash
- , warn_unused_record
- , warn_deprecated_function
- , warn_obsolete_guard
- , strict_validation
- , warn_export_vars
- , warn_exported_vars
- , warn_missing_spec
- , warn_untyped_record
- , debug_info
- ]}.
+{ct_compile_opts,
+ [warn_unused_vars,
+ warn_export_all,
+ warn_shadow_vars,
+ warn_unused_import,
+ warn_unused_function,
+ warn_bif_clash,
+ warn_unused_record,
+ warn_deprecated_function,
+ warn_obsolete_guard,
+ strict_validation,
+ warn_export_vars,
+ warn_exported_vars,
+ warn_missing_spec,
+ warn_untyped_record,
+ debug_info]}.
{ct_opts, []}.
{alias, [{test, [dialyzer, ct, cover]}]}.
-{ct_extra_params,"-no_auto_compile -dir ebin -logdir log/ct --erl_args -smp enable -boot start_sasl"}.
+{ct_extra_params,
+ "-no_auto_compile -dir ebin -logdir log/ct --erl_args -smp enable -boot start_sasl"}.
-{edoc_opts, [ {report_missing_types, true}
- , {source_path, ["src"]}
- , {report_missing_types, true}
- , {todo, true}
- , {packages, false}
- , {subpackages, false}
- ]}.
+{edoc_opts,
+ [{report_missing_types, true},
+ {source_path, ["src"]},
+ {report_missing_types, true},
+ {todo, true},
+ {packages, false},
+ {subpackages, false}]}.
-{dialyzer, [ {warnings, [ race_conditions
- , no_return
- , unmatched_returns
- , error_handling
- , unknown
- ]}
- , {plt_apps, all_deps}
- , {plt_extra_apps, [erts, kernel, stdlib]}
- , {plt_location, local}
- , {base_plt_apps, [stdlib, kernel]}
- , {base_plt_location, global}
- ]}.
+{dialyzer,
+ [{warnings,
+ [race_conditions, unknown, no_return, unmatched_returns, error_handling, underspecs]}]}.
+
+{project_plugins,
+ [{rebar3_hex, "~> 6.11.7"},
+ {rebar3_format, "~> 1.0.1"},
+ {rebar3_lint, "~> 0.5.0"},
+ {rebar3_hank, "~> 1.2.2"},
+ rebar3_depup]}.
+
+{cover_enabled, true}.
+
+{cover_opts, [verbose]}.
+
+{alias,
+ [{test, [compile, format, lint, hank, dialyzer, {ct, "--verbose"}, cover, edoc]}]}.
+
+{format, [{files, ["*.config", "src/*", "test/*"]}]}.
diff --git a/src/worker_pool.app.src b/src/worker_pool.app.src
index c60fdbb..c24d631 100644
--- a/src/worker_pool.app.src
+++ b/src/worker_pool.app.src
@@ -14,20 +14,19 @@
% specific language governing permissions and limitations
% under the License.
-{ application
-, worker_pool
-, [ {description, "Erlang Worker Pool"}
- , {vsn, "4.0.3"}
- , {id, "worker_pool"}
- , {registered, []}
- , {modules, []}
- , {applications, [kernel, stdlib]}
- , {mod, {wpool, []}}
- , {env, []}
- , {licenses, ["Apache2"]}
- , {links, [ {"Github", "https://github.com/inaka/worker_pool"}
- , {"Blog Post", "https://web.archive.org/web/20170602054156/http://inaka.net/blog/2014/09/25/worker-pool/"}
- ]}
- , {build_tools,["rebar3"]}
- ]
-}.
+{application,
+ worker_pool,
+ [{description, "Erlang Worker Pool"},
+ {vsn, "4.0.3"},
+ {id, "worker_pool"},
+ {registered, []},
+ {modules, []},
+ {applications, [kernel, stdlib]},
+ {mod, {wpool, []}},
+ {env, []},
+ {licenses, ["Apache2"]},
+ {links,
+ [{"Github", "https://github.com/inaka/worker_pool"},
+ {"Blog Post",
+ "https://web.archive.org/web/20170602054156/http://inaka.net/blog/2014/09/25/worker-pool/"}]},
+ {build_tools, ["rebar3"]}]}.
diff --git a/src/wpool.erl b/src/wpool.erl
index 7ab8af9..7ccf742 100644
--- a/src/wpool.erl
+++ b/src/wpool.erl
@@ -15,167 +15,152 @@
%%% @doc Worker pool main interface.
%%% Use functions provided by this module to manage your pools of workers
-module(wpool).
--author('elbrujohalcon@inaka.net').
--define(DEFAULTS, [ {overrun_warning, infinity}
- , {max_overrun_warnings, infinity}
- , {overrun_handler, {error_logger, warning_report}}
- , {workers, 100}, {worker_opt, []},
- {queue_type, fifo}
- ]).
+-behaviour(application).
+
+-define(DEFAULTS,
+ [{overrun_warning, infinity},
+ {max_overrun_warnings, infinity},
+ {overrun_handler, {error_logger, warning_report}},
+ {workers, 100},
+ {worker_opt, []},
+ {queue_type, fifo}]).
%% Copied from gen.erl
--type debug_flag() :: 'trace' | 'log' | 'statistics' | 'debug'
- | {'logfile', string()}.
--type gen_option() :: {'timeout', timeout()}
- | {'debug', [debug_flag()]}
- | {'spawn_opt', [proc_lib:spawn_option()]}.
+-type debug_flag() :: trace | log | statistics | debug | {logfile, string()}.
+-type gen_option() ::
+ {timeout, timeout()} | {debug, [debug_flag()]} | {spawn_opt, [proc_lib:spawn_option()]}.
-type gen_options() :: [gen_option()].
-
-type name() :: atom().
--type supervisor_strategy() :: { supervisor:strategy()
- , non_neg_integer()
- , pos_integer()
- }.
--type option() :: {overrun_warning, infinity|pos_integer()}
- | {max_overrun_warnings, infinity|pos_integer()}
- | {overrun_handler, {Module::atom(), Fun::atom()}}
- | {workers, pos_integer()}
- | {worker_opt, gen_options()}
- | {worker, {Module::atom(), InitArg::term()}}
- | {strategy, supervisor_strategy()}
- | {worker_type, gen_server}
- | {pool_sup_intensity, non_neg_integer()}
- | {pool_sup_period, non_neg_integer()}
- | {queue_type, wpool_queue_manager:queue_type()}
- .
--type custom_strategy() :: fun(([atom()])-> Atom::atom()).
--type strategy() :: best_worker
- | random_worker
- | next_worker
- | available_worker
- | next_available_worker
- | {hash_worker, term()}
- | custom_strategy().
--type worker_stats() :: [ {messsage_queue_len, non_neg_integer()}
- | {memory, pos_integer()}
- ].
--type stats() :: [ {pool, name()}
- | {supervisor, pid()}
- | {options, [option()]}
- | {size, non_neg_integer()}
- | {next_worker, pos_integer()}
- | {total_message_queue_len, non_neg_integer()}
- | {workers, [{pos_integer(), worker_stats()}]}
- ].
--export_type([ name/0
- , option/0
- , custom_strategy/0
- , strategy/0
- , worker_stats/0
- , stats/0
- ]).
-
--export([ start/0
- , start/2
- , stop/0
- , stop/1
- ]).
--export([ start_pool/1
- , start_pool/2
- , start_sup_pool/1
- , start_sup_pool/2
- ]).
--export([ stop_pool/1
- , stop_sup_pool/1
- ]).
--export([ call/2
- , cast/2
- , call/3
- , cast/3
- , call/4
- , broadcast/2
- ]).
--export([ stats/0
- , stats/1
- ]).
--export([ default_strategy/0
- ]).
+-type supervisor_strategy() :: {supervisor:strategy(), non_neg_integer(), pos_integer()}.
+-type option() ::
+ {overrun_warning, infinity | pos_integer()} |
+ {max_overrun_warnings, infinity | pos_integer()} |
+ {overrun_handler, {Module :: atom(), Fun :: atom()}} |
+ {workers, pos_integer()} |
+ {worker_opt, gen_options()} |
+ {worker, {Module :: atom(), InitArg :: term()}} |
+ {strategy, supervisor_strategy()} |
+ {worker_type, gen_server} |
+ {pool_sup_intensity, non_neg_integer()} |
+ {pool_sup_period, non_neg_integer()} |
+ {queue_type, wpool_queue_manager:queue_type()}.
+-type custom_strategy() :: fun(([atom()]) -> Atom :: atom()).
+-type strategy() ::
+ best_worker |
+ random_worker |
+ next_worker |
+ available_worker |
+ next_available_worker |
+ {hash_worker, term()} |
+ custom_strategy().
+-type worker_stats() ::
+ [{messsage_queue_len, non_neg_integer()} | {memory, pos_integer()}].
+-type stats() ::
+ [{pool, name()} |
+ {supervisor, pid()} |
+ {options, [option()]} |
+ {size, non_neg_integer()} |
+ {next_worker, pos_integer()} |
+ {total_message_queue_len, non_neg_integer()} |
+ {workers, [{pos_integer(), worker_stats()}]}].
+
+-export_type([name/0, option/0, custom_strategy/0, strategy/0, worker_stats/0, stats/0]).
+
+-export([start/0, start/2, stop/0, stop/1]).
+-export([start_pool/1, start_pool/2, start_sup_pool/1, start_sup_pool/2]).
+-export([stop_pool/1, stop_sup_pool/1]).
+-export([call/2, cast/2, call/3, cast/3, call/4, broadcast/2]).
+-export([stats/0, stats/1]).
+-export([default_strategy/0]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ADMIN API
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Starts the application
-spec start() -> ok | {error, {already_started, ?MODULE}}.
-start() -> application:start(worker_pool).
+start() ->
+ application:start(worker_pool).
%% @doc Stops the application
-spec stop() -> ok.
-stop() -> application:stop(worker_pool).
+stop() ->
+ application:stop(worker_pool).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% BEHAVIOUR CALLBACKS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @private
-spec start(any(), any()) -> {ok, pid()} | {error, term()}.
-start(_StartType, _StartArgs) -> wpool_sup:start_link().
+start(_StartType, _StartArgs) ->
+ wpool_sup:start_link().
%% @private
-spec stop(any()) -> ok.
-stop(_State) -> ok.
+stop(_State) ->
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PUBLIC API
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @equiv start_pool(Name, [])
-spec start_pool(name()) -> {ok, pid()}.
-start_pool(Name) -> start_pool(Name, []).
+start_pool(Name) ->
+ start_pool(Name, []).
%% @doc Starts (and links) a pool of N wpool_processes.
%% The result pid belongs to a supervisor (in case you want to add it to a
%% supervisor tree)
-spec start_pool(name(), [option()]) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
-start_pool(Name, Options) -> wpool_pool:start_link(Name, all_opts(Options)).
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
+start_pool(Name, Options) ->
+ wpool_pool:start_link(Name, all_opts(Options)).
%% @doc Stops the pool
-spec stop_pool(name()) -> true.
stop_pool(Name) ->
- case whereis(Name) of
- undefined -> true;
- Pid -> exit(Pid, normal)
- end.
+ case whereis(Name) of
+ undefined ->
+ true;
+ Pid ->
+ exit(Pid, normal)
+ end.
%% @equiv start_sup_pool(Name, [])
--spec start_sup_pool(name()) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
-start_sup_pool(Name) -> start_sup_pool(Name, []).
+-spec start_sup_pool(name()) -> {ok, pid()} | {error, {already_started, pid()} | term()}.
+start_sup_pool(Name) ->
+ start_sup_pool(Name, []).
%% @doc Starts a pool of N wpool_processes supervised by {@link wpool_sup}
-spec start_sup_pool(name(), [option()]) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
start_sup_pool(Name, Options) ->
- wpool_sup:start_pool(Name, all_opts(Options)).
+ wpool_sup:start_pool(Name, all_opts(Options)).
%% @doc Stops the pool
-spec stop_sup_pool(name()) -> ok.
-stop_sup_pool(Name) -> wpool_sup:stop_pool(Name).
+stop_sup_pool(Name) ->
+ wpool_sup:stop_pool(Name).
%% @doc Default strategy
-spec default_strategy() -> strategy().
default_strategy() ->
- case application:get_env(worker_pool, default_strategy) of
- undefined -> available_worker;
- {ok, Strategy} -> Strategy
- end.
+ case application:get_env(worker_pool, default_strategy) of
+ undefined ->
+ available_worker;
+ {ok, Strategy} ->
+ Strategy
+ end.
%% @equiv call(Sup, Call, default_strategy())
-spec call(name(), term()) -> term().
-call(Sup, Call) -> call(Sup, Call, default_strategy()).
+call(Sup, Call) ->
+ call(Sup, Call, default_strategy()).
%% @equiv call(Sup, Call, Strategy, 5000)
-spec call(name(), term(), strategy()) -> term().
-call(Sup, Call, Strategy) -> call(Sup, Call, Strategy, 5000).
+call(Sup, Call, Strategy) ->
+ call(Sup, Call, Strategy, 5000).
%% @doc Picks a server and issues the call to it.
%% For all strategies except available_worker, Timeout applies only to the
@@ -184,43 +169,51 @@ call(Sup, Call, Strategy) -> call(Sup, Call, Strategy, 5000).
%% For available_worker the time used choosing a worker is also considered
-spec call(name(), term(), strategy(), timeout()) -> term().
call(Sup, Call, available_worker, Timeout) ->
- wpool_pool:call_available_worker(Sup, Call, Timeout);
+ wpool_pool:call_available_worker(Sup, Call, Timeout);
call(Sup, Call, {hash_worker, HashKey}, Timeout) ->
- wpool_process:call(wpool_pool:hash_worker(Sup, HashKey), Call, Timeout);
+ wpool_process:call(
+ wpool_pool:hash_worker(Sup, HashKey), Call, Timeout);
call(Sup, Call, Fun, Timeout) when is_function(Fun) ->
- wpool_process:call(Fun(Sup), Call, Timeout);
+ wpool_process:call(Fun(Sup), Call, Timeout);
call(Sup, Call, Strategy, Timeout) ->
- wpool_process:call(wpool_pool:Strategy(Sup), Call, Timeout).
+ wpool_process:call(
+ wpool_pool:Strategy(Sup), Call, Timeout).
%% @equiv cast(Sup, Cast, default_strategy())
-spec cast(name(), term()) -> ok.
-cast(Sup, Cast) -> cast(Sup, Cast, default_strategy()).
+cast(Sup, Cast) ->
+ cast(Sup, Cast, default_strategy()).
%% @doc Picks a server and issues the cast to it
-spec cast(name(), term(), strategy()) -> ok.
cast(Sup, Cast, available_worker) ->
- wpool_pool:cast_to_available_worker(Sup, Cast);
+ wpool_pool:cast_to_available_worker(Sup, Cast);
cast(Sup, Cast, {hash_worker, HashKey}) ->
- wpool_process:cast(wpool_pool:hash_worker(Sup, HashKey), Cast);
+ wpool_process:cast(
+ wpool_pool:hash_worker(Sup, HashKey), Cast);
cast(Sup, Cast, Fun) when is_function(Fun) ->
- wpool_process:cast(Fun(Sup), Cast);
+ wpool_process:cast(Fun(Sup), Cast);
cast(Sup, Cast, Strategy) ->
- wpool_process:cast(wpool_pool:Strategy(Sup), Cast).
+ wpool_process:cast(
+ wpool_pool:Strategy(Sup), Cast).
%% @doc Retrieves a snapshot of the pool stats
-spec stats() -> [stats()].
-stats() -> wpool_pool:stats().
+stats() ->
+ wpool_pool:stats().
%% @doc Retrieves a snapshot of a given pool stats
-spec stats(name()) -> stats().
-stats(Sup) -> wpool_pool:stats(Sup).
+stats(Sup) ->
+ wpool_pool:stats(Sup).
%% @doc Casts a message to all the workers within the given pool.
-spec broadcast(wpool:name(), term()) -> ok.
broadcast(Sup, Cast) ->
- wpool_pool:broadcast(Sup, Cast).
+ wpool_pool:broadcast(Sup, Cast).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PRIVATE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all_opts(Options) -> Options ++ ?DEFAULTS.
+all_opts(Options) ->
+ Options ++ ?DEFAULTS.
diff --git a/src/wpool_pool.erl b/src/wpool_pool.erl
index f06c224..6594832 100644
--- a/src/wpool_pool.erl
+++ b/src/wpool_pool.erl
@@ -15,52 +15,31 @@
%%% @doc A pool of workers. If you want to put it in your supervisor tree,
%%% remember it's a supervisor.
-module(wpool_pool).
--author('elbrujohalcon@inaka.net').
-behaviour(supervisor).
%% API
--export([ start_link/2
- , create_table/0
- ]).
--export([ best_worker/1
- , random_worker/1
- , next_worker/1
- , hash_worker/2
- , next_available_worker/1
- , call_available_worker/3
- , time_checker_name/1
- ]).
--export([ cast_to_available_worker/2
- , broadcast/2
- ]).
--export([ stats/0
- , stats/1
- ]).
--export([ worker_name/2
- , find_wpool/1
- , all/0
- ]).
--export([ next/2
- , wpool_get/2
- ]).
--export([ add_callback_module/2
- , remove_callback_module/2
- ]).
-
+-export([start_link/2, create_table/0]).
+-export([best_worker/1, random_worker/1, next_worker/1, hash_worker/2,
+ next_available_worker/1, call_available_worker/3, time_checker_name/1]).
+-export([cast_to_available_worker/2, broadcast/2]).
+-export([stats/0, stats/1]).
+-export([worker_name/2, find_wpool/1, all/0]).
+-export([next/2, wpool_get/2]).
+-export([add_callback_module/2, remove_callback_module/2]).
%% Supervisor callbacks
--export([ init/1
- ]).
+-export([init/1]).
--record(wpool, { name :: wpool:name()
- , size :: pos_integer()
- , next :: pos_integer()
- , opts :: [wpool:option()]
- , qmanager :: wpool_queue_manager:queue_mgr()
- , born = os:timestamp() :: erlang:timestamp()
- }).
+-record(wpool,
+ {name :: wpool:name(),
+ size :: pos_integer(),
+ next :: pos_integer(),
+ opts :: [wpool:option()],
+ qmanager :: wpool_queue_manager:queue_mgr(),
+ born = os:timestamp() :: erlang:timestamp()}).
-opaque wpool() :: #wpool{}.
+
-export_type([wpool/0]).
%% ===================================================================
@@ -69,72 +48,80 @@
%% @doc Creates the ets table that will hold the information about active pools
-spec create_table() -> ok.
create_table() ->
- _ = ets:new(
- ?MODULE,
- [public, named_table, set,
- {read_concurrency, true}, {keypos, #wpool.name}]),
- ok.
+ _ = ets:new(?MODULE,
+ [public, named_table, set, {read_concurrency, true}, {keypos, #wpool.name}]),
+ ok.
%% @doc Starts a supervisor with several {@link wpool_process}es as its children
-spec start_link(wpool:name(), [wpool:option()]) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
start_link(Name, Options) ->
- supervisor:start_link({local, Name}, ?MODULE, {Name, Options}).
+ supervisor:start_link({local, Name}, ?MODULE, {Name, Options}).
%% @doc Picks the worker with the smaller queue of messages.
%% @throws no_workers
-spec best_worker(wpool:name()) -> atom().
best_worker(Sup) ->
- case find_wpool(Sup) of
- undefined -> exit(no_workers);
- Wpool -> min_message_queue(Wpool)
- end.
+ case find_wpool(Sup) of
+ undefined ->
+ exit(no_workers);
+ Wpool ->
+ min_message_queue(Wpool)
+ end.
%% @doc Picks a random worker
%% @throws no_workers
-spec random_worker(wpool:name()) -> atom().
random_worker(Sup) ->
- case wpool_size(Sup) of
- undefined -> exit(no_workers);
- WpoolSize ->
- WorkerNumber = rand:uniform(WpoolSize),
- worker_name(Sup, WorkerNumber)
- end.
+ case wpool_size(Sup) of
+ undefined ->
+ exit(no_workers);
+ WpoolSize ->
+ WorkerNumber = rand:uniform(WpoolSize),
+ worker_name(Sup, WorkerNumber)
+ end.
%% @doc Picks the next worker in a round robin fashion
%% @throws no_workers
-spec next_worker(wpool:name()) -> atom().
next_worker(Sup) ->
- case move_wpool(Sup) of
- undefined -> exit(no_workers);
- Next -> worker_name(Sup, Next)
- end.
+ case move_wpool(Sup) of
+ undefined ->
+ exit(no_workers);
+ Next ->
+ worker_name(Sup, Next)
+ end.
%% @doc Picks the first available worker, if any
%% @throws no_workers | no_available_workers
-spec next_available_worker(wpool:name()) -> atom().
next_available_worker(Sup) ->
- case find_wpool(Sup) of
- undefined -> exit(no_workers);
- Wpool ->
- case worker_with_no_task(Wpool) of
- undefined -> exit(no_available_workers);
- Worker -> Worker
- end
- end.
+ case find_wpool(Sup) of
+ undefined ->
+ exit(no_workers);
+ Wpool ->
+ case worker_with_no_task(Wpool) of
+ undefined ->
+ exit(no_available_workers);
+ Worker ->
+ Worker
+ end
+ end.
%% @doc Picks the first available worker and sends the call to it.
%% The timeout provided includes the time it takes to get a worker
%% and for it to process the call.
%% @throws no_workers | timeout
--spec call_available_worker(wpool:name(), any(), timeout()) -> any().
+-spec call_available_worker(wpool:name(), any(), timeout()) -> atom().
call_available_worker(Sup, Call, Timeout) ->
- case wpool_queue_manager:call_available_worker(
- queue_manager_name(Sup), Call, Timeout) of
- noproc -> exit(no_workers);
- timeout -> exit(timeout);
- Result -> Result
- end.
+ case wpool_queue_manager:call_available_worker(queue_manager_name(Sup), Call, Timeout) of
+ noproc ->
+ exit(no_workers);
+ timeout ->
+ exit(timeout);
+ Result ->
+ Result
+ end.
%% @doc Picks a worker base on a hash result.
%%
phash2(Term, Range)
returns hash = integer,
@@ -142,12 +129,13 @@ call_available_worker(Sup, Call, Timeout) ->
%% @throws no_workers
-spec hash_worker(wpool:name(), term()) -> atom().
hash_worker(Sup, HashKey) ->
- case wpool_size(Sup) of
- undefined -> exit(no_workers);
- WpoolSize ->
- Index = 1 + erlang:phash2(HashKey, WpoolSize),
- worker_name(Sup, Index)
- end.
+ case wpool_size(Sup) of
+ undefined ->
+ exit(no_workers);
+ WpoolSize ->
+ Index = 1 + erlang:phash2(HashKey, WpoolSize),
+ worker_name(Sup, Index)
+ end.
%% @doc Casts a message to the first available worker.
%% Since we can wait forever for a wpool:cast to be delivered
@@ -155,352 +143,370 @@ hash_worker(Sup, HashKey) ->
%% just forwards the cast when it gets the worker
-spec cast_to_available_worker(wpool:name(), term()) -> ok.
cast_to_available_worker(Sup, Cast) ->
- wpool_queue_manager:cast_to_available_worker(queue_manager_name(Sup), Cast).
+ wpool_queue_manager:cast_to_available_worker(queue_manager_name(Sup), Cast).
%% @doc Casts a message to all the workers within the given pool.
-spec broadcast(wpool:name(), term()) -> ok.
broadcast(Sup, Cast) ->
- lists:foreach( fun(Worker) -> ok = wpool_process:cast(Worker, Cast) end
- , all_workers(Sup)
- ).
+ lists:foreach(fun(Worker) -> ok = wpool_process:cast(Worker, Cast) end, all_workers(Sup)).
-spec all() -> [wpool:name()].
all() ->
- [Name || #wpool{name = Name} <- ets:tab2list(?MODULE)
- , find_wpool(Name) /= undefined].
+ [Name || #wpool{name = Name} <- ets:tab2list(?MODULE), find_wpool(Name) /= undefined].
%% @doc Retrieves the pool stats for all pools
-spec stats() -> [wpool:stats()].
-stats() -> [stats(Sup) || Sup <- all()].
+stats() ->
+ [stats(Sup) || Sup <- all()].
%% @doc Retrieves a snapshot of the pool stats
%% @throws no_workers
-spec stats(wpool:name()) -> wpool:stats().
stats(Sup) ->
- case find_wpool(Sup) of
- undefined -> exit(no_workers);
- Wpool ->
- stats(Wpool, Sup)
- end.
+ case find_wpool(Sup) of
+ undefined ->
+ exit(no_workers);
+ Wpool ->
+ stats(Wpool, Sup)
+ end.
stats(Wpool, Sup) ->
- {Total, WorkerStats} =
- lists:foldl(
- fun(N, {T, L}) ->
- case worker_info(Sup, N, [ message_queue_len
- , memory
- , current_function
- , current_location
- , dictionary
- ]) of
- undefined ->
- {T, L};
- [{message_queue_len, MQL} = MQLT, Memory, Function, Location, {dictionary, Dictionary}] ->
- WS = [MQLT, Memory] ++
- function_location(Function, Location) ++
- task(proplists:get_value(wpool_task, Dictionary)),
- {T + MQL, [{N, WS} | L]}
- end
- end, {0, []}, lists:seq(1, Wpool#wpool.size)),
- PendingTasks = wpool_queue_manager:pending_task_count(Wpool#wpool.qmanager),
- [ {pool, Sup}
- , {supervisor, erlang:whereis(Sup)}
- , {options, lists:ukeysort(1, proplists:unfold(Wpool#wpool.opts))}
- , {size, Wpool#wpool.size}
- , {next_worker, Wpool#wpool.next}
- , {total_message_queue_len, Total + PendingTasks}
- , {workers, WorkerStats}
- ].
+ {Total, WorkerStats} =
+ lists:foldl(fun(N, {T, L}) ->
+ case worker_info(Sup,
+ N,
+ [message_queue_len,
+ memory,
+ current_function,
+ current_location,
+ dictionary])
+ of
+ undefined ->
+ {T, L};
+ [{message_queue_len, MQL} = MQLT,
+ Memory,
+ Function,
+ Location,
+ {dictionary, Dictionary}] ->
+ WS = [MQLT, Memory]
+ ++ function_location(Function, Location)
+ ++ task(proplists:get_value(wpool_task, Dictionary)),
+ {T + MQL, [{N, WS} | L]}
+ end
+ end,
+ {0, []},
+ lists:seq(1, Wpool#wpool.size)),
+ PendingTasks = wpool_queue_manager:pending_task_count(Wpool#wpool.qmanager),
+ [{pool, Sup},
+ {supervisor, erlang:whereis(Sup)},
+ {options, lists:ukeysort(1, proplists:unfold(Wpool#wpool.opts))},
+ {size, Wpool#wpool.size},
+ {next_worker, Wpool#wpool.next},
+ {total_message_queue_len, Total + PendingTasks},
+ {workers, WorkerStats}].
worker_info(Sup, N, Info) ->
- case erlang:whereis(worker_name(Sup, N)) of
- undefined ->
- undefined;
- Worker ->
- erlang:process_info(Worker, Info)
- end.
+ case erlang:whereis(worker_name(Sup, N)) of
+ undefined ->
+ undefined;
+ Worker ->
+ erlang:process_info(Worker, Info)
+ end.
function_location({current_function, {gen_server, loop, _}}, _) ->
- [];
+ [];
function_location({current_function, {erlang, hibernate, _}}, _) ->
- [];
+ [];
function_location(Function, Location) ->
- [Function, Location].
+ [Function, Location].
+
task(undefined) ->
- [];
+ [];
task({_TaskId, Started, Task}) ->
- Time =
- calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
- [{task, Task}, {runtime, Time - Started}].
+ Time =
+ calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ [{task, Task}, {runtime, Time - Started}].
%% @doc the number of workers in the pool
-spec wpool_size(atom()) -> non_neg_integer() | undefined.
wpool_size(Name) ->
- try ets:update_counter(?MODULE, Name, {#wpool.size, 0}) of
- WpoolSize ->
- case erlang:whereis(Name) of
- undefined ->
- ets:delete(?MODULE, Name),
- undefined;
- _ ->
- WpoolSize
- end
- catch
- _:badarg ->
- case build_wpool(Name) of
- undefined -> undefined;
- Wpool -> Wpool#wpool.size
- end
- end.
-
+ try ets:update_counter(?MODULE, Name, {#wpool.size, 0}) of
+ WpoolSize ->
+ case erlang:whereis(Name) of
+ undefined ->
+ ets:delete(?MODULE, Name),
+ undefined;
+ _ ->
+ WpoolSize
+ end
+ catch
+ _:badarg ->
+ case build_wpool(Name) of
+ undefined ->
+ undefined;
+ Wpool ->
+ Wpool#wpool.size
+ end
+ end.
%% @doc Set next within the worker pool record. Useful when using
%% a custom strategy function.
-spec next(pos_integer(), wpool()) -> wpool().
-next(Next, WPool) -> WPool#wpool{next = Next}.
+next(Next, WPool) ->
+ WPool#wpool{next = Next}.
-spec add_callback_module(wpool:name(), module()) -> ok | {error, term()}.
add_callback_module(Pool, Module) ->
- EventManager = event_manager_name(Pool),
- wpool_process_callbacks:add_callback_module(EventManager, Module).
+ EventManager = event_manager_name(Pool),
+ wpool_process_callbacks:add_callback_module(EventManager, Module).
-spec remove_callback_module(wpool:name(), module()) -> ok | {error, term()}.
remove_callback_module(Pool, Module) ->
- EventManager = event_manager_name(Pool),
- wpool_process_callbacks:remove_callback_module(EventManager, Module).
+ EventManager = event_manager_name(Pool),
+ wpool_process_callbacks:remove_callback_module(EventManager, Module).
%% @doc Get values from the worker pool record. Useful when using a custom
%% strategy function.
--spec wpool_get(atom(), wpool()) -> any(); ([atom()], wpool()) -> any().
-wpool_get(List, WPool) when is_list(List)->
- [g(Atom, WPool) || Atom <- List];
+-spec wpool_get(atom(), wpool()) -> any();
+ ([atom()], wpool()) -> any().
+wpool_get(List, WPool) when is_list(List) ->
+ [g(Atom, WPool) || Atom <- List];
wpool_get(Atom, WPool) when is_atom(Atom) ->
- g(Atom, WPool).
-
-g(name, #wpool{name=Ret}) -> Ret;
-g(size, #wpool{size=Ret}) -> Ret;
-g(next, #wpool{next=Ret}) -> Ret;
-g(opts, #wpool{opts=Ret}) -> Ret;
-g(qmanager, #wpool{qmanager=Ret}) -> Ret;
-g(born, #wpool{born=Ret}) -> Ret.
+ g(Atom, WPool).
+
+g(name, #wpool{name = Ret}) ->
+ Ret;
+g(size, #wpool{size = Ret}) ->
+ Ret;
+g(next, #wpool{next = Ret}) ->
+ Ret;
+g(opts, #wpool{opts = Ret}) ->
+ Ret;
+g(qmanager, #wpool{qmanager = Ret}) ->
+ Ret;
+g(born, #wpool{born = Ret}) ->
+ Ret.
-spec time_checker_name(wpool:name()) -> atom().
time_checker_name(Sup) ->
- list_to_atom(
- ?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-time-checker").
+ list_to_atom(?MODULE_STRING ++ [$- | atom_to_list(Sup)] ++ "-time-checker").
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
%% @private
-spec init({wpool:name(), [wpool:option()]}) ->
- {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
+ {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init({Name, Options}) ->
- Workers = proplists:get_value(workers, Options, 100),
- QueueType = proplists:get_value(queue_type, Options),
- OverrunHandler =
- proplists:get_value(
- overrun_handler, Options, {error_logger, warning_report}),
- TimeChecker = time_checker_name(Name),
- QueueManager = queue_manager_name(Name),
- ProcessSup = process_sup_name(Name),
- EventManagerName = event_manager_name(Name),
- _Wpool =
- store_wpool(
- #wpool{ name = Name
- , size = Workers
- , next = 1
- , opts = Options
- , qmanager = QueueManager
- }),
- TimeCheckerSpec =
- { TimeChecker
- , {wpool_time_checker, start_link, [Name, TimeChecker, OverrunHandler]}
- , permanent
- , brutal_kill
- , worker
- , [wpool_time_checker]
- },
- QueueManagerSpec =
- { QueueManager
- , {wpool_queue_manager, start_link, [Name, QueueManager,
- [{queue_type, QueueType}]]
- }
- , permanent
- , brutal_kill
- , worker
- , [wpool_queue_manager]
- },
-
- EventManagerSpec =
- { EventManagerName
- , {gen_event, start_link, [{local, EventManagerName}]}
- , permanent
- , brutal_kill
- , worker
- , dynamic
- },
-
- SupShutdown = proplists:get_value(pool_sup_shutdown, Options, brutal_kill),
- WorkerOpts =
- [{queue_manager, QueueManager}, {time_checker, TimeChecker}
- | Options] ++ maybe_event_manager(Options, {event_manager, EventManagerName}),
- ProcessSupSpec =
- { ProcessSup
- , {wpool_process_sup, start_link, [Name, ProcessSup, WorkerOpts]}
- , permanent
- , SupShutdown
- , supervisor
- , [wpool_process_sup]
- },
-
- Children = [TimeCheckerSpec, QueueManagerSpec] ++
- maybe_event_manager(Options, EventManagerSpec) ++
- [ProcessSupSpec],
-
- SupIntensity = proplists:get_value(pool_sup_intensity, Options, 5),
- SupPeriod = proplists:get_value(pool_sup_period, Options, 60),
- SupStrategy = {one_for_all, SupIntensity, SupPeriod},
- {ok, {SupStrategy, Children}}.
+ Workers = proplists:get_value(workers, Options, 100),
+ QueueType = proplists:get_value(queue_type, Options),
+ OverrunHandler =
+ proplists:get_value(overrun_handler, Options, {error_logger, warning_report}),
+ TimeChecker = time_checker_name(Name),
+ QueueManager = queue_manager_name(Name),
+ ProcessSup = process_sup_name(Name),
+ EventManagerName = event_manager_name(Name),
+ _Wpool =
+ store_wpool(#wpool{name = Name,
+ size = Workers,
+ next = 1,
+ opts = Options,
+ qmanager = QueueManager}),
+ TimeCheckerSpec =
+ {TimeChecker,
+ {wpool_time_checker, start_link, [Name, TimeChecker, OverrunHandler]},
+ permanent,
+ brutal_kill,
+ worker,
+ [wpool_time_checker]},
+ QueueManagerSpec =
+ {QueueManager,
+ {wpool_queue_manager, start_link, [Name, QueueManager, [{queue_type, QueueType}]]},
+ permanent,
+ brutal_kill,
+ worker,
+ [wpool_queue_manager]},
+
+ EventManagerSpec =
+ {EventManagerName,
+ {gen_event, start_link, [{local, EventManagerName}]},
+ permanent,
+ brutal_kill,
+ worker,
+ dynamic},
+
+ SupShutdown = proplists:get_value(pool_sup_shutdown, Options, brutal_kill),
+ WorkerOpts =
+ [{queue_manager, QueueManager}, {time_checker, TimeChecker} | Options]
+ ++ maybe_event_manager(Options, {event_manager, EventManagerName}),
+ ProcessSupSpec =
+ {ProcessSup,
+ {wpool_process_sup, start_link, [Name, ProcessSup, WorkerOpts]},
+ permanent,
+ SupShutdown,
+ supervisor,
+ [wpool_process_sup]},
+
+ Children =
+ [TimeCheckerSpec, QueueManagerSpec]
+ ++ maybe_event_manager(Options, EventManagerSpec)
+ ++ [ProcessSupSpec],
+
+ SupIntensity = proplists:get_value(pool_sup_intensity, Options, 5),
+ SupPeriod = proplists:get_value(pool_sup_period, Options, 60),
+ SupStrategy = {one_for_all, SupIntensity, SupPeriod},
+ {ok, {SupStrategy, Children}}.
%% @private
-spec worker_name(wpool:name(), pos_integer()) -> atom().
worker_name(Sup, I) ->
- list_to_atom(
- ?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ [$-| integer_to_list(I)]).
+ list_to_atom(?MODULE_STRING ++ [$- | atom_to_list(Sup)] ++ [$- | integer_to_list(I)]).
%% ===================================================================
%% Private functions
%% ===================================================================
process_sup_name(Sup) ->
- list_to_atom(?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-process-sup").
+ list_to_atom(?MODULE_STRING ++ [$- | atom_to_list(Sup)] ++ "-process-sup").
+
queue_manager_name(Sup) ->
- list_to_atom(?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-queue-manager").
+ list_to_atom(?MODULE_STRING ++ [$- | atom_to_list(Sup)] ++ "-queue-manager").
+
event_manager_name(Sup) ->
- list_to_atom(?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-event-manager").
+ list_to_atom(?MODULE_STRING ++ [$- | atom_to_list(Sup)] ++ "-event-manager").
worker_with_no_task(Wpool) ->
- %% Moving the beginning of the list to a random point to ensure that clients
- %% do not always start asking for process_info to the processes that are most
- %% likely to have bigger message queues
- First = rand:uniform(Wpool#wpool.size),
- worker_with_no_task(0, Wpool#wpool{next = First}).
+ %% Moving the beginning of the list to a random point to ensure that clients
+ %% do not always start asking for process_info to the processes that are most
+ %% likely to have bigger message queues
+ First = rand:uniform(Wpool#wpool.size),
+ worker_with_no_task(0, Wpool#wpool{next = First}).
+
worker_with_no_task(Size, #wpool{size = Size}) ->
- undefined;
+ undefined;
worker_with_no_task(Checked, Wpool) ->
- Worker = worker_name(Wpool#wpool.name, Wpool#wpool.next),
- case try_process_info(whereis(Worker), [message_queue_len, dictionary]) of
- [{message_queue_len, 0}, {dictionary, Dictionary}] ->
- case proplists:get_value(wpool_task, Dictionary) of
- undefined -> Worker;
- _ -> worker_with_no_task(Checked + 1, next_wpool(Wpool))
- end;
- _ ->
- worker_with_no_task(Checked + 1, next_wpool(Wpool))
- end.
+ Worker = worker_name(Wpool#wpool.name, Wpool#wpool.next),
+ case try_process_info(whereis(Worker), [message_queue_len, dictionary]) of
+ [{message_queue_len, 0}, {dictionary, Dictionary}] ->
+ case proplists:get_value(wpool_task, Dictionary) of
+ undefined ->
+ Worker;
+ _ ->
+ worker_with_no_task(Checked + 1, next_wpool(Wpool))
+ end;
+ _ ->
+ worker_with_no_task(Checked + 1, next_wpool(Wpool))
+ end.
try_process_info(undefined, _) ->
- [];
+ [];
try_process_info(Pid, Keys) ->
- erlang:process_info(Pid, Keys).
+ erlang:process_info(Pid, Keys).
min_message_queue(Wpool) ->
- %% Moving the beginning of the list to a random point to ensure that clients
- %% do not always start asking for process_info to the processes that are most
- %% likely to have bigger message queues
- First = rand:uniform(Wpool#wpool.size),
- min_message_queue(0, Wpool#wpool{next = First}, []).
+ %% Moving the beginning of the list to a random point to ensure that clients
+ %% do not always start asking for process_info to the processes that are most
+ %% likely to have bigger message queues
+ First = rand:uniform(Wpool#wpool.size),
+ min_message_queue(0, Wpool#wpool{next = First}, []).
+
min_message_queue(Size, #wpool{size = Size}, Found) ->
- {_, Worker} = lists:min(Found),
- Worker;
+ {_, Worker} = lists:min(Found),
+ Worker;
min_message_queue(Checked, Wpool, Found) ->
- Worker = worker_name(Wpool#wpool.name, Wpool#wpool.next),
- QLength = queue_length(whereis(Worker)),
- min_message_queue(Checked + 1, next_wpool(Wpool),
- [{QLength, Worker} | Found]).
+ Worker = worker_name(Wpool#wpool.name, Wpool#wpool.next),
+ QLength = queue_length(whereis(Worker)),
+ min_message_queue(Checked + 1, next_wpool(Wpool), [{QLength, Worker} | Found]).
queue_length(undefined) ->
- infinity;
+ infinity;
queue_length(Pid) when is_pid(Pid) ->
- case erlang:process_info(Pid, message_queue_len) of
- {message_queue_len, L} -> L;
- undefined -> infinity
- end.
+ case erlang:process_info(Pid, message_queue_len) of
+ {message_queue_len, L} ->
+ L;
+ undefined ->
+ infinity
+ end.
-spec all_workers(wpool:name()) -> [atom()].
all_workers(Wpool) ->
- WPoolSize = wpool_size(Wpool),
- case WPoolSize of
- undefined -> exit(no_workers);
- _ -> [wpool_pool:worker_name(Wpool, N) || N <- lists:seq(1, WPoolSize)]
- end.
+ WPoolSize = wpool_size(Wpool),
+ case WPoolSize of
+ undefined ->
+ exit(no_workers);
+ _ ->
+ [wpool_pool:worker_name(Wpool, N) || N <- lists:seq(1, WPoolSize)]
+ end.
%% ===================================================================
%% ETS functions
%% ===================================================================
store_wpool(Wpool) ->
- true = ets:insert(?MODULE, Wpool),
- Wpool.
+ true = ets:insert(?MODULE, Wpool),
+ Wpool.
move_wpool(Name) ->
- try
- WpoolSize = ets:update_counter(?MODULE, Name, {#wpool.size, 0}),
- ets:update_counter(?MODULE, Name, {#wpool.next, 1, WpoolSize, 1})
- catch
- _:badarg ->
- case build_wpool(Name) of
- undefined -> undefined;
- Wpool -> Wpool#wpool.next
- end
- end.
+ try
+ WpoolSize = ets:update_counter(?MODULE, Name, {#wpool.size, 0}),
+ ets:update_counter(?MODULE, Name, {#wpool.next, 1, WpoolSize, 1})
+ catch
+ _:badarg ->
+ case build_wpool(Name) of
+ undefined ->
+ undefined;
+ Wpool ->
+ Wpool#wpool.next
+ end
+ end.
%% @doc Use this function to get the Worker pool record in a custom worker.
-spec find_wpool(atom()) -> undefined | wpool().
find_wpool(Name) ->
- try ets:lookup(?MODULE, Name) of
- [Wpool | _] ->
- case erlang:whereis(Name) of
- undefined ->
- ets:delete(?MODULE, Name),
- undefined;
+ try ets:lookup(?MODULE, Name) of
+ [Wpool | _] ->
+ case erlang:whereis(Name) of
+ undefined ->
+ ets:delete(?MODULE, Name),
+ undefined;
+ _ ->
+ Wpool
+ end;
_ ->
- Wpool
- end;
- _ -> build_wpool(Name)
- catch
- _:badarg ->
- build_wpool(Name)
- end.
+ build_wpool(Name)
+ catch
+ _:badarg ->
+ build_wpool(Name)
+ end.
%% @doc We use this function not to report an error if for some reason we've
%% lost the record on the ets table. This SHOULDN'T be called too much
build_wpool(Name) ->
- error_logger:warning_msg(
- "Building a #wpool record for ~p. Something must have failed.", [Name]),
- try supervisor:count_children(process_sup_name(Name)) of
- Children ->
- Size = proplists:get_value(active, Children, 0),
- Wpool =
- #wpool{ name = Name
- , size = Size
- , next = 1
- , opts = []
- , qmanager = queue_manager_name(Name)
- },
- store_wpool(Wpool)
- catch
- _:Error ->
- error_logger:warning_msg("Wpool ~p not found: ~p", [Name, Error]),
- undefined
- end.
+ error_logger:warning_msg("Building a #wpool record for ~p. Something must have failed.",
+ [Name]),
+ try supervisor:count_children(process_sup_name(Name)) of
+ Children ->
+ Size = proplists:get_value(active, Children, 0),
+ Wpool =
+ #wpool{name = Name,
+ size = Size,
+ next = 1,
+ opts = [],
+ qmanager = queue_manager_name(Name)},
+ store_wpool(Wpool)
+ catch
+ _:Error ->
+ error_logger:warning_msg("Wpool ~p not found: ~p", [Name, Error]),
+ undefined
+ end.
next_wpool(Wpool) ->
- Wpool#wpool{next = (Wpool#wpool.next rem Wpool#wpool.size) + 1}.
+ Wpool#wpool{next = Wpool#wpool.next rem Wpool#wpool.size + 1}.
maybe_event_manager(Options, Item) ->
- EnableEventManager = proplists:get_value(enable_callbacks, Options, false),
- case EnableEventManager of
- true ->
- [Item];
- _ -> []
- end.
+ EnableEventManager = proplists:get_value(enable_callbacks, Options, false),
+ case EnableEventManager of
+ true ->
+ [Item];
+ _ ->
+ []
+ end.
diff --git a/src/wpool_process.erl b/src/wpool_process.erl
index e27cf01..2f6b379 100644
--- a/src/wpool_process.erl
+++ b/src/wpool_process.erl
@@ -15,264 +15,262 @@
%%% @doc Decorator over {@link gen_server} that lets {@link wpool_pool}
%%% control certain aspects of the execution
-module(wpool_process).
--author('elbrujohalcon@inaka.net').
-behaviour(gen_server).
--record(state, {name :: atom(),
- mod :: atom(),
- state :: term(),
- options :: [ {time_checker|queue_manager, atom()}
- | wpool:option()
- ]
- }).
--type state() :: #state{}.
+-record(state,
+ {name :: atom(),
+ mod :: atom(),
+ state :: term(),
+ options :: [{time_checker | queue_manager, atom()} | wpool:option()]}).
+-type state() :: #state{}.
-type from() :: {pid(), reference()}.
-type next_step() :: timeout() | hibernate | {continue, term()}.
%% api
--export([ start_link/4
- , call/3
- , cast/2
- , cast_call/3
- ]).
-
+-export([start_link/4, call/3, cast/2, cast_call/3]).
%% gen_server callbacks
--export([ init/1
- , terminate/2
- , code_change/3
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- , handle_continue/2
- , format_status/2
- ]).
+-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2,
+ handle_continue/2, format_status/2]).
%%%===================================================================
%%% API
%%%===================================================================
%% @doc Starts a named process
-spec start_link(wpool:name(), module(), term(), [wpool:option()]) ->
- {ok, pid()} | ignore | {error, {already_started, pid()} | term()}.
+ {ok, pid()} | ignore | {error, {already_started, pid()} | term()}.
start_link(Name, Module, InitArgs, Options) ->
- WorkerOpt = proplists:get_value(worker_opt, Options, []),
- gen_server:start_link(
- {local, Name}, ?MODULE, {Name, Module, InitArgs, Options}, WorkerOpt).
+ WorkerOpt = proplists:get_value(worker_opt, Options, []),
+ gen_server:start_link({local, Name},
+ ?MODULE,
+ {Name, Module, InitArgs, Options},
+ WorkerOpt).
%% @equiv gen_server:call(Process, Call, Timeout)
-spec call(wpool:name() | pid(), term(), timeout()) -> term().
-call(Process, Call, Timeout) -> gen_server:call(Process, Call, Timeout).
+call(Process, Call, Timeout) ->
+ gen_server:call(Process, Call, Timeout).
%% @equiv gen_server:cast(Process, {cast, Cast})
-spec cast(wpool:name() | pid(), term()) -> ok.
-cast(Process, Cast) -> gen_server:cast(Process, {cast, Cast}).
+cast(Process, Cast) ->
+ gen_server:cast(Process, {cast, Cast}).
%% @equiv gen_server:cast(Process, {call, From, Call})
-spec cast_call(wpool:name() | pid(), from(), term()) -> ok.
cast_call(Process, From, Call) ->
- gen_server:cast(Process, {call, From, Call}).
+ gen_server:cast(Process, {call, From, Call}).
%%%===================================================================
%%% init, terminate, code_change, info callbacks
%%%===================================================================
%% @private
-spec init({atom(), atom(), term(), [wpool:option()]}) ->
- {ok, state()} | {ok, state(), next_step()} | {stop, can_not_ignore} | {stop, term()}.
+ {ok, state()} | {ok, state(), next_step()} | {stop, can_not_ignore} | {stop, term()}.
init({Name, Mod, InitArgs, Options}) ->
- wpool_process_callbacks:notify(handle_init_start, Options, [Name]),
+ wpool_process_callbacks:notify(handle_init_start, Options, [Name]),
- case Mod:init(InitArgs) of
- {ok, ModState} ->
- ok = wpool_utils:notify_queue_manager(new_worker, Name, Options),
- wpool_process_callbacks:notify(handle_worker_creation, Options, [Name]),
- {ok, #state{ name = Name
- , mod = Mod
- , state = ModState
- , options = Options
- }};
- {ok, ModState, NextStep} ->
- ok = wpool_utils:notify_queue_manager(new_worker, Name, Options),
- wpool_process_callbacks:notify(handle_worker_creation, Options, [Name]),
- {ok, #state{ name = Name
- , mod = Mod
- , state = ModState
- , options = Options
- }, NextStep};
- ignore -> {stop, can_not_ignore};
- Error -> Error
- end.
+ case Mod:init(InitArgs) of
+ {ok, ModState} ->
+ ok = wpool_utils:notify_queue_manager(new_worker, Name, Options),
+ wpool_process_callbacks:notify(handle_worker_creation, Options, [Name]),
+ {ok,
+ #state{name = Name,
+ mod = Mod,
+ state = ModState,
+ options = Options}};
+ {ok, ModState, NextStep} ->
+ ok = wpool_utils:notify_queue_manager(new_worker, Name, Options),
+ wpool_process_callbacks:notify(handle_worker_creation, Options, [Name]),
+ {ok,
+ #state{name = Name,
+ mod = Mod,
+ state = ModState,
+ options = Options},
+ NextStep};
+ ignore ->
+ {stop, can_not_ignore};
+ Error ->
+ Error
+ end.
%% @private
-spec terminate(atom(), state()) -> term().
terminate(Reason, State) ->
- #state{mod=Mod, state=ModState, name=Name, options=Options} = State,
- ok = wpool_utils:notify_queue_manager(worker_dead, Name, Options),
- wpool_process_callbacks:notify(handle_worker_death, Options, [Name, Reason]),
- Mod:terminate(Reason, ModState).
+ #state{mod = Mod,
+ state = ModState,
+ name = Name,
+ options = Options} =
+ State,
+ ok = wpool_utils:notify_queue_manager(worker_dead, Name, Options),
+ wpool_process_callbacks:notify(handle_worker_death, Options, [Name, Reason]),
+ Mod:terminate(Reason, ModState).
%% @private
-spec code_change(string(), state(), any()) -> {ok, state()} | {error, term()}.
code_change(OldVsn, State, Extra) ->
- case (State#state.mod):code_change(OldVsn, State#state.state, Extra) of
- {ok, NewState} -> {ok, State#state{state = NewState}};
- Error -> {error, Error}
- end.
+ case (State#state.mod):code_change(OldVsn, State#state.state, Extra) of
+ {ok, NewState} ->
+ {ok, State#state{state = NewState}};
+ Error ->
+ {error, Error}
+ end.
%% @private
-spec handle_info(any(), state()) ->
- {noreply, state()} | {noreply, state(), next_step()} | {stop, term(), state()}.
+ {noreply, state()} | {noreply, state(), next_step()} | {stop, term(), state()}.
handle_info(Info, State) ->
- try (State#state.mod):handle_info(Info, State#state.state) of
- {noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- {noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- {stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}}
- catch
- _:{noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- _:{noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- _:{stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}}
- end.
+ try (State#state.mod):handle_info(Info, State#state.state) of
+ {noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ {noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ {stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}}
+ catch
+ _:{noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ _:{noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ _:{stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}}
+ end.
%% @private
-spec handle_continue(any(), state()) ->
- {noreply, state()} | {noreply, state(), next_step()} | {stop, term(), state()}.
+ {noreply, state()} |
+ {noreply, state(), next_step()} |
+ {stop, term(), state()}.
handle_continue(Continue, State) ->
- try (State#state.mod):handle_continue(Continue, State#state.state) of
- {noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- {noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- {stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}}
- catch
- _:{noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- _:{noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- _:{stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}}
- end.
+ try (State#state.mod):handle_continue(Continue, State#state.state) of
+ {noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ {noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ {stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}}
+ catch
+ _:{noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ _:{noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ _:{stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}}
+ end.
%% @private
-spec format_status(normal | terminate, [[{_, _}] | state(), ...]) -> term().
format_status(Opt, [PDict, State]) ->
- case erlang:function_exported(State#state.mod, format_status, 2) of
- false ->
- case Opt of % This is copied from gen_server:format_status/4
- terminate -> State#state.state;
- normal -> [{data, [{"State", State#state.state}]}]
- end;
- true ->
- (State#state.mod):format_status(Opt, [PDict, State#state.state])
- end.
+ case erlang:function_exported(State#state.mod, format_status, 2) of
+ false ->
+ case Opt % This is copied from gen_server:format_status/4
+ of
+ terminate ->
+ State#state.state;
+ normal ->
+ [{data, [{"State", State#state.state}]}]
+ end;
+ true ->
+ (State#state.mod):format_status(Opt, [PDict, State#state.state])
+ end.
%%%===================================================================
%%% real (i.e. interesting) callbacks
%%%===================================================================
%% @private
-spec handle_cast(term(), state()) ->
- {noreply, state()} | {noreply, state(), next_step()} | {stop, term(), state()}.
+ {noreply, state()} | {noreply, state(), next_step()} | {stop, term(), state()}.
handle_cast({call, From, Call}, State) ->
- case handle_call(Call, From, State) of
- {reply, Response, NewState} ->
- gen_server:reply(From, Response),
- {noreply, NewState};
- {reply, Response, NewState, NextStep} ->
- gen_server:reply(From, Response),
- {noreply, NewState, NextStep};
- {stop, Reason, Response, NewState} ->
- gen_server:reply(From, Response),
- {stop, Reason, NewState};
- Reply -> Reply
- end;
+ case handle_call(Call, From, State) of
+ {reply, Response, NewState} ->
+ gen_server:reply(From, Response),
+ {noreply, NewState};
+ {reply, Response, NewState, NextStep} ->
+ gen_server:reply(From, Response),
+ {noreply, NewState, NextStep};
+ {stop, Reason, Response, NewState} ->
+ gen_server:reply(From, Response),
+ {stop, Reason, NewState};
+ Reply ->
+ Reply
+ end;
handle_cast({cast, Cast}, State) ->
- Task =
- wpool_utils:task_init(
- {cast, Cast},
- proplists:get_value(time_checker, State#state.options, undefined),
- proplists:get_value(overrun_warning, State#state.options, infinity),
- proplists:get_value(max_overrun_warnings, State#state.options, infinity)),
- ok = wpool_utils:notify_queue_manager(worker_busy
- , State#state.name
- , State#state.options),
- Reply =
- try (State#state.mod):handle_cast(Cast, State#state.state) of
- {noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- {noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- {stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}}
- catch
- _:{noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- _:{noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- _:{stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}}
- end,
- wpool_utils:task_end(Task),
- ok =
- wpool_utils:notify_queue_manager(worker_ready
- , State#state.name
- , State#state.options),
- Reply.
+ Task =
+ wpool_utils:task_init({cast, Cast},
+ proplists:get_value(time_checker, State#state.options, undefined),
+ proplists:get_value(overrun_warning, State#state.options, infinity),
+ proplists:get_value(max_overrun_warnings,
+ State#state.options,
+ infinity)),
+ ok = wpool_utils:notify_queue_manager(worker_busy, State#state.name, State#state.options),
+ Reply =
+ try (State#state.mod):handle_cast(Cast, State#state.state) of
+ {noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ {noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ {stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}}
+ catch
+ _:{noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ _:{noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ _:{stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}}
+ end,
+ wpool_utils:task_end(Task),
+ ok =
+ wpool_utils:notify_queue_manager(worker_ready, State#state.name, State#state.options),
+ Reply.
%% @private
-spec handle_call(term(), from(), state()) ->
- {reply, term(), state()}
- | {reply, term(), state(), next_step()}
- | {noreply, state()}
- | {noreply, state(), next_step()}
- | {stop, term(), term(), state()}
- | {stop, term(), state()}.
+ {reply, term(), state()} |
+ {reply, term(), state(), next_step()} |
+ {noreply, state()} |
+ {noreply, state(), next_step()} |
+ {stop, term(), term(), state()} |
+ {stop, term(), state()}.
handle_call(Call, From, State) ->
- Task =
- wpool_utils:task_init(
- {call, Call},
- proplists:get_value(time_checker, State#state.options, undefined),
- proplists:get_value(overrun_warning, State#state.options, infinity),
- proplists:get_value(max_overrun_warnings, State#state.options, infinity)),
- ok = wpool_utils:notify_queue_manager(worker_busy
- , State#state.name
- , State#state.options),
- Reply =
- try (State#state.mod):handle_call(Call, From, State#state.state) of
- {noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- {noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- {reply, Response, NewState} ->
- {reply, Response, State#state{state = NewState}};
- {reply, Response, NewState, NextStep} ->
- {reply, Response, State#state{state = NewState}, NextStep};
- {stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}};
- {stop, Reason, Response, NewState} ->
- {stop, Reason, Response, State#state{state = NewState}}
- catch
- _:{noreply, NewState} ->
- {noreply, State#state{state = NewState}};
- _:{noreply, NewState, NextStep} ->
- {noreply, State#state{state = NewState}, NextStep};
- _:{reply, Response, NewState} ->
- {reply, Response, State#state{state = NewState}};
- _:{reply, Response, NewState, NextStep} ->
- {reply, Response, State#state{state = NewState}, NextStep};
- _:{stop, Reason, NewState} ->
- {stop, Reason, State#state{state = NewState}};
- _:{stop, Reason, Response, NewState} ->
- {stop, Reason, Response, State#state{state = NewState}}
- end,
- wpool_utils:task_end(Task),
- ok =
- wpool_utils:notify_queue_manager(worker_ready
- , State#state.name
- , State#state.options),
- Reply.
+ Task =
+ wpool_utils:task_init({call, Call},
+ proplists:get_value(time_checker, State#state.options, undefined),
+ proplists:get_value(overrun_warning, State#state.options, infinity),
+ proplists:get_value(max_overrun_warnings,
+ State#state.options,
+ infinity)),
+ ok = wpool_utils:notify_queue_manager(worker_busy, State#state.name, State#state.options),
+ Reply =
+ try (State#state.mod):handle_call(Call, From, State#state.state) of
+ {noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ {noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ {reply, Response, NewState} ->
+ {reply, Response, State#state{state = NewState}};
+ {reply, Response, NewState, NextStep} ->
+ {reply, Response, State#state{state = NewState}, NextStep};
+ {stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}};
+ {stop, Reason, Response, NewState} ->
+ {stop, Reason, Response, State#state{state = NewState}}
+ catch
+ _:{noreply, NewState} ->
+ {noreply, State#state{state = NewState}};
+ _:{noreply, NewState, NextStep} ->
+ {noreply, State#state{state = NewState}, NextStep};
+ _:{reply, Response, NewState} ->
+ {reply, Response, State#state{state = NewState}};
+ _:{reply, Response, NewState, NextStep} ->
+ {reply, Response, State#state{state = NewState}, NextStep};
+ _:{stop, Reason, NewState} ->
+ {stop, Reason, State#state{state = NewState}};
+ _:{stop, Reason, Response, NewState} ->
+ {stop, Reason, Response, State#state{state = NewState}}
+ end,
+ wpool_utils:task_end(Task),
+ ok =
+ wpool_utils:notify_queue_manager(worker_ready, State#state.name, State#state.options),
+ Reply.
diff --git a/src/wpool_process_callbacks.erl b/src/wpool_process_callbacks.erl
index 52f9788..eaf2edf 100644
--- a/src/wpool_process_callbacks.erl
+++ b/src/wpool_process_callbacks.erl
@@ -2,83 +2,76 @@
-behaviour(gen_event).
-%% gen_event callbacks
+%% The callbacks are called in an extremely dynamic from call/3.
+-hank([unused_callbacks]).
--export([ init/1
- , handle_event/2
- , handle_call/2
- ]).
+-export([init/1, handle_event/2, handle_call/2]).
+-export([notify/3, add_callback_module/2, remove_callback_module/2]).
--export([ notify/3
- , add_callback_module/2
- , remove_callback_module/2
- ]).
-type state() :: module().
-
-type event() :: handle_init_start | handle_worker_creation | handle_worker_death.
-callback handle_init_start(wpool:name()) -> any().
-callback handle_worker_creation(wpool:name()) -> any().
-callback handle_worker_death(wpool:name(), term()) -> any().
--optional_callbacks([handle_init_start/1, handle_worker_creation/1, handle_worker_death/2]).
+-optional_callbacks([handle_init_start/1, handle_worker_creation/1,
+ handle_worker_death/2]).
-spec init(module()) -> {ok, state()}.
init(Module) ->
- {ok, Module}.
+ {ok, Module}.
-spec handle_event({event(), [any()]}, state()) -> {ok, state()}.
handle_event({Event, Args}, Module) ->
- call(Module, Event, Args),
- {ok, Module}.
+ call(Module, Event, Args),
+ {ok, Module}.
-spec handle_call(Msg, state()) -> {ok, {error, {unexpected_call, Msg}}, state()}.
handle_call(Msg, State) ->
- {ok, {error, {unexpected_call, Msg}}, State}.
+ {ok, {error, {unexpected_call, Msg}}, State}.
-spec notify(event(), [wpool:option()], [any()]) -> ok.
notify(Event, Options, Args) ->
- case lists:keyfind(event_manager, 1, Options) of
- {event_manager, EventMgr} ->
- gen_event:notify(EventMgr, {Event, Args});
- _ ->
- ok
- end.
+ case lists:keyfind(event_manager, 1, Options) of
+ {event_manager, EventMgr} ->
+ gen_event:notify(EventMgr, {Event, Args});
+ _ ->
+ ok
+ end.
-spec add_callback_module(wpool:name(), module()) -> ok | {error, any()}.
add_callback_module(EventManager, Module) ->
- case ensure_loaded(Module) of
- ok ->
- gen_event:add_handler(EventManager,
- {wpool_process_callbacks, Module}, Module);
- Other ->
- Other
- end.
-
+ case ensure_loaded(Module) of
+ ok ->
+ gen_event:add_handler(EventManager, {wpool_process_callbacks, Module}, Module);
+ Other ->
+ Other
+ end.
-spec remove_callback_module(wpool:name(), module()) -> ok | {error, any()}.
remove_callback_module(EventManager, Module) ->
- gen_event:delete_handler(EventManager, {wpool_process_callbacks, Module}, Module).
+ gen_event:delete_handler(EventManager, {wpool_process_callbacks, Module}, Module).
call(Module, Event, Args) ->
- try
- case erlang:function_exported(Module, Event, length(Args)) of
- true ->
- erlang:apply(Module, Event, Args);
- _ ->
- ok
- end
- catch
- E:R ->
- error_logger:warning_msg("Could not call callback module, error:~p, reason:~p", [E, R])
- end.
+ try
+ case erlang:function_exported(Module, Event, length(Args)) of
+ true ->
+ erlang:apply(Module, Event, Args);
+ _ ->
+ ok
+ end
+ catch
+ E:R ->
+ error_logger:warning_msg("Could not call callback module, error:~p, reason:~p", [E, R])
+ end.
ensure_loaded(Module) ->
- case code:ensure_loaded(Module) of
- {module, Module} ->
- ok;
- {error, embedded} -> %% We are in embedded mode so the module was loaded if exists
- ok;
- Other ->
- Other
- end.
+ case code:ensure_loaded(Module) of
+ {module, Module} ->
+ ok;
+ {error, embedded} -> %% We are in embedded mode so the module was loaded if exists
+ ok;
+ Other ->
+ Other
+ end.
diff --git a/src/wpool_process_sup.erl b/src/wpool_process_sup.erl
index 5bf5375..24b58ef 100644
--- a/src/wpool_process_sup.erl
+++ b/src/wpool_process_sup.erl
@@ -13,66 +13,59 @@
% under the License.
%%% @hidden
-module(wpool_process_sup).
--author('elbrujohalcon@inaka.net').
-behaviour(supervisor).
%% API
-export([start_link/3]).
-
%% Supervisor callbacks
-export([init/1]).
%% @private
-spec start_link(wpool:name(), atom(), [wpool:option()]) -> {ok, pid()}.
start_link(Parent, Name, Options) ->
- supervisor:start_link({local, Name}, ?MODULE, {Parent, Options}).
+ supervisor:start_link({local, Name}, ?MODULE, {Parent, Options}).
%% @private
-spec init({wpool:name(), [wpool:option()]}) ->
- {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
+ {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init({Name, Options}) ->
- Workers = proplists:get_value(workers, Options, 100),
- Strategy = proplists:get_value(strategy, Options, {one_for_one, 5, 60}),
- maybe_add_event_handler(Options),
- {WorkerType, Worker, InitArgs} =
- case proplists:get_value(worker_type, Options, gen_server) of
- gen_server ->
- {W, IA} =
- proplists:get_value(worker, Options, {wpool_worker, undefined}),
- {wpool_process, W, IA}
- %% We'll eventually add more types (like gen_statem),
- %% that's why this case remains
- end,
- WorkerShutdown = proplists:get_value(worker_shutdown, Options, 5000),
- WorkerSpecs =
- [ { wpool_pool:worker_name(Name, I)
- , { WorkerType
- , start_link
- , [wpool_pool:worker_name(Name, I), Worker, InitArgs, Options]
- }
- , permanent
- , WorkerShutdown
- , worker
- , [Worker]
- } || I <- lists:seq(1, Workers)],
- {ok, {Strategy, WorkerSpecs}}.
+ Workers = proplists:get_value(workers, Options, 100),
+ Strategy = proplists:get_value(strategy, Options, {one_for_one, 5, 60}),
+ maybe_add_event_handler(Options),
+ {WorkerType, Worker, InitArgs} =
+ case proplists:get_value(worker_type, Options, gen_server) of
+ gen_server ->
+ {W, IA} = proplists:get_value(worker, Options, {wpool_worker, undefined}),
+ {wpool_process, W, IA}
+ end,
+ %% We'll eventually add more types (like gen_statem),
+ %% that's why this case remains
+ WorkerShutdown = proplists:get_value(worker_shutdown, Options, 5000),
+ WorkerSpecs =
+ [{wpool_pool:worker_name(Name, I),
+ {WorkerType, start_link, [wpool_pool:worker_name(Name, I), Worker, InitArgs, Options]},
+ permanent,
+ WorkerShutdown,
+ worker,
+ [Worker]}
+ || I <- lists:seq(1, Workers)],
+ {ok, {Strategy, WorkerSpecs}}.
maybe_add_event_handler(Options) ->
- case proplists:get_value(event_manager, Options, undefined) of
- undefined ->
- ok;
- EventMgr ->
- lists:foreach(fun(M) -> add_initial_callback(EventMgr, M) end,
- proplists:get_value(callbacks, Options, []))
- end.
+ case proplists:get_value(event_manager, Options, undefined) of
+ undefined ->
+ ok;
+ EventMgr ->
+ lists:foreach(fun(M) -> add_initial_callback(EventMgr, M) end,
+ proplists:get_value(callbacks, Options, []))
+ end.
add_initial_callback(EventManager, Module) ->
- case wpool_process_callbacks:add_callback_module(EventManager, Module) of
- ok ->
- ok;
- Other ->
- error_logger:warning_msg("The callback module:~p could not be loaded, reason:~p",
- [Module, Other])
- end.
-
+ case wpool_process_callbacks:add_callback_module(EventManager, Module) of
+ ok ->
+ ok;
+ Other ->
+ error_logger:warning_msg("The callback module:~p could not be loaded, reason:~p",
+ [Module, Other])
+ end.
diff --git a/src/wpool_queue_manager.erl b/src/wpool_queue_manager.erl
index 55144d8..de17ecf 100644
--- a/src/wpool_queue_manager.erl
+++ b/src/wpool_queue_manager.erl
@@ -13,49 +13,33 @@
% under the License.
%%% @hidden
-module(wpool_queue_manager).
--author('elbrujohalcon@inaka.net').
-behaviour(gen_server).
%% api
--export([ start_link/2
- , start_link/3
- ]).
--export([ call_available_worker/3
- , cast_to_available_worker/2
- , new_worker/2
- , worker_dead/2
- , worker_ready/2
- , worker_busy/2
- , pending_task_count/1
- ]).
-
+-export([start_link/2, start_link/3]).
+-export([call_available_worker/3, cast_to_available_worker/2, new_worker/2, worker_dead/2,
+ worker_ready/2, worker_busy/2, pending_task_count/1]).
%% gen_server callbacks
--export([ init/1
- , terminate/2
- , code_change/3
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- ]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
--record(state, { wpool :: wpool:name()
- , clients :: queue:queue({cast|{pid(), _}, term()})
- , workers :: gb_sets:set(atom())
- , monitors :: gb_trees:tree(atom(), monitored_from())
- , queue_type :: queue_type()
- }).
--type state() :: #state{}.
+-record(state,
+ {wpool :: wpool:name(),
+ clients :: queue:queue({cast | {pid(), _}, term()}),
+ workers :: gb_sets:set(atom()),
+ monitors :: gb_trees:tree(atom(), monitored_from()),
+ queue_type :: queue_type()}).
+-type state() :: #state{}.
-type from() :: {pid(), reference()}.
-type monitored_from() :: {reference(), from()}.
-type options() :: [{option(), term()}].
-type option() :: queue_type.
-type args() :: [{arg(), term()}].
-type arg() :: option() | pool.
-
-type queue_mgr() :: atom().
-type queue_type() :: fifo | lifo.
+
-export_type([queue_mgr/0, queue_type/0]).
%%%===================================================================
@@ -63,37 +47,33 @@
%%%===================================================================
%% @equiv start_link(WPool, Name, [])
-spec start_link(wpool:name(), queue_mgr()) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
start_link(WPool, Name) ->
- start_link(WPool, Name, []).
+ start_link(WPool, Name, []).
%% @private
-spec start_link(wpool:name(), queue_mgr(), options()) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
start_link(WPool, Name, Options) ->
- gen_server:start_link({local, Name}, ?MODULE, [{pool, WPool} | Options],
- []).
+ gen_server:start_link({local, Name}, ?MODULE, [{pool, WPool} | Options], []).
%% @doc returns the first available worker in the pool
--spec call_available_worker(queue_mgr(), any(), timeout()) ->
- noproc | timeout | atom().
+-spec call_available_worker(queue_mgr(), any(), timeout()) -> noproc | timeout | any().
call_available_worker(QueueManager, Call, Timeout) ->
- Expires = expires(Timeout),
- try
- gen_server:call(QueueManager, {available_worker, Call, Expires}, Timeout)
- of
- {'EXIT', _, noproc} ->
- noproc;
- {'EXIT', Worker, Exit} ->
- exit({Exit, {gen_server, call, [Worker, Call, Timeout]}});
- Result ->
- Result
- catch
- _:{noproc, {gen_server, call, _}} ->
- noproc;
- _:{timeout, {gen_server, call, _}} ->
- timeout
- end.
+ Expires = expires(Timeout),
+ try gen_server:call(QueueManager, {available_worker, Call, Expires}, Timeout) of
+ {'EXIT', _, noproc} ->
+ noproc;
+ {'EXIT', Worker, Exit} ->
+ exit({Exit, {gen_server, call, [Worker, Call, Timeout]}});
+ Result ->
+ Result
+ catch
+ _:{noproc, {gen_server, call, _}} ->
+ noproc;
+ _:{timeout, {gen_server, call, _}} ->
+ timeout
+ end.
%% @doc Casts a message to the first available worker.
%% Since we can wait forever for a wpool:cast to be delivered
@@ -101,33 +81,33 @@ call_available_worker(QueueManager, Call, Timeout) ->
%% just forwards the cast when it gets the worker
-spec cast_to_available_worker(queue_mgr(), term()) -> ok.
cast_to_available_worker(QueueManager, Cast) ->
- gen_server:cast(QueueManager, {cast_to_available_worker, Cast}).
+ gen_server:cast(QueueManager, {cast_to_available_worker, Cast}).
%% @doc Mark a brand new worker as available
-spec new_worker(queue_mgr(), atom()) -> ok.
new_worker(QueueManager, Worker) ->
- gen_server:cast(QueueManager, {new_worker, Worker}).
+ gen_server:cast(QueueManager, {new_worker, Worker}).
%% @doc Mark a worker as available
-spec worker_ready(queue_mgr(), atom()) -> ok.
worker_ready(QueueManager, Worker) ->
- gen_server:cast(QueueManager, {worker_ready, Worker}).
+ gen_server:cast(QueueManager, {worker_ready, Worker}).
%% @doc Mark a worker as no longer available
-spec worker_busy(queue_mgr(), atom()) -> ok.
worker_busy(QueueManager, Worker) ->
- gen_server:cast(QueueManager, {worker_busy, Worker}).
+ gen_server:cast(QueueManager, {worker_busy, Worker}).
%% @doc Decrement the total number of workers
-spec worker_dead(queue_mgr(), atom()) -> ok.
worker_dead(QueueManager, Worker) ->
- gen_server:cast(QueueManager, {worker_dead, Worker}).
+ gen_server:cast(QueueManager, {worker_dead, Worker}).
%% @doc Retrieves the number of pending tasks (used for stats)
%% @see wpool_pool:stats/1
-spec pending_task_count(queue_mgr()) -> non_neg_integer().
pending_task_count(QueueManager) ->
- gen_server:call(QueueManager, pending_task_count).
+ gen_server:call(QueueManager, pending_task_count).
%%%===================================================================
%%% gen_server callbacks
@@ -135,142 +115,147 @@ pending_task_count(QueueManager) ->
%% @private
-spec init(args()) -> {ok, state()}.
init(Args) ->
- WPool = proplists:get_value(pool, Args),
- QueueType = proplists:get_value(queue_type, Args),
- put(pending_tasks, 0),
- {ok, #state{wpool = WPool, clients = queue:new(),
- workers = gb_sets:new(), monitors = gb_trees:empty(),
- queue_type = QueueType}}.
+ WPool = proplists:get_value(pool, Args),
+ QueueType = proplists:get_value(queue_type, Args),
+ put(pending_tasks, 0),
+ {ok,
+ #state{wpool = WPool,
+ clients = queue:new(),
+ workers = gb_sets:new(),
+ monitors = gb_trees:empty(),
+ queue_type = QueueType}}.
-type worker_event() :: new_worker | worker_dead | worker_busy | worker_ready.
+
%% @private
-spec handle_cast({worker_event(), atom()}, state()) -> {noreply, state()}.
handle_cast({new_worker, Worker}, State) ->
- handle_cast({worker_ready, Worker}, State);
+ handle_cast({worker_ready, Worker}, State);
handle_cast({worker_dead, Worker}, #state{workers = Workers} = State) ->
- NewWorkers = gb_sets:delete_any(Worker, Workers),
- {noreply, State#state{workers = NewWorkers}};
+ NewWorkers = gb_sets:delete_any(Worker, Workers),
+ {noreply, State#state{workers = NewWorkers}};
handle_cast({worker_busy, Worker}, #state{workers = Workers} = State) ->
- {noreply, State#state{workers = gb_sets:delete_any(Worker, Workers)}};
+ {noreply, State#state{workers = gb_sets:delete_any(Worker, Workers)}};
handle_cast({worker_ready, Worker}, State0) ->
- #state{workers = Workers, clients = Clients, monitors = Mons,
- queue_type = QueueType} = State0,
- State = case gb_trees:is_defined(Worker, Mons) of
- true ->
- {Ref, _Client} = gb_trees:get(Worker, Mons),
- demonitor(Ref, [flush]),
- State0#state{monitors = gb_trees:delete(Worker, Mons)};
- false ->
- State0
- end,
- case queue_out(Clients, QueueType) of
- {empty, _Clients} ->
- {noreply, State#state{workers = gb_sets:add(Worker, Workers)}};
- {{value, {cast, Cast}}, NewClients} ->
- dec_pending_tasks(),
- ok = wpool_process:cast(Worker, Cast),
- {noreply, State#state{clients = NewClients}};
- {{value, {Client = {ClientPid, _}, Call, Expires}}, NewClients} ->
- dec_pending_tasks(),
- NewState = State#state{clients = NewClients},
- case is_process_alive(ClientPid) andalso
- Expires > now_in_microseconds() of
+ #state{workers = Workers,
+ clients = Clients,
+ monitors = Mons,
+ queue_type = QueueType} =
+ State0,
+ State =
+ case gb_trees:is_defined(Worker, Mons) of
+ true ->
+ {Ref, _Client} = gb_trees:get(Worker, Mons),
+ demonitor(Ref, [flush]),
+ State0#state{monitors = gb_trees:delete(Worker, Mons)};
+ false ->
+ State0
+ end,
+ case queue_out(Clients, QueueType) of
+ {empty, _Clients} ->
+ {noreply, State#state{workers = gb_sets:add(Worker, Workers)}};
+ {{value, {cast, Cast}}, NewClients} ->
+ dec_pending_tasks(),
+ ok = wpool_process:cast(Worker, Cast),
+ {noreply, State#state{clients = NewClients}};
+ {{value, {Client = {ClientPid, _}, Call, Expires}}, NewClients} ->
+ dec_pending_tasks(),
+ NewState = State#state{clients = NewClients},
+ case is_process_alive(ClientPid) andalso Expires > now_in_microseconds() of
+ true ->
+ MonitorState = monitor_worker(Worker, Client, NewState),
+ ok = wpool_process:cast_call(Worker, Client, Call),
+ {noreply, MonitorState};
+ false ->
+ handle_cast({worker_ready, Worker}, NewState)
+ end
+ end;
+handle_cast({cast_to_available_worker, Cast}, State) ->
+ #state{workers = Workers, clients = Clients} = State,
+ case gb_sets:is_empty(Workers) of
true ->
- MonitorState = monitor_worker(Worker, Client, NewState),
- ok = wpool_process:cast_call(Worker, Client, Call),
- {noreply, MonitorState};
+ inc_pending_tasks(),
+ {noreply, State#state{clients = queue:in({cast, Cast}, Clients)}};
false ->
- handle_cast({worker_ready, Worker}, NewState)
- end
- end;
-handle_cast({cast_to_available_worker, Cast}, State) ->
- #state{workers = Workers, clients = Clients} = State,
- case gb_sets:is_empty(Workers) of
- true ->
- inc_pending_tasks(),
- {noreply, State#state{clients = queue:in({cast, Cast}, Clients)}};
- false ->
- {Worker, NewWorkers} = gb_sets:take_smallest(Workers),
- ok = wpool_process:cast(Worker, Cast),
- {noreply, State#state{workers = NewWorkers}}
- end.
+ {Worker, NewWorkers} = gb_sets:take_smallest(Workers),
+ ok = wpool_process:cast(Worker, Cast),
+ {noreply, State#state{workers = NewWorkers}}
+ end.
+
+-type call_request() :: {available_worker, infinity | pos_integer()} | pending_task_count.
--type call_request() ::
- {available_worker, infinity|pos_integer()} | pending_task_count.
%% @private
-spec handle_call(call_request(), from(), state()) ->
- {reply, {ok, atom()}, state()} | {noreply, state()}.
-handle_call(
- {available_worker, Call, Expires}, Client = {ClientPid, _Ref}, State) ->
- #state{workers = Workers, clients = Clients} = State,
- case gb_sets:is_empty(Workers) of
- true ->
- inc_pending_tasks(),
- { noreply
- , State#state{clients = queue:in({Client, Call, Expires}, Clients)}
- };
- false ->
- {Worker, NewWorkers} = gb_sets:take_smallest(Workers),
- %NOTE: It could've been a while since this call was made, so we check
- case erlang:is_process_alive(ClientPid) andalso
- Expires > now_in_microseconds() of
- true ->
- NewState = monitor_worker(Worker, Client,
- State#state{workers = NewWorkers}),
- ok = wpool_process:cast_call(Worker, Client, Call),
- {noreply, NewState};
+ {reply, {ok, atom()}, state()} | {noreply, state()}.
+handle_call({available_worker, Call, Expires}, Client = {ClientPid, _Ref}, State) ->
+ #state{workers = Workers, clients = Clients} = State,
+ case gb_sets:is_empty(Workers) of
+ true ->
+ inc_pending_tasks(),
+ {noreply, State#state{clients = queue:in({Client, Call, Expires}, Clients)}};
false ->
- {noreply, State}
- end
- end;
+ {Worker, NewWorkers} = gb_sets:take_smallest(Workers),
+ %NOTE: It could've been a while since this call was made, so we check
+ case erlang:is_process_alive(ClientPid) andalso Expires > now_in_microseconds() of
+ true ->
+ NewState = monitor_worker(Worker, Client, State#state{workers = NewWorkers}),
+ ok = wpool_process:cast_call(Worker, Client, Call),
+ {noreply, NewState};
+ false ->
+ {noreply, State}
+ end
+ end;
handle_call(pending_task_count, _From, State) ->
- {reply, get(pending_tasks), State}.
+ {reply, get(pending_tasks), State}.
%% @private
-spec handle_info(any(), state()) -> {noreply, state()}.
handle_info({'DOWN', Ref, Type, {Worker, _Node}, Exit}, State) ->
handle_info({'DOWN', Ref, Type, Worker, Exit}, State);
handle_info({'DOWN', _, _, Worker, Exit}, State = #state{monitors = Mons}) ->
- case gb_trees:is_defined(Worker, Mons) of
- true ->
- {_Ref, Client} = gb_trees:get(Worker, Mons),
- gen_server:reply(Client, {'EXIT', Worker, Exit}),
- {noreply, State#state{monitors = gb_trees:delete(Worker, Mons)}};
- false ->
- {noreply, State}
- end;
-handle_info(_Info, State) -> {noreply, State}.
-
-%% @private
--spec terminate(atom(), state()) -> ok.
-terminate(_Reason, _State) -> ok.
-
-%% @private
--spec code_change(string(), state(), any()) -> {ok, state()}.
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
+ case gb_trees:is_defined(Worker, Mons) of
+ true ->
+ {_Ref, Client} = gb_trees:get(Worker, Mons),
+ gen_server:reply(Client, {'EXIT', Worker, Exit}),
+ {noreply, State#state{monitors = gb_trees:delete(Worker, Mons)}};
+ false ->
+ {noreply, State}
+ end;
+handle_info(_Info, State) ->
+ {noreply, State}.
%%%===================================================================
%%% private
%%%===================================================================
-inc_pending_tasks() -> inc(pending_tasks).
-dec_pending_tasks() -> dec(pending_tasks).
+inc_pending_tasks() ->
+ inc(pending_tasks).
+
+dec_pending_tasks() ->
+ dec(pending_tasks).
+
+inc(Key) ->
+ put(Key, get(Key) + 1).
-inc(Key) -> put(Key, get(Key) + 1).
-dec(Key) -> put(Key, get(Key) - 1).
+dec(Key) ->
+ put(Key, get(Key) - 1).
-now_in_microseconds() -> timer:now_diff(os:timestamp(), {0, 0, 0}).
+now_in_microseconds() ->
+ timer:now_diff(
+ os:timestamp(), {0, 0, 0}).
expires(Timeout) ->
- case Timeout of
- infinity -> infinity;
- Timeout -> now_in_microseconds() + Timeout * 1000
- end.
+ case Timeout of
+ infinity ->
+ infinity;
+ Timeout ->
+ now_in_microseconds() + Timeout * 1000
+ end.
monitor_worker(Worker, Client, State = #state{monitors = Mons}) ->
- Ref = monitor(process, Worker),
- State#state{monitors = gb_trees:enter(Worker, {Ref, Client}, Mons)}.
+ Ref = monitor(process, Worker),
+ State#state{monitors = gb_trees:enter(Worker, {Ref, Client}, Mons)}.
queue_out(Clients, fifo) ->
- queue:out(Clients);
+ queue:out(Clients);
queue_out(Clients, lifo) ->
- queue:out_r(Clients).
+ queue:out_r(Clients).
diff --git a/src/wpool_sup.erl b/src/wpool_sup.erl
index 90f62fa..e0c3527 100644
--- a/src/wpool_sup.erl
+++ b/src/wpool_sup.erl
@@ -13,7 +13,6 @@
% under the License.
%%% @hidden
-module(wpool_sup).
--author('elbrujohalcon@inaka.net').
-behaviour(supervisor).
@@ -24,43 +23,34 @@
%% PUBLIC API
%%-------------------------------------------------------------------
%% @doc Starts the supervisor
--spec start_link() ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
-start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+-spec start_link() -> {ok, pid()} | {error, {already_started, pid()} | term()}.
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% @doc Starts a new pool
-spec start_pool(wpool:name(), [wpool:option()]) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
-start_pool(Name, Options) -> supervisor:start_child(?MODULE, [Name, Options]).
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
+start_pool(Name, Options) ->
+ supervisor:start_child(?MODULE, [Name, Options]).
%% @doc Stops a pool
-spec stop_pool(wpool:name()) -> ok.
stop_pool(Name) ->
- case erlang:whereis(Name) of
- undefined ->
- error_logger:warning_msg("Couldn't stop ~p. It was not running", [Name]),
- ok;
- Pid ->
- ok = supervisor:terminate_child(?MODULE, Pid)
- end.
+ case erlang:whereis(Name) of
+ undefined ->
+ error_logger:warning_msg("Couldn't stop ~p. It was not running", [Name]),
+ ok;
+ Pid ->
+ ok = supervisor:terminate_child(?MODULE, Pid)
+ end.
%%----------------------------------------------------------------------
%% Supervisor behaviour callbacks
%%----------------------------------------------------------------------
%% @hidden
--spec init([]) ->
- {ok, {{simple_one_for_one, 5, 60}, [supervisor:child_spec()]}}.
+-spec init([]) -> {ok, {{simple_one_for_one, 5, 60}, [supervisor:child_spec()]}}.
init([]) ->
- ok = wpool_pool:create_table(),
- { ok
- , { {simple_one_for_one, 5, 60}
- , [ { wpool_pool
- , {wpool_pool, start_link, []}
- , permanent
- , 2000
- , supervisor
- , dynamic
- }
- ]
- }
- }.
+ ok = wpool_pool:create_table(),
+ {ok,
+ {{simple_one_for_one, 5, 60},
+ [{wpool_pool, {wpool_pool, start_link, []}, permanent, 2000, supervisor, dynamic}]}}.
diff --git a/src/wpool_time_checker.erl b/src/wpool_time_checker.erl
index 72d936e..cf65c88 100644
--- a/src/wpool_time_checker.erl
+++ b/src/wpool_time_checker.erl
@@ -13,71 +13,54 @@
% under the License.
%%% @hidden
-module(wpool_time_checker).
--author('elbrujohalcon@inaka.net').
-behaviour(gen_server).
-type handler() :: {atom(), atom()}.
--record(state, { wpool :: wpool:name()
- , handlers :: [handler()]
- }).
+-record(state, {wpool :: wpool:name(), handlers :: [handler()]}).
+
-type state() :: #state{}.
%% api
--export([ start_link/3
- , add_handler/2
- ]).
-
+-export([start_link/3, add_handler/2]).
%% gen_server callbacks
--export([ init/1
- , terminate/2
- , code_change/3
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- ]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
%%%===================================================================
%%% API
%%%===================================================================
%% @private
-spec start_link(wpool:name(), atom(), handler() | [handler()]) ->
- {ok, pid()} | {error, {already_started, pid()} | term()}.
+ {ok, pid()} | {error, {already_started, pid()} | term()}.
start_link(WPool, Name, Handlers) when is_list(Handlers) ->
- gen_server:start_link({local, Name}, ?MODULE, {WPool, Handlers}, []);
+ gen_server:start_link({local, Name}, ?MODULE, {WPool, Handlers}, []);
start_link(WPool, Name, Handler) when is_tuple(Handler) ->
- start_link(WPool, Name, [Handler]).
+ start_link(WPool, Name, [Handler]).
%% @private
-spec add_handler(atom(), handler()) -> ok.
add_handler(Name, Handler) ->
- gen_server:call(Name, {add_handler, Handler}).
+ gen_server:call(Name, {add_handler, Handler}).
%%%===================================================================
-%%% init, terminate, code_change, info callbacks
+%%% simple callbacks
%%%===================================================================
%% @private
-spec init({wpool:name(), [{atom(), atom()}]}) -> {ok, state()}.
-init({WPool, Handlers}) -> {ok, #state{wpool = WPool, handlers = Handlers}}.
-
-%% @private
--spec terminate(atom(), state()) -> ok.
-terminate(_Reason, _State) -> ok.
-
-%% @private
--spec code_change(string(), state(), any()) -> {ok, state()}.
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
+init({WPool, Handlers}) ->
+ {ok, #state{wpool = WPool, handlers = Handlers}}.
%% @private
-spec handle_cast(term(), state()) -> {noreply, state()}.
-handle_cast(_Cast, State) -> {noreply, State}.
+handle_cast(_Cast, State) ->
+ {noreply, State}.
-type from() :: {pid(), reference()}.
--spec handle_call({add_handler, handler()}, from(), state()) ->
- {reply, ok, state()}.
+
+-spec handle_call({add_handler, handler()}, from(), state()) -> {reply, ok, state()}.
handle_call({add_handler, Handler}, _, State = #state{handlers = Handlers}) ->
- {reply, ok, State#state{handlers = [Handler | Handlers]}}.
+ {reply, ok, State#state{handlers = [Handler | Handlers]}}.
%%%===================================================================
%%% real (i.e. interesting) callbacks
@@ -85,30 +68,43 @@ handle_call({add_handler, Handler}, _, State = #state{handlers = Handlers}) ->
%% @private
-spec handle_info(any(), state()) -> {noreply, state()}.
handle_info({check, Pid, TaskId, Runtime, WarningsLeft}, State) ->
- case erlang:process_info(Pid, dictionary) of
- {dictionary, Values} ->
- run_task(
- TaskId, proplists:get_value(wpool_task, Values), Pid,
- State#state.wpool, State#state.handlers, Runtime, WarningsLeft);
- _ -> ok
- end,
- {noreply, State};
-handle_info(_Info, State) -> {noreply, State}.
+ case erlang:process_info(Pid, dictionary) of
+ {dictionary, Values} ->
+ run_task(TaskId,
+ proplists:get_value(wpool_task, Values),
+ Pid,
+ State#state.wpool,
+ State#state.handlers,
+ Runtime,
+ WarningsLeft);
+ _ ->
+ ok
+ end,
+ {noreply, State};
+handle_info(_Info, State) ->
+ {noreply, State}.
run_task(TaskId, {TaskId, _, Task}, Pid, Pool, Handlers, Runtime, 1) ->
- send_reports(Handlers, max_overrun_limit, Pool, Pid, Task, Runtime),
- exit(Pid, kill),
- ok;
+ send_reports(Handlers, max_overrun_limit, Pool, Pid, Task, Runtime),
+ exit(Pid, kill),
+ ok;
run_task(TaskId, {TaskId, _, Task}, Pid, Pool, Handlers, Runtime, WarningsLeft) ->
- send_reports(Handlers, overrun, Pool, Pid, Task, Runtime),
- case new_overrun_time(Runtime, WarningsLeft) of
- NewOverrunTime when NewOverrunTime =< 4294967295 ->
- erlang:send_after(
- Runtime, self(), {check, Pid, TaskId, NewOverrunTime, decrease_warnings(WarningsLeft)}),
- ok;
- _ -> ok
- end;
-run_task(_TaskId, _Value, _Pid, _Pool, _Handlers, _Runtime, _WarningsLeft) -> ok.
+ send_reports(Handlers, overrun, Pool, Pid, Task, Runtime),
+ case new_overrun_time(Runtime, WarningsLeft) of
+ NewOverrunTime when NewOverrunTime =< 4294967295 ->
+ erlang:send_after(Runtime,
+ self(),
+ {check,
+ Pid,
+ TaskId,
+ NewOverrunTime,
+ decrease_warnings(WarningsLeft)}),
+ ok;
+ _ ->
+ ok
+ end;
+run_task(_TaskId, _Value, _Pid, _Pool, _Handlers, _Runtime, _WarningsLeft) ->
+ ok.
-spec new_overrun_time(pos_integer(), pos_integer() | infinity) -> pos_integer().
new_overrun_time(Runtime, infinity) ->
@@ -117,13 +113,19 @@ new_overrun_time(Runtime, _) ->
Runtime * 2.
-spec decrease_warnings(pos_integer() | infinity) -> non_neg_integer() | infinity.
-decrease_warnings(infinity) -> infinity;
-decrease_warnings(N) -> N -1.
-
--spec send_reports([{atom(), atom()}], atom(), atom(), pid(), term(), infinity | pos_integer()) ->
- ok.
+decrease_warnings(infinity) ->
+ infinity;
+decrease_warnings(N) ->
+ N - 1.
+
+-spec send_reports([{atom(), atom()}],
+ atom(),
+ atom(),
+ pid(),
+ term(),
+ infinity | pos_integer()) ->
+ ok.
send_reports(Handlers, Alert, Pool, Pid, Task, Runtime) ->
- Args = [ {alert, Alert}, {pool, Pool}, {worker, Pid}, {task, Task}
- , {runtime, Runtime}],
- _ = [catch Mod:Fun(Args) || {Mod, Fun} <- Handlers],
- ok.
+ Args = [{alert, Alert}, {pool, Pool}, {worker, Pid}, {task, Task}, {runtime, Runtime}],
+ _ = [catch Mod:Fun(Args) || {Mod, Fun} <- Handlers],
+ ok.
diff --git a/src/wpool_utils.erl b/src/wpool_utils.erl
index c8c9374..b5dc881 100644
--- a/src/wpool_utils.erl
+++ b/src/wpool_utils.erl
@@ -14,13 +14,11 @@
%%% @author Felipe Ripoll
%%% @doc Common functions for wpool_process and other modules.
-module(wpool_utils).
+
-author('ferigis@gmail.com').
%% API
--export([ task_init/4
- , task_end/1
- , notify_queue_manager/3]).
-
+-export([task_init/4, task_end/1, notify_queue_manager/3]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Api
@@ -28,28 +26,36 @@
%% @doc Marks Task as started in this worker
-spec task_init(term(), atom(), infinity | pos_integer(), infinity | pos_integer()) ->
- undefined | reference().
+ undefined | reference().
task_init(Task, _TimeChecker, infinity, _MaxWarnings) ->
- Time = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
- erlang:put(wpool_task, {undefined, Time, Task}),
- undefined;
+ Time =
+ calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ erlang:put(wpool_task, {undefined, Time, Task}),
+ undefined;
task_init(Task, TimeChecker, OverrunTime, MaxWarnings) ->
- TaskId = erlang:make_ref(),
- Time = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
- erlang:put(wpool_task, {TaskId, Time, Task}),
- erlang:send_after(
- OverrunTime, TimeChecker, {check, self(), TaskId, OverrunTime, MaxWarnings}).
+ TaskId = erlang:make_ref(),
+ Time =
+ calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ erlang:put(wpool_task, {TaskId, Time, Task}),
+ erlang:send_after(OverrunTime,
+ TimeChecker,
+ {check, self(), TaskId, OverrunTime, MaxWarnings}).
%% @doc Removes the current task from the worker
-spec task_end(undefined | reference()) -> ok.
-task_end(undefined) -> erlang:erase(wpool_task);
+task_end(undefined) ->
+ erlang:erase(wpool_task);
task_end(TimerRef) ->
- _ = erlang:cancel_timer(TimerRef),
- erlang:erase(wpool_task).
+ _ = erlang:cancel_timer(TimerRef),
+ erlang:erase(wpool_task).
-spec notify_queue_manager(atom(), atom(), list()) -> ok | any().
notify_queue_manager(Function, Name, Options) ->
- case proplists:get_value(queue_manager, Options) of
- undefined -> ok;
- QueueManager -> wpool_queue_manager:Function(QueueManager, Name)
- end.
+ case proplists:get_value(queue_manager, Options) of
+ undefined ->
+ ok;
+ QueueManager ->
+ wpool_queue_manager:Function(QueueManager, Name)
+ end.
diff --git a/src/wpool_worker.erl b/src/wpool_worker.erl
index 5d48bcc..044f6ea 100644
--- a/src/wpool_worker.erl
+++ b/src/wpool_worker.erl
@@ -14,23 +14,13 @@
%%% @author Fernando Benavides
%%% @doc Default instance for {@link wpool_process}
-module(wpool_worker).
--author('elbrujohalcon@inaka.net').
-behaviour(gen_server).
%% api
--export([ call/4
- , cast/4
- ]).
-
+-export([call/4, cast/4]).
%% gen_server callbacks
--export([ init/1
- , terminate/2
- , code_change/3
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- ]).
+-export([init/1, handle_call/3, handle_cast/2]).
%%%===================================================================
%%% API
@@ -38,34 +28,30 @@
%% @doc Returns the result of M:F(A) from any of the workers of the pool S
-spec call(wpool:name(), module(), atom(), [term()]) -> term().
call(S, M, F, A) ->
- case wpool:call(S, {M, F, A}) of
- {ok, Result} -> Result;
- {error, Error} -> exit(Error)
- end.
+ case wpool:call(S, {M, F, A}) of
+ {ok, Result} ->
+ Result;
+ {error, Error} ->
+ exit(Error)
+ end.
%% @doc Executes M:F(A) in any of the workers of the pool S
-spec cast(wpool:name(), module(), atom(), [term()]) -> ok.
-cast(S, M, F, A) -> wpool:cast(S, {M, F, A}).
+cast(S, M, F, A) ->
+ wpool:cast(S, {M, F, A}).
%%%===================================================================
-%%% init, terminate, code_change, info callbacks
+%%% simple callbacks
%%%===================================================================
-record(state, {}).
+
-type state() :: #state{}.
%% @private
-spec init(undefined) -> {ok, state()}.
-init(undefined) -> {ok, #state{}}.
-%% @private
--spec terminate(atom(), state()) -> ok.
-terminate(_Reason, _State) -> ok.
-%% @private
--spec code_change(string(), state(), any()) -> {ok, state()}.
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
-%% @private
--spec handle_info(any(), state()) -> {noreply, state()}.
-handle_info(_Info, State) -> {noreply, State}.
+init(undefined) ->
+ {ok, #state{}}.
%%%===================================================================
%%% real (i.e. interesting) callbacks
@@ -73,39 +59,39 @@ handle_info(_Info, State) -> {noreply, State}.
%% @private
-spec handle_cast(term(), state()) -> {noreply, state(), hibernate}.
handle_cast({M, F, A}, State) ->
- try erlang:apply(M, F, A) of
- _ ->
- {noreply, State, hibernate}
- catch
- _:Error:Stacktrace ->
- log_error(M, F, A, Error, Stacktrace),
- {noreply, State, hibernate}
- end;
+ try erlang:apply(M, F, A) of
+ _ ->
+ {noreply, State, hibernate}
+ catch
+ _:Error:Stacktrace ->
+ log_error(M, F, A, Error, Stacktrace),
+ {noreply, State, hibernate}
+ end;
handle_cast(Cast, State) ->
- error_logger:error_msg("Invalid cast:~p", [Cast]),
- {noreply, State, hibernate}.
+ error_logger:error_msg("Invalid cast:~p", [Cast]),
+ {noreply, State, hibernate}.
-type from() :: {pid(), reference()}.
+
%% @private
-spec handle_call(term(), from(), state()) ->
- {reply, {ok, term()} | {error, term()}, state(), hibernate}.
+ {reply, {ok, term()} | {error, term()}, state(), hibernate}.
handle_call({M, F, A}, _From, State) ->
- try erlang:apply(M, F, A) of
- R ->
- {reply, {ok, R}, State, hibernate}
- catch
- _:Error:Stacktrace ->
- log_error(M, F, A, Error, Stacktrace),
- {reply, {error, Error}, State, hibernate}
- end;
+ try erlang:apply(M, F, A) of
+ R ->
+ {reply, {ok, R}, State, hibernate}
+ catch
+ _:Error:Stacktrace ->
+ log_error(M, F, A, Error, Stacktrace),
+ {reply, {error, Error}, State, hibernate}
+ end;
handle_call(Call, _From, State) ->
- error_logger:error_msg("Invalid call:~p", [Call]),
- {reply, {error, invalid_request}, State, hibernate}.
+ error_logger:error_msg("Invalid call:~p", [Call]),
+ {reply, {error, invalid_request}, State, hibernate}.
%%%===================================================================
%%% not exported functions
%%%===================================================================
log_error(M, F, A, Error, Stacktrace) ->
- error_logger:error_msg(
- "Error on ~p:~p~p >> ~p Backtrace ~p",
- [M, F, A, Error, Stacktrace]).
+ error_logger:error_msg("Error on ~p:~p~p >> ~p Backtrace ~p",
+ [M, F, A, Error, Stacktrace]).
diff --git a/test/crashy_server.erl b/test/crashy_server.erl
index 7dcc673..e58a658 100644
--- a/test/crashy_server.erl
+++ b/test/crashy_server.erl
@@ -17,13 +17,8 @@
-behaviour(gen_server).
%% gen_server callbacks
--export([ init/1
- , terminate/2
- , code_change/3
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- ]).
+-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2,
+ handle_info/2]).
-dialyzer([no_behaviours]).
@@ -31,24 +26,35 @@
%%% callbacks
%%%===================================================================
-spec init(Something) -> Something.
-init(Something) -> {ok, Something}.
+init(Something) ->
+ {ok, Something}.
-spec terminate(Any, term()) -> Any.
-terminate(Reason, _State) -> Reason.
+terminate(Reason, _State) ->
+ Reason.
-spec code_change(string(), State, any()) -> {ok, State}.
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
-spec handle_info(timeout | Info, term()) -> {noreply, timeout} | Info.
-handle_info(timeout, _State) -> {noreply, timeout};
-handle_info(Info, _State) -> Info.
+handle_info(timeout, _State) ->
+ {noreply, timeout};
+handle_info(Info, _State) ->
+ Info.
-spec handle_cast(Cast, term()) -> Cast.
-handle_cast(crash, _State) -> error(crash_requested);
-handle_cast(Cast, _State) -> Cast.
+handle_cast(crash, _State) ->
+ error(crash_requested);
+handle_cast(Cast, _State) ->
+ Cast.
-type from() :: {pid(), reference()}.
+
-spec handle_call(state | Call, from(), State) -> {reply, State, State} | Call.
-handle_call(state, _From, State) -> {reply, State, State};
-handle_call(crash, _From, _State) -> error(crash_requested);
-handle_call(Call, _From, State) -> {reply, Call, State}.
+handle_call(state, _From, State) ->
+ {reply, State, State};
+handle_call(crash, _From, _State) ->
+ error(crash_requested);
+handle_call(Call, _From, State) ->
+ {reply, Call, State}.
diff --git a/test/echo_server.erl b/test/echo_server.erl
index a06ab51..d748ff8 100644
--- a/test/echo_server.erl
+++ b/test/echo_server.erl
@@ -13,20 +13,12 @@
% under the License.
%% @doc a gen_server built to test wpool_process
-module(echo_server).
--author('elbrujohalcon@inaka.net').
-behaviour(gen_server).
%% gen_server callbacks
--export([ init/1
- , terminate/2
- , code_change/3
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- , handle_continue/2
- , format_status/2
- ]).
+-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2,
+ handle_continue/2, format_status/2]).
-dialyzer([no_behaviours]).
@@ -34,28 +26,38 @@
%%% callbacks
%%%===================================================================
-spec init(Something) -> Something.
-init(Something) -> Something.
+init(Something) ->
+ Something.
-spec terminate(Any, term()) -> Any.
-terminate(Reason, _State) -> Reason.
+terminate(Reason, _State) ->
+ Reason.
-spec code_change(string(), State, any()) -> any() | {ok, State}.
-code_change(_OldVsn, _State, Extra) -> Extra.
+code_change(_OldVsn, _State, Extra) ->
+ Extra.
-spec handle_info(timeout | Info, term()) -> {noreply, timeout} | Info.
-handle_info(timeout, _State) -> {noreply, timeout};
-handle_info(Info, _State) -> Info.
+handle_info(timeout, _State) ->
+ {noreply, timeout};
+handle_info(Info, _State) ->
+ Info.
-spec handle_cast(Cast, term()) -> Cast.
-handle_cast(Cast, _State) -> Cast.
+handle_cast(Cast, _State) ->
+ Cast.
-type from() :: {pid(), reference()}.
+
-spec handle_call(Call, from(), term()) -> Call.
-handle_call(Call, _From, _State) -> Call.
+handle_call(Call, _From, _State) ->
+ Call.
-spec handle_continue(Continue, term()) -> Continue.
-handle_continue(Continue, _State) -> Continue.
+handle_continue(Continue, _State) ->
+ Continue.
-spec format_status(normal | terminate, [[{_, _}] | State, ...]) ->
+ {formatted_state, State}.
+format_status(_, [_PDict, State]) ->
{formatted_state, State}.
-format_status(_, [_PDict, State]) -> {formatted_state, State}.
diff --git a/test/sleepy_server.erl b/test/sleepy_server.erl
index 763ba83..3f7014f 100644
--- a/test/sleepy_server.erl
+++ b/test/sleepy_server.erl
@@ -13,15 +13,11 @@
% under the License.
%% @doc a gen_server built to test wpool_process
-module(sleepy_server).
--author('elbrujohalcon@inaka.net').
-behaviour(gen_server).
%% gen_server callbacks
--export([ init/1
- , handle_call/3
- , handle_cast/2
- ]).
+-export([init/1, handle_call/3, handle_cast/2]).
-dialyzer([no_behaviours]).
@@ -30,18 +26,19 @@
%%%===================================================================
-spec init(pos_integer()) -> {ok, state}.
init(TimeToSleep) ->
- ct:pal("Waiting ~pms to return...", [TimeToSleep]),
- _ = timer:sleep(TimeToSleep),
- ct:pal("Done waiting ~pms", [TimeToSleep]),
- {ok, state}.
+ ct:pal("Waiting ~pms to return...", [TimeToSleep]),
+ _ = timer:sleep(TimeToSleep),
+ ct:pal("Done waiting ~pms", [TimeToSleep]),
+ {ok, state}.
-spec handle_cast(pos_integer(), State) -> {noreply, State}.
handle_cast(TimeToSleep, State) ->
- _ = timer:sleep(TimeToSleep),
- {noreply, State}.
+ _ = timer:sleep(TimeToSleep),
+ {noreply, State}.
-type from() :: {pid(), reference()}.
+
-spec handle_call(pos_integer(), from(), State) -> {reply, ok, State}.
handle_call(TimeToSleep, _From, State) ->
- _ = timer:sleep(TimeToSleep),
- {reply, ok, State}.
+ _ = timer:sleep(TimeToSleep),
+ {reply, ok, State}.
diff --git a/test/wpool_SUITE.erl b/test/wpool_SUITE.erl
index 4418781..6faba19 100644
--- a/test/wpool_SUITE.erl
+++ b/test/wpool_SUITE.erl
@@ -15,443 +15,448 @@
%% @hidden
-module(wpool_SUITE).
+-behaviour(ct_suite).
+
+-elvis([{elvis_style, atom_naming_convention, disable}]).
+
-type config() :: [{atom(), term()}].
--export([ all/0
- ]).
--export([ init_per_suite/1
- , end_per_suite/1
- ]).
--export([ stats/1
- , stop_pool/1
- , non_brutal_shutdown/1
- , brutal_worker_shutdown/1
- , overrun/1
- , kill_on_overrun/1
- , too_much_overrun/1
- , default_strategy/1
- , overrun_handler1/1
- , overrun_handler2/1
- , default_options/1
- , complete_coverage/1
- , broadcast/1
- , worker_killed_stats/1
- ]).
+-export([all/0]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([stats/1, stop_pool/1, non_brutal_shutdown/1, brutal_worker_shutdown/1, overrun/1,
+ kill_on_overrun/1, too_much_overrun/1, default_strategy/1, overrun_handler1/1,
+ overrun_handler2/1, default_options/1, complete_coverage/1, broadcast/1,
+ worker_killed_stats/1]).
-spec all() -> [atom()].
all() ->
- [too_much_overrun, overrun, stop_pool, non_brutal_shutdown, brutal_worker_shutdown,
- stats, default_strategy, default_options, complete_coverage, broadcast,
- kill_on_overrun, worker_killed_stats].
+ [too_much_overrun,
+ overrun,
+ stop_pool,
+ non_brutal_shutdown,
+ brutal_worker_shutdown,
+ stats,
+ default_strategy,
+ default_options,
+ complete_coverage,
+ broadcast,
+ kill_on_overrun,
+ worker_killed_stats].
-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
- ok = wpool:start(),
- Config.
+ ok = wpool:start(),
+ Config.
-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
- wpool:stop(),
- Config.
+ wpool:stop(),
+ Config.
-spec overrun_handler1(M) -> M.
-overrun_handler1(M) -> overrun_handler ! {overrun1, M}.
+overrun_handler1(M) ->
+ overrun_handler ! {overrun1, M}.
-spec overrun_handler2(M) -> M.
-overrun_handler2(M) -> overrun_handler ! {overrun2, M}.
+overrun_handler2(M) ->
+ overrun_handler ! {overrun2, M}.
-spec too_much_overrun(config()) -> {comment, []}.
too_much_overrun(_Config) ->
- ct:comment("Receiving overruns here..."),
- true = register(overrun_handler, self()),
- {ok, PoolPid} =
- wpool:start_sup_pool(
- wpool_SUITE_too_much_overrun,
- [ {workers, 1}
- , {overrun_warning, 999}
- , {overrun_handler, {?MODULE, overrun_handler1}}
- ]),
-
- CheckerName = wpool_pool:time_checker_name(wpool_SUITE_too_much_overrun),
- ok = wpool_time_checker:add_handler(CheckerName, {?MODULE, overrun_handler2}),
-
- ct:comment("Find the worker and the time checker..."),
- {ok, Worker} = wpool:call(wpool_SUITE_too_much_overrun, {erlang, self, []}),
- TCPid = get_time_checker(PoolPid),
-
- ct:comment("Start a long running task..."),
- ok = wpool:cast(wpool_SUITE_too_much_overrun, {timer, sleep, [5000]}),
- TaskId =
- ktn_task:wait_for_success(
- fun() ->
- {dictionary, Dict} = erlang:process_info(Worker, dictionary),
- {TId, _, _} = proplists:get_value(wpool_task, Dict),
- TId
- end),
-
- ct:comment("Simulate overrun warning..."),
- % huge runtime => no more overruns
- TCPid ! {check, Worker, TaskId, 9999999999, infinity},
-
- ct:comment("Get overrun message..."),
- _ = receive
- {overrun1, Message1} ->
- overrun = proplists:get_value(alert, Message1),
- wpool_SUITE_too_much_overrun = proplists:get_value(pool, Message1),
- Worker = proplists:get_value(worker, Message1),
- {cast, {timer, sleep, [5000]}} = proplists:get_value(task, Message1),
- 9999999999 = proplists:get_value(runtime, Message1)
- after 100 ->
- ct:fail(no_overrun)
- end,
-
- ct:comment("Get overrun message..."),
- _ = receive
- {overrun2, Message2} ->
- overrun = proplists:get_value(alert, Message2),
- wpool_SUITE_too_much_overrun = proplists:get_value(pool, Message2),
- Worker = proplists:get_value(worker, Message2),
- {cast, {timer, sleep, [5000]}} = proplists:get_value(task, Message2),
- 9999999999 = proplists:get_value(runtime, Message2)
- after 100 ->
- ct:fail(no_overrun)
- end,
-
- ct:comment("No more overruns..."),
- _ = case get_messages(100) of
- [] -> ok;
- Msgs1 -> ct:fail({unexpected_messages, Msgs1})
- end,
-
- ct:comment("Kill the worker..."),
- exit(Worker, kill),
-
- ct:comment("Simulate overrun warning..."),
- TCPid ! {check, Worker, TaskId, 100}, % tiny runtime, to check
-
- ct:comment("Nothing happens..."),
- ok = no_messages(),
-
- ct:comment("Stop pool..."),
- ok = wpool:stop_sup_pool(wpool_SUITE_too_much_overrun),
-
- {comment, []}.
+ ct:comment("Receiving overruns here..."),
+ true = register(overrun_handler, self()),
+ {ok, PoolPid} =
+ wpool:start_sup_pool(wpool_SUITE_too_much_overrun,
+ [{workers, 1},
+ {overrun_warning, 999},
+ {overrun_handler, {?MODULE, overrun_handler1}}]),
+
+ CheckerName = wpool_pool:time_checker_name(wpool_SUITE_too_much_overrun),
+ ok = wpool_time_checker:add_handler(CheckerName, {?MODULE, overrun_handler2}),
+
+ ct:comment("Find the worker and the time checker..."),
+ {ok, Worker} = wpool:call(wpool_SUITE_too_much_overrun, {erlang, self, []}),
+ TCPid = get_time_checker(PoolPid),
+
+ ct:comment("Start a long running task..."),
+ ok = wpool:cast(wpool_SUITE_too_much_overrun, {timer, sleep, [5000]}),
+ TaskId =
+ ktn_task:wait_for_success(fun() ->
+ {dictionary, Dict} = erlang:process_info(Worker, dictionary),
+ {TId, _, _} = proplists:get_value(wpool_task, Dict),
+ TId
+ end),
+
+ ct:comment("Simulate overrun warning..."),
+ % huge runtime => no more overruns
+ TCPid ! {check, Worker, TaskId, 9999999999, infinity},
+
+ ct:comment("Get overrun message..."),
+ _ = receive
+ {overrun1, Message1} ->
+ overrun = proplists:get_value(alert, Message1),
+ wpool_SUITE_too_much_overrun = proplists:get_value(pool, Message1),
+ Worker = proplists:get_value(worker, Message1),
+ {cast, {timer, sleep, [5000]}} = proplists:get_value(task, Message1),
+ 9999999999 = proplists:get_value(runtime, Message1)
+ after 100 ->
+ ct:fail(no_overrun)
+ end,
+
+ ct:comment("Get overrun message..."),
+ _ = receive
+ {overrun2, Message2} ->
+ overrun = proplists:get_value(alert, Message2),
+ wpool_SUITE_too_much_overrun = proplists:get_value(pool, Message2),
+ Worker = proplists:get_value(worker, Message2),
+ {cast, {timer, sleep, [5000]}} = proplists:get_value(task, Message2),
+ 9999999999 = proplists:get_value(runtime, Message2)
+ after 100 ->
+ ct:fail(no_overrun)
+ end,
+
+ ct:comment("No more overruns..."),
+ _ = case get_messages(100) of
+ [] ->
+ ok;
+ Msgs1 ->
+ ct:fail({unexpected_messages, Msgs1})
+ end,
+
+ ct:comment("Kill the worker..."),
+ exit(Worker, kill),
+
+ ct:comment("Simulate overrun warning..."),
+ TCPid ! {check, Worker, TaskId, 100}, % tiny runtime, to check
+
+ ct:comment("Nothing happens..."),
+ ok = no_messages(),
+
+ ct:comment("Stop pool..."),
+ ok = wpool:stop_sup_pool(wpool_SUITE_too_much_overrun),
+
+ {comment, []}.
-spec overrun(config()) -> {comment, []}.
overrun(_Config) ->
- true = register(overrun_handler, self()),
- {ok, _Pid} =
- wpool:start_sup_pool(
- wpool_SUITE_overrun_pool,
- [ {workers, 1}
- , {overrun_warning, 1000}
- , {overrun_handler, {?MODULE, overrun_handler1}}
- ]),
- ok = wpool:cast(wpool_SUITE_overrun_pool, {timer, sleep, [1500]}),
- _ = receive
- {overrun1, Message} ->
- overrun = proplists:get_value(alert, Message),
- wpool_SUITE_overrun_pool = proplists:get_value(pool, Message),
- WPid = proplists:get_value(worker, Message),
- true = is_pid(WPid),
- {cast, {timer, sleep, [1500]}} = proplists:get_value(task, Message),
- Runtime = proplists:get_value(runtime, Message),
- true = Runtime >= 1000
- after 1500 ->
- ct:fail(no_overrun)
- end,
-
- ok = no_messages(),
-
- ok = wpool:stop_sup_pool(wpool_SUITE_overrun_pool),
-
- {comment, []}.
+ true = register(overrun_handler, self()),
+ {ok, _Pid} =
+ wpool:start_sup_pool(wpool_SUITE_overrun_pool,
+ [{workers, 1},
+ {overrun_warning, 1000},
+ {overrun_handler, {?MODULE, overrun_handler1}}]),
+ ok = wpool:cast(wpool_SUITE_overrun_pool, {timer, sleep, [1500]}),
+ _ = receive
+ {overrun1, Message} ->
+ overrun = proplists:get_value(alert, Message),
+ wpool_SUITE_overrun_pool = proplists:get_value(pool, Message),
+ WPid = proplists:get_value(worker, Message),
+ true = is_pid(WPid),
+ {cast, {timer, sleep, [1500]}} = proplists:get_value(task, Message),
+ Runtime = proplists:get_value(runtime, Message),
+ true = Runtime >= 1000
+ after 1500 ->
+ ct:fail(no_overrun)
+ end,
+
+ ok = no_messages(),
+
+ ok = wpool:stop_sup_pool(wpool_SUITE_overrun_pool),
+
+ {comment, []}.
-spec kill_on_overrun(config()) -> {comment, []}.
kill_on_overrun(_Config) ->
- true = register(overrun_handler, self()),
- {ok, _Pid} =
- wpool:start_sup_pool(
- wpool_SUITE_kill_on_overrun_pool,
- [ {workers, 1}
- , {overrun_warning, 500}
- , {max_overrun_warnings, 2} %% The worker must be killed after 2 overrun
- %% warnings, which is after 1 secs with this
- %% configuration
- , {overrun_handler, {?MODULE, overrun_handler1}}
- ]),
- ok = wpool:cast(wpool_SUITE_kill_on_overrun_pool, {timer, sleep, [2000]}),
- _ = receive
- {overrun1, Message} ->
- overrun = proplists:get_value(alert, Message),
- wpool_SUITE_kill_on_overrun_pool = proplists:get_value(pool, Message),
- WPid = proplists:get_value(worker, Message),
- true = is_pid(WPid),
- true = erlang:is_process_alive(WPid)
- after 2000 ->
- ct:fail(no_overrun)
- end,
-
- _ = receive
- {overrun1, Message2} ->
- max_overrun_limit = proplists:get_value(alert, Message2),
- wpool_SUITE_kill_on_overrun_pool =
- proplists:get_value(pool, Message2),
- WPid2 = proplists:get_value(worker, Message2),
- true = is_pid(WPid2),
- false = erlang:is_process_alive(WPid2)
- after 2000 ->
- ct:fail(no_overrun)
- end,
- ok = no_messages(),
-
- ok = wpool:stop_sup_pool(wpool_SUITE_overrun_pool),
-
- {comment, []}.
+ true = register(overrun_handler, self()),
+ {ok, _Pid} =
+ wpool:start_sup_pool(wpool_SUITE_kill_on_overrun_pool,
+ [{workers, 1},
+ {overrun_warning, 500},
+ {max_overrun_warnings,
+ 2}, %% The worker must be killed after 2 overrun
+ %% warnings, which is after 1 secs with this
+ %% configuration
+ {overrun_handler, {?MODULE, overrun_handler1}}]),
+ ok = wpool:cast(wpool_SUITE_kill_on_overrun_pool, {timer, sleep, [2000]}),
+ _ = receive
+ {overrun1, Message} ->
+ overrun = proplists:get_value(alert, Message),
+ wpool_SUITE_kill_on_overrun_pool = proplists:get_value(pool, Message),
+ WPid = proplists:get_value(worker, Message),
+ true = is_pid(WPid),
+ true = erlang:is_process_alive(WPid)
+ after 2000 ->
+ ct:fail(no_overrun)
+ end,
+
+ _ = receive
+ {overrun1, Message2} ->
+ max_overrun_limit = proplists:get_value(alert, Message2),
+ wpool_SUITE_kill_on_overrun_pool = proplists:get_value(pool, Message2),
+ WPid2 = proplists:get_value(worker, Message2),
+ true = is_pid(WPid2),
+ false = erlang:is_process_alive(WPid2)
+ after 2000 ->
+ ct:fail(no_overrun)
+ end,
+ ok = no_messages(),
+
+ ok = wpool:stop_sup_pool(wpool_SUITE_overrun_pool),
+
+ {comment, []}.
-spec stop_pool(config()) -> {comment, []}.
stop_pool(_Config) ->
- {ok, PoolPid} = wpool:start_sup_pool(wpool_SUITE_stop_pool, [{workers, 1}]),
- true = erlang:is_process_alive(PoolPid),
- ok = wpool:stop_sup_pool(wpool_SUITE_stop_pool),
- false = erlang:is_process_alive(PoolPid),
- ok = wpool:stop_sup_pool(wpool_SUITE_stop_pool),
+ {ok, PoolPid} = wpool:start_sup_pool(wpool_SUITE_stop_pool, [{workers, 1}]),
+ true = erlang:is_process_alive(PoolPid),
+ ok = wpool:stop_sup_pool(wpool_SUITE_stop_pool),
+ false = erlang:is_process_alive(PoolPid),
+ ok = wpool:stop_sup_pool(wpool_SUITE_stop_pool),
- {comment, []}.
+ {comment, []}.
-spec non_brutal_shutdown(config()) -> {comment, []}.
non_brutal_shutdown(_Config) ->
- {ok, PoolPid} = wpool:start_sup_pool(wpool_SUITE_non_brutal_shutdown,
- [{workers, 1},
- {pool_sup_shutdown, 100}]),
- true = erlang:is_process_alive(PoolPid),
- Stats = wpool:stats(wpool_SUITE_non_brutal_shutdown),
- {workers, [{WorkerId, _}]} = lists:keyfind(workers, 1, Stats),
- Worker = wpool_pool:worker_name(wpool_SUITE_non_brutal_shutdown, WorkerId),
- monitor(process, Worker),
- ok = wpool:stop_sup_pool(wpool_SUITE_non_brutal_shutdown),
- receive {'DOWN', _, process, {Worker, _}, Reason} -> shutdown = Reason
- after 200 -> ct:fail(worker_not_stopped)
- end,
-
- {comment, []}.
+ {ok, PoolPid} =
+ wpool:start_sup_pool(wpool_SUITE_non_brutal_shutdown,
+ [{workers, 1}, {pool_sup_shutdown, 100}]),
+ true = erlang:is_process_alive(PoolPid),
+ Stats = wpool:stats(wpool_SUITE_non_brutal_shutdown),
+ {workers, [{WorkerId, _}]} = lists:keyfind(workers, 1, Stats),
+ Worker = wpool_pool:worker_name(wpool_SUITE_non_brutal_shutdown, WorkerId),
+ monitor(process, Worker),
+ ok = wpool:stop_sup_pool(wpool_SUITE_non_brutal_shutdown),
+ receive
+ {'DOWN', _, process, {Worker, _}, Reason} ->
+ shutdown = Reason
+ after 200 ->
+ ct:fail(worker_not_stopped)
+ end,
+
+ {comment, []}.
-spec brutal_worker_shutdown(config()) -> {comment, []}.
brutal_worker_shutdown(_Config) ->
- {ok, PoolPid} = wpool:start_sup_pool(wpool_SUITE_non_brutal_shutdown,
- [{workers, 1},
- {pool_sup_shutdown, 100},
- {worker_shutdown, brutal_kill}]),
- true = erlang:is_process_alive(PoolPid),
- Stats = wpool:stats(wpool_SUITE_non_brutal_shutdown),
- {workers, [{WorkerId, _}]} = lists:keyfind(workers, 1, Stats),
- Worker = wpool_pool:worker_name(wpool_SUITE_non_brutal_shutdown, WorkerId),
- monitor(process, Worker),
- ok = wpool:stop_sup_pool(wpool_SUITE_non_brutal_shutdown),
- receive {'DOWN', _, process, {Worker, _}, Reason} -> killed = Reason
- after 200 ->
- ct:fail(worker_not_stopped)
- end,
-
- {comment, []}.
+ {ok, PoolPid} =
+ wpool:start_sup_pool(wpool_SUITE_non_brutal_shutdown,
+ [{workers, 1},
+ {pool_sup_shutdown, 100},
+ {worker_shutdown, brutal_kill}]),
+ true = erlang:is_process_alive(PoolPid),
+ Stats = wpool:stats(wpool_SUITE_non_brutal_shutdown),
+ {workers, [{WorkerId, _}]} = lists:keyfind(workers, 1, Stats),
+ Worker = wpool_pool:worker_name(wpool_SUITE_non_brutal_shutdown, WorkerId),
+ monitor(process, Worker),
+ ok = wpool:stop_sup_pool(wpool_SUITE_non_brutal_shutdown),
+ receive
+ {'DOWN', _, process, {Worker, _}, Reason} ->
+ killed = Reason
+ after 200 ->
+ ct:fail(worker_not_stopped)
+ end,
+
+ {comment, []}.
-spec stats(config()) -> {comment, []}.
stats(_Config) ->
- Get = fun proplists:get_value/2,
-
- ok = try _ = wpool:stats(?MODULE), ok
- catch _:no_workers -> ok
- end,
-
- {ok, PoolPid} = wpool:start_sup_pool(wpool_SUITE_stats_pool, [{workers, 10}]),
- true = is_process_alive(PoolPid),
- PoolPid = erlang:whereis(wpool_SUITE_stats_pool),
-
- % Checks ...
- [InitStats] = wpool:stats(),
- wpool_SUITE_stats_pool = Get(pool, InitStats),
- PoolPid = Get(supervisor, InitStats),
- Options = Get(options, InitStats),
- infinity = Get(overrun_warning, Options),
- {error_logger, warning_report} = Get(overrun_handler, Options),
- 10 = Get(workers, Options),
- 10 = Get(size, InitStats),
- 1 = Get(next_worker, InitStats),
- InitWorkers = Get(workers, InitStats),
- 10 = length(InitWorkers),
- _ = [ begin
- WorkerStats = Get(I, InitWorkers),
- 0 = Get(message_queue_len, WorkerStats),
- [] =
- lists:keydelete(
- message_queue_len, 1, lists:keydelete(memory, 1, WorkerStats))
- end || I <- lists:seq(1, 10)],
-
- % Start a long task on every worker
- Sleep = {timer, sleep, [2000]},
- [wpool:cast(wpool_SUITE_stats_pool, Sleep, next_worker)
- || _ <- lists:seq(1, 10)],
-
- ok =
- ktn_task:wait_for_success(
- fun() ->
- WorkingStats = wpool:stats(wpool_SUITE_stats_pool),
- wpool_SUITE_stats_pool = Get(pool, WorkingStats),
- PoolPid = Get(supervisor, WorkingStats),
- Options = Get(options, WorkingStats),
- 10 = Get(size, WorkingStats),
- 1 = Get(next_worker, WorkingStats),
- WorkingWorkers = Get(workers, WorkingStats),
- 10 = length(WorkingWorkers),
- [ begin
- WorkerStats = Get(I, WorkingWorkers),
- 0 = Get(message_queue_len, WorkerStats),
- {timer, sleep, 1} = Get(current_function, WorkerStats),
- {timer, sleep, 1, _} = Get(current_location, WorkerStats),
- {cast, Sleep} = Get(task, WorkerStats),
- true = is_number(Get(runtime, WorkerStats))
- end || I <- lists:seq(1, 10)],
- ok
- end),
-
- wpool:stop_sup_pool(wpool_SUITE_stats_pool),
-
- no_workers =
- ktn_task:wait_for(
- fun() ->
- try wpool:stats(wpool_SUITE_stats_pool)
- catch
- _:E -> E
- end
- end, no_workers, 100, 50),
+ Get = fun proplists:get_value/2,
- {comment, []}.
+ ok =
+ try
+ _ = wpool:stats(?MODULE),
+ ok
+ catch
+ _:no_workers ->
+ ok
+ end,
+
+ {ok, PoolPid} = wpool:start_sup_pool(wpool_SUITE_stats_pool, [{workers, 10}]),
+ true = is_process_alive(PoolPid),
+ PoolPid = erlang:whereis(wpool_SUITE_stats_pool),
+
+ % Checks ...
+ [InitStats] = wpool:stats(),
+ wpool_SUITE_stats_pool = Get(pool, InitStats),
+ PoolPid = Get(supervisor, InitStats),
+ Options = Get(options, InitStats),
+ infinity = Get(overrun_warning, Options),
+ {error_logger, warning_report} = Get(overrun_handler, Options),
+ 10 = Get(workers, Options),
+ 10 = Get(size, InitStats),
+ 1 = Get(next_worker, InitStats),
+ InitWorkers = Get(workers, InitStats),
+ 10 = length(InitWorkers),
+ _ = [begin
+ WorkerStats = Get(I, InitWorkers),
+ 0 = Get(message_queue_len, WorkerStats),
+ [] = lists:keydelete(message_queue_len, 1, lists:keydelete(memory, 1, WorkerStats))
+ end
+ || I <- lists:seq(1, 10)],
+
+ % Start a long task on every worker
+ Sleep = {timer, sleep, [2000]},
+ [wpool:cast(wpool_SUITE_stats_pool, Sleep, next_worker) || _ <- lists:seq(1, 10)],
+
+ ok =
+ ktn_task:wait_for_success(fun() ->
+ WorkingStats = wpool:stats(wpool_SUITE_stats_pool),
+ wpool_SUITE_stats_pool = Get(pool, WorkingStats),
+ PoolPid = Get(supervisor, WorkingStats),
+ Options = Get(options, WorkingStats),
+ 10 = Get(size, WorkingStats),
+ 1 = Get(next_worker, WorkingStats),
+ WorkingWorkers = Get(workers, WorkingStats),
+ 10 = length(WorkingWorkers),
+ [begin
+ WorkerStats = Get(I, WorkingWorkers),
+ 0 = Get(message_queue_len, WorkerStats),
+ {timer, sleep, 1} = Get(current_function, WorkerStats),
+ {timer, sleep, 1, _} = Get(current_location, WorkerStats),
+ {cast, Sleep} = Get(task, WorkerStats),
+ true = is_number(Get(runtime, WorkerStats))
+ end
+ || I <- lists:seq(1, 10)],
+ ok
+ end),
+
+ wpool:stop_sup_pool(wpool_SUITE_stats_pool),
+
+ no_workers =
+ ktn_task:wait_for(fun() ->
+ try
+ wpool:stats(wpool_SUITE_stats_pool)
+ catch
+ _:E ->
+ E
+ end
+ end,
+ no_workers,
+ 100,
+ 50),
+
+ {comment, []}.
-spec default_strategy(config()) -> {comment, []}.
default_strategy(_Config) ->
- application:unset_env(worker_pool, default_strategy),
- available_worker = wpool:default_strategy(),
- application:set_env(worker_pool, default_strategy, best_worker),
- best_worker = wpool:default_strategy(),
- application:unset_env(worker_pool, default_strategy),
- available_worker = wpool:default_strategy(),
- {comment, []}.
+ application:unset_env(worker_pool, default_strategy),
+ available_worker = wpool:default_strategy(),
+ application:set_env(worker_pool, default_strategy, best_worker),
+ best_worker = wpool:default_strategy(),
+ application:unset_env(worker_pool, default_strategy),
+ available_worker = wpool:default_strategy(),
+ {comment, []}.
-spec default_options(config()) -> {comment, []}.
default_options(_Config) ->
- ct:comment("Starts a pool with default options"),
- {ok, PoolPid} = wpool:start_pool(default_pool),
- true = is_pid(PoolPid),
+ ct:comment("Starts a pool with default options"),
+ {ok, PoolPid} = wpool:start_pool(default_pool),
+ true = is_pid(PoolPid),
- ct:comment("Starts a supervised pool with default options"),
- {ok, SupPoolPid} = wpool:start_sup_pool(default_sup_pool),
- true = is_pid(SupPoolPid),
+ ct:comment("Starts a supervised pool with default options"),
+ {ok, SupPoolPid} = wpool:start_sup_pool(default_sup_pool),
+ true = is_pid(SupPoolPid),
- {comment, []}.
+ {comment, []}.
-spec complete_coverage(config()) -> {comment, []}.
complete_coverage(_Config) ->
+ ct:comment("Time checker"),
+ {ok, _} = wpool_time_checker:init({pool, [{x, y}]}),
- ct:comment("Time checker"),
- {ok, State} = wpool_time_checker:init({pool, [{x, y}]}),
- ok = wpool_time_checker:terminate(reason, State),
- {ok, State} = wpool_time_checker:code_change("oldvsn", State, extra),
-
- {ok, PoolPid} = wpool:start_pool(coverage, []),
- TCPid = get_time_checker(PoolPid),
- TCPid ! info,
- ok = gen_server:cast(TCPid, cast),
+ {ok, PoolPid} = wpool:start_pool(coverage, []),
+ TCPid = get_time_checker(PoolPid),
+ TCPid ! info,
+ ok = gen_server:cast(TCPid, cast),
- ct:comment("Queue Manager"),
- QMPid = get_queue_manager(PoolPid),
- QMPid ! info,
- {ok, QMState} = wpool_queue_manager:init([{pool, pool}]),
- ok = wpool_queue_manager:terminate(reason, QMState),
- {ok, QMState} = wpool_queue_manager:code_change("oldvsn", QMState, extra),
+ ct:comment("Queue Manager"),
+ QMPid = get_queue_manager(PoolPid),
+ QMPid ! info,
+ {ok, _} = wpool_queue_manager:init([{pool, pool}]),
- {comment, []}.
+ {comment, []}.
-spec broadcast(config()) -> {comment, []}.
broadcast(_Config) ->
- Pool = broadcast,
- WorkersCount = 19,
- {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount}]),
-
- ct:comment("Check mecked function is called ~p times.", [WorkersCount]),
- meck:new(x, [non_strict]),
- meck:expect(x, x, fun() -> ok end),
- % Broadcast x:x() execution to workers.
- wpool:broadcast(Pool, {x, x, []}),
- % Give some time for the workers to perform the calls.
- WorkersCount =
- ktn_task:wait_for(fun() -> meck:num_calls(x, x, '_') end, WorkersCount),
-
- ct:comment("Check they all are \"working\""),
- % Make all the workers sleep for 1.5 seconds
- wpool:broadcast(Pool, {timer, sleep, [1500]}),
- % check they all are actually busy (executing timer:sleep/1 function).
- try
- wpool:call(Pool, {io, format, ["I am awake.~n"]}, next_available_worker),
- ct:fail("There was at least 1 worker available")
- catch
- _:no_available_workers -> ok
- end,
-
- meck:unload(x),
- {comment, []}.
+ Pool = broadcast,
+ WorkersCount = 19,
+ {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount}]),
+
+ ct:comment("Check mecked function is called ~p times.", [WorkersCount]),
+ meck:new(x, [non_strict]),
+ meck:expect(x, x, fun() -> ok end),
+ % Broadcast x:x() execution to workers.
+ wpool:broadcast(Pool, {x, x, []}),
+ % Give some time for the workers to perform the calls.
+ WorkersCount = ktn_task:wait_for(fun() -> meck:num_calls(x, x, '_') end, WorkersCount),
+
+ ct:comment("Check they all are \"working\""),
+ % Make all the workers sleep for 1.5 seconds
+ wpool:broadcast(Pool, {timer, sleep, [1500]}),
+ % check they all are actually busy (executing timer:sleep/1 function).
+ try
+ wpool:call(Pool, {io, format, ["I am awake.~n"]}, next_available_worker),
+ ct:fail("There was at least 1 worker available")
+ catch
+ _:no_available_workers ->
+ ok
+ end,
+
+ meck:unload(x),
+ {comment, []}.
-spec worker_killed_stats(config()) -> {comment, []}.
worker_killed_stats(_Config) ->
- %% Each server will take 100ms to start, but the start_sup_pool/2 call is synchronous anyway
- {ok, PoolPid} = wpool:start_sup_pool(
- wpool_SUITE_worker_killed_stats, [{workers, 3}, {worker, {sleepy_server, 500}}]),
- true = erlang:is_process_alive(PoolPid),
+ %% Each server will take 100ms to start, but the start_sup_pool/2 call is synchronous anyway
+ {ok, PoolPid} =
+ wpool:start_sup_pool(wpool_SUITE_worker_killed_stats,
+ [{workers, 3}, {worker, {sleepy_server, 500}}]),
+ true = erlang:is_process_alive(PoolPid),
- Workers = fun() -> lists:keyfind(workers, 1, wpool:stats(wpool_SUITE_worker_killed_stats)) end,
- WorkerName = wpool_pool:worker_name(wpool_SUITE_worker_killed_stats, 1),
+ Workers =
+ fun() -> lists:keyfind(workers, 1, wpool:stats(wpool_SUITE_worker_killed_stats)) end,
+ WorkerName = wpool_pool:worker_name(wpool_SUITE_worker_killed_stats, 1),
- ct:comment("wpool:stats/1 should work normally"),
- {workers, [_, _, _]} = Workers(),
+ ct:comment("wpool:stats/1 should work normally"),
+ {workers, [_, _, _]} = Workers(),
- ct:comment("wpool:stats/1 should work even if a process just dies and it's not yet back alive"),
- exit(whereis(WorkerName), kill),
- {workers, [_, _]} = Workers(),
+ ct:comment("wpool:stats/1 should work even if a process just dies and it's not yet back alive"),
+ exit(whereis(WorkerName), kill),
+ {workers, [_, _]} = Workers(),
- ct:comment("Once the process is alive again, we should see it at the stats"),
- true = ktn_task:wait_for(fun() -> is_pid(whereis(WorkerName)) end, true, 10, 75),
- {workers, [_, _, _]} = Workers(),
+ ct:comment("Once the process is alive again, we should see it at the stats"),
+ true = ktn_task:wait_for(fun() -> is_pid(whereis(WorkerName)) end, true, 10, 75),
+ {workers, [_, _, _]} = Workers(),
- {comment, []}.
+ {comment, []}.
%% =============================================================================
%% Helpers
%% =============================================================================
get_time_checker(PoolPid) ->
- [TCPid] =
- [ P
- || {_, P, worker, [wpool_time_checker]} <-
- supervisor:which_children(PoolPid)
- ],
- TCPid.
+ [TCPid] =
+ [P || {_, P, worker, [wpool_time_checker]} <- supervisor:which_children(PoolPid)],
+ TCPid.
get_queue_manager(PoolPid) ->
- [QMPid] =
- [ P
- || {_, P, worker, [wpool_queue_manager]} <-
- supervisor:which_children(PoolPid)
- ],
- QMPid.
+ [QMPid] =
+ [P || {_, P, worker, [wpool_queue_manager]} <- supervisor:which_children(PoolPid)],
+ QMPid.
get_messages(MaxTimeout) ->
- get_messages(MaxTimeout, []).
+ get_messages(MaxTimeout, []).
get_messages(MaxTimeout, Acc) ->
- receive Any -> get_messages(MaxTimeout, [Any | Acc])
- after MaxTimeout -> Acc
- end.
+ receive
+ Any ->
+ get_messages(MaxTimeout, [Any | Acc])
+ after MaxTimeout ->
+ Acc
+ end.
no_messages() ->
- case get_messages(1000) of
- [] -> ok;
- Msgs2 -> ct:fail({unexpected_messages, Msgs2})
- end.
+ case get_messages(1000) of
+ [] ->
+ ok;
+ Msgs2 ->
+ ct:fail({unexpected_messages, Msgs2})
+ end.
diff --git a/test/wpool_bench.erl b/test/wpool_bench.erl
index 0a30318..4c53da6 100644
--- a/test/wpool_bench.erl
+++ b/test/wpool_bench.erl
@@ -1,32 +1,30 @@
-module(wpool_bench).
--author('elbrujohalcon@inaka.net').
-export([run_tasks/3]).
%% @doc Returns the average time involved in processing the small tasks
--spec run_tasks(
- [{small|large, pos_integer()}, ...],
- wpool:strategy(), [wpool:option()]) -> float().
+-spec run_tasks([{small | large, pos_integer()}, ...],
+ wpool:strategy(),
+ [wpool:option()]) ->
+ float().
run_tasks(TaskGroups, Strategy, Options) ->
- Tasks = lists:flatten([lists:duplicate(N, Type) || {Type, N} <- TaskGroups]),
- {ok, _Pool} = wpool:start_sup_pool(?MODULE, Options),
- try lists:foldl(
- fun(Task, Acc) -> run_task(Task, Strategy, Acc) end,
- [], Tasks) of
- [] ->
- error_logger:warning_msg("No times"),
- 0.0;
- Times ->
- error_logger:info_msg("Times: ~p", [Times]),
- lists:sum(Times) / length(Times)
- after
- wpool:stop_sup_pool(?MODULE)
- end.
+ Tasks = lists:flatten([lists:duplicate(N, Type) || {Type, N} <- TaskGroups]),
+ {ok, _Pool} = wpool:start_sup_pool(?MODULE, Options),
+ try lists:foldl(fun(Task, Acc) -> run_task(Task, Strategy, Acc) end, [], Tasks) of
+ [] ->
+ error_logger:warning_msg("No times"),
+ 0.0;
+ Times ->
+ error_logger:info_msg("Times: ~p", [Times]),
+ lists:sum(Times) / length(Times)
+ after
+ wpool:stop_sup_pool(?MODULE)
+ end.
run_task(small, Strategy, Acc) ->
- {Time, {ok, 0}} =
- timer:tc(wpool, call, [?MODULE, {erlang, '+', [0, 0]}, Strategy, infinity]),
- [Time/1000|Acc];
+ {Time, {ok, 0}} =
+ timer:tc(wpool, call, [?MODULE, {erlang, '+', [0, 0]}, Strategy, infinity]),
+ [Time / 1000 | Acc];
run_task(large, Strategy, Acc) ->
- wpool:cast(?MODULE, {timer, sleep, [30000]}, Strategy),
- Acc.
+ wpool:cast(?MODULE, {timer, sleep, [30000]}, Strategy),
+ Acc.
diff --git a/test/wpool_meta_SUITE.erl b/test/wpool_meta_SUITE.erl
deleted file mode 100644
index 2860d05..0000000
--- a/test/wpool_meta_SUITE.erl
+++ /dev/null
@@ -1,27 +0,0 @@
--module(wpool_meta_SUITE).
-
--include_lib("mixer/include/mixer.hrl").
--mixin([{ktn_meta_SUITE, [dialyzer/1, elvis/1]}]).
-
--export([init_per_suite/1, end_per_suite/1]).
-
--type config() :: [{atom(), term()}].
-
--export([all/0]).
-
--spec all() -> [dialyzer | elvis].
-all() -> [dialyzer, elvis].
-
--spec init_per_suite(config()) -> config().
-init_per_suite(Config) ->
- [ {application, worker_pool}
- %% Until the next version of katana-test fixes the missing test deps in plt
- %% issue, we can't use the default warnings that include 'unknown' here.
- , { dialyzer_warnings
- , [error_handling, race_conditions, unmatched_returns, no_return]
- }
- | Config
- ].
-
--spec end_per_suite(config()) -> config().
-end_per_suite(Config) -> Config.
diff --git a/test/wpool_pool_SUITE.erl b/test/wpool_pool_SUITE.erl
index a0fb306..e5c1302 100644
--- a/test/wpool_pool_SUITE.erl
+++ b/test/wpool_pool_SUITE.erl
@@ -15,525 +15,533 @@
%% @hidden
-module(wpool_pool_SUITE).
+-behaviour(ct_suite).
+
-type config() :: [{atom(), term()}].
-define(WORKERS, 6).
--export([ all/0
- ]).
--export([ init_per_suite/1
- , end_per_suite/1
- , init_per_testcase/2
- , end_per_testcase/2
- ]).
--export([ stop_worker/1
- , best_worker/1
- , next_worker/1
- , random_worker/1
- , available_worker/1
- , hash_worker/1
- , custom_worker/1
- , next_available_worker/1
- , wpool_record/1
- , queue_type_fifo/1
- , queue_type_lifo/1
- ]).
--export([ manager_crash/1
- , super_fast/1
- , ets_mess_up/1
- ]).
+-export([all/0]).
+-export([init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2]).
+-export([stop_worker/1, best_worker/1, next_worker/1, random_worker/1, available_worker/1,
+ hash_worker/1, custom_worker/1, next_available_worker/1, wpool_record/1,
+ queue_type_fifo/1, queue_type_lifo/1]).
+-export([manager_crash/1, super_fast/1, ets_mess_up/1]).
-spec all() -> [atom()].
all() ->
- [Fun || {Fun, 1} <- module_info(exports),
- not lists:member( Fun
- , [ init_per_suite
- , end_per_suite
- , module_info
- ]
- )].
+ [Fun
+ || {Fun, 1} <- module_info(exports),
+ not lists:member(Fun, [init_per_suite, end_per_suite, module_info])].
-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
- ok = wpool:start(),
- Config.
+ ok = wpool:start(),
+ Config.
-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
- wpool:stop(),
- Config.
+ wpool:stop(),
+ Config.
-spec init_per_testcase(atom(), config()) -> config().
init_per_testcase(queue_type_lifo = TestCase, Config) ->
- {ok, _} = wpool:start_pool(TestCase, [
- {workers, 1}, {queue_type, lifo}]
- ),
- Config;
-
+ {ok, _} = wpool:start_pool(TestCase, [{workers, 1}, {queue_type, lifo}]),
+ Config;
init_per_testcase(queue_type_fifo = TestCase, Config) ->
- {ok, _} = wpool:start_pool(TestCase, [
- {workers, 1}, {queue_type, fifo}]
- ),
- Config;
-
+ {ok, _} = wpool:start_pool(TestCase, [{workers, 1}, {queue_type, fifo}]),
+ Config;
init_per_testcase(TestCase, Config) ->
- {ok, _} = wpool:start_pool(TestCase, [{workers, ?WORKERS}]),
- Config.
+ {ok, _} = wpool:start_pool(TestCase, [{workers, ?WORKERS}]),
+ Config.
-spec end_per_testcase(atom(), config()) -> config().
end_per_testcase(TestCase, Config) ->
- catch wpool:stop_sup_pool(TestCase),
- Config.
+ catch wpool:stop_sup_pool(TestCase),
+ Config.
-spec stop_worker(config()) -> {comment, []}.
stop_worker(_Config) ->
- true = (undefined /= wpool_pool:find_wpool(stop_worker)),
- true = wpool:stop_pool(stop_worker),
- undefined =
- ktn_task:wait_for(
- fun() -> wpool_pool:find_wpool(stop_worker) end, undefined),
- true = wpool:stop_pool(stop_worker),
- undefined = wpool_pool:find_wpool(stop_worker),
- {comment, ""}.
+ true = undefined /= wpool_pool:find_wpool(stop_worker),
+ true = wpool:stop_pool(stop_worker),
+ undefined = ktn_task:wait_for(fun() -> wpool_pool:find_wpool(stop_worker) end, undefined),
+ true = wpool:stop_pool(stop_worker),
+ undefined = wpool_pool:find_wpool(stop_worker),
+ {comment, ""}.
-spec available_worker(config()) -> {comment, []}.
available_worker(_Config) ->
- Pool = available_worker,
- try wpool:call(not_a_pool, x) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
+ Pool = available_worker,
+ try wpool:call(not_a_pool, x) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
+ end,
- ct:log(
- "Put them all to work, each request should go to a different worker"),
- [wpool:cast(Pool, {timer, sleep, [5000]}) || _ <- lists:seq(1, ?WORKERS)],
+ ct:log("Put them all to work, each request should go to a different worker"),
+ [wpool:cast(Pool, {timer, sleep, [5000]}) || _ <- lists:seq(1, ?WORKERS)],
- [0] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [0]),
+ [0] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [0]),
- ct:log(
- "Now send another round of messages,
+ ct:log("Now send another round of messages,
the workers queues should still be empty"),
- [wpool:cast(Pool, {timer, sleep, [100 * I]}) || I <- lists:seq(1, ?WORKERS)],
-
- % Check that we have ?WORKERS pending tasks
- ?WORKERS =
- ktn_task:wait_for(
- fun() ->
- Stats1 = wpool:stats(Pool),
- [0] =
- lists:usort(
- [proplists:get_value(message_queue_len, WS)
- || {_, WS} <- proplists:get_value(workers, Stats1)]),
- proplists:get_value(total_message_queue_len, Stats1)
- end, ?WORKERS),
-
- ct:log("If we can't wait we get no workers"),
- try wpool:call(Pool, {erlang, self, []}, available_worker, 100) of
- R -> should_fail = R
- catch
- _:Error -> timeout = Error
- end,
-
- ct:log("Let's wait until all workers are free"),
- wpool:call(Pool, {erlang, self, []}, available_worker, infinity),
-
- % Check we have no pending tasks
- Stats2 = wpool:stats(Pool),
- 0 = proplists:get_value(total_message_queue_len, Stats2),
-
- ct:log("Now they all should be free"),
- ct:log("We get half of them working for a while"),
- [wpool:cast(Pool, {timer, sleep, [60000]}) || _ <- lists:seq(1, ?WORKERS, 2)],
-
- % Check we have no pending tasks
- 0 =
- ktn_task:wait_for(
- fun() ->
- proplists:get_value(total_message_queue_len, wpool:stats(Pool))
- end, 0),
-
- ct:log(
- "We run tons of calls, and none is blocked,
+ [wpool:cast(Pool, {timer, sleep, [100 * I]}) || I <- lists:seq(1, ?WORKERS)],
+
+ % Check that we have ?WORKERS pending tasks
+ ?WORKERS =
+ ktn_task:wait_for(fun() ->
+ Stats1 = wpool:stats(Pool),
+ [0] =
+ lists:usort([proplists:get_value(message_queue_len, WS)
+ || {_, WS} <- proplists:get_value(workers, Stats1)]),
+ proplists:get_value(total_message_queue_len, Stats1)
+ end,
+ ?WORKERS),
+
+ ct:log("If we can't wait we get no workers"),
+ try wpool:call(Pool, {erlang, self, []}, available_worker, 100) of
+ R ->
+ should_fail = R
+ catch
+ _:Error ->
+ timeout = Error
+ end,
+
+ ct:log("Let's wait until all workers are free"),
+ wpool:call(Pool, {erlang, self, []}, available_worker, infinity),
+
+ % Check we have no pending tasks
+ Stats2 = wpool:stats(Pool),
+ 0 = proplists:get_value(total_message_queue_len, Stats2),
+
+ ct:log("Now they all should be free"),
+ ct:log("We get half of them working for a while"),
+ [wpool:cast(Pool, {timer, sleep, [60000]}) || _ <- lists:seq(1, ?WORKERS, 2)],
+
+ % Check we have no pending tasks
+ 0 =
+ ktn_task:wait_for(fun() -> proplists:get_value(total_message_queue_len, wpool:stats(Pool))
+ end,
+ 0),
+
+ ct:log("We run tons of calls, and none is blocked,
because all of them are handled by different workers"),
- Workers =
- [ wpool:call(Pool, {erlang, self, []}, available_worker, 5000)
- || _ <- lists:seq(1, 20 * ?WORKERS)],
- UniqueWorkers = sets:to_list(sets:from_list(Workers)),
- {?WORKERS, UniqueWorkers, true} =
- {?WORKERS, UniqueWorkers, (?WORKERS/2) >= length(UniqueWorkers)},
+ Workers =
+ [wpool:call(Pool, {erlang, self, []}, available_worker, 5000)
+ || _ <- lists:seq(1, 20 * ?WORKERS)],
+ UniqueWorkers =
+ sets:to_list(
+ sets:from_list(Workers)),
+ {?WORKERS, UniqueWorkers, true} =
+ {?WORKERS, UniqueWorkers, ?WORKERS / 2 >= length(UniqueWorkers)},
- {comment, []}.
+ {comment, []}.
-spec best_worker(config()) -> {comment, []}.
best_worker(_Config) ->
- Pool = best_worker,
- try wpool:call(not_a_pool, x, best_worker) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
+ Pool = best_worker,
+ try wpool:call(not_a_pool, x, best_worker) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
+ end,
- %% Fill up their message queues...
- [ wpool:cast(Pool, {timer, sleep, [60000]}, next_worker)
- || _ <- lists:seq(1, ?WORKERS)],
- [0] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [0]),
+ %% Fill up their message queues...
+ [wpool:cast(Pool, {timer, sleep, [60000]}, next_worker) || _ <- lists:seq(1, ?WORKERS)],
+ [0] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [0]),
- [ wpool:cast(Pool, {timer, sleep, [60000]}, best_worker)
- || _ <- lists:seq(1, ?WORKERS)],
+ [wpool:cast(Pool, {timer, sleep, [60000]}, best_worker) || _ <- lists:seq(1, ?WORKERS)],
- [1] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [1]),
+ [1] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [1]),
- %% Now try best worker once per worker
- [ wpool:cast(Pool, {timer, sleep, [60000]}, best_worker)
- || _ <- lists:seq(1, ?WORKERS)],
- %% The load should be evenly distributed...
- [2] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [2]),
+ %% Now try best worker once per worker
+ [wpool:cast(Pool, {timer, sleep, [60000]}, best_worker) || _ <- lists:seq(1, ?WORKERS)],
+ %% The load should be evenly distributed...
+ [2] = ktn_task:wait_for(fun() -> worker_msg_queue_lengths(Pool) end, [2]),
- {comment, []}.
+ {comment, []}.
-spec next_available_worker(config()) -> {comment, []}.
next_available_worker(_Config) ->
- Pool = next_available_worker,
- ct:log("not_a_pool is not a pool"),
- try wpool:call(not_a_pool, x, next_available_worker) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
-
- ct:log("Put them all to work..."),
- [ wpool:cast(Pool, {timer, sleep, [1500 + I]}, next_available_worker)
- || I <- lists:seq(0, (?WORKERS - 1) * 60000, 60000)],
-
- AvailableWorkers =
- fun() ->
- length(
- [a_worker
- || {_, WS} <- proplists:get_value(workers, wpool:stats(Pool))
- , proplists:get_value(task, WS) == undefined])
+ Pool = next_available_worker,
+ ct:log("not_a_pool is not a pool"),
+ try wpool:call(not_a_pool, x, next_available_worker) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
end,
- ct:log("All busy..."),
- 0 = ktn_task:wait_for(AvailableWorkers, 0),
-
- ct:log("No available workers..."),
- try wpool:cast(Pool, {timer, sleep, [60000]}, next_available_worker) of
- ok -> ct:fail("Exception expected")
- catch
- _:no_available_workers -> ok
- end,
+ ct:log("Put them all to work..."),
+ [wpool:cast(Pool, {timer, sleep, [1500 + I]}, next_available_worker)
+ || I <- lists:seq(0, (?WORKERS - 1) * 60000, 60000)],
+
+ AvailableWorkers =
+ fun() ->
+ length([a_worker
+ || {_, WS} <- proplists:get_value(workers, wpool:stats(Pool)),
+ proplists:get_value(task, WS) == undefined])
+ end,
+
+ ct:log("All busy..."),
+ 0 = ktn_task:wait_for(AvailableWorkers, 0),
+
+ ct:log("No available workers..."),
+ try wpool:cast(Pool, {timer, sleep, [60000]}, next_available_worker) of
+ ok ->
+ ct:fail("Exception expected")
+ catch
+ _:no_available_workers ->
+ ok
+ end,
- ct:log("Wait until the first frees up..."),
- 1 = ktn_task:wait_for(AvailableWorkers, 1),
+ ct:log("Wait until the first frees up..."),
+ 1 = ktn_task:wait_for(AvailableWorkers, 1),
- ok = wpool:cast(Pool, {timer, sleep, [60000]}, next_available_worker),
+ ok = wpool:cast(Pool, {timer, sleep, [60000]}, next_available_worker),
- ct:log("No more available workers..."),
- try wpool:cast(Pool, {timer, sleep, [60000]}, next_available_worker) of
- ok -> ct:fail("Exception expected")
- catch
- _:no_available_workers -> ok
- end,
+ ct:log("No more available workers..."),
+ try wpool:cast(Pool, {timer, sleep, [60000]}, next_available_worker) of
+ ok ->
+ ct:fail("Exception expected")
+ catch
+ _:no_available_workers ->
+ ok
+ end,
- {comment, []}.
+ {comment, []}.
-spec next_worker(config()) -> {comment, []}.
next_worker(_Config) ->
- Pool = next_worker,
-
- try wpool:call(not_a_pool, x, next_worker) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
-
- Res0 = [begin
- Stats = wpool:stats(Pool),
- I = proplists:get_value(next_worker, Stats),
- wpool:call(Pool, {erlang, self, []}, next_worker, infinity)
- end || I <- lists:seq(1, ?WORKERS)],
- ?WORKERS = sets:size(sets:from_list(Res0)),
- Res0 = [begin
- Stats = wpool:stats(Pool),
- I = proplists:get_value(next_worker, Stats),
- wpool:call(Pool, {erlang, self, []}, next_worker)
- end || I <- lists:seq(1, ?WORKERS)],
-
- {comment, []}.
+ Pool = next_worker,
+
+ try wpool:call(not_a_pool, x, next_worker) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
+ end,
+
+ Res0 =
+ [begin
+ Stats = wpool:stats(Pool),
+ I = proplists:get_value(next_worker, Stats),
+ wpool:call(Pool, {erlang, self, []}, next_worker, infinity)
+ end
+ || I <- lists:seq(1, ?WORKERS)],
+ ?WORKERS =
+ sets:size(
+ sets:from_list(Res0)),
+ Res0 =
+ [begin
+ Stats = wpool:stats(Pool),
+ I = proplists:get_value(next_worker, Stats),
+ wpool:call(Pool, {erlang, self, []}, next_worker)
+ end
+ || I <- lists:seq(1, ?WORKERS)],
+
+ {comment, []}.
-spec random_worker(config()) -> {comment, []}.
random_worker(_Config) ->
- Pool = random_worker,
-
- try wpool:call(not_a_pool, x, random_worker) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
-
- %% Ask for a random worker's identity 20x more than the number of workers
- %% and expect to get an answer from every worker at least once.
- Serial =
- [ wpool:call(Pool, {erlang, self, []}, random_worker)
- || _ <- lists:seq(1, 20 * ?WORKERS)],
- ?WORKERS = sets:size(sets:from_list(Serial)),
-
- %% Now do the same with a freshly spawned process for each request to ensure
- %% randomness isn't reset with each spawn of the process_dictionary
- Self = self(),
- _ = [spawn(fun() ->
- WorkerId = wpool:call(Pool, {erlang, self, []}, random_worker),
- Self ! {worker, WorkerId}
- end) || _ <- lists:seq(1, 20 * ?WORKERS)],
- Concurrent = collect_results(20 * ?WORKERS, []),
- ?WORKERS = sets:size(sets:from_list(Concurrent)),
-
- {comment, []}.
+ Pool = random_worker,
+
+ try wpool:call(not_a_pool, x, random_worker) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
+ end,
+
+ %% Ask for a random worker's identity 20x more than the number of workers
+ %% and expect to get an answer from every worker at least once.
+ Serial =
+ [wpool:call(Pool, {erlang, self, []}, random_worker) || _ <- lists:seq(1, 20 * ?WORKERS)],
+ ?WORKERS =
+ sets:size(
+ sets:from_list(Serial)),
+
+ %% Now do the same with a freshly spawned process for each request to ensure
+ %% randomness isn't reset with each spawn of the process_dictionary
+ Self = self(),
+ _ = [spawn(fun() ->
+ WorkerId = wpool:call(Pool, {erlang, self, []}, random_worker),
+ Self ! {worker, WorkerId}
+ end)
+ || _ <- lists:seq(1, 20 * ?WORKERS)],
+ Concurrent = collect_results(20 * ?WORKERS, []),
+ ?WORKERS =
+ sets:size(
+ sets:from_list(Concurrent)),
+
+ {comment, []}.
-spec hash_worker(config()) -> {comment, []}.
hash_worker(_Config) ->
- Pool = hash_worker,
-
- try wpool:call(not_a_pool, x, {hash_worker, 1}) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
-
- %% Use two hash keys that have different values (0, 1) to target only
- %% two workers. Other workers should be missing.
- Targeted =
- [ wpool:call(Pool, {erlang, self, []}, {hash_worker, I rem 2})
- || I <- lists:seq(1, 20 * ?WORKERS)],
- 2 = sets:size(sets:from_list(Targeted)),
+ Pool = hash_worker,
+
+ try wpool:call(not_a_pool, x, {hash_worker, 1}) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
+ end,
- %% Now use many different hash keys. All workers should be hit.
- Spread =
- [ wpool:call(Pool, {erlang, self, []}, {hash_worker, I})
+ %% Use two hash keys that have different values (0, 1) to target only
+ %% two workers. Other workers should be missing.
+ Targeted =
+ [wpool:call(Pool, {erlang, self, []}, {hash_worker, I rem 2})
+ || I <- lists:seq(1, 20 * ?WORKERS)],
+ 2 =
+ sets:size(
+ sets:from_list(Targeted)),
+
+ %% Now use many different hash keys. All workers should be hit.
+ Spread =
+ [wpool:call(Pool, {erlang, self, []}, {hash_worker, I})
+ || I <- lists:seq(1, 20 * ?WORKERS)],
+ ?WORKERS =
+ sets:size(
+ sets:from_list(Spread)),
+
+ %% Fill up their message queues...
+ [wpool:cast(Pool, {timer, sleep, [60000]}, {hash_worker, I})
|| I <- lists:seq(1, 20 * ?WORKERS)],
- ?WORKERS = sets:size(sets:from_list(Spread)),
- %% Fill up their message queues...
- [ wpool:cast(Pool, {timer, sleep, [60000]}, {hash_worker, I})
- || I <- lists:seq(1, 20 * ?WORKERS)],
+ false =
+ ktn_task:wait_for(fun() -> lists:member(0, worker_msg_queue_lengths(Pool)) end, false),
- false =
- ktn_task:wait_for(
- fun() -> lists:member(0, worker_msg_queue_lengths(Pool)) end, false),
-
- {comment, []}.
+ {comment, []}.
-spec custom_worker(config()) -> {comment, []}.
custom_worker(_Config) ->
- Pool = custom_worker,
-
- Strategy = fun wpool_pool:next_worker/1,
-
- try wpool:call(not_a_pool, x, Strategy) of
- Result -> no_result = Result
- catch
- _:no_workers -> ok
- end,
-
- _ =
- [ begin
- Stats = wpool:stats(Pool),
- I = proplists:get_value(next_worker, Stats),
- wpool:cast(Pool, {io, format, ["ok!"]}, Strategy)
- end || I <- lists:seq(1, ?WORKERS)],
-
- Res0 = [begin
- Stats = wpool:stats(Pool),
- I = proplists:get_value(next_worker, Stats),
- wpool:call(Pool, {erlang, self, []}, Strategy, infinity)
- end || I <- lists:seq(1, ?WORKERS)],
- ?WORKERS = sets:size(sets:from_list(Res0)),
- Res0 = [begin
- Stats = wpool:stats(Pool),
- I = proplists:get_value(next_worker, Stats),
- wpool:call(Pool, {erlang, self, []}, Strategy)
- end || I <- lists:seq(1, ?WORKERS)],
-
- {comment, []}.
+ Pool = custom_worker,
+
+ Strategy = fun wpool_pool:next_worker/1,
+
+ try wpool:call(not_a_pool, x, Strategy) of
+ Result ->
+ no_result = Result
+ catch
+ _:no_workers ->
+ ok
+ end,
+
+ _ = [begin
+ Stats = wpool:stats(Pool),
+ I = proplists:get_value(next_worker, Stats),
+ wpool:cast(Pool, {io, format, ["ok!"]}, Strategy)
+ end
+ || I <- lists:seq(1, ?WORKERS)],
+
+ Res0 =
+ [begin
+ Stats = wpool:stats(Pool),
+ I = proplists:get_value(next_worker, Stats),
+ wpool:call(Pool, {erlang, self, []}, Strategy, infinity)
+ end
+ || I <- lists:seq(1, ?WORKERS)],
+ ?WORKERS =
+ sets:size(
+ sets:from_list(Res0)),
+ Res0 =
+ [begin
+ Stats = wpool:stats(Pool),
+ I = proplists:get_value(next_worker, Stats),
+ wpool:call(Pool, {erlang, self, []}, Strategy)
+ end
+ || I <- lists:seq(1, ?WORKERS)],
+
+ {comment, []}.
-spec manager_crash(config()) -> {comment, []}.
manager_crash(_Config) ->
- Pool = manager_crash,
- QueueManager = 'wpool_pool-manager_crash-queue-manager',
+ Pool = manager_crash,
+ QueueManager = 'wpool_pool-manager_crash-queue-manager',
- ct:log("Check that the pool is working"),
- {ok, ok} = send_io_format(Pool),
+ ct:log("Check that the pool is working"),
+ {ok, ok} = send_io_format(Pool),
- OldPid = whereis(QueueManager),
+ OldPid = whereis(QueueManager),
- ct:log("Crash the pool manager"),
- exit(whereis(QueueManager), kill),
+ ct:log("Crash the pool manager"),
+ exit(whereis(QueueManager), kill),
- false =
- ktn_task:wait_for(
- fun() ->
- lists:member(whereis(QueueManager), [OldPid, undefined])
- end, false),
+ false =
+ ktn_task:wait_for(fun() -> lists:member(whereis(QueueManager), [OldPid, undefined]) end,
+ false),
- ct:log("Check that the pool is working again"),
- {ok, ok} = send_io_format(Pool),
+ ct:log("Check that the pool is working again"),
+ {ok, ok} = send_io_format(Pool),
- {comment, []}.
+ {comment, []}.
-spec super_fast(config()) -> {comment, []}.
super_fast(_Config) ->
- Pool = super_fast,
-
- ct:log("Check that the pool is working"),
- {ok, ok} = send_io_format(Pool),
-
- ct:log("Impossible task"),
- Self = self(),
- try wpool:call(
- Pool, {erlang, send, [Self, something]}, available_worker, 0) of
- R -> ct:fail("Unexpected ~p", [R])
- catch
- _:timeout -> ok
- end,
+ Pool = super_fast,
+
+ ct:log("Check that the pool is working"),
+ {ok, ok} = send_io_format(Pool),
+
+ ct:log("Impossible task"),
+ Self = self(),
+ try wpool:call(Pool, {erlang, send, [Self, something]}, available_worker, 0) of
+ R ->
+ ct:fail("Unexpected ~p", [R])
+ catch
+ _:timeout ->
+ ok
+ end,
- ct:log("Wait a second and nothing gets here"),
- receive
- X -> ct:fail("Unexpected ~p", [X])
- after 1000 ->
- ok
- end,
+ ct:log("Wait a second and nothing gets here"),
+ receive
+ X ->
+ ct:fail("Unexpected ~p", [X])
+ after 1000 ->
+ ok
+ end,
- {comment, []}.
+ {comment, []}.
-spec queue_type_fifo(config()) -> {comment, []}.
queue_type_fifo(_Config) ->
- Pool = queue_type_fifo,
- Self = self(),
- TasksNumber = 10,
- Tasks = lists:seq(1, TasksNumber),
+ Pool = queue_type_fifo,
+ Self = self(),
+ TasksNumber = 10,
+ Tasks = lists:seq(1, TasksNumber),
- ct:log("Pretend worker is busy"),
- wpool:cast(Pool, {timer, sleep, [timer:seconds(2)]}),
+ ct:log("Pretend worker is busy"),
+ wpool:cast(Pool, {timer, sleep, [timer:seconds(2)]}),
- ct:log(
- "Cast 10 enumerated tasks. Tasks should be queued because worker is busy."),
- cast_tasks(Pool, TasksNumber, Self),
+ ct:log("Cast 10 enumerated tasks. Tasks should be queued because worker is busy."),
+ cast_tasks(Pool, TasksNumber, Self),
- ct:log("Collect task results"),
- Result = collect_tasks(TasksNumber),
+ ct:log("Collect task results"),
+ Result = collect_tasks(TasksNumber),
- ct:log("Check if tasks were performd in FIFO order."),
- Result = Tasks,
+ ct:log("Check if tasks were performd in FIFO order."),
+ Result = Tasks,
- {comment, []}.
+ {comment, []}.
-spec queue_type_lifo(config()) -> {comment, []}.
queue_type_lifo(_Config) ->
- Pool = queue_type_lifo,
- Self = self(),
- TasksNumber = 10,
- Tasks = lists:seq(1, TasksNumber),
+ Pool = queue_type_lifo,
+ Self = self(),
+ TasksNumber = 10,
+ Tasks = lists:seq(1, TasksNumber),
- ct:log("Pretend worker is busy"),
- wpool:cast(Pool, {timer, sleep, [timer:seconds(4)]}),
+ ct:log("Pretend worker is busy"),
+ wpool:cast(Pool, {timer, sleep, [timer:seconds(4)]}),
- ct:log(
- "Cast 10 enumerated tasks. Tasks should be queued because worker is busy."),
- cast_tasks(Pool, TasksNumber, Self),
+ ct:log("Cast 10 enumerated tasks. Tasks should be queued because worker is busy."),
+ cast_tasks(Pool, TasksNumber, Self),
- ct:log("Collect task results"),
- Result = collect_tasks(TasksNumber),
+ ct:log("Collect task results"),
+ Result = collect_tasks(TasksNumber),
- ct:log("Check if tasks were performd in LIFO order."),
- Result = lists:reverse(Tasks),
+ ct:log("Check if tasks were performd in LIFO order."),
+ Result = lists:reverse(Tasks),
- {comment, []}.
+ {comment, []}.
-spec wpool_record(config()) -> {comment, []}.
wpool_record(_Config) ->
- WPool = wpool_pool:find_wpool(wpool_record),
- wpool_record = wpool_pool:wpool_get(name, WPool),
- 6 = wpool_pool:wpool_get(size, WPool),
- [_, _, _, _] = wpool_pool:wpool_get([next, opts, qmanager, born], WPool),
-
- WPool2 = wpool_pool:next(3, WPool),
- 3 = wpool_pool:wpool_get(next, WPool2),
+ WPool = wpool_pool:find_wpool(wpool_record),
+ wpool_record = wpool_pool:wpool_get(name, WPool),
+ 6 = wpool_pool:wpool_get(size, WPool),
+ [_, _, _, _] = wpool_pool:wpool_get([next, opts, qmanager, born], WPool),
- {comment, []}.
+ WPool2 = wpool_pool:next(3, WPool),
+ 3 = wpool_pool:wpool_get(next, WPool2),
+ {comment, []}.
-spec ets_mess_up(config()) -> {comment, []}.
ets_mess_up(_Config) ->
- Pool = ets_mess_up,
-
- ct:comment("Mess up with ets table..."),
- true = ets:delete(wpool_pool, Pool),
-
- ct:comment("Rebuild stats"),
- 1 = proplists:get_value(next_worker, wpool:stats(Pool)),
-
- ct:comment("Mess up with ets table again..."),
- true = ets:delete(wpool_pool, Pool),
- {ok, ok} = wpool:call(Pool, {io, format, ["1!~n"]}, random_worker),
-
- ct:comment("Mess up with ets table once more..."),
- {ok, ok} = wpool:call(Pool, {io, format, ["2!~n"]}, next_worker),
- 2 = proplists:get_value(next_worker, wpool:stats(Pool)),
- true = ets:delete(wpool_pool, Pool),
- {ok, ok} = wpool:call(Pool, {io, format, ["3!~n"]}, next_worker),
- 1 = proplists:get_value(next_worker, wpool:stats(Pool)),
-
- ct:comment("Mess up with ets table one final time..."),
- true = ets:delete(wpool_pool, Pool),
- _ = wpool_pool:find_wpool(Pool),
-
- ct:comment("Now, delete the pool"),
- Flag = process_flag(trap_exit, true),
- exit(whereis(Pool), kill),
- ok =
- ktn_task:wait_for(
- fun() ->
- try wpool:call(Pool, {io, format, ["1!~n"]}, random_worker) of
- X -> {unexpected, X}
- catch
- _:no_workers -> ok
- end
- end, ok),
-
- true = process_flag(trap_exit, Flag),
-
- ct:comment("And now delete the ets table altogether"),
- true = ets:delete(wpool_pool),
- _ = wpool_pool:find_wpool(Pool),
-
- wpool:stop(),
- ok = wpool:start(),
-
- {comment, []}.
+ Pool = ets_mess_up,
+
+ ct:comment("Mess up with ets table..."),
+ true = ets:delete(wpool_pool, Pool),
+
+ ct:comment("Rebuild stats"),
+ 1 = proplists:get_value(next_worker, wpool:stats(Pool)),
+
+ ct:comment("Mess up with ets table again..."),
+ true = ets:delete(wpool_pool, Pool),
+ {ok, ok} = wpool:call(Pool, {io, format, ["1!~n"]}, random_worker),
+
+ ct:comment("Mess up with ets table once more..."),
+ {ok, ok} = wpool:call(Pool, {io, format, ["2!~n"]}, next_worker),
+ 2 = proplists:get_value(next_worker, wpool:stats(Pool)),
+ true = ets:delete(wpool_pool, Pool),
+ {ok, ok} = wpool:call(Pool, {io, format, ["3!~n"]}, next_worker),
+ 1 = proplists:get_value(next_worker, wpool:stats(Pool)),
+
+ ct:comment("Mess up with ets table one final time..."),
+ true = ets:delete(wpool_pool, Pool),
+ _ = wpool_pool:find_wpool(Pool),
+
+ ct:comment("Now, delete the pool"),
+ Flag = process_flag(trap_exit, true),
+ exit(whereis(Pool), kill),
+ ok =
+ ktn_task:wait_for(fun() ->
+ try wpool:call(Pool, {io, format, ["1!~n"]}, random_worker) of
+ X ->
+ {unexpected, X}
+ catch
+ _:no_workers ->
+ ok
+ end
+ end,
+ ok),
+
+ true = process_flag(trap_exit, Flag),
+
+ ct:comment("And now delete the ets table altogether"),
+ true = ets:delete(wpool_pool),
+ _ = wpool_pool:find_wpool(Pool),
+
+ wpool:stop(),
+ ok = wpool:start(),
+
+ {comment, []}.
cast_tasks(Pool, TasksNumber, ReplyTo) ->
- lists:foreach(fun(N) ->
- wpool:cast(Pool, {erlang, send, [ReplyTo, {task, N}]})
- end, lists:seq(1, TasksNumber)).
+ lists:foreach(fun(N) -> wpool:cast(Pool, {erlang, send, [ReplyTo, {task, N}]}) end,
+ lists:seq(1, TasksNumber)).
collect_tasks(TasksNumber) ->
- lists:map(fun(_) ->
- receive
- {task, N} -> N
- end
- end, lists:seq(1, TasksNumber)).
-
-collect_results(0, Results) -> Results;
+ lists:map(fun(_) ->
+ receive
+ {task, N} ->
+ N
+ end
+ end,
+ lists:seq(1, TasksNumber)).
+
+collect_results(0, Results) ->
+ Results;
collect_results(N, Results) ->
- receive {worker, WorkerId} -> collect_results(N-1, [WorkerId | Results])
- after 100 -> timeout
- end.
+ receive
+ {worker, WorkerId} ->
+ collect_results(N - 1, [WorkerId | Results])
+ after 100 ->
+ timeout
+ end.
send_io_format(Pool) ->
- {ok, ok} = wpool:call(Pool, {io, format, ["ok!~n"]}, available_worker).
+ {ok, ok} = wpool:call(Pool, {io, format, ["ok!~n"]}, available_worker).
worker_msg_queue_lengths(Pool) ->
- lists:usort(
- [proplists:get_value(message_queue_len, WS)
- || {_, WS} <- proplists:get_value(workers, wpool:stats(Pool))]).
+ lists:usort([proplists:get_value(message_queue_len, WS)
+ || {_, WS} <- proplists:get_value(workers, wpool:stats(Pool))]).
diff --git a/test/wpool_process_SUITE.erl b/test/wpool_process_SUITE.erl
index d13209e..e0c066d 100644
--- a/test/wpool_process_SUITE.erl
+++ b/test/wpool_process_SUITE.erl
@@ -15,308 +15,293 @@
%% @hidden
-module(wpool_process_SUITE).
--type config() :: [{atom(), term()}].
+-behaviour(ct_suite).
--export([ all/0
- ]).
--export([ init_per_suite/1
- , end_per_suite/1
- , init_per_testcase/2
- , end_per_testcase/2
- ]).
--export([ init/1
- , init_timeout/1
- , info/1
- , cast/1
- , call/1
- , continue/1
- , format_status/1
- , no_format_status/1
- , stop/1
- ]).
--export([ pool_restart_crash/1
- , pool_norestart_crash/1
- , complete_coverage/1
- ]).
+-type config() :: [{atom(), term()}].
+-export([all/0]).
+-export([init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2]).
+-export([init/1, init_timeout/1, info/1, cast/1, call/1, continue/1, format_status/1,
+ no_format_status/1, stop/1]).
+-export([pool_restart_crash/1, pool_norestart_crash/1, complete_coverage/1]).
-spec all() -> [atom()].
all() ->
- [Fun || {Fun, 1} <- module_info(exports),
- not lists:member(Fun, [init_per_suite, end_per_suite, module_info])].
+ [Fun
+ || {Fun, 1} <- module_info(exports),
+ not lists:member(Fun, [init_per_suite, end_per_suite, module_info])].
-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
- ok = wpool:start(),
- Config.
+ ok = wpool:start(),
+ Config.
-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
- wpool:stop(),
- Config.
+ wpool:stop(),
+ Config.
-spec init_per_testcase(atom(), config()) -> config().
init_per_testcase(_TestCase, Config) ->
- process_flag(trap_exit, true),
- Config.
+ process_flag(trap_exit, true),
+ Config.
-spec end_per_testcase(atom(), config()) -> config().
end_per_testcase(_TestCase, Config) ->
- process_flag(trap_exit, false),
- receive after 0 -> ok end,
- Config.
+ process_flag(trap_exit, false),
+ receive after 0 ->
+ ok
+ end,
+ Config.
-spec init(config()) -> {comment, []}.
init(_Config) ->
- {error, can_not_ignore} =
- wpool_process:start_link(?MODULE, echo_server, ignore, []),
- {error, ?MODULE} =
- wpool_process:start_link(?MODULE, echo_server, {stop, ?MODULE}, []),
- {ok, _Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
- wpool_process:cast(?MODULE, {stop, normal, state}),
+ {error, can_not_ignore} = wpool_process:start_link(?MODULE, echo_server, ignore, []),
+ {error, ?MODULE} = wpool_process:start_link(?MODULE, echo_server, {stop, ?MODULE}, []),
+ {ok, _Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
+ wpool_process:cast(?MODULE, {stop, normal, state}),
- {comment, []}.
+ {comment, []}.
-spec init_timeout(config()) -> {comment, []}.
init_timeout(_Config) ->
- {ok, Pid} =
- wpool_process:start_link(?MODULE, echo_server, {ok, state, 0}, []),
- timeout = get_state(?MODULE),
- Pid ! {stop, normal, state},
- false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+ {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state, 0}, []),
+ timeout = get_state(?MODULE),
+ Pid ! {stop, normal, state},
+ false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
- {comment, []}.
+ {comment, []}.
-spec info(config()) -> {comment, []}.
info(_Config) ->
- {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
- Pid ! {noreply, newstate},
- newstate = get_state(?MODULE),
- Pid ! {noreply, newerstate, 1},
- timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
- Pid ! {stop, normal, state},
- false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+ {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
+ Pid ! {noreply, newstate},
+ newstate = get_state(?MODULE),
+ Pid ! {noreply, newerstate, 1},
+ timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
+ Pid ! {stop, normal, state},
+ false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
- {comment, []}.
+ {comment, []}.
-spec cast(config()) -> {comment, []}.
cast(_Config) ->
- {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
- wpool_process:cast(Pid, {noreply, newstate}),
- newstate = get_state(?MODULE),
- wpool_process:cast(Pid, {noreply, newerstate, 0}),
- timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
- wpool_process:cast(Pid, {stop, normal, state}),
- false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+ {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
+ wpool_process:cast(Pid, {noreply, newstate}),
+ newstate = get_state(?MODULE),
+ wpool_process:cast(Pid, {noreply, newerstate, 0}),
+ timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
+ wpool_process:cast(Pid, {stop, normal, state}),
+ false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
- {comment, []}.
+ {comment, []}.
-spec continue(config()) -> {comment, []}.
continue(_Config) ->
- C = fun(ContinueState) -> {noreply, ContinueState} end,
- %% init/1 returns {continue, continue_state}
- {ok, Pid} =
- wpool_process:start_link(
- ?MODULE, echo_server, {ok, state, {continue, C(continue_state)}}, []),
- continue_state = get_state(Pid),
-
- %% handle_call/3 returns {continue, ...}
- ok =
- wpool_process:call(
- Pid, {reply, ok, state, {continue, C(continue_state_2)}}, 5000),
- continue_state_2 = get_state(Pid),
- try wpool_process:call(
- Pid, {noreply, state, {continue, C(continue_state_3)}}, 100) of
- Result -> ct:fail("Unexpected Result: ~p", [Result])
- catch
- _:{timeout, _} ->
- continue_state_3 = get_state(Pid)
- end,
-
- %% handle_cast/2 returns {continue, ...}
- wpool_process:cast(Pid, {noreply, state, {continue, C(continue_state_4)}}),
- continue_state_4 = get_state(Pid),
-
- %% handle_continue/2 returns {continue, ...}
- SecondContinueResponse = C(continue_state_5),
- FirstContinueResponse =
- {noreply, another_state, {continue, SecondContinueResponse}},
- CastResponse = {noreply, state, {continue, FirstContinueResponse}},
- wpool_process:cast(Pid, CastResponse),
- continue_state_5 = get_state(Pid),
-
- %% handle_info/2 returns {continue, ...}
- Pid ! {noreply, state, {continue, C(continue_state_6)}},
- continue_state_6 = get_state(Pid),
-
- %% handle_continue/2 returns {continue, ...}
- SecondContinueResponse = C(continue_state_5),
- FirstContinueResponse =
- {noreply, another_state, {continue, SecondContinueResponse}},
- CastResponse = {noreply, state, {continue, FirstContinueResponse}},
- wpool_process:cast(Pid, CastResponse),
- continue_state_5 = get_state(Pid),
-
- %% handle_continue/2 returns timeout = 0
- wpool_process:cast(
- Pid, {noreply, state, {continue, {noreply, continue_state_7, 0}}}),
- timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
-
- %% handle_continue/2 returns {stop, normal, state}
- wpool_process:cast(Pid, {noreply, state, {continue, {stop, normal, state}}}),
- false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
-
- {comment, []}.
+ C = fun(ContinueState) -> {noreply, ContinueState} end,
+ %% init/1 returns {continue, continue_state}
+ {ok, Pid} =
+ wpool_process:start_link(?MODULE,
+ echo_server,
+ {ok, state, {continue, C(continue_state)}},
+ []),
+ continue_state = get_state(Pid),
+
+ %% handle_call/3 returns {continue, ...}
+ ok = wpool_process:call(Pid, {reply, ok, state, {continue, C(continue_state_b)}}, 5000),
+ continue_state_b = get_state(Pid),
+ try wpool_process:call(Pid, {noreply, state, {continue, C(continue_state_c)}}, 100) of
+ Result ->
+ ct:fail("Unexpected Result: ~p", [Result])
+ catch
+ _:{timeout, _} ->
+ continue_state_c = get_state(Pid)
+ end,
+
+ %% handle_cast/2 returns {continue, ...}
+ wpool_process:cast(Pid, {noreply, state, {continue, C(continue_state_d)}}),
+ continue_state_d = get_state(Pid),
+
+ %% handle_continue/2 returns {continue, ...}
+ SecondContinueResponse = C(continue_state_e),
+ FirstContinueResponse = {noreply, another_state, {continue, SecondContinueResponse}},
+ CastResponse = {noreply, state, {continue, FirstContinueResponse}},
+ wpool_process:cast(Pid, CastResponse),
+ continue_state_e = get_state(Pid),
+
+ %% handle_info/2 returns {continue, ...}
+ Pid ! {noreply, state, {continue, C(continue_state_f)}},
+ continue_state_f = get_state(Pid),
+
+ %% handle_continue/2 returns {continue, ...}
+ SecondContinueResponse = C(continue_state_e),
+ FirstContinueResponse = {noreply, another_state, {continue, SecondContinueResponse}},
+ CastResponse = {noreply, state, {continue, FirstContinueResponse}},
+ wpool_process:cast(Pid, CastResponse),
+ continue_state_e = get_state(Pid),
+
+ %% handle_continue/2 returns timeout = 0
+ wpool_process:cast(Pid, {noreply, state, {continue, {noreply, continue_state_g, 0}}}),
+ timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
+
+ %% handle_continue/2 returns {stop, normal, state}
+ wpool_process:cast(Pid, {noreply, state, {continue, {stop, normal, state}}}),
+ false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+
+ {comment, []}.
-spec format_status(config()) -> {comment, []}.
format_status(_Config) ->
- %% echo_server implements format_status/2
- {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
- %% therefore it returns {formatted_state, State} as its status
- {status, Pid, {module, gen_server}, SItems} = sys:get_status(Pid),
- [state] =
- [S || SItemList = [_|_] <- SItems, {formatted_state, S} <- SItemList],
- %% this code is actually what we use to retrieve the state in other tests
- state = get_state(Pid),
- {comment, []}.
+ %% echo_server implements format_status/2
+ {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
+ %% therefore it returns {formatted_state, State} as its status
+ {status, Pid, {module, gen_server}, SItems} = sys:get_status(Pid),
+ [state] = [S || SItemList = [_ | _] <- SItems, {formatted_state, S} <- SItemList],
+ %% this code is actually what we use to retrieve the state in other tests
+ state = get_state(Pid),
+ {comment, []}.
-spec no_format_status(config()) -> {comment, []}.
no_format_status(_Config) ->
- %% crashy_server doesn't implement format_status/2
- {ok, Pid} = wpool_process:start_link(?MODULE, crashy_server, state, []),
- %% therefore it uses the default format for the stauts (but with the status of
- %% the gen_server, not wpool_process)
- {status, Pid, {module, gen_server}, SItems} = sys:get_status(Pid),
- [state] =
- [S || SItemList = [_|_] <- SItems
- , {data, Data} <- SItemList
- , {"State", S} <- Data
- ],
- {comment, []}.
+ %% crashy_server doesn't implement format_status/2
+ {ok, Pid} = wpool_process:start_link(?MODULE, crashy_server, state, []),
+ %% therefore it uses the default format for the stauts (but with the status of
+ %% the gen_server, not wpool_process)
+ {status, Pid, {module, gen_server}, SItems} = sys:get_status(Pid),
+ [state] =
+ [S || SItemList = [_ | _] <- SItems, {data, Data} <- SItemList, {"State", S} <- Data],
+ {comment, []}.
-spec call(config()) -> {comment, []}.
call(_Config) ->
- {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
- ok1 = wpool_process:call(Pid, {reply, ok1, newstate}, 5000),
- newstate = get_state(?MODULE),
- ok2 = wpool_process:call(Pid, {reply, ok2, newerstate, 1}, 5000),
- timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
- ok3 = wpool_process:call(Pid, {stop, normal, ok3, state}, 5000),
- false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+ {ok, Pid} = wpool_process:start_link(?MODULE, echo_server, {ok, state}, []),
+ ok1 = wpool_process:call(Pid, {reply, ok1, newstate}, 5000),
+ newstate = get_state(?MODULE),
+ ok2 = wpool_process:call(Pid, {reply, ok2, newerstate, 1}, 5000),
+ timeout = ktn_task:wait_for(fun() -> get_state(?MODULE) end, timeout),
+ ok3 = wpool_process:call(Pid, {stop, normal, ok3, state}, 5000),
+ false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
- {comment, []}.
+ {comment, []}.
-spec pool_restart_crash(config()) -> {comment, []}.
pool_restart_crash(_Config) ->
- Pool = pool_restart_crash,
- PoolOptions = [{workers, 2}, {worker, {crashy_server, []}}],
- {ok, Pid} = wpool:start_pool(Pool, PoolOptions),
- ct:log("Check that the pool is working"),
- true = erlang:is_process_alive(Pid),
- hello = wpool:call(Pool, hello),
+ Pool = pool_restart_crash,
+ PoolOptions = [{workers, 2}, {worker, {crashy_server, []}}],
+ {ok, Pid} = wpool:start_pool(Pool, PoolOptions),
+ ct:log("Check that the pool is working"),
+ true = erlang:is_process_alive(Pid),
+ hello = wpool:call(Pool, hello),
- ct:log("Crash a worker"),
- wpool:cast(Pool, crash),
+ ct:log("Crash a worker"),
+ wpool:cast(Pool, crash),
- ct:log("Check that the pool wouldn't crash"),
- wpool:cast(Pool, crash, best_worker),
+ ct:log("Check that the pool wouldn't crash"),
+ wpool:cast(Pool, crash, best_worker),
- ct:log("Check that the pool didn't die"),
- {error, {timeout, {badmatch, true}}} =
- ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
- hello = wpool:call(Pool, hello),
+ ct:log("Check that the pool didn't die"),
+ {error, {timeout, {badmatch, true}}} =
+ ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+ hello = wpool:call(Pool, hello),
- {comment, []}.
+ {comment, []}.
-spec pool_norestart_crash(config()) -> {comment, []}.
pool_norestart_crash(_Config) ->
- Pool = pool_norestart_crash,
- PoolOptions = [ {workers, 2}
- , {worker, {crashy_server, []}}
- , {strategy, {one_for_all, 0, 10}}
- , {pool_sup_intensity, 0}
- , {pool_sup_period, 10}
- ],
- {ok, Pid} = wpool:start_pool(Pool, PoolOptions),
+ Pool = pool_norestart_crash,
+ PoolOptions =
+ [{workers, 2},
+ {worker, {crashy_server, []}},
+ {strategy, {one_for_all, 0, 10}},
+ {pool_sup_intensity, 0},
+ {pool_sup_period, 10}],
+ {ok, Pid} = wpool:start_pool(Pool, PoolOptions),
- ct:log("Check that the pool is working"),
- true = erlang:is_process_alive(Pid),
- hello = wpool:call(Pool, hello),
+ ct:log("Check that the pool is working"),
+ true = erlang:is_process_alive(Pid),
+ hello = wpool:call(Pool, hello),
- ct:log("Crash a worker"),
- wpool:cast(Pool, crash),
+ ct:log("Crash a worker"),
+ wpool:cast(Pool, crash),
- ct:log("Check that the pool is not working"),
- false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
+ ct:log("Check that the pool is not working"),
+ false = ktn_task:wait_for(fun() -> erlang:is_process_alive(Pid) end, false),
- {comment, []}.
+ {comment, []}.
-spec stop(config()) -> {comment, []}.
stop(_Config) ->
- From = {self(), Ref = make_ref()},
-
- ct:comment("cast_call with stop/reply"),
- {ok, Pid1} = wpool_process:start_link(stopper, echo_server, {ok, state}, []),
- ok = wpool_process:cast_call(stopper, From, {stop, reason, response, state}),
- receive
- {Ref, response} -> ok
- after 5000 ->
- ct:fail("no response")
- end,
- receive
- {'EXIT', Pid1, reason} -> ok
- after 500 ->
- ct:fail("Missing exit signal")
- end,
-
- ct:comment("cast_call with regular stop"),
- {ok, Pid2} = wpool_process:start_link(stopper, echo_server, {ok, state}, []),
- ok = wpool_process:cast_call(stopper, From, {stop, reason, state}),
- receive
- {Ref, _} -> ct:fail("unexpected response");
- {'EXIT', Pid2, reason} -> ok
- after 500 ->
- ct:fail("Missing exit signal")
- end,
-
- ct:comment("call with regular stop"),
- {ok, Pid3} = wpool_process:start_link(stopper, echo_server, {ok, state}, []),
- try wpool_process:call(stopper, {noreply, state}, 100) of
- _ -> ct:fail("unexpected response")
- catch
- _:{timeout, _} -> ok
- end,
- receive
- {'EXIT', Pid3, _} -> ct:fail("Unexpected process crash")
- after 500 ->
- ok
- end,
-
- ct:comment("call with timeout stop"),
- try wpool_process:call(stopper, {noreply, state, hibernate}, 100) of
- _ -> ct:fail("unexpected response")
- catch
- _:{timeout, _} -> ok
- end,
- receive
- {'EXIT', Pid3, _} -> ct:fail("Unexpected process crash")
- after 500 ->
- ok
- end,
-
- {comment, []}.
-
+ From = {self(), Ref = make_ref()},
+
+ ct:comment("cast_call with stop/reply"),
+ {ok, Pid1} = wpool_process:start_link(stopper, echo_server, {ok, state}, []),
+ ok = wpool_process:cast_call(stopper, From, {stop, reason, response, state}),
+ receive
+ {Ref, response} ->
+ ok
+ after 5000 ->
+ ct:fail("no response")
+ end,
+ receive
+ {'EXIT', Pid1, reason} ->
+ ok
+ after 500 ->
+ ct:fail("Missing exit signal")
+ end,
+
+ ct:comment("cast_call with regular stop"),
+ {ok, Pid2} = wpool_process:start_link(stopper, echo_server, {ok, state}, []),
+ ok = wpool_process:cast_call(stopper, From, {stop, reason, state}),
+ receive
+ {Ref, _} ->
+ ct:fail("unexpected response");
+ {'EXIT', Pid2, reason} ->
+ ok
+ after 500 ->
+ ct:fail("Missing exit signal")
+ end,
+
+ ct:comment("call with regular stop"),
+ {ok, Pid3} = wpool_process:start_link(stopper, echo_server, {ok, state}, []),
+ try wpool_process:call(stopper, {noreply, state}, 100) of
+ _ ->
+ ct:fail("unexpected response")
+ catch
+ _:{timeout, _} ->
+ ok
+ end,
+ receive
+ {'EXIT', Pid3, _} ->
+ ct:fail("Unexpected process crash")
+ after 500 ->
+ ok
+ end,
+
+ ct:comment("call with timeout stop"),
+ try wpool_process:call(stopper, {noreply, state, hibernate}, 100) of
+ _ ->
+ ct:fail("unexpected response")
+ catch
+ _:{timeout, _} ->
+ ok
+ end,
+ receive
+ {'EXIT', Pid3, _} ->
+ ct:fail("Unexpected process crash")
+ after 500 ->
+ ok
+ end,
+
+ {comment, []}.
-spec complete_coverage(config()) -> {comment, []}.
complete_coverage(_Config) ->
- ct:comment("Code Change"),
- {ok, State} =
- wpool_process:init({complete_coverage, echo_server, {ok, state}, []}),
- {ok, _} = wpool_process:code_change("oldvsn", State, {ok, state}),
- {error, bad} = wpool_process:code_change("oldvsn", State, bad),
-
- {comment, []}.
+ ct:comment("Code Change"),
+ {ok, State} = wpool_process:init({complete_coverage, echo_server, {ok, state}, []}),
+ {ok, _} = wpool_process:code_change("oldvsn", State, {ok, state}),
+ {error, bad} = wpool_process:code_change("oldvsn", State, bad),
+ {comment, []}.
%% @doc We can use this function in tests since echo_server implements
%% format_status/2 by returning the state as a tuple {formatted_state, S}.
@@ -324,9 +309,8 @@ complete_coverage(_Config) ->
%% @see gen_server:format_status/2
%% @see sys:get_status/2
get_state(Atom) when is_atom(Atom) ->
- get_state(whereis(Atom));
+ get_state(whereis(Atom));
get_state(Pid) ->
- {status, Pid, {module, gen_server}, SItems} = sys:get_status(Pid),
- [State] =
- [S || SItemList = [_|_] <- SItems, {formatted_state, S} <- SItemList],
- State.
+ {status, Pid, {module, gen_server}, SItems} = sys:get_status(Pid),
+ [State] = [S || SItemList = [_ | _] <- SItems, {formatted_state, S} <- SItemList],
+ State.
diff --git a/test/wpool_process_callbacks_SUITE.erl b/test/wpool_process_callbacks_SUITE.erl
index 8a12959..6f0a872 100644
--- a/test/wpool_process_callbacks_SUITE.erl
+++ b/test/wpool_process_callbacks_SUITE.erl
@@ -1,187 +1,178 @@
-module(wpool_process_callbacks_SUITE).
+-behaviour(ct_suite).
+
-type config() :: [{atom(), term()}].
--export([ all/0
- ]).
--export([ init_per_suite/1
- , end_per_suite/1
- ]).
--export([ complete_callback_passed_when_starting_pool/1
- , partial_callback_passed_when_starting_pool/1
- , callback_can_be_added_and_removed_after_pool_is_started/1
- , crashing_callback_does_not_affect_others/1
- , non_existsing_module_does_not_affect_others/1
- , complete_coverage/1
- ]).
+-export([all/0]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([complete_callback_passed_when_starting_pool/1,
+ partial_callback_passed_when_starting_pool/1,
+ callback_can_be_added_and_removed_after_pool_is_started/1,
+ crashing_callback_does_not_affect_others/1, non_existsing_module_does_not_affect_others/1,
+ complete_coverage/1]).
-spec all() -> [atom()].
all() ->
- [ complete_callback_passed_when_starting_pool
- , partial_callback_passed_when_starting_pool
- , callback_can_be_added_and_removed_after_pool_is_started
- , crashing_callback_does_not_affect_others
- , non_existsing_module_does_not_affect_others
- ].
+ [complete_callback_passed_when_starting_pool,
+ partial_callback_passed_when_starting_pool,
+ callback_can_be_added_and_removed_after_pool_is_started,
+ crashing_callback_does_not_affect_others,
+ non_existsing_module_does_not_affect_others].
-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
- ok = wpool:start(),
- Config.
+ ok = wpool:start(),
+ Config.
-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
- wpool:stop(),
- Config.
-
+ wpool:stop(),
+ Config.
-spec complete_callback_passed_when_starting_pool(config()) -> ok.
complete_callback_passed_when_starting_pool(_Config) ->
- Pool = callbacks_test,
- WorkersCount = 13,
- meck:new(callbacks, [non_strict]),
- meck:expect(callbacks, handle_init_start, fun(_AWorkerName) -> ok end),
- meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
- meck:expect(callbacks, handle_worker_death, fun(_AWName, _Reason) -> ok end),
- {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount},
- {enable_callbacks, true},
- {worker, {crashy_server, []}},
- {callbacks, [callbacks]}]),
-
- WorkersCount = ktn_task:wait_for(function_calls(callbacks, handle_init_start,
- ['_']), WorkersCount),
- WorkersCount = ktn_task:wait_for(function_calls(callbacks,
- handle_worker_creation,
- ['_']), WorkersCount),
- Worker = wpool_pool:random_worker(Pool),
- Worker ! crash,
- 1 = ktn_task:wait_for(function_calls(callbacks, handle_worker_death,
- ['_', '_']), 1),
- wpool:stop_pool(Pool),
- meck:unload(callbacks),
-
- ok.
+ Pool = callbacks_test,
+ WorkersCount = 13,
+ meck:new(callbacks, [non_strict]),
+ meck:expect(callbacks, handle_init_start, fun(_AWorkerName) -> ok end),
+ meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
+ meck:expect(callbacks, handle_worker_death, fun(_AWName, _Reason) -> ok end),
+ {ok, _Pid} =
+ wpool:start_pool(Pool,
+ [{workers, WorkersCount},
+ {enable_callbacks, true},
+ {worker, {crashy_server, []}},
+ {callbacks, [callbacks]}]),
+
+ WorkersCount =
+ ktn_task:wait_for(function_calls(callbacks, handle_init_start, ['_']), WorkersCount),
+ WorkersCount =
+ ktn_task:wait_for(function_calls(callbacks, handle_worker_creation, ['_']), WorkersCount),
+ Worker = wpool_pool:random_worker(Pool),
+ Worker ! crash,
+ 1 = ktn_task:wait_for(function_calls(callbacks, handle_worker_death, ['_', '_']), 1),
+ wpool:stop_pool(Pool),
+ meck:unload(callbacks),
+
+ ok.
-spec partial_callback_passed_when_starting_pool(config) -> ok.
partial_callback_passed_when_starting_pool(_Config) ->
- Pool = partial_callbacks_test,
- WorkersCount = 7,
- meck:new(callbacks, [non_strict]),
- meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
- meck:expect(callbacks, handle_worker_death, fun(_AWName, _Reason) -> ok end),
- {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount},
- {enable_callbacks, true},
- {callbacks, [callbacks]}]),
- WorkersCount = ktn_task:wait_for(function_calls(callbacks,
- handle_worker_creation,
- ['_']), WorkersCount),
- wpool:stop_pool(Pool),
- meck:unload(callbacks),
-
- ok.
+ Pool = partial_callbacks_test,
+ WorkersCount = 7,
+ meck:new(callbacks, [non_strict]),
+ meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
+ meck:expect(callbacks, handle_worker_death, fun(_AWName, _Reason) -> ok end),
+ {ok, _Pid} =
+ wpool:start_pool(Pool,
+ [{workers, WorkersCount},
+ {enable_callbacks, true},
+ {callbacks, [callbacks]}]),
+ WorkersCount =
+ ktn_task:wait_for(function_calls(callbacks, handle_worker_creation, ['_']), WorkersCount),
+ wpool:stop_pool(Pool),
+ meck:unload(callbacks),
+
+ ok.
-spec callback_can_be_added_and_removed_after_pool_is_started(config()) -> ok.
callback_can_be_added_and_removed_after_pool_is_started(_Config) ->
- Pool = after_start_callbacks_test,
- WorkersCount = 3,
- meck:new(callbacks, [non_strict]),
- meck:expect(callbacks, handle_worker_death, fun(_AWName, _Reason) -> ok end),
- meck:new(callbacks2, [non_strict]),
- meck:expect(callbacks2, handle_worker_death, fun(_AWName, _Reason) -> ok end),
- {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount},
- {worker, {crashy_server, []}},
- {enable_callbacks, true}]),
- %% Now we are adding 2 callback modules
- _ = wpool_pool:add_callback_module(Pool, callbacks),
- _ = wpool_pool:add_callback_module(Pool, callbacks2),
- Worker = wpool_pool:random_worker(Pool),
- Worker ! crash,
-
- %% they both are called
- 1 = ktn_task:wait_for(function_calls(callbacks, handle_worker_death,
- ['_', '_']), 1),
- 1 = ktn_task:wait_for(function_calls(callbacks2, handle_worker_death,
- ['_', '_']), 1),
-
- %% then the first module is removed
- _ = wpool_pool:remove_callback_module(Pool, callbacks),
- Worker2 = wpool_pool:random_worker(Pool),
- Worker2 ! crash,
-
- %% and only the scond one is called
- 1 = ktn_task:wait_for(function_calls(callbacks, handle_worker_death,
- ['_', '_']), 1),
- 2 = ktn_task:wait_for(function_calls(callbacks2, handle_worker_death,
- ['_', '_']), 2),
-
- wpool:stop_pool(Pool),
- meck:unload(callbacks),
- meck:unload(callbacks2),
-
- ok.
+ Pool = after_start_callbacks_test,
+ WorkersCount = 3,
+ meck:new(callbacks, [non_strict]),
+ meck:expect(callbacks, handle_worker_death, fun(_AWName, _Reason) -> ok end),
+ meck:new(callbacks2, [non_strict]),
+ meck:expect(callbacks2, handle_worker_death, fun(_AWName, _Reason) -> ok end),
+ {ok, _Pid} =
+ wpool:start_pool(Pool,
+ [{workers, WorkersCount},
+ {worker, {crashy_server, []}},
+ {enable_callbacks, true}]),
+ %% Now we are adding 2 callback modules
+ _ = wpool_pool:add_callback_module(Pool, callbacks),
+ _ = wpool_pool:add_callback_module(Pool, callbacks2),
+ Worker = wpool_pool:random_worker(Pool),
+ Worker ! crash,
+
+ %% they both are called
+ 1 = ktn_task:wait_for(function_calls(callbacks, handle_worker_death, ['_', '_']), 1),
+ 1 = ktn_task:wait_for(function_calls(callbacks2, handle_worker_death, ['_', '_']), 1),
+
+ %% then the first module is removed
+ _ = wpool_pool:remove_callback_module(Pool, callbacks),
+ Worker2 = wpool_pool:random_worker(Pool),
+ Worker2 ! crash,
+
+ %% and only the scond one is called
+ 1 = ktn_task:wait_for(function_calls(callbacks, handle_worker_death, ['_', '_']), 1),
+ 2 = ktn_task:wait_for(function_calls(callbacks2, handle_worker_death, ['_', '_']), 2),
+
+ wpool:stop_pool(Pool),
+ meck:unload(callbacks),
+ meck:unload(callbacks2),
+ ok.
-spec crashing_callback_does_not_affect_others(config()) -> ok.
crashing_callback_does_not_affect_others(_Config) ->
- Pool = crashing_callbacks_test,
- WorkersCount = 3,
- meck:new(callbacks, [non_strict]),
- meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
- meck:new(callbacks2, [non_strict]),
- meck:expect(callbacks2, handle_worker_creation,
- fun(AWorkerName) -> {not_going_to_work} = AWorkerName end),
- {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount},
- {worker, {crashy_server, []}},
- {enable_callbacks, true},
- {callbacks, [callbacks, callbacks2]}]),
-
- WorkersCount = ktn_task:wait_for(function_calls(callbacks,
- handle_worker_creation,
- ['_']), WorkersCount),
- WorkersCount = ktn_task:wait_for(function_calls(callbacks2,
- handle_worker_creation,
- ['_']), WorkersCount),
-
- wpool:stop_pool(Pool),
- meck:unload(callbacks),
- meck:unload(callbacks2),
-
- ok.
+ Pool = crashing_callbacks_test,
+ WorkersCount = 3,
+ meck:new(callbacks, [non_strict]),
+ meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
+ meck:new(callbacks2, [non_strict]),
+ meck:expect(callbacks2,
+ handle_worker_creation,
+ fun(AWorkerName) -> {not_going_to_work} = AWorkerName end),
+ {ok, _Pid} =
+ wpool:start_pool(Pool,
+ [{workers, WorkersCount},
+ {worker, {crashy_server, []}},
+ {enable_callbacks, true},
+ {callbacks, [callbacks, callbacks2]}]),
+
+ WorkersCount =
+ ktn_task:wait_for(function_calls(callbacks, handle_worker_creation, ['_']), WorkersCount),
+ WorkersCount =
+ ktn_task:wait_for(function_calls(callbacks2, handle_worker_creation, ['_']),
+ WorkersCount),
+
+ wpool:stop_pool(Pool),
+ meck:unload(callbacks),
+ meck:unload(callbacks2),
+ ok.
-spec non_existsing_module_does_not_affect_others(config()) -> ok.
non_existsing_module_does_not_affect_others(_Config) ->
- Pool = non_existing_callbacks_test,
- WorkersCount = 4,
- meck:new(callbacks, [non_strict]),
- meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
- {ok, _Pid} = wpool:start_pool(Pool, [{workers, WorkersCount},
- {worker, {crashy_server, []}},
- {enable_callbacks, true},
- {callbacks, [callbacks, non_existing_m]}
- ]),
-
- {error, nofile} = wpool_pool:add_callback_module(Pool, non_existing_m2),
-
- WorkersCount = ktn_task:wait_for(function_calls(callbacks,
- handle_worker_creation,
- ['_']), WorkersCount),
-
- wpool:stop_pool(Pool),
- meck:unload(callbacks),
-
- ok.
-function_calls(Module, Function, MeckMatchSpec) ->
- fun() ->
- meck:num_calls(Module, Function, MeckMatchSpec)
- end.
+ Pool = non_existing_callbacks_test,
+ WorkersCount = 4,
+ meck:new(callbacks, [non_strict]),
+ meck:expect(callbacks, handle_worker_creation, fun(_AWorkerName) -> ok end),
+ {ok, _Pid} =
+ wpool:start_pool(Pool,
+ [{workers, WorkersCount},
+ {worker, {crashy_server, []}},
+ {enable_callbacks, true},
+ {callbacks, [callbacks, non_existing_m]}]),
+
+ {error, nofile} = wpool_pool:add_callback_module(Pool, non_existing_m2),
+
+ WorkersCount =
+ ktn_task:wait_for(function_calls(callbacks, handle_worker_creation, ['_']), WorkersCount),
+
+ wpool:stop_pool(Pool),
+ meck:unload(callbacks),
+ ok.
+
+function_calls(Module, Function, MeckMatchSpec) ->
+ fun() -> meck:num_calls(Module, Function, MeckMatchSpec) end.
-spec complete_coverage(config()) -> ok.
complete_coverage(_Config) ->
{ok, EventManager} = gen_event:start_link(),
- gen_event:add_handler(
- EventManager, {wpool_process_callbacks, ?MODULE}, ?MODULE),
+ gen_event:add_handler(EventManager, {wpool_process_callbacks, ?MODULE}, ?MODULE),
{error, {unexpected_call, call}} =
gen_event:call(EventManager, {wpool_process_callbacks, ?MODULE}, call),
ok.
diff --git a/test/wpool_worker_SUITE.erl b/test/wpool_worker_SUITE.erl
index aa65f28..527bf85 100644
--- a/test/wpool_worker_SUITE.erl
+++ b/test/wpool_worker_SUITE.erl
@@ -15,6 +15,8 @@
%% @hidden
-module(wpool_worker_SUITE).
+-behaviour(ct_suite).
+
-type config() :: [{atom(), term()}].
-export([all/0]).
@@ -24,72 +26,73 @@
-spec all() -> [atom()].
all() ->
- [Fun || {Fun, 1} <- module_info(exports),
- not lists:member(Fun, [init_per_suite, end_per_suite, module_info])].
+ [Fun
+ || {Fun, 1} <- module_info(exports),
+ not lists:member(Fun, [init_per_suite, end_per_suite, module_info])].
-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
- ok = wpool:start(),
- Config.
+ ok = wpool:start(),
+ Config.
-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
- wpool:stop(),
- Config.
+ wpool:stop(),
+ Config.
-spec ok() -> ?MODULE.
-ok() -> ?MODULE.
+ok() ->
+ ?MODULE.
-spec error() -> no_return().
-error() -> exit(?MODULE).
+error() ->
+ exit(?MODULE).
-spec call(config()) -> {comment, []}.
call(_Config) ->
- start_pool(),
- ?MODULE = wpool_worker:call(?MODULE, ?MODULE, ok, []),
- try wpool_worker:call(?MODULE, ?MODULE, error, []) of
- R -> no_result = R
- catch
- exit:?MODULE -> ok
- end,
- {error, invalid_request} = wpool:call(?MODULE, error),
- ok = wpool:stop_sup_pool(?MODULE),
-
- {comment, []}.
+ start_pool(),
+ ?MODULE = wpool_worker:call(?MODULE, ?MODULE, ok, []),
+ try wpool_worker:call(?MODULE, ?MODULE, error, []) of
+ R ->
+ no_result = R
+ catch
+ exit:?MODULE ->
+ ok
+ end,
+ {error, invalid_request} = wpool:call(?MODULE, error),
+ ok = wpool:stop_sup_pool(?MODULE),
+
+ {comment, []}.
-spec cast(config()) -> {comment, []}.
cast(_Config) ->
- start_pool(),
- ok = wpool_worker:cast(?MODULE, ?MODULE, ok, []),
- ok = wpool_worker:cast(?MODULE, ?MODULE, error, []),
- ok = wpool:cast(?MODULE, x),
- ok = wpool_worker:cast(?MODULE, erlang, send, [self(), {a, message}]),
- receive
- {a, message} -> ok
- after 1000 ->
- ct:fail("Timeout while waiting for cast response")
- end,
- ok = wpool:stop_sup_pool(?MODULE),
-
- {comment, []}.
+ start_pool(),
+ ok = wpool_worker:cast(?MODULE, ?MODULE, ok, []),
+ ok = wpool_worker:cast(?MODULE, ?MODULE, error, []),
+ ok = wpool:cast(?MODULE, x),
+ ok = wpool_worker:cast(?MODULE, erlang, send, [self(), {a, message}]),
+ receive
+ {a, message} ->
+ ok
+ after 1000 ->
+ ct:fail("Timeout while waiting for cast response")
+ end,
+ ok = wpool:stop_sup_pool(?MODULE),
+
+ {comment, []}.
-spec complete_coverage(config()) -> {comment, []}.
complete_coverage(_Config) ->
- start_pool(),
- {ok, AWorker} = wpool:call(?MODULE, {erlang, self, []}),
- true = is_process_alive(AWorker),
- AWorker ! info,
-
- true = is_process_alive(AWorker),
-
- {ok, {state}} = wpool_worker:code_change("oldvsn", {state}, extra),
+ start_pool(),
+ {ok, AWorker} = wpool:call(?MODULE, {erlang, self, []}),
+ true = is_process_alive(AWorker),
+ AWorker ! info,
- ok = wpool_worker:terminate(reason, {state}),
+ true = is_process_alive(AWorker),
- {comment, []}.
+ {comment, []}.
start_pool() ->
- {ok, _Pid} =
- wpool:start_sup_pool(
- ?MODULE, [{workers, 1}, {worker, {wpool_worker, undefined}}]),
- started.
+ {ok, _Pid} =
+ wpool:start_sup_pool(?MODULE, [{workers, 1}, {worker, {wpool_worker, undefined}}]),
+ started.