From 89d80db0450b08dd23f407d8863cfa087fab5871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pe=CC=81ter=20Go=CC=88mo=CC=88ri?= Date: Thu, 15 May 2014 17:03:10 +0200 Subject: [PATCH] Fix race condition in dynamic pool creation --- src/lhttpc_manager.erl | 4 +++- test/lhttpc_manager_tests.erl | 40 ++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/lhttpc_manager.erl b/src/lhttpc_manager.erl index 80ee047c..9872be21 100644 --- a/src/lhttpc_manager.erl +++ b/src/lhttpc_manager.erl @@ -195,7 +195,7 @@ start_link(Options0) -> %% @end %%------------------------------------------------------------------------------ -spec ensure_call(pool_id(), pid(), host(), port_num(), boolean(), options()) -> - socket() | 'no_socket'. + socket() | 'undefined'. ensure_call(Pool, Pid, Host, Port, Ssl, Options) -> SocketRequest = {socket, Pid, Host, Port, Ssl}, try gen_server:call(Pool, SocketRequest, infinity) of @@ -224,6 +224,8 @@ ensure_call(Pool, Pid, Host, Port, Ssl, Options) -> case lhttpc:add_pool(Pool, ConnTimeout, PoolMaxSize) of {ok, _Pid} -> ensure_call(Pool, Pid, Host, Port, Ssl, Options); + {error, already_exists} -> + ensure_call(Pool, Pid, Host, Port, Ssl, Options); _ -> %% Failed to create pool, exit as expected exit({noproc, Reason}) diff --git a/test/lhttpc_manager_tests.erl b/test/lhttpc_manager_tests.erl index 4e24afe0..7183e1cc 100644 --- a/test/lhttpc_manager_tests.erl +++ b/test/lhttpc_manager_tests.erl @@ -53,7 +53,8 @@ manager_test_() -> ?_test(one_socket()), {timeout, 60, ?_test(connection_timeout())}, {timeout, 60, ?_test(many_sockets())}, - {timeout, 60, ?_test(closed_race_cond())} + {timeout, 60, ?_test(closed_race_cond())}, + {timeout, 60, ?_test(dyn_pool_race())} ]} }. @@ -320,6 +321,43 @@ closed_race_cond() -> unlink(whereis(lhttpc_manager)), ok. +dyn_pool_race() -> + ?assertEqual(undefined, whereis(mypool)), + + %% suspend the supervisor so both process reaches + %% and gets stuck in lhttpc:add_pool/3 + erlang:suspend_process(whereis(lhttpc_sup)), + + Parent = self(), + Pid1 = spawn_pool_req(Parent), + Pid2 = spawn_pool_req(Parent), + + erlang:yield(), % make sure that the spawned processes have run + erlang:resume_process(whereis(lhttpc_sup)), + + worker_done([Pid1, Pid2]), + ?assertNotEqual(undefined, whereis(mypool)), + ok. + +spawn_pool_req(Parent) -> + spawn_link( + fun() -> + Opts = [{pool, mypool}, {pool_ensure, true}], + undefined = lhttpc_manager:ensure_call( + mypool, self(), ?HOST, get_port(), ?SSL, Opts), + Parent ! {self(), done} + end). + +worker_done([Pid|Pids]) -> + receive + {Pid, done} -> worker_done(Pids) + after + 1000 -> error({error, worker_timeout}) + end; +worker_done([]) -> + ok. + + %%% Helpers functions spawn_client() ->