gproc_eqc_tests.erl 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. %%% File : gproc_eqc_tests.erl
  2. %%% Author : <norton@alum.mit.edu>
  3. %%% : <Ulf.Wiger@erlang-consulting.com>
  4. %%% : <John.Hughes@quviq.com>
  5. %%% Description : QuickCheck test model for gproc
  6. %%% Created : 11 Dec 2008 by <John Hughes@JTABLET2007>
  7. -module(gproc_eqc_tests).
  8. -ifdef(EQC).
  9. -include_lib("eqc/include/eqc.hrl").
  10. -include_lib("eqc/include/eqc_statem.hrl").
  11. -ifdef(TEST).
  12. -include_lib("eunit/include/eunit.hrl").
  13. -endif.
  14. -compile(export_all).
  15. %%
  16. %% QUESTIONS:
  17. %%
  18. %% - does set_value for Class==a make sense?
  19. %% - shouldn't mreg return {true, keys()} rather than the {true,
  20. %% objects()} upon success?
  21. %%
  22. %% TODO:
  23. %%
  24. %% - implement mreg
  25. %% - implement send
  26. %% - implement info
  27. %% - implement select
  28. %% - implement first/next/prev/last
  29. %% - implement table
  30. %%
  31. %% records
  32. -record(key,
  33. {class %% class()
  34. , scope %% scope()
  35. , name %% name()
  36. }).
  37. -record(reg,
  38. {pid %% pid()
  39. , key %% key()
  40. , value %% int()
  41. }).
  42. -record(state,
  43. {pids=[] %% [pid()]
  44. , killed=[] %% [pid()]
  45. , regs=[] %% [reg()]
  46. , waiters=[] %% [{key(),pid()}]
  47. }).
  48. %% external API
  49. good_number_of_tests() ->
  50. 3000.
  51. %% hook to run from eunit. Note: a small number of tests.
  52. %% I recommend running at least 3000 to get to interesting stuff.
  53. %%
  54. gproc_test_() ->
  55. {timeout, 60, [fun() -> run(100) end]}.
  56. %% When run from eunit, we need to set the group leader so that EQC
  57. %% reporting (the dots) are made visible - that is, if that's what we want.
  58. verbose_run(N) ->
  59. erlang:group_leader(whereis(user), self()),
  60. run(N).
  61. %% 3000 tests seems like a large number, but this seems to be needed
  62. %% to reach enough variation in the tests.
  63. all_tests() ->
  64. eqc:module({numtests, good_number_of_tests()}, ?MODULE).
  65. run() ->
  66. run(good_number_of_tests()).
  67. run(Num) ->
  68. error_logger:delete_report_handler(error_logger_tty_h),
  69. eqc:quickcheck(eqc:numtests(Num, prop_gproc())).
  70. %% Command generator, S is the state
  71. command(S) ->
  72. oneof(
  73. %% spawn
  74. [ {call,?MODULE,spawn, []} ]
  75. %% where
  76. ++ [ {call,?MODULE,where, [key()]} ]
  77. ++ [ {call,?MODULE,await_new, [name_key()]} ]
  78. %% kill
  79. ++ [ oneof([
  80. %% kill
  81. {call,?MODULE,kill, [elements(S#state.pids)]}
  82. %% register
  83. , {call,?MODULE,reg, ?LET(Key, key(),[elements(S#state.pids), Key, reg_value(Key)])}
  84. %% unregister
  85. , {call,?MODULE,unreg, [elements(S#state.pids), key()]}
  86. %% many register
  87. , {call, ?MODULE, mreg, ?LET({Pid,Class,Scope}, {elements(S#state.pids),class(),scope()}, [Pid, Class, Scope, mreg_values(S, Class, Scope)])}
  88. %%, {call,?MODULE,mreg, [elements(S#state.pids), class(), scope()
  89. %% , list({name(), value()})]}
  90. %% set_value
  91. , {call,?MODULE,set_value, ?LET(Key, key(),[elements(S#state.pids), Key, reg_value(Key)])}
  92. %% update_counter
  93. , {call,?MODULE,update_counter, [elements(S#state.pids), key(), value()]}
  94. %% get_value
  95. , {call,?MODULE,get_value, [elements(S#state.pids), key()]}
  96. %% lookup_pid
  97. , {call,?MODULE,lookup_pid, [key()]}
  98. %% lookup_pids
  99. , {call,?MODULE,lookup_pids, [key()]}
  100. ])
  101. || S#state.pids/=[] ]
  102. ++ [
  103. %% await on existing value
  104. {call,?MODULE,await_existing, [elements(S#state.regs)]}
  105. || S#state.regs/=[] ]
  106. ).
  107. %% generator class
  108. class() -> elements([n,p,c,a]).
  109. %% generator scope
  110. scope() -> l.
  111. %% generator name
  112. name() -> elements(names()).
  113. names() -> [x,y,z,w].
  114. %% generator key
  115. key() -> key(class(), scope(), name()).
  116. key(Class, Scope, Name) ->
  117. #key{class=Class, scope=Scope, name=Name}.
  118. name_key() ->
  119. key(n, scope(), name()).
  120. %% generator value
  121. value() -> frequency([{8, int()}, {1, undefined}, {1, make_ref()}]).
  122. %% value for reg and set_value
  123. %% 'a' and 'c' should only have integers as values (reg: value is ignored for 'a')
  124. reg_value(#key{class=C}) when C == a; C == c -> int();
  125. reg_value(_) -> value().
  126. mreg_values(_S, Class, Scope) ->
  127. ?LET(Names, subset(names()),
  128. [?LET(K, key(Class, Scope, N), {K, reg_value(K)}) || N <- Names]).
  129. %% Snipped from the TrapExit QuickCheck tutorials
  130. %% http://trapexit.org/SubSetGenerator
  131. subset(Generators) ->
  132. ?LET(Keep,[ {bool(),G} || G<-Generators],
  133. [ G || {true,G}<-Keep]).
  134. %% helpers
  135. is_register_ok(_S,_Pid,#key{class=c},Value) when not is_integer(Value) ->
  136. false;
  137. is_register_ok(_S,_Pid,#key{class=a},Value) ->
  138. Value == undefined;
  139. is_register_ok(S,Pid,Key,_Value) ->
  140. [] == [ Pid1 || #reg{pid=Pid1,key=Key1}
  141. <- S#state.regs, is_register_eq(Pid,Key,Pid1,Key1) ].
  142. is_mreg_ok(S, Pid, List) ->
  143. lists:all(fun({Key, Value}) ->
  144. is_register_ok(S, Pid, Key, Value)
  145. end, List).
  146. is_register_eq(_PidA,#key{class=Class}=KeyA,_PidB,KeyB)
  147. when Class == n; Class ==a ->
  148. KeyA==KeyB;
  149. is_register_eq(PidA,KeyA,PidB,KeyB) ->
  150. PidA==PidB andalso KeyA==KeyB.
  151. is_unregister_ok(S,Pid,Key) ->
  152. [] /= [ Pid1 || #reg{pid=Pid1,key=Key1}
  153. <- S#state.regs, is_unregister_eq(Pid,Key,Pid1,Key1) ].
  154. is_unregister_eq(PidA,KeyA,PidB,KeyB) ->
  155. KeyA==KeyB andalso PidA==PidB.
  156. is_registered_and_alive(S,Pid,Key) ->
  157. is_unregister_ok(S,Pid,Key)
  158. andalso lists:member(Pid,S#state.pids).
  159. %% Initialize the state
  160. initial_state() ->
  161. #state{}.
  162. %% Next state transformation, S is the current state
  163. %% spawn
  164. next_state(S,V,{call,_,spawn,_}) ->
  165. S#state{pids=[V|S#state.pids]};
  166. %% kill
  167. next_state(S,_V,{call,_,kill,[Pid]}) ->
  168. S#state{pids=S#state.pids -- [Pid]
  169. , killed=[Pid|S#state.killed]
  170. , regs=[ X || #reg{pid=Pid1}=X <- S#state.regs, Pid/=Pid1 ]
  171. };
  172. %% reg
  173. next_state(S,_V,{call,_,reg,[Pid,Key,Value]}) ->
  174. case is_register_ok(S,Pid,Key,Value) of
  175. false ->
  176. S;
  177. true ->
  178. update_state_reg(S, Pid, Key, Value)
  179. end;
  180. next_state(S,_V,{call,_,mreg,[Pid, _Class, _Scope, List]}) ->
  181. case is_mreg_ok(S, Pid, List) of
  182. false ->
  183. S;
  184. true ->
  185. lists:foldl(
  186. fun({Key, Value}, Acc) ->
  187. update_state_reg(Acc, Pid, Key, Value)
  188. end, S, List)
  189. end;
  190. %% unreg
  191. next_state(S,_V,{call,_,unreg,[Pid,Key]}) ->
  192. case is_unregister_ok(S,Pid,Key) of
  193. false ->
  194. S;
  195. true ->
  196. FunC = fun(#reg{pid=Pid1,key=Key1}) -> (Pid==Pid1 andalso Key==Key1) end,
  197. case lists:partition(FunC, S#state.regs) of
  198. {[#reg{value=Value}], Others} ->
  199. S1 = S#state{regs=Others},
  200. case Key of
  201. #key{class=c,name=Name} ->
  202. %% update aggr counter
  203. FunA = fun(#reg{key=#key{class=Class1,name=Name1}}) -> (Class1 == a andalso Name==Name1) end,
  204. case lists:partition(FunA, S1#state.regs) of
  205. {[], _Others1} ->
  206. S1;
  207. {[Reg], Others1} ->
  208. S1#state{regs=[Reg#reg{value=Reg#reg.value-Value}|Others1]}
  209. end;
  210. _ ->
  211. S1
  212. end
  213. end
  214. end;
  215. %% set_value
  216. next_state(S,_V,{call,_,set_value,[Pid,Key,Value]}) ->
  217. case is_registered_and_alive(S,Pid,Key) of
  218. false ->
  219. S;
  220. true ->
  221. FunC = fun(#reg{pid=Pid1,key=Key1}) -> (Pid==Pid1 andalso Key==Key1) end,
  222. case lists:partition(FunC, S#state.regs) of
  223. {[#reg{value=OldValue}=OldReg], Others} ->
  224. S1 = S#state{regs=[OldReg#reg{value=Value}|Others]},
  225. case Key of
  226. #key{class=c,name=Name} ->
  227. %% aggr counter update
  228. FunA = fun(#reg{key=#key{class=Class1,name=Name1}}) -> (Class1 == a andalso Name==Name1) end,
  229. case lists:partition(FunA, S1#state.regs) of
  230. {[], _Others1} ->
  231. S1;
  232. {[Reg], Others1} ->
  233. S1#state{regs=[Reg#reg{value=Reg#reg.value-OldValue+Value}|Others1]}
  234. end;
  235. _ ->
  236. S1
  237. end
  238. end
  239. end;
  240. %% update_counter
  241. next_state(S,_V,{call,_,update_counter,[Pid,#key{class=Class}=Key,Incr]})
  242. when Class == c, is_integer(Incr) ->
  243. case is_registered_and_alive(S,Pid,Key) of
  244. false ->
  245. S;
  246. true ->
  247. FunC = fun(#reg{pid=Pid1,key=Key1}) -> (Pid==Pid1 andalso Key==Key1) end,
  248. case lists:partition(FunC, S#state.regs) of
  249. {[#reg{value=OldValue}=OldReg], Others} ->
  250. S1 = S#state{regs=[OldReg#reg{value=OldValue+Incr}|Others]},
  251. case Key of
  252. #key{class=c,name=Name} ->
  253. %% aggr counter update
  254. FunA = fun(#reg{key=#key{class=Class1,name=Name1}}) -> (Class1 == a andalso Name==Name1) end,
  255. case lists:partition(FunA, S1#state.regs) of
  256. {[], _Others1} ->
  257. S1;
  258. {[Reg], Others1} ->
  259. S1#state{regs=[Reg#reg{value=Reg#reg.value+Incr}|Others1]}
  260. end;
  261. _ ->
  262. S1
  263. end
  264. end
  265. end;
  266. next_state(S,V,{call,_,await_new,[Key]}) ->
  267. S#state{waiters = [{Key,V}|S#state.waiters]};
  268. %% otherwise
  269. next_state(S,_V,{call,_,_,_}) ->
  270. S.
  271. update_state_reg(S, Pid, Key, Value) ->
  272. case Key of
  273. #key{class=a,name=Name} ->
  274. %% initialize aggr counter
  275. FunC = fun(#reg{key=#key{class=Class1,name=Name1}}) -> (Class1 == c andalso Name==Name1) end,
  276. {Regs, _Others} = lists:partition(FunC, S#state.regs),
  277. InitialValue = lists:sum([ V || #reg{value=V} <- Regs ]),
  278. S#state{regs=[#reg{pid=Pid,key=Key,value=InitialValue}|S#state.regs]};
  279. #key{class=c,name=Name} ->
  280. S1 = S#state{regs=[#reg{pid=Pid,key=Key,value=Value}|S#state.regs]},
  281. %% update aggr counter
  282. FunA = fun(#reg{key=#key{class=Class1,name=Name1}}) -> (Class1 == a andalso Name==Name1) end,
  283. case lists:partition(FunA, S1#state.regs) of
  284. {[Reg], Others} ->
  285. S1#state{regs=[Reg#reg{value=Reg#reg.value+Value}|Others]};
  286. {[], _Others} ->
  287. S1
  288. end;
  289. _ ->
  290. S#state{regs=[#reg{pid=Pid,key=Key,value=Value}|S#state.regs],
  291. waiters = [W || {K,_} = W <- S#state.waiters,
  292. K =/= Key]}
  293. end.
  294. %% Precondition, checked before command is added to the command
  295. %% sequence
  296. precondition(S, {call,_,reg, [Pid, _Key, _Value]}) ->
  297. lists:member(Pid, S#state.pids);
  298. precondition(S, {call,_,unreg, [Pid, _Key]}) ->
  299. lists:member(Pid, S#state.pids);
  300. precondition(S, {call,_,await_new,[#key{class=C}=Key]}) ->
  301. C == n andalso
  302. not lists:keymember(Key,#reg.key,S#state.regs);
  303. precondition(S, {call,_,mreg,[Pid, Class, _Scope, List]}) ->
  304. %% TODO: lift this restriction to generate all classes mreg can handle
  305. Class == n andalso
  306. lists:member(Pid, S#state.pids) andalso
  307. lists:all(fun({#key{class=C},_}) -> C == n end, List);
  308. precondition(S, {call,_,await_existing,[#reg{key=#key{class=C}=Key}]}) ->
  309. C == n andalso
  310. lists:keymember(Key, #reg.key, S#state.regs);
  311. precondition(S,{call,_,get_value,[Pid,_]}) ->
  312. lists:member(Pid,S#state.pids);
  313. precondition(_S,{call,_,_,_}) ->
  314. true.
  315. %% Postcondition, checked after command has been evaluated. S is the
  316. %% state before next_state(S,_,<command>)
  317. %% spawn
  318. postcondition(_S,{call,_,spawn,_},_Res) ->
  319. true;
  320. %% kill
  321. postcondition(_S,{call,_,kill,_},_Res) ->
  322. true;
  323. %% where
  324. postcondition(S,{call,_,where,[#key{class=Class}=Key]},Res) ->
  325. if Class == n orelse Class == a ->
  326. case lists:keysearch(Key,#reg.key,S#state.regs) of
  327. {value, #reg{pid=Pid}} ->
  328. Res == Pid;
  329. false ->
  330. Res == undefined
  331. end;
  332. true ->
  333. case Res of
  334. {'EXIT', {badarg, _}} ->
  335. true;
  336. _ ->
  337. false
  338. end
  339. end;
  340. %% reg
  341. postcondition(S,{call,_,reg,[Pid,Key,Value]},Res) ->
  342. case Res of
  343. true ->
  344. is_register_ok(S,Pid,Key,Value) andalso
  345. check_waiters(Pid, Key, Value, S#state.waiters);
  346. {'EXIT', {badarg, _}} ->
  347. is_unregister_ok(S,Pid,Key)
  348. orelse not is_register_ok(S,Pid,Key,Value)
  349. end;
  350. postcondition(S,{call,_,mreg,[Pid,_Class,_Scope,List]},Res) ->
  351. case Res of
  352. true ->
  353. is_mreg_ok(S,Pid,List)
  354. andalso lists:all(fun({K,V}) ->
  355. check_waiters(Pid,K,V, S#state.waiters)
  356. end, List);
  357. {'EXIT', {badarg,_}} ->
  358. not is_mreg_ok(S,Pid,List)
  359. end;
  360. %% unreg
  361. postcondition(S,{call,_,unreg,[Pid,Key]},Res) ->
  362. case Res of
  363. true ->
  364. is_unregister_ok(S,Pid,Key);
  365. {'EXIT', {badarg, _}} ->
  366. not is_unregister_ok(S,Pid,Key)
  367. end;
  368. %% set_value
  369. postcondition(S,{call,_,set_value,[Pid,Key,_Value]},Res) ->
  370. case Res of
  371. true ->
  372. is_registered_and_alive(S,Pid,Key);
  373. {'EXIT', {badarg, _}} ->
  374. not is_registered_and_alive(S,Pid,Key)
  375. orelse (Key#key.class == c
  376. andalso is_registered_and_alive(S, Pid, Key))
  377. end;
  378. %% update_counter
  379. postcondition(S,{call,_,update_counter,[Pid,#key{class=Class}=Key,Incr]},Res)
  380. when Class == c, is_integer(Incr) ->
  381. case [ Value1 || #reg{pid=Pid1,key=Key1,value=Value1} <- S#state.regs
  382. , (Pid==Pid1 andalso Key==Key1) ] of
  383. [] ->
  384. case Res of {'EXIT', {badarg, _}} -> true; _ -> false end;
  385. [Value] when is_integer(Value) ->
  386. Res == Value+Incr;
  387. [_] ->
  388. case Res of {'EXIT', {badarg, _}} -> true; _ -> false end
  389. end;
  390. postcondition(_S,{call,_,update_counter,[_Pid,_Key,_Incr]},Res) ->
  391. case Res of {'EXIT', {badarg, _}} -> true; _ -> false end;
  392. %% get_value
  393. postcondition(S,{call,_,get_value,[Pid,Key]},Res) ->
  394. case [ Value1 || #reg{pid=Pid1,key=Key1,value=Value1} <- S#state.regs
  395. , (Pid==Pid1 andalso Key==Key1) ] of
  396. [] ->
  397. case Res of {'EXIT', {badarg, _}} -> true; _ -> false end;
  398. [Value] ->
  399. Res == Value
  400. end;
  401. %% lookup_pid
  402. postcondition(S,{call,_,lookup_pid,[#key{class=Class}=Key]},Res)
  403. when Class == n; Class == a ->
  404. case [ Pid1 || #reg{pid=Pid1,key=Key1} <- S#state.regs
  405. , Key==Key1 ] of
  406. [] ->
  407. case Res of {'EXIT', {badarg, _}} -> true; _ -> false end;
  408. [Pid] ->
  409. Res == Pid
  410. end;
  411. postcondition(_S,{call,_,lookup_pid,[_Key]},Res) ->
  412. case Res of {'EXIT', {badarg, _}} -> true; _ -> false end;
  413. %% lookup_pids
  414. postcondition(S,{call,_,lookup_pids,[#key{class=Class}=Key]},Res)
  415. when Class == n; Class == a; Class == c; Class == p ->
  416. Pids = [ Pid1 || #reg{pid=Pid1,key=Key1} <- S#state.regs
  417. , Key==Key1 ],
  418. lists:sort(Res) == lists:sort(Pids);
  419. postcondition(_S, {call,_,await_new,[#key{}]}, Pid) ->
  420. is_pid(Pid);
  421. postcondition(S,{call,_,await_existing,[#reg{key=Key}]}, {P1,V1}) ->
  422. case lists:keyfind(Key, #reg.key, S#state.regs) of
  423. #reg{pid=P1, value = V1} -> true;
  424. _ -> false
  425. end;
  426. %% postcondition(_S,{call,_,lookup_pids,[_Key]},Res) ->
  427. %% case Res of {'EXIT', {badarg, _}} -> true; _ -> false end;
  428. %% otherwise
  429. postcondition(_S,{call,_,_,_},_Res) ->
  430. false.
  431. %%% Spec fixes
  432. %%%
  433. %%% - added precondition for set_value must be integer (could be changed
  434. %%% to neg. test)
  435. %%% - updated postcondition for update_counter to check for non-integers
  436. %%%
  437. %%% It still crashes on lists:sum in next_state... Maybe we should change
  438. %%% the generators instead!
  439. prop_gproc() ->
  440. ?FORALL(Cmds,commands(?MODULE),
  441. ?TRAPEXIT(
  442. begin
  443. ok = stop_app(),
  444. ok = start_app(),
  445. {H,S,Res} = run_commands(?MODULE,Cmds),
  446. kill_all_pids({H,S}),
  447. %% whenfail
  448. ?WHENFAIL(
  449. begin
  450. io:format("~nHISTORY:"),
  451. if
  452. length(H) < 1 ->
  453. io:format(" none~n");
  454. true ->
  455. CmdsH = eqc_statem:zip(Cmds,H),
  456. [ begin
  457. {Cmd,{State,Reply}} = lists:nth(N,CmdsH),
  458. io:format("~n #~p:~n\tCmd: ~p~n\tReply: ~p~n\tState: ~p~n",
  459. [N,Cmd,Reply,State])
  460. end
  461. || N <- lists:seq(1,length(CmdsH)) ]
  462. end,
  463. io:format("~nRESULT:~n\t~p~n",[Res]),
  464. io:format("~nSTATE:~n\t~p~n",[S])
  465. end,
  466. Res == ok)
  467. end)).
  468. %% helpers
  469. start_app() ->
  470. case application:start(gproc) of
  471. {error, {already_started,_}} ->
  472. stop_app(),
  473. ok = application:start(gproc);
  474. ok ->
  475. ok
  476. end.
  477. stop_app() ->
  478. case application:stop(gproc) of
  479. {error, {not_started,_}} ->
  480. ok;
  481. ok ->
  482. ok
  483. end.
  484. %% If using the scheduler... This code needs to run in a separate
  485. %% module, so it can be compiled without instrumentation.
  486. kill_all_pids(Pid) when is_pid(Pid) ->
  487. case is_process_alive(Pid) of
  488. true ->
  489. exit(Pid,kill);
  490. false ->
  491. ok
  492. end;
  493. kill_all_pids(Tup) when is_tuple(Tup) ->
  494. kill_all_pids(tuple_to_list(Tup));
  495. kill_all_pids([H|T]) ->
  496. kill_all_pids(H),
  497. kill_all_pids(T);
  498. kill_all_pids(_) ->
  499. ok.
  500. %% spawn
  501. spawn() ->
  502. spawn(fun() -> loop() end).
  503. loop() ->
  504. receive
  505. {From, Ref, F} ->
  506. From ! {Ref, catch F()},
  507. loop();
  508. stop -> ok
  509. end.
  510. %% kill
  511. kill(Pid) ->
  512. exit(Pid,foo),
  513. timer:sleep(10).
  514. %% where
  515. where(#key{class=Class,scope=Scope,name=Name}) ->
  516. catch gproc:where({Class,Scope,Name}).
  517. %% reg
  518. reg(Pid,#key{class=Class,scope=Scope,name=Name},Value) ->
  519. do(Pid, fun() -> catch gproc:reg({Class,Scope,Name},Value) end).
  520. mreg(Pid, Class, Scope, List) ->
  521. do(Pid, fun() -> catch gproc:mreg(Class,Scope,[{Name,Value} || {#key{name = Name}, Value} <- List]) end).
  522. %% unreg
  523. unreg(Pid,#key{class=Class,scope=Scope,name=Name}) ->
  524. do(Pid, fun() -> catch gproc:unreg({Class,Scope,Name}) end).
  525. %% set_value
  526. set_value(Pid,#key{class=Class,scope=Scope,name=Name},Value) ->
  527. do(Pid, fun() -> catch gproc:set_value({Class,Scope,Name},Value) end).
  528. %% update_counter
  529. update_counter(Pid, #key{class=Class,scope=Scope,name=Name},Incr) ->
  530. do(Pid, fun() -> catch gproc:update_counter({Class,Scope,Name},Incr) end).
  531. %% get_value
  532. get_value(Pid,#key{class=Class,scope=Scope,name=Name}) ->
  533. do(Pid, fun() -> catch gproc:get_value({Class,Scope,Name}) end).
  534. %% lookup_pid
  535. lookup_pid(#key{class=Class,scope=Scope,name=Name}) ->
  536. catch gproc:lookup_pid({Class,Scope,Name}).
  537. %% lookup_pids
  538. lookup_pids(#key{class=Class,scope=Scope,name=Name}) ->
  539. catch gproc:lookup_pids({Class,Scope,Name}).
  540. %% do
  541. do(Pid, F) ->
  542. Ref = erlang:monitor(process, Pid),
  543. Pid ! {self(), Ref, F},
  544. receive
  545. {'DOWN', Ref, process, Pid, Reason} ->
  546. {'EXIT', {'DOWN', Reason}};
  547. {Ref, Result} ->
  548. erlang:demonitor(Ref),
  549. Result
  550. after 3000 ->
  551. {'EXIT', timeout}
  552. end.
  553. await_existing(#reg{key = #key{class=Class,scope=Scope,name=Name}}) ->
  554. %% short timeout, this call is expected to work
  555. gproc:await({Class,Scope,Name}, 10000).
  556. await_new(#key{class=Class,scope=Scope,name=Name}) ->
  557. spawn(
  558. fun() ->
  559. Res = (catch gproc:await({Class,Scope,Name})),
  560. receive
  561. {From, send_result} ->
  562. From ! {result, Res},
  563. timer:sleep(1000)
  564. end
  565. end).
  566. check_waiters(Pid, Key, Value, Waiters) ->
  567. case [W || {K, W} <- Waiters,
  568. K == Key] of
  569. [] ->
  570. true;
  571. WPids ->
  572. lists:all(fun(WPid) ->
  573. check_waiter(WPid, Pid, Key, Value)
  574. end, WPids)
  575. end.
  576. check_waiter(WPid, Pid, _Key, Value) ->
  577. MRef = erlang:monitor(process, WPid),
  578. WPid ! {self(), send_result},
  579. receive
  580. {result, Res} ->
  581. {Pid,Value} == Res;
  582. {'DOWN', MRef, _, _, R} ->
  583. erlang:error(R)
  584. after 1000 ->
  585. erlang:error(timeout)
  586. end.
  587. -endif.