uwiger / gproc

Extended process registry for Erlang
Apache License 2.0
1.07k stars 232 forks source link

Removing a worker from a pool does not make room for a new worker #167

Closed flambard closed 5 years ago

flambard commented 5 years ago

I'm using gproc_pool for simple process pooling and I'm running in to an issue when trying make things synchronous for some property-based testing of my project.

Is there a way to make sure that there is room for a new worker after a worker has been removed from a pool? I made simple waiting function that just keeps polling defined_workers/1 until the worker is gone (I also tried worker_pool/1).

I have boiled down my issue to a runnable eunit test:

gproc_pool_add_worker_race_test() ->
    ok = application:ensure_started(gproc),
    ok = gproc_pool:new(my_pool, direct, [{size, 1}, {auto_size, false}]),
    [] = gproc_pool:defined_workers(my_pool),
    Pos1 = gproc_pool:add_worker(my_pool, worker1),
    [{worker1, Pos1, 0}] = gproc_pool:defined_workers(my_pool),
    Pid = spawn(fun() ->
                        true = gproc_pool:connect_worker(my_pool, worker1),
                        receive after 100 -> bye end
                end),
    Monitor = monitor(process, Pid),
    receive
        {'DOWN', Monitor, process, _, _} -> ok
    end,
    true = gproc_pool:remove_worker(my_pool, worker1),
    wait_until_worker_is_removed_from_pool(my_pool, worker1),
    [] = gproc_pool:defined_workers(my_pool), %% the pool seems to be empty
    Pos2 = gproc_pool:add_worker(my_pool, worker2), %% throws error:pool_full
    true = is_integer(Pos2).

wait_until_worker_is_removed_from_pool(Pool, Name) ->
    case lists:keymember(Name, 1, gproc_pool:defined_workers(Pool)) of
        false -> ok;
        true ->
            receive
            after 200 -> wait_until_worker_is_removed_from_pool(Pool, Name)
            end
    end.
uwiger commented 5 years ago

It was a bug. I've created a PR and added your test case to the eunit suite.