Browse Source

Merge pull request #63 from uwiger/uw-pool-pick-worker

Issue #62: fix doc & add pick_worker/[1,2]
Ulf Wiger 11 years ago
parent
commit
e9d73d609f
3 changed files with 118 additions and 43 deletions
  1. 15 15
      README.md
  2. 36 2
      doc/gproc_pool.md
  3. 67 26
      src/gproc_pool.erl

+ 15 - 15
README.md

@@ -77,8 +77,8 @@ alternative sources, and cache them for efficient lookup. Caching also provides
 a way to see which processes rely on certain configuration values, as well as
 which values they actually ended up using.
 
-See [`gproc:get_env/4`](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md#get_env-4), [`gproc:get_set_env/4`](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md#get_set_env-4) and
-[`gproc:set_env/5`](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md#set_env-5) for details.
+See [`gproc:get_env/4`](http://github.com/esl/gproc/blob/master/doc/gproc.md#get_env-4), [`gproc:get_set_env/4`](http://github.com/esl/gproc/blob/master/doc/gproc.md#get_set_env-4) and
+[`gproc:set_env/5`](http://github.com/esl/gproc/blob/master/doc/gproc.md#set_env-5) for details.
 
 
 ## Testing ##
@@ -98,23 +98,23 @@ global gproc.
 By default, `./rebar doc` generates Github-flavored Markdown files.
 If you want to change this, remove the `edoc_opts` line from `rebar.config`.
 Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
-Freiburg 2007 ([Paper available here](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/erlang07-wiger.pdf)).
+Freiburg 2007 ([Paper available here](http://github.com/esl/gproc/blob/master/doc/erlang07-wiger.pdf)).
 
 
 ## Modules ##
 
 
 <table width="100%" border="0" summary="list of modules">
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md" class="module">gproc</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_app.md" class="module">gproc_app</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_bcast.md" class="module">gproc_bcast</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_dist.md" class="module">gproc_dist</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_info.md" class="module">gproc_info</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_init.md" class="module">gproc_init</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_lib.md" class="module">gproc_lib</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_monitor.md" class="module">gproc_monitor</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_pool.md" class="module">gproc_pool</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_ps.md" class="module">gproc_ps</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_pt.md" class="module">gproc_pt</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_sup.md" class="module">gproc_sup</a></td></tr></table>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc.md" class="module">gproc</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_app.md" class="module">gproc_app</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_bcast.md" class="module">gproc_bcast</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_dist.md" class="module">gproc_dist</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_info.md" class="module">gproc_info</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_init.md" class="module">gproc_init</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_lib.md" class="module">gproc_lib</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_monitor.md" class="module">gproc_monitor</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_pool.md" class="module">gproc_pool</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_ps.md" class="module">gproc_ps</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_pt.md" class="module">gproc_pt</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_sup.md" class="module">gproc_sup</a></td></tr></table>
 

+ 36 - 2
doc/gproc_pool.md

@@ -63,7 +63,7 @@ jobs will not exceed the size of the pool.<a name="index"></a>
 ## Function Index ##
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#active_workers-1">active_workers/1</a></td><td>Return a list of currently connected workers in the pool.</td></tr><tr><td valign="top"><a href="#add_worker-2">add_worker/2</a></td><td>Assign a worker name to the pool, returning the worker's position.</td></tr><tr><td valign="top"><a href="#add_worker-3">add_worker/3</a></td><td>Assign a worker name to a given slot in the pool, returning the slot.</td></tr><tr><td valign="top"><a href="#claim-2">claim/2</a></td><td>Picks the first available worker in the pool and applies <code>Fun</code>.</td></tr><tr><td valign="top"><a href="#connect_worker-2">connect_worker/2</a></td><td>Connect the current process to <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#defined_workers-1">defined_workers/1</a></td><td>Return a list of added workers in the pool.</td></tr><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td>Delete an existing pool.</td></tr><tr><td valign="top"><a href="#disconnect_worker-2">disconnect_worker/2</a></td><td>Disconnect the current process from <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#force_delete-1">force_delete/1</a></td><td>Forcibly remove a pool, terminating all active workers.</td></tr><tr><td valign="top"><a href="#log-1">log/1</a></td><td>Update a counter associated with a worker name.</td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td>Equivalent to <a href="#new-3"><tt>new(Pool, round_robin, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#new-3">new/3</a></td><td>Create a new pool.</td></tr><tr><td valign="top"><a href="#pick-1">pick/1</a></td><td>Pick a worker from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick-2">pick/2</a></td><td>Pick a worker from the pool based on <code>Value</code>.</td></tr><tr><td valign="top"><a href="#ptest-4">ptest/4</a></td><td></td></tr><tr><td valign="top"><a href="#randomize-1">randomize/1</a></td><td>Randomizes the "next" pointer for the pool.</td></tr><tr><td valign="top"><a href="#remove_worker-2">remove_worker/2</a></td><td>Remove a previously added worker.</td></tr><tr><td valign="top"><a href="#test_run0-2">test_run0/2</a></td><td></td></tr><tr><td valign="top"><a href="#whereis_worker-2">whereis_worker/2</a></td><td>Look up the pid of a connected worker.</td></tr><tr><td valign="top"><a href="#worker_id-2">worker_id/2</a></td><td>Return the unique gproc name corresponding to a name in the pool.</td></tr><tr><td valign="top"><a href="#worker_pool-1">worker_pool/1</a></td><td>Return a list of slots and/or named workers in the pool.</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#active_workers-1">active_workers/1</a></td><td>Return a list of currently connected workers in the pool.</td></tr><tr><td valign="top"><a href="#add_worker-2">add_worker/2</a></td><td>Assign a worker name to the pool, returning the worker's position.</td></tr><tr><td valign="top"><a href="#add_worker-3">add_worker/3</a></td><td>Assign a worker name to a given slot in the pool, returning the slot.</td></tr><tr><td valign="top"><a href="#claim-2">claim/2</a></td><td>Picks the first available worker in the pool and applies <code>Fun</code>.</td></tr><tr><td valign="top"><a href="#connect_worker-2">connect_worker/2</a></td><td>Connect the current process to <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#defined_workers-1">defined_workers/1</a></td><td>Return a list of added workers in the pool.</td></tr><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td>Delete an existing pool.</td></tr><tr><td valign="top"><a href="#disconnect_worker-2">disconnect_worker/2</a></td><td>Disconnect the current process from <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#force_delete-1">force_delete/1</a></td><td>Forcibly remove a pool, terminating all active workers.</td></tr><tr><td valign="top"><a href="#log-1">log/1</a></td><td>Update a counter associated with a worker name.</td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td>Equivalent to <a href="#new-3"><tt>new(Pool, round_robin, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#new-3">new/3</a></td><td>Create a new pool.</td></tr><tr><td valign="top"><a href="#pick-1">pick/1</a></td><td>Pick a worker from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick-2">pick/2</a></td><td>Pick a worker from the pool based on <code>Value</code>.</td></tr><tr><td valign="top"><a href="#pick_worker-1">pick_worker/1</a></td><td>Pick a worker pid from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick_worker-2">pick_worker/2</a></td><td>Pick a worker pid from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#ptest-4">ptest/4</a></td><td></td></tr><tr><td valign="top"><a href="#randomize-1">randomize/1</a></td><td>Randomizes the "next" pointer for the pool.</td></tr><tr><td valign="top"><a href="#remove_worker-2">remove_worker/2</a></td><td>Remove a previously added worker.</td></tr><tr><td valign="top"><a href="#test_run0-2">test_run0/2</a></td><td></td></tr><tr><td valign="top"><a href="#whereis_worker-2">whereis_worker/2</a></td><td>Look up the pid of a connected worker.</td></tr><tr><td valign="top"><a href="#worker_id-2">worker_id/2</a></td><td>Return the unique gproc name corresponding to a name in the pool.</td></tr><tr><td valign="top"><a href="#worker_pool-1">worker_pool/1</a></td><td>Return a list of slots and/or named workers in the pool.</td></tr></table>
 
 
 <a name="functions"></a>
@@ -302,7 +302,7 @@ if the currently connected worker dies.
 
 
 <pre><code>
-new(Pool::any()) -&gt; true
+new(Pool::any()) -&gt; ok
 </code></pre>
 
 <br></br>
@@ -379,6 +379,40 @@ If the pool is of type `direct`, `Value` must be an integer corresponding to
 a position in the pool (modulo the size of the pool). If the type is
 `hash`, `Value` may be any term, and its hash value will serve as a guide for
 selecting a worker.
+<a name="pick_worker-1"></a>
+
+### pick_worker/1 ###
+
+
+<pre><code>
+pick_worker(Pool::any()) -&gt; pid() | false
+</code></pre>
+
+<br></br>
+
+
+
+Pick a worker pid from the pool given the pool's load-balancing algorithm.
+
+
+Like [`pick/1`](#pick-1), but returns the worker pid instead of the name.
+<a name="pick_worker-2"></a>
+
+### pick_worker/2 ###
+
+
+<pre><code>
+pick_worker(Pool::any(), Value::any()) -&gt; pid() | false
+</code></pre>
+
+<br></br>
+
+
+
+Pick a worker pid from the pool given the pool's load-balancing algorithm.
+
+
+Like [`pick/2`](#pick-2), but returns the worker pid instead of the name.
 <a name="ptest-4"></a>
 
 ### ptest/4 ###

+ 67 - 26
src/gproc_pool.erl

@@ -70,6 +70,8 @@
          worker_pool/1,        % (Pool)
          pick/1,               % (Pool)
          pick/2,               % (Pool, Value)
+         pick_worker/1,        % (Pool)
+         pick_worker/2,        % (Pool, Value)
          claim/2,              % (Pool, Fun)
          log/1,                % (WorkerId)
          randomize/1]).        % (Pool)
@@ -91,7 +93,7 @@
 
 -record(st, {}).
 
-%% @spec new(Pool::any()) -> true
+%% @spec new(Pool::any()) -> ok
 %%
 %% @equiv new(Pool, round_robin, [])
 new(Pool) ->
@@ -294,7 +296,21 @@ pick(Pool) ->
     case gproc:get_value(?POOL(Pool), shared) of
         {0, _} -> false;
         {Sz, Type} when Type == round_robin; Type == random ->
-            pick(Pool, Sz, Type);
+            pick(Pool, Sz, Type, name);
+        _ ->
+            error(badarg)
+    end.
+
+%% @spec pick_worker(Pool::any()) -> pid() | false
+%% @doc Pick a worker pid from the pool given the pool's load-balancing algorithm.
+%%
+%% Like {@link pick/1}, but returns the worker pid instead of the name.
+%% @end
+pick_worker(Pool) ->
+    case gproc:get_value(?POOL(Pool), shared) of
+        {0, _} -> false;
+        {Sz, Type} when Type == round_robin; Type == random ->
+            pick(Pool, Sz, Type, pid);
         _ ->
             error(badarg)
     end.
@@ -315,57 +331,82 @@ pick(Pool, N) ->
     case gproc:get_value(?POOL(Pool), shared) of
         {0, _} -> false;
         {Sz, Type} when Type == hash; Type == direct ->
-            pick(Pool, Sz, Type, N);
+            pick(Pool, Sz, Type, N, name);
         _ ->
             error(badarg)
     end.
 
+%% @spec pick_worker(Pool::any(), Value::any()) -> pid() | false
+%% @doc Pick a worker pid from the pool given the pool's load-balancing algorithm.
+%%
+%% Like {@link pick/2}, but returns the worker pid instead of the name.
+%% @end
+pick_worker(Pool, N) ->
+    case gproc:get_value(?POOL(Pool), shared) of
+        {0, _} -> false;
+        {Sz, Type} when Type == hash; Type == direct ->
+            pick(Pool, Sz, Type, N, pid);
+        _ ->
+            error(badarg)
+    end.
 
-pick(Pool, Sz, round_robin) ->
+pick(Pool, Sz, round_robin, Ret) ->
     Next = incr(Pool, 1, Sz),
-    case gproc:next({l,n}, {n,l,[?MODULE,Pool,Next]}) of
-        {n,l,[?MODULE,Pool,Actual,_Name]} = Pick ->
+    case ets:next(gproc, {{n,l,[?MODULE,Pool,Next]},n}) of
+        {{n,l,[?MODULE,Pool,Actual,_Name]} = Pick, _} ->
             case Actual - Next of
                 Diff when Diff > 1 ->
                     gproc:update_counter(
                       ?POOL_CUR(Pool), shared, {Diff, Sz, 1}),
-                    Pick;
+                    ret(Pick, Ret);
                 _ ->
-                    Pick
+                    ret(Pick, Ret)
             end;
         _ ->
-            case gproc:next({l,n}, {n,l,[?MODULE,Pool,0]}) of
-                {n,l,[?MODULE,Pool,Actual1,_Name1]} = Pick ->
+            case ets:next(gproc, {{n,l,[?MODULE,Pool,0]}, n}) of
+                {{n,l,[?MODULE,Pool,Actual1,_Name1]} = Pick, _} ->
                     incr(Pool, Sz-Next+Actual1, Sz),
                     %% gproc:update_counter(
                     %%   ?POOL_CUR(Pool), shared, {Sz-Next+Actual1, Sz, 1}),
-                    Pick;
+                    ret(Pick, Ret);
                 _ ->
                     false
             end
     end;
-pick(Pool, Sz, random) ->
-    pick_near(Pool, crypto:rand_uniform(1, Sz + 1)).
-
-pick(Pool, Sz, hash, Val) ->
-    pick_near(Pool, erlang:phash2(Val, Sz) + 1);
-pick(Pool, Sz, direct, N) when is_integer(N), N > 0 ->
-    pick_near(Pool, case (N rem Sz-1) + 1  of 0 -> Sz; N1 -> N1 end).
-
-pick_near(Pool, N) ->
-    case gproc:next({l,n}, {n,l,[?MODULE,Pool,N]}) of
-        {n,l,[?MODULE,Pool,_,_]} = Pick ->
-            Pick;
+pick(Pool, Sz, random, Ret) ->
+    pick_near(Pool, crypto:rand_uniform(1, Sz + 1), Ret).
+
+pick(Pool, Sz, hash, Val, Ret) ->
+    pick_near(Pool, erlang:phash2(Val, Sz) + 1, Ret);
+pick(Pool, Sz, direct, N, Ret) when is_integer(N), N > 0 ->
+    pick_near(Pool, case (N rem Sz-1) + 1  of 0 -> Sz; N1 -> N1 end, Ret).
+
+pick_near(Pool, N, Ret) ->
+    case ets:next(gproc, {{n,l,[?MODULE,Pool,N]}, n}) of
+        {{n,l,[?MODULE,Pool,_,_]} = Pick, _} ->
+            ret(Pick, Ret);
         _ ->
             %% wrap
-            case gproc:next({l,n}, {n,l,[?MODULE,Pool,1]}) of
-                {n,l,[?MODULE,Pool,_,_]} = Pick ->
-                    Pick;
+            case ets:next(gproc, {{n,l,[?MODULE,Pool,1]}, n}) of
+                {{n,l,[?MODULE,Pool,_,_]} = Pick, _} ->
+                    ret(Pick, Ret);
                 _ ->
                     false
             end
     end.
 
+ret(Name, name) ->
+    Name;
+ret(Name, pid) ->
+    case ets:lookup(gproc, {Name,n}) of
+        [{_, Pid, _}] ->
+            Pid;
+        [] ->
+            %% possible race
+            false
+    end.
+
+
 %% @spec claim(Pool::any(), Fun::function()) -> {true, Res} | false
 %% @doc Picks the first available worker in the pool and applies `Fun'.
 %%