gproc_lib.erl 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. %% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*-
  2. %% --------------------------------------------------
  3. %% This file is provided to you under the Apache License,
  4. %% Version 2.0 (the "License"); you may not use this file
  5. %% except in compliance with the License. You may obtain
  6. %% a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing,
  11. %% software distributed under the License is distributed on an
  12. %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. %% KIND, either express or implied. See the License for the
  14. %% specific language governing permissions and limitations
  15. %% under the License.
  16. %% --------------------------------------------------
  17. %%
  18. %% @author Ulf Wiger <ulf@wiger.net>
  19. %%
  20. %% @doc Extended process registry
  21. %% <p>This module implements an extended process registry</p>
  22. %% <p>For a detailed description, see gproc/doc/erlang07-wiger.pdf.</p>
  23. %% @end
  24. -module(gproc_lib).
  25. -export([await/3,
  26. do_set_counter_value/3,
  27. do_set_value/3,
  28. ensure_monitor/2,
  29. insert_many/4,
  30. insert_reg/4, insert_reg/5,
  31. insert_attr/4,
  32. remove_many/4,
  33. remove_reg/3, remove_reg/4,
  34. monitors/1,
  35. standbys/1,
  36. followers/1,
  37. remove_monitor_pid/2,
  38. does_pid_monitor/2,
  39. add_monitor/4,
  40. remove_monitor/3,
  41. remove_monitors/3,
  42. remove_reverse_mapping/3, remove_reverse_mapping/4,
  43. notify/2, notify/3,
  44. remove_wait/4,
  45. update_aggr_counter/3,
  46. update_counter/3,
  47. decrement_resource_count/2,
  48. valid_opts/2]).
  49. -export([dbg/1]).
  50. -include("gproc_int.hrl").
  51. -include("gproc.hrl").
  52. dbg(Mods) ->
  53. dbg:tracer(),
  54. [dbg:tpl(M,x) || M <- Mods],
  55. dbg:tp(ets,'_',[{[gproc,'_'], [], [{message,{exception_trace}}]}]),
  56. dbg:p(all,[c]).
  57. %% We want to store names and aggregated counters with the same
  58. %% structure as properties, but at the same time, we must ensure
  59. %% that the key is unique. We replace the Pid in the key part
  60. %% with an atom. To know which Pid owns the object, we lug the
  61. %% Pid around as payload as well. This is a bit redundant, but
  62. %% symmetric.
  63. %%
  64. -spec insert_reg(gproc:key(), any(), pid() | shared, gproc:scope()) -> boolean().
  65. insert_reg(K, Value, Pid, Scope) ->
  66. insert_reg(K, Value, Pid, Scope, registered).
  67. insert_reg({T,_,Name} = K, Value, Pid, Scope, Event) when T==a; T==n; T==rc ->
  68. Res = case ets:insert_new(?TAB, {{K,T}, Pid, Value}) of
  69. true ->
  70. %% Use insert_new to avoid overwriting existing entry
  71. _ = ets:insert_new(?TAB, {{Pid,K}, []}),
  72. true;
  73. false ->
  74. maybe_waiters(K, Pid, Value, T, Event)
  75. end,
  76. maybe_scan(T, Pid, Scope, Name, K),
  77. Res;
  78. insert_reg({p,Scope,_} = K, Value, shared, Scope, _E)
  79. when Scope == g; Scope == l ->
  80. %% shared properties are unique
  81. Info = [{{K, shared}, shared, Value}, {{shared,K}, []}],
  82. ets:insert_new(?TAB, Info);
  83. insert_reg({c,Scope,Ctr} = Key, Value, Pid, Scope, _E) when Scope==l; Scope==g ->
  84. %% Non-unique keys; store Pid in the key part
  85. K = {Key, Pid},
  86. Kr = {Pid, Key},
  87. Res = ets:insert_new(?TAB, [{K, Pid, Value}, {Kr, [{initial, Value}]}]),
  88. case Res of
  89. true ->
  90. update_aggr_counter(Scope, Ctr, Value);
  91. false ->
  92. ignore
  93. end,
  94. Res;
  95. insert_reg({r,Scope,R} = Key, Value, Pid, Scope, _E) when Scope==l; Scope==g ->
  96. K = {Key, Pid},
  97. Kr = {Pid, Key},
  98. Res = ets:insert_new(?TAB, [{K, Pid, Value}, {Kr, [{initial, Value}]}]),
  99. case Res of
  100. true ->
  101. update_resource_count(Scope, R, 1);
  102. false ->
  103. ignore
  104. end,
  105. Res;
  106. insert_reg({_,_,_} = Key, Value, Pid, _Scope, _E) when is_pid(Pid) ->
  107. %% Non-unique keys; store Pid in the key part
  108. K = {Key, Pid},
  109. Kr = {Pid, Key},
  110. ets:insert_new(?TAB, [{K, Pid, Value}, {Kr, []}]).
  111. maybe_scan(a, Pid, Scope, Name, K) ->
  112. Initial = scan_existing_counters(Scope, Name),
  113. ets:insert(?TAB, {{K,a}, Pid, Initial});
  114. maybe_scan(rc, Pid, Scope, Name, K) ->
  115. Initial = scan_existing_resources(Scope, Name),
  116. ets:insert(?TAB, {{K,rc}, Pid, Initial});
  117. maybe_scan(_, _, _, _, _) ->
  118. true.
  119. insert_attr({_,Scope,_} = Key, Attrs, Pid, Scope) when Scope==l;
  120. Scope==g ->
  121. case ets:lookup(?TAB, K = {Pid, Key}) of
  122. [{_, Attrs0}] when is_list(Attrs) ->
  123. As = proplists:get_value(attrs, Attrs0, []),
  124. As1 = lists:foldl(fun({K1,_} = Attr, Acc) ->
  125. lists:keystore(K1, 1, Acc, Attr)
  126. end, As, Attrs),
  127. Attrs1 = lists:keystore(attrs, 1, Attrs0, {attrs, As1}),
  128. ets:insert(?TAB, {K, Attrs1}),
  129. Attrs1;
  130. _ ->
  131. false
  132. end.
  133. get_attr(Attr, Pid, {_,_,_} = Key, Default) ->
  134. case ets:lookup(?TAB, {Pid, Key}) of
  135. [{_, Opts}] when is_list(Opts) ->
  136. case lists:keyfind(attrs, 1, Opts) of
  137. {_, Attrs} ->
  138. case lists:keyfind(Attr, 1, Attrs) of
  139. {_, Val} ->
  140. Val;
  141. _ ->
  142. Default
  143. end;
  144. _ ->
  145. Default
  146. end;
  147. _ ->
  148. Default
  149. end.
  150. -spec insert_many(gproc:type(), gproc:scope(), [{gproc:key(),any()}], pid()) ->
  151. {true,list()} | false.
  152. insert_many(T, Scope, KVL, Pid) ->
  153. Objs = mk_reg_objs(T, Scope, Pid, KVL),
  154. case ets:insert_new(?TAB, Objs) of
  155. true ->
  156. RevObjs = mk_reg_rev_objs(T, Scope, Pid, KVL),
  157. ets:insert(?TAB, RevObjs),
  158. _ = gproc_lib:ensure_monitor(Pid, Scope),
  159. {true, Objs};
  160. false ->
  161. Existing = [{Obj, ets:lookup(?TAB, K)} || {K,_,_} = Obj <- Objs],
  162. case lists:any(fun({_, [{_, _, _}]}) ->
  163. true;
  164. (_) ->
  165. %% (not found), or waiters registered
  166. false
  167. end, Existing) of
  168. true ->
  169. %% conflict; return 'false', indicating failure
  170. false;
  171. false ->
  172. %% possibly waiters, but they are handled in next step
  173. insert_objects(Existing),
  174. _ = gproc_lib:ensure_monitor(Pid, Scope),
  175. {true, Objs}
  176. end
  177. end.
  178. -spec insert_objects([{gproc:key(), pid(), any()}]) -> ok.
  179. insert_objects(Objs) ->
  180. lists:foreach(
  181. fun({{{Id,_} = _K, Pid, V} = Obj, Existing}) ->
  182. ets:insert(?TAB, [Obj, {{Pid, Id}, []}]),
  183. case Existing of
  184. [] -> ok;
  185. [{_, Waiters}] ->
  186. notify_waiters(Waiters, Id, Pid, V, registered)
  187. end
  188. end, Objs).
  189. await({T,C,_} = Key, WPid, {_Pid, Ref} = From) ->
  190. Rev = {{WPid,Key}, []},
  191. case ets:lookup(?TAB, {Key,T}) of
  192. [{_, P, Value}] ->
  193. %% for symmetry, we always reply with Ref and then send a message
  194. if C == g ->
  195. %% in the global case, we bundle the reply, since otherwise
  196. %% the messages can pass each other
  197. {reply, {Ref, {Key, P, Value}}};
  198. true ->
  199. gen_server:reply(From, Ref),
  200. WPid ! {gproc, Ref, registered, {Key, P, Value}},
  201. noreply
  202. end;
  203. [{K, Waiters}] ->
  204. NewWaiters = [{WPid,Ref} | Waiters],
  205. W = {K, NewWaiters},
  206. ets:insert(?TAB, [W, Rev]),
  207. _ = gproc_lib:ensure_monitor(WPid,C),
  208. {reply, Ref, [W,Rev]};
  209. [] ->
  210. W = {{Key,T}, [{WPid,Ref}]},
  211. ets:insert(?TAB, [W, Rev]),
  212. _ = gproc_lib:ensure_monitor(WPid,C),
  213. {reply, Ref, [W,Rev]}
  214. end.
  215. maybe_waiters(_, _, _, _, []) ->
  216. false;
  217. maybe_waiters(K, Pid, Value, T, Event) ->
  218. case ets:lookup(?TAB, {K,T}) of
  219. [{_, Waiters}] when is_list(Waiters) ->
  220. Followers = [F || {_,_,follow} = F <- Waiters],
  221. ets:insert(?TAB, [{{K,T}, Pid, Value},
  222. {{Pid,K}, [{monitor, Followers}
  223. || Followers =/= []]}]),
  224. notify_waiters(Waiters, K, Pid, Value, Event),
  225. true;
  226. _ ->
  227. false
  228. end.
  229. -spec notify_waiters([{pid(), reference()}], gproc:key(), pid(), any(), any()) -> ok.
  230. notify_waiters([{P, Ref}|T], K, Pid, V, E) ->
  231. P ! {gproc, Ref, registered, {K, Pid, V}},
  232. notify_waiters(T, K, Pid, V, E);
  233. notify_waiters([{P, Ref, follow}|T], K, Pid, V, E) ->
  234. %% This is really a monitor, lurking in the Waiters list
  235. P ! {gproc, E, Ref, K},
  236. notify_waiters(T, K, Pid, V, E);
  237. notify_waiters([], _, _, _, _) ->
  238. ok.
  239. remove_wait({T,_,_} = Key, Pid, Ref, Waiters) ->
  240. Rev = {Pid,Key},
  241. case remove_from_waiters(Waiters, Pid, Ref) of
  242. [] ->
  243. ets:delete(?TAB, {Key,T}),
  244. ets:delete(?TAB, Rev),
  245. [{delete, [{Key,T}, Rev], []}];
  246. NewWaiters ->
  247. ets:insert(?TAB, {Key, NewWaiters}),
  248. case lists:keymember(Pid, 1, NewWaiters) of
  249. true ->
  250. %% should be extremely unlikely
  251. [{insert, [{Key, NewWaiters}]}];
  252. false ->
  253. %% delete the reverse entry
  254. ets:delete(?TAB, Rev),
  255. [{insert, [{Key, NewWaiters}]},
  256. {delete, [Rev], []}]
  257. end
  258. end.
  259. remove_from_waiters(Waiters, Pid, all) ->
  260. [W || W <- Waiters,
  261. element(1,W) =/= Pid];
  262. remove_from_waiters(Waiters, Pid, Ref) ->
  263. [W || W <- Waiters, not is_waiter(W, Pid, Ref)].
  264. is_waiter({Pid, Ref} , Pid, Ref) -> true;
  265. is_waiter({Pid, Ref, _}, Pid, Ref) -> true;
  266. is_waiter(_, _, _) ->
  267. false.
  268. remove_monitors(Key, Pid, MPid) ->
  269. case ets:lookup(?TAB, {Pid, Key}) of
  270. [{_, r}] ->
  271. [];
  272. [{K, Opts}] when is_list(Opts) ->
  273. case lists:keyfind(monitor, 1, Opts) of
  274. false ->
  275. [];
  276. {_, Ms} ->
  277. Ms1 = [{P,R,T} || {P,R,T} <- Ms,
  278. P =/= MPid],
  279. NewMs = lists:keyreplace(monitor, 1, Opts, {monitor,Ms1}),
  280. ets:insert(?TAB, {K, NewMs}),
  281. [{insert, [{{Pid,Key}, NewMs}]}]
  282. end;
  283. _ ->
  284. []
  285. end.
  286. does_pid_monitor(Pid, Opts) ->
  287. case lists:keyfind(monitor, 1, Opts) of
  288. false ->
  289. false;
  290. {_, Ms} ->
  291. lists:keymember(Pid, 1, Ms)
  292. end.
  293. mk_reg_objs(T, Scope, Pid, L) when T==n; T==a; T==rc ->
  294. lists:map(fun({K,V}) ->
  295. {{{T,Scope,K},T}, Pid, V};
  296. (_) ->
  297. erlang:error(badarg)
  298. end, L);
  299. mk_reg_objs(p = T, Scope, Pid, L) ->
  300. lists:map(fun({K,V}) ->
  301. {{{T,Scope,K},Pid}, Pid, V};
  302. (_) ->
  303. erlang:error(badarg)
  304. end, L).
  305. mk_reg_rev_objs(T, Scope, Pid, L) ->
  306. [{{Pid,{T,Scope,K}}, []} || {K,_} <- L].
  307. ensure_monitor(shared, _) ->
  308. ok;
  309. ensure_monitor(Pid, _) when Pid == self() ->
  310. %% monitoring is ensured through a 'monitor_me' message
  311. ok;
  312. ensure_monitor(Pid, Scope) when Scope==g; Scope==l ->
  313. case ets:insert_new(?TAB, {{Pid, Scope}}) of
  314. false -> ok;
  315. true -> erlang:monitor(process, Pid)
  316. end.
  317. remove_reg(Key, Pid, Event) ->
  318. Reg = remove_reg_1(Key, Pid),
  319. Rev = remove_reverse_mapping(Event, Pid, Key),
  320. [Reg, Rev].
  321. remove_reg(Key, Pid, Event, Opts) ->
  322. Reg = remove_reg_1(Key, Pid),
  323. Rev = remove_reverse_mapping(Event, Pid, Key, Opts),
  324. [Reg, Rev].
  325. remove_reverse_mapping(Event, Pid, Key) ->
  326. Opts = case ets:lookup(?TAB, {Pid, Key}) of
  327. [] -> [];
  328. [{_, r}] -> [];
  329. [{_, L}] when is_list(L) ->
  330. L
  331. end,
  332. remove_reverse_mapping(Event, Pid, Key, Opts).
  333. remove_reverse_mapping(Event, Pid, Key, Opts) when Event==unreg;
  334. element(1,Event)==migrated;
  335. element(1,Event)==failover ->
  336. Rev = {Pid, Key},
  337. _ = notify(Event, Key, Opts),
  338. ets:delete(?TAB, Rev),
  339. Rev.
  340. notify(Key, Opts) ->
  341. notify(unreg, Key, Opts).
  342. monitors(Opts) ->
  343. case lists:keyfind(monitor, 1, Opts) of
  344. false ->
  345. [];
  346. {_, Mons} ->
  347. Mons
  348. end.
  349. standbys(Opts) ->
  350. select_monitors(monitors(Opts), standby, []).
  351. followers(Opts) ->
  352. select_monitors(monitors(Opts), follow, []).
  353. select_monitors([{_,_,Type}=H|T], Type, Acc) ->
  354. select_monitors(T, Type, [H|Acc]);
  355. select_monitors([_|T], Type, Acc) ->
  356. select_monitors(T, Type, Acc);
  357. select_monitors([], _, Acc) ->
  358. Acc.
  359. remove_monitor_pid([{monitor, Mons}|T], Pid) ->
  360. [{monitor, [M || M <- Mons,
  361. element(1, M) =/= Pid]}|T];
  362. remove_monitor_pid([H|T], Pid) ->
  363. [H | remove_monitor_pid(T, Pid)];
  364. remove_monitor_pid([], _) ->
  365. [].
  366. notify([], _, _) ->
  367. ok;
  368. notify(Event, Key, Opts) ->
  369. notify_(monitors(Opts), Event, Key).
  370. %% Also handle old-style monitors
  371. notify_([{Pid,Ref}|T], Event, Key) ->
  372. Pid ! {gproc, Event, Ref, Key},
  373. notify_(T, Event, Key);
  374. notify_([{Pid,Ref,_}|T], Event, {_,l,_} = Key) ->
  375. Pid ! {gproc, Event, Ref, Key},
  376. notify_(T, Event, Key);
  377. notify_([{Pid,Ref,_}|T], Event, {_,g,_} = Key) when node(Pid) == node() ->
  378. Pid ! {gproc, Event, Ref, Key},
  379. notify_(T, Event, Key);
  380. notify_([_|T], Event, Key) ->
  381. notify_(T, Event, Key);
  382. notify_([], _, _) ->
  383. ok.
  384. add_monitor([{monitor, Mons}|T], Pid, Ref, Type) ->
  385. [{monitor, [{Pid,Ref,Type}|Mons]}|T];
  386. add_monitor([H|T], Pid, Ref, Type) ->
  387. [H|add_monitor(T, Pid, Ref, Type)];
  388. add_monitor([], Pid, Ref, Type) ->
  389. [{monitor, [{Pid, Ref, Type}]}].
  390. remove_monitor([{monitor, Mons}|T], Pid, Ref) ->
  391. [{monitor, [Mon || Mon <- Mons, not is_mon(Mon,Pid,Ref)]} | T];
  392. remove_monitor([H|T], Pid, Ref) ->
  393. [H|remove_monitor(T, Pid, Ref)];
  394. remove_monitor([], _Pid, _Ref) ->
  395. [].
  396. is_mon({Pid,Ref,_}, Pid, Ref) -> true;
  397. is_mon({Pid,Ref}, Pid, Ref) -> true;
  398. is_mon(_, _, _) ->
  399. false.
  400. remove_many(T, Scope, L, Pid) ->
  401. lists:flatmap(fun(K) ->
  402. Key = {T, Scope, K},
  403. remove_reg(Key, Pid, unreg, unreg_opts(Key, Pid))
  404. end, L).
  405. unreg_opts(Key, Pid) ->
  406. case ets:lookup(?TAB, {Pid, Key}) of
  407. [] ->
  408. [];
  409. [{_,r}] ->
  410. [];
  411. [{_,Opts}] ->
  412. Opts
  413. end.
  414. remove_reg_1({c,_,_} = Key, Pid) ->
  415. remove_counter_1(Key, ets:lookup_element(?TAB, Reg = {Key,Pid}, 3), Pid),
  416. Reg;
  417. remove_reg_1({r,_,_} = Key, Pid) ->
  418. remove_resource_1(Key, ets:lookup_element(?TAB, Reg = {Key,Pid}, 3), Pid),
  419. Reg;
  420. remove_reg_1({T,_,_} = Key, _Pid) when T==a; T==n; T==rc ->
  421. ets:delete(?TAB, Reg = {Key,T}),
  422. Reg;
  423. remove_reg_1({_,_,_} = Key, Pid) ->
  424. ets:delete(?TAB, Reg = {Key, Pid}),
  425. Reg.
  426. remove_counter_1({c,C,N} = Key, Val, Pid) ->
  427. Res = ets:delete(?TAB, {Key, Pid}),
  428. update_aggr_counter(C, N, -Val),
  429. Res.
  430. remove_resource_1({r,C,N} = Key, _, Pid) ->
  431. Res = ets:delete(?TAB, {Key, Pid}),
  432. update_resource_count(C, N, -1),
  433. Res.
  434. do_set_value({T,_,_} = Key, Value, Pid) ->
  435. K2 = if Pid == shared -> shared;
  436. T==n orelse T==a orelse T==rc -> T;
  437. true -> Pid
  438. end,
  439. try ets:lookup_element(?TAB, {Key,K2}, 2) of
  440. Pid ->
  441. ets:insert(?TAB, {{Key, K2}, Pid, Value});
  442. _ ->
  443. false
  444. catch
  445. error:_ -> false
  446. end.
  447. do_set_counter_value({_,C,N} = Key, Value, Pid) ->
  448. OldVal = ets:lookup_element(?TAB, {Key, Pid}, 3), % may fail with badarg
  449. Res = ets:insert(?TAB, {{Key, Pid}, Pid, Value}),
  450. update_aggr_counter(C, N, Value - OldVal),
  451. Res.
  452. update_counter({T,l,Ctr} = Key, Incr, Pid) when is_integer(Incr), T==c;
  453. is_integer(Incr), T==n ->
  454. Res = ets:update_counter(?TAB, {Key, Pid}, {3,Incr}),
  455. if T==c ->
  456. update_aggr_counter(l, Ctr, Incr);
  457. true ->
  458. ok
  459. end,
  460. Res;
  461. update_counter({T,l,Ctr} = Key, {Incr, Threshold, SetValue}, Pid)
  462. when is_integer(Incr), is_integer(Threshold), is_integer(SetValue), T==c;
  463. is_integer(Incr), is_integer(Threshold), is_integer(SetValue), T==n ->
  464. [Prev, New] = ets:update_counter(?TAB, {Key, Pid},
  465. [{3, 0}, {3, Incr, Threshold, SetValue}]),
  466. if T==c ->
  467. update_aggr_counter(l, Ctr, New - Prev);
  468. true ->
  469. ok
  470. end,
  471. New;
  472. update_counter({T,l,Ctr} = Key, Ops, Pid) when is_list(Ops), T==c;
  473. is_list(Ops), T==r;
  474. is_list(Ops), T==n ->
  475. case ets:update_counter(?TAB, {Key, Pid},
  476. [{3, 0} | expand_ops(Ops)]) of
  477. [_] ->
  478. [];
  479. [Prev | Rest] ->
  480. [New | _] = lists:reverse(Rest),
  481. if T==c ->
  482. update_aggr_counter(l, Ctr, New - Prev);
  483. true ->
  484. ok
  485. end,
  486. Rest
  487. end;
  488. update_counter(_, _, _) ->
  489. ?THROW_GPROC_ERROR(badarg).
  490. expand_ops([{Incr,Thr,SetV}|T])
  491. when is_integer(Incr), is_integer(Thr), is_integer(SetV) ->
  492. [{3, Incr, Thr, SetV}|expand_ops(T)];
  493. expand_ops([Incr|T]) when is_integer(Incr) ->
  494. [{3, Incr}|expand_ops(T)];
  495. expand_ops([]) ->
  496. [];
  497. expand_ops(_) ->
  498. ?THROW_GPROC_ERROR(badarg).
  499. update_aggr_counter(C, N, Val) ->
  500. ?MAY_FAIL(ets:update_counter(?TAB, {{a,C,N},a}, {3, Val})).
  501. decrement_resource_count(C, N) ->
  502. update_resource_count(C, N, -1).
  503. update_resource_count(C, N, Val) ->
  504. try ets:update_counter(?TAB, {{rc,C,N},rc}, {3, Val}) of
  505. 0 ->
  506. resource_count_zero(C, N);
  507. _ ->
  508. ok
  509. catch
  510. _:_ -> ok
  511. end.
  512. resource_count_zero(C, N) ->
  513. case ets:lookup(?TAB, {K = {rc,C,N},rc}) of
  514. [{_, Pid, _}] ->
  515. case get_attr(on_zero, Pid, K, undefined) of
  516. undefined -> ok;
  517. Actions ->
  518. perform_on_zero(Actions, C, N, Pid)
  519. end;
  520. _ -> ok
  521. end.
  522. perform_on_zero(Actions, C, N, Pid) ->
  523. lists:foreach(
  524. fun(A) ->
  525. try perform_on_zero_(A, C, N, Pid)
  526. catch error:_ -> ignore
  527. end
  528. end, Actions).
  529. perform_on_zero_({send, ToProc}, C, N, Pid) ->
  530. gproc:send(ToProc, {gproc, resource_on_zero, C, N, Pid}),
  531. ok;
  532. perform_on_zero_({bcast, ToProc}, C, N, Pid) ->
  533. gproc:bcast(ToProc, {gproc, resource_on_zero, C, N, Pid}),
  534. ok;
  535. perform_on_zero_(publish, C, N, Pid) ->
  536. gproc_ps:publish(C, gproc_resource_on_zero, {C, N, Pid}),
  537. ok;
  538. perform_on_zero_({unreg_shared, T,N}, C, _, _) ->
  539. K = {T, C, N},
  540. case ets:member(?TAB, {K, shared}) of
  541. true ->
  542. Objs = remove_reg(K, shared, unreg),
  543. _ = if C == g -> self() ! {gproc_unreg, Objs};
  544. true -> ok
  545. end,
  546. ok;
  547. false ->
  548. ok
  549. end;
  550. perform_on_zero_(_, _, _, _) ->
  551. ok.
  552. scan_existing_counters(Ctxt, Name) ->
  553. Head = {{{c,Ctxt,Name},'_'},'_','$1'},
  554. Cs = ets:select(?TAB, [{Head, [], ['$1']}]),
  555. lists:sum(Cs).
  556. scan_existing_resources(Ctxt, Name) ->
  557. Head = {{{r,Ctxt,Name},'_'},'_','_'},
  558. ets:select_count(?TAB, [{Head, [], [true]}]).
  559. valid_opts(Type, Default) ->
  560. Opts = get_app_env(Type, Default),
  561. check_opts(Type, Opts).
  562. check_opts(Type, Opts) when is_list(Opts) ->
  563. Check = check_option_f(Type),
  564. lists:map(fun(X) ->
  565. case Check(X) of
  566. true -> X;
  567. false ->
  568. erlang:error({illegal_option, X}, [Type, Opts])
  569. end
  570. end, Opts);
  571. check_opts(Type, Other) ->
  572. erlang:error(invalid_options, [Type, Other]).
  573. check_option_f(ets_options) -> fun check_ets_option/1;
  574. check_option_f(server_options) -> fun check_server_option/1.
  575. check_ets_option({read_concurrency , B}) -> is_boolean(B);
  576. check_ets_option({write_concurrency, B}) -> is_boolean(B);
  577. check_ets_option(_) -> false.
  578. check_server_option({priority, P}) ->
  579. %% Forbid setting priority to 'low' since that would
  580. %% surely cause problems. Unsure about 'max'...
  581. lists:member(P, [normal, high, max]);
  582. check_server_option(_) ->
  583. %% assume it's a valid spawn option
  584. true.
  585. get_app_env(Key, Default) ->
  586. case application:get_env(Key) of
  587. undefined -> Default;
  588. {ok, undefined} -> Default;
  589. {ok, Value} -> Value
  590. end.