PageRenderTime 61ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/proc/src/proc.erl

http://github.com/gebi/jungerl
Erlang | 1651 lines | 802 code | 178 blank | 671 comment | 24 complexity | cdec37ea41d52bc5d8d6804bc5039128 MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. %%%
  2. %%% The contents of this file are subject to the Erlang Public License,
  3. %%% Version 1.0, (the "License"); you may not use this file except in
  4. %%% compliance with the License. You may obtain a copy of the License at
  5. %%% http://www.erlang.org/license/EPL1_0.txt
  6. %%%
  7. %%% Software distributed under the License is distributed on an "AS IS"
  8. %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %%% the License for the specific language governing rights and limitations
  10. %%% under the License.
  11. %%%
  12. %%% The Original Code is proc-1.0.
  13. %%%
  14. %%% The Initial Developer of the Original Code is Ericsson AB
  15. %%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB
  16. %%% All Rights Reserved.
  17. %%%
  18. %%% Contributor(s): ______________________________________.
  19. %%%----------------------------------------------------------------------
  20. %%% #0. BASIC INFORMATION
  21. %%%----------------------------------------------------------------------
  22. %%% File: proc.erl
  23. %%% Author : Ulf Wiger <ulf.wiger@ericsson.com>
  24. %%% Description : Flexible Local Process Registry
  25. %%%
  26. %%%----------------------------------------------------------------------
  27. %%% @doc Flexible Local Process Registry
  28. %%%
  29. %%% <p>This application supports local registration of processes using any
  30. %%% erlang term. Furthermore, each process can register by several
  31. %%% different names at the same time.</p>
  32. %%%
  33. %%% <h2>Semantics:</h2> <p>`proc' works with unique `names' and
  34. %%% non-unique `properties'. </p><p>A name can be any erlang term
  35. %%% (except a pid or '_'), and each process can register itself under many
  36. %%% different unique names.
  37. %%% A `property' can be any term (except '_'), and must be unique
  38. %%% within a process, but several processes can register the same
  39. %%% property.</p>
  40. %%% <p>Furthermore, there are "fold" and "select"
  41. %%% functions to allow for efficient iteration over names or properties in the
  42. %%% process registry.</p>
  43. %%% <p>Many of the operations to access names and properties rely on
  44. %%% the semantics given by {@link //stdlib/ets}, and this is often
  45. %%% mentioned in the interface documentation. This means that it is
  46. %%% safe to assume that the functions conform to the search semantics
  47. %%% of ordered_set ets tables. However, it is <i>not</i> safe to assume
  48. %%% anything about whether the data in fact resides in ets tables, and
  49. %%% if so, what those tables are called, or indeed how the data is
  50. %%% represented internally.</p>
  51. %%% @end
  52. -module(proc).
  53. -id('102/190 55-CNA 113 33 Ux').
  54. -vsn('/main/R1A/16').
  55. -date('2005-12-09').
  56. -behaviour(gen_server).
  57. %%%----------------------------------------------------------------------
  58. %%% Template Id: ETX/B 00201 - 19055/1 Rev C
  59. %%%
  60. %%% #Copyright (C) 1996
  61. %%% by ERICSSON TELECOM AB
  62. %%% S - 125 26 STOCKHOLM
  63. %%% SWEDEN, tel int + 46 8 719 0000
  64. %%%
  65. %%% The program may be used and/or copied only with the written permission from
  66. %%% ERICSSON TELECOM AB, or in accordance with the terms and conditions
  67. %%% stipulated in the agreement/contract under which the program has been
  68. %%% supplied.
  69. %%%
  70. %%% All rights reserved
  71. %%%
  72. %%%----------------------------------------------------------------------
  73. %%% #1. REVISION LOG
  74. %%%----------------------------------------------------------------------
  75. %%% Rev Date Name What
  76. %%% ----- ------- -------- -------------------------------------
  77. %%% R1A/1 20050630 ebcfsva Checked Ulf Wiger's code into CC
  78. %%% R1A/2 20050704 etxuwig Renamed tables, use proc_tabs, etc.
  79. %%% R1A/3 20050816 etxuwig Fixed fold_keys, added await_reg()
  80. %%% R1A/4 20050901 etxuwig Added load regulation support, etc.
  81. %%% R1A/5 20050902 etxuwig Changed semantics of non-unique keys
  82. %%% R1A/6 20050902 etxuwig Changed names (API)
  83. %%% R1A/7 20050905 etxuwig update_property -> replace_property
  84. %%%----------------------------------------------------------------------
  85. %%% #2. EXPORT LISTS
  86. %%%----------------------------------------------------------------------
  87. %%% #2.1 EXPORTED INTERFACE FUNCTIONS
  88. %%%----------------------------------------------------------------------
  89. -export([
  90. %% Functions for registering, unregistering, sending,
  91. reg/1,
  92. unreg/1,
  93. where/1,
  94. send/2,
  95. %% New functions for setting properies (what used to be (Key,Value))
  96. add_property/1, add_property/2,
  97. is_property/1, is_property/2,
  98. replace_property/2, replace_property/3,
  99. del_property/1, del_property/2,
  100. set_access/1,
  101. %% Counters
  102. add_counter/2, add_counter/3,
  103. read_counter/1, read_counter/2,
  104. del_counter/1, del_counter/2,
  105. update_counter/2, update_counter/3,
  106. is_counter/1, is_counter/2,
  107. guards/1,
  108. %% Utility function allowing for notifications when someone registers
  109. await_reg/1, clear_await_reg/1,
  110. %%
  111. pids/1, pids/2,
  112. select_pattern/1, select_pattern/2,
  113. %% Functions for searching properties
  114. select_properties/1, select_properties/2,
  115. fold_properties/3, fold_properties/4, fold_properties/5,
  116. select_fold_properties/3, select_fold_properties/4,
  117. select_fold_properties/5,
  118. properties_by_pid/1, properties_by_pid/2, properties_by_pid/3,
  119. %% Functions for searching unique names
  120. select_names/1, select_names/2,
  121. select/1,
  122. fold_names/3, fold_names/4, fold_names/5,
  123. first/1,
  124. next/2,
  125. previous/2,
  126. last/1
  127. ]).
  128. -export([info/1,
  129. info/2]).
  130. -export([enable_i/0]).
  131. %%% gen_server start function
  132. -export([start_link/0]).
  133. %%% utility functions
  134. -export([i/1]).
  135. %%% gen_server callbacks
  136. -export([init/1,
  137. handle_call/3,
  138. handle_cast/2,
  139. handle_info/2,
  140. terminate/2,
  141. code_change/3]).
  142. %%-export([start/0]).
  143. -include_lib("stdlib/include/ms_transform.hrl").
  144. -define(REG, proc).
  145. -define(REG_REV, procRev).
  146. -define(PROPS, procProps).
  147. -define(PROPS_REV, procPropsRev).
  148. -define(PRIVS, procPrivs).
  149. -define(MONITORS, procMonitors).
  150. -define(BADARG, throw(badarg)).
  151. -define(EACCESS, throw({badarg,access})).
  152. %% start() ->
  153. %% application:start(proc).
  154. %%% ===================== (unique) Names =========================
  155. %%% @spec reg(Name) -> true
  156. %%% @doc Register a unique `Name'. Returns `true' or exits.
  157. %%% <p>This function differs from `erlang:register(Name,Pid)' in
  158. %%% the following ways:</p>
  159. %%% <ul>
  160. %%% <li>Pid is implicit: always the current process.</li>
  161. %%% <li>Name can be any term (except a pid or '_'), not just an atom.</li>
  162. %%% <li>A process can be registered under several unique names.</li>
  163. %%% </ul>
  164. %%% @end
  165. %%%
  166. reg(Pid) when is_pid(Pid) ->
  167. erlang:error(badarg);
  168. reg('_') ->
  169. erlang:error(badarg);
  170. reg(Name) ->
  171. call({reg, Name}).
  172. %%% @spec unreg(Name) -> true
  173. %%% @doc Unregister `Name'
  174. %%% <p>This function exits if `Name' isn't registered to the current
  175. %%% process.</p>
  176. %%% @end
  177. %%%
  178. unreg(Name) ->
  179. call({unreg, Name}).
  180. %%% @spec where(Name) -> pid() | undefined
  181. %%% @doc Analogous to `erlang:whereis(Name)'
  182. %%%
  183. where(Name) ->
  184. case ets:lookup(?REG, Name) of
  185. [{_, Pid}] ->
  186. case erlang:is_process_alive(Pid) of
  187. true ->
  188. Pid;
  189. false ->
  190. undefined
  191. end;
  192. [] ->
  193. undefined
  194. end.
  195. %%% @spec send(Name, Msg) -> Msg
  196. %%% @doc Analogous to `Name ! Msg', except that send({Node,Dest}, Msg)
  197. %%% will be interpreted as 'send to the local process registered as
  198. %%% {Node, Dest}'. If Name is a pid, this function will send the message
  199. %%% to the process with process identifier Name (recall that it is not
  200. %%% possible to register a pid as a unique name in proc.)
  201. %%% @end
  202. %%%
  203. send(Pid, Msg) when is_pid(Pid) ->
  204. Pid ! Msg;
  205. send(Name, Msg) ->
  206. case where(Name) of
  207. undefined ->
  208. erlang:error(badarg);
  209. Pid when is_pid(Pid) ->
  210. Pid ! Msg
  211. end.
  212. %%% @spec await_reg(Name) -> {already_registered,pid()} | Reg
  213. %%% @doc Request to be notified when someone registers as `Name'.
  214. %%% <p>If `Name' is already registered, the function returns
  215. %%% with the value `{already_registered, Pid}'. Otherwise, it returns
  216. %%% a "reference" (not necessarily of type `reference()') that can be used
  217. %%% to recognize a future message.</p>
  218. %%% <p>When some process `P' registers as `Name', a message on the form
  219. %%% `{sysProgReg, Ref, reg, Name, Pid}' is sent to each waiting process
  220. %%% (note that Ref will be different for each waiting process.)</p>
  221. %%% @end
  222. %%%
  223. await_reg(Pid) when is_pid(Pid) ->
  224. erlang:error(badarg);
  225. await_reg('_') ->
  226. erlang:error(badarg);
  227. await_reg(Name) ->
  228. call({await_reg, Name}).
  229. %%% @spec clear_await_reg(Ref) -> true
  230. %%% @doc Cancel waiting for notification of process registration of a
  231. %%% given name.
  232. %%% <p>This function exits with `badarg' if Ref does not appear to be
  233. %%% a valid reference returned by {@link await_reg/1}; otherwise returns
  234. %%% `true'.</p>
  235. %%% @end
  236. %%%
  237. clear_await_reg(Ref) ->
  238. call({clear_await_reg, Ref}).
  239. %%% ================ (non-unique) Properties =====================
  240. %%% @spec add_property(Property::term()) -> true
  241. %%% @doc Publish a property of the current process.
  242. %%% <p>If `Property' is already published for the current process,
  243. %%% this function exits with badarg.</p>
  244. %%%
  245. %%% <p>This operation can be viewed as publishing the meta-data `Property'
  246. %%% as part of the interface of the process. Several processes may publish
  247. %%% the same property. The process can be found (as part of a group)
  248. %%% through this property, using one of the functions {@link
  249. %%% fold_properties/3}, {@link pids/1}, et al.</p>
  250. %%% @end
  251. %%%
  252. add_property('_') ->
  253. erlang:error(badarg);
  254. add_property(Property) ->
  255. call({add_property, self(), Property}).
  256. %%% @spec add_property(Process, Property) -> true
  257. %%% @doc Publish a property on behalf of another process.
  258. %%% <p>Adding properties on behalf of another process is only
  259. %%% allowed if the other process has called {@link set_access/1},
  260. %%% giving this process the proper rights to act as proxy.</p>
  261. %%% <p>`Process' can be either a `pid()' or a registered name.</p>
  262. %%% @end
  263. %%%
  264. add_property(_Process, '_') ->
  265. erlang:error(badarg);
  266. add_property(Process, Property) ->
  267. call({add_property, Process, Property}).
  268. %%% @spec set_access(Access::[{Action,Processes,Ops}]) -> true
  269. %%% Action = grant | revoke
  270. %%% Ops = [Op]
  271. %%% Op = add_property | replace_property | del_property | properties |
  272. %%% add_counter | update_counter | del_counter | counters
  273. %%% @doc Control the ability of Processes to perform certain functions
  274. %%% on behalf of the current process.
  275. %%% <p>`Op' must be one of</p>
  276. %%% <ul>
  277. %%% <li>`add_property'</li>
  278. %%% <li>`replace_property'</li>
  279. %%% <li>`del_property'</li>
  280. %%% <li>`properties' (perform all operations on properties)</li>
  281. %%% <li>`add_counter'</li>
  282. %%% <li>`update_counter'</li>
  283. %%% <li>`del_counter'</li>
  284. %%% <li>`counters' (perform all operations on counters)</li>
  285. %%% </ul>
  286. %%% <p>`Processes' is a list of registered names and/or pids.</p>
  287. %%% <p>Example:</p><pre>
  288. %%% set_access([{grant, ["foo","bar"], [update_counter]},
  289. %%% {revoke, ["baz"], [properties]}])</pre>
  290. %%% <p>Allows the processes registered as `"foo"' and `"bar"' to update
  291. %%% counters, and makes sure that `"baz"' is no longer able to add, delete
  292. %%% or replace properties.</p>
  293. %%% @end
  294. %%%
  295. set_access(Access) when is_list(Access) ->
  296. call({set_access, Access}).
  297. %%% @spec is_property(Property) -> true | false
  298. %%% @doc Returns `true' if `Property' is a published property of the current
  299. %%% process.
  300. %%% @end
  301. %%%
  302. is_property(Property) ->
  303. is_property(self(), Property).
  304. %%% @spec is_property(Process, Property) -> true | false
  305. %%% @doc Check whether Property is in fact a property registered with Process.
  306. %%% <p>`Process' can be either a pid or a registered name.</p>
  307. %%% @end
  308. %%%
  309. is_property(Pid, Property) when is_pid(Pid) ->
  310. ets:member(?PROPS_REV, {Pid, Property});
  311. is_property(Process, Property) ->
  312. Pid = on_pid(Process),
  313. ets:member(?PROPS_REV, {Pid, Property}).
  314. %%% @spec is_counter(Property) -> true | false
  315. %%% @doc Check whether `Property' is a counter property.
  316. %%% <p>Counter properties are created through {@link add_counter/2}, and
  317. %%% behave as normal properties with the following exceptions:</p>
  318. %%% <ul>
  319. %%% <li>A counter property has an integer value attached, which can be
  320. %%% updated using {@link update_counter/2}</li>
  321. %%% <li>Counter properties cannot be replaced using
  322. %%% {@link replace_property/2}</li>
  323. %%% </ul>
  324. %%% @end
  325. %%%
  326. is_counter(Property) ->
  327. is_counter(self(), Property).
  328. %%% @spec is_counter(Process, Property) -> true | false
  329. %%% @doc Check whether `Propery' registered under `Process' is a counter
  330. %%% property.
  331. %%% <p>`Process' can be either a pid or a registered name.</p>
  332. %%% @end
  333. %%%
  334. is_counter(Pid, Property) when is_pid(Pid) ->
  335. (ets:select_count(?PROPS_REV, [{{{Pid,Property},counter},[],[true]}]) > 0);
  336. is_counter(Process, Property) ->
  337. Pid = on_pid(Process),
  338. (ets:select_count(?PROPS_REV, [{{{Pid,Property},counter},[],[true]}]) > 0).
  339. %%% @spec replace_property(OldProperty, NewProperty) -> true
  340. %%% @doc Equivalent to del_property(OldProperty), add_property(NewProperty),
  341. %%% but much more efficient.
  342. %%% <p>If `OldProperty' is not a published property of the current process
  343. %%% (see {@link add_property/1}), the function exits with `badarg'.</p>
  344. %%% <p>One could use properties as a "published process dictionary",
  345. %%% e.g. by replacing `put(Tag,Value)' with
  346. %%% `proc:add_property({Tag,Value})', and `get(Tag)' with (roughly)
  347. %%% `proc:properties_by_pid(self(),Tag)'.
  348. %%% While this would be somewhat less efficient than the built-in process
  349. %%% dictionary, it has the advantage of allowing other processes to key
  350. %%% on process meta-data in a much more efficient and disciplined way than
  351. %%% `{_,Dict} = process_info(Pid,dictionary), lists:keysearch(Tag,1,Dict)'
  352. %%% (Which makes no distinction between public and private data, and therefore
  353. %%% is rightfully frowned upon, and hopefully never attempted.)"</p>
  354. %%% <p><i>Note:</i> This function does not work on counter properties.</p>
  355. %%% @end
  356. %%%
  357. replace_property(_OldProp, '_') ->
  358. erlang:error(badarg);
  359. replace_property(OldProperty, NewProperty) ->
  360. call({replace_property, self(), OldProperty, NewProperty}).
  361. %%% @spec replace_property(Process, OldProperty, NewProperty) -> true
  362. %%% @doc Replace `OldProperty' with `NewProperty' on behalf of `Process'.
  363. %%% <p>This function is allowed only if permission to replace properties
  364. %%% has been given through `Process' calling {@link grant_access/2}.</p>
  365. %%% <p>If permission to replace properties has not been given, this function
  366. %%% exits with `access'.</p>
  367. %%% @end
  368. %%%
  369. replace_property(_Process, _OldProperty, '_') ->
  370. erlang:error(badarg);
  371. replace_property(Process, OldProperty, NewProperty) ->
  372. call({replace_property, Process, OldProperty, NewProperty}).
  373. %%% @spec del_property(Property) -> true
  374. %%% @doc Un-publishes the property `Property' for the current process.
  375. %%% <p>If there is no published property `Property'
  376. %%% (see {@link add_property/1}), for the current process, the function
  377. %%% exits with `badarg'.</p>
  378. %%% @end
  379. %%%
  380. del_property(Property) ->
  381. call({del_property, self(), Property}).
  382. %%% @spec del_property(Process, Property) -> true
  383. %%% @doc Un-publish the property `Property' on behalf of `Process'.
  384. %%% <p>This function is allowed only if permission to delete properties
  385. %%% has been given through `Process' calling {@link grant_access/2}.</p>
  386. %%% <p>If permission to delete properties has not been given, this function
  387. %%% exits with `access'.</p>
  388. %%% @end
  389. %%%
  390. del_property(Process, Property) ->
  391. call({del_property, Process, Property}).
  392. %%% @spec add_counter(Name, InitialValue::integer()) -> true
  393. %%% @doc Publish a counter property for the current process.
  394. %%% <p>Counter properties behave as normal properties with
  395. %%% the following exceptions:</p>
  396. %%% <ul>
  397. %%% <li>A counter property has an integer value attached, which can be
  398. %%% updated using {@link update_counter/2}</li>
  399. %%% <li>Counter properties cannot be replaced using
  400. %%% {@link replace_property/2}</li>
  401. %%% </ul>
  402. %%% @end
  403. %%%
  404. add_counter('_', _Initial) ->
  405. erlang:error(badarg);
  406. add_counter(Name, InitialValue) when is_integer(InitialValue) ->
  407. call({add_counter, self(), Name, InitialValue}).
  408. %%% @spec add_counter(Process, Name, InitialValue::integer()) -> true
  409. %%% @doc Publish a counter property on behalf of `Process'.
  410. %%% <p>This function is allowed only if permission to add counters
  411. %%% has been given through `Process' calling {@link grant_access/2}.</p>
  412. %%% <p>If permission to add counters has not been given, this function
  413. %%% exits with `access'.</p>
  414. %%% @end
  415. %%%
  416. add_counter(_Process, '_', _Initial) ->
  417. erlang:error(badarg);
  418. add_counter(Process, Name, InitialValue) when is_integer(InitialValue) ->
  419. call({add_counter, Process, Name, InitialValue}).
  420. %%% @spec del_counter(Name) -> true
  421. %%% @doc Un-publish a counter property for the current process.
  422. %%%
  423. del_counter(Name) ->
  424. call({del_counter, self(), Name}).
  425. %%% @spec del_counter(Process, Name) -> true
  426. %%% @doc Un-publish a counter property on behalf of `Process'.
  427. %%% <p>This function is allowed only if permission to delete counters
  428. %%% has been given through `Process' calling {@link grant_access/2}.</p>
  429. %%% <p>If permission to delete counters has not been given, this function
  430. %%% exits with `access'.</p>
  431. %%% @end
  432. %%%
  433. del_counter(Process, Name) ->
  434. call({del_counter, Process, Name}).
  435. %%% @spec read_counter(Name) -> integer()
  436. %%% @doc Read the value of a counter property for the current process.
  437. %%%
  438. read_counter(Name) ->
  439. do_read_counter(self(), Name).
  440. %%% @spec read_counter(Process, Name) -> integer()
  441. %%% @doc Read the value of a counter property published by `Process'.
  442. %%%
  443. read_counter(Pid, Name) when is_pid(Pid) ->
  444. do_read_counter(Pid, Name);
  445. read_counter(Process, Name) ->
  446. case where(Process) of
  447. Pid when is_pid(Pid) -> do_read_counter(Pid, Name);
  448. _ ->
  449. erlang:error(badarg)
  450. end.
  451. do_read_counter(Pid, Name) ->
  452. case ets:lookup_element(?PROPS, {Name, Pid}, 2) of
  453. I when is_integer(I) ->
  454. I;
  455. _ ->
  456. erlang:error(badarg)
  457. end.
  458. %%% @spec update_counter(Name, Incr) -> integer()
  459. %%% @doc Update the counter attribute of a counter property.
  460. %%% <p>This function only works on counter properties. The `Incr' argument
  461. %%% is passed as-is to {@link //stlib/ets:update_counter/3}.
  462. %%% If a complex option, such as `{Pos, Incr}' is used,
  463. %%% `Pos' must be equal to 2.</p>
  464. %%% @end
  465. %%%
  466. update_counter(Name, Incr) ->
  467. ets:update_counter(?PROPS, {Name, self()}, Incr).
  468. %%% @spec update_counter(Process, Name, Incr) -> integer()
  469. %%% @doc Update counter attribute on behalf of `Process'.
  470. %%% <p>This function is allowed only if permission to update counters
  471. %%% has been given through `Process' calling {@link grant_access/2}.</p>
  472. %%% <p>If permission to update counters has not been given, this function
  473. %%% exits with `access'.</p>
  474. %%% @end
  475. %%%
  476. update_counter(Process, Name, Value) ->
  477. try begin
  478. Pid = on_pid(Process),
  479. verify_access(Pid, update_counter, self()),
  480. ets:update_counter(?PROPS, {Name, Pid}, Value)
  481. end
  482. catch
  483. throw:Error ->
  484. erlang:error(Error)
  485. end.
  486. %%% @spec pids(Property) -> [pid()]
  487. %%% @doc Returns all pids for which a property `Property' is published.
  488. %%% <p>To be more precise, `Property' can be any pattern that would be
  489. %%% accepted by {@link //stdlib/ets:select/2}, e.g. &apos;_&apos;</p>
  490. %%% <p>If one imagines an `ordered_set ets' table `Tab' where all properties
  491. %%% are stored on the form `{{Property,Pid},1}', then calling
  492. %%% this function is equivalent to calling
  493. %%% <pre>ets:select(Tab, [{{{Property,'$1'},'_'}, [], ['$1']}]).</pre></p>
  494. %%% <p>Note that this is also true as regards performance. If the `Key'
  495. %%% pattern is unbound, the operation will be a linear search.</p>
  496. %%% @end
  497. %%%
  498. pids(Property) ->
  499. pids(Property, []).
  500. %%% @spec pids(Property, Guards::list()) -> [pid()]
  501. %%% @doc Similar to {@link pids/1}, but also allowing select guards.
  502. %%% <p>If one imagines an `ordered_set ets' table `Tab' where all non-unique
  503. %%% keys are stored on the form `{{Property,Pid},1}', then calling
  504. %%% this function is equivalent to calling
  505. %%% <pre>ets:select(Tab, [{{{Property,'$1'},'_'}, Guards, ['$1']}]).</pre>
  506. %%% </p>
  507. %%% <p>Note that this is also true as regards performance. If the `Property'
  508. %%% pattern is unbound, the operation will be a linear search.</p>
  509. %%% @end
  510. %%%
  511. pids(Property, Guards) ->
  512. ets:select(?PROPS, [{{{Property,'_'},'_'}, Guards,
  513. [{element,2,{element,1,'$_'}}]}]).
  514. %%% --
  515. %%% @spec fold_properties(Fun::function(), Acc, Property) -> NewAcc
  516. %%% @doc Similar to {@link //stdlib/lists:foldl/3}, but based on a
  517. %%% select pattern on `Property', identifying a sub-set of
  518. %%% published properties.
  519. %%% <p>For each matching key, a call is made to
  520. %%% <pre>Fun({Property1,Pid1}, Acc1)</pre>, which is expected to
  521. %%% return `NewAcc'. Note that, as in {@link pids/1}, `Property'
  522. %%% can be select a pattern. The following expression would return a
  523. %%% list of all published instances of `Property' together with the processes
  524. %%% that registered them:</p>
  525. %%% <pre>
  526. %%% proc:fold_properties(fun(X,Acc) -> [X|Acc] end, [], '_')
  527. %%% </pre>
  528. %%% <p>This function is equivalent to `fold_properties(Fun,Acc,Property,1)',
  529. %%% (see {@link fold_properties/4}.) See also {@link select_pattern/1}.</p>
  530. %%% @end
  531. %%%
  532. fold_properties(Fun, Acc, Property) ->
  533. fold_properties(Fun, Acc, Property, 1, fun(_) -> true end).
  534. %%% @spec fold_properties(Fun::function(), Acc, Key, Limit::integer()) ->
  535. %%% NewAcc
  536. %%% @doc Like {@link fold_properties/3}, but works in batches of `Limit'
  537. %%% objects at a time, in order to improve raw performance.
  538. %%% <p>`fold_properties/3' uses a default limit of 1 object at a time.</p>
  539. %%% <p>This function is equivalent to
  540. %%% `fold_properties(Fun,Acc,Property,Limit,fun(_) -> true end)',
  541. %%% (see {@link fold_properties/5}.) See also {@link select_pattern/1}.</p>
  542. %%% @end
  543. %%%
  544. fold_properties(Fun, Acc, Properties, Limit) ->
  545. fold_properties(Fun, Acc, Properties, Limit, fun(_) -> true end).
  546. %%% @spec fold_properties(Fun::function(), Acc, Property,
  547. %%% Limit::integer(), Regulator::function()) -> NewAcc
  548. %%% @doc Like {@link fold_properties/4}, but applies a "Regulator function"
  549. %%% after each batch of objects before proceeding with the next one.
  550. %%% <p>The `Regulator' function is expected to take the current accumulator
  551. %%% as an argument and return either `true' (in which case processing
  552. %%% continues), or `false' (in which case the `fold_properties/5' function
  553. %%% breaks and returns the current accumulator. See also
  554. %%% {@link select_pattern/1}.</p>
  555. %%% @end
  556. fold_properties(Fun, Acc, Property, Limit, Regulator) ->
  557. select_fold_properties(
  558. Fun, Acc, [{Property,[],[true]}], Limit, Regulator).
  559. %%% @spec select_pattern(What) -> Patterns
  560. %%% @doc Helper function to generate select patterns from the more
  561. %%% intuitive patterns `Property' (or `Name', as it works equally for names).
  562. %%% <p>The return value from this function can be used in e.g.
  563. %%% {@link select_properties/1}, {@link select_properties/2},
  564. %%% {@link select_fold_properties/3},
  565. %%% {@link select_fold_properties/4} or {@link select_fold_properties/5},
  566. %%% or similarly, {@link select_names/1}, {@link select_names/2},
  567. %%% {@link select_fold_names/3}, {@link select_fold_names/4}, or
  568. %%% {@link select_fold_names/5}.</p>
  569. %%% <p>`Patterns' is a list of match specifications on the same form as
  570. %%% that used by {@link //stdlib/ets:select_delete/2} and
  571. %%% {@link //stdlib/ets:select_count/2},
  572. %%% that is:</p>
  573. %%% <pre>
  574. %%% * Patterns = [MatchFunction] | '_'
  575. %%% * MatchFunction = {MatchHead, [Guard], [Result]}
  576. %%% * MatchHead = "Pattern as in ets:match"
  577. %%% * Guard = {"Guardtest name", ...}
  578. %%% * Result = true (if object is to be included)
  579. %%% </pre>
  580. %%% @end
  581. select_pattern(What) ->
  582. [{What, [], [true]}].
  583. %%% @spec select_pattern(What, Guards) -> Patterns
  584. %%% @doc As {@link select_pattern/1}, but with the option to add Guard
  585. %%% expressions.
  586. %%%
  587. select_pattern(What, Guards) ->
  588. [{What, Guards, [true]}].
  589. %%% @spec guards(List) -> NewList
  590. %%% @doc Expand a list of `select' guards, allowing for the pseudo guard
  591. %%% `is_counter'.
  592. %%% <p>How counter properties can be distinguished from regular properties
  593. %%% internally is not defined in the proc interface. Using this function,
  594. %%% it is however possible to specify a guard test, `is_counter', which will
  595. %%% expand to a legal guard. The `is_counter' test can be combined with the
  596. %%% standard logical operators, `not', `and', `or', `orelse', `andalso',
  597. %%% and `xor'.</p>
  598. %%% <p>Example:</p>
  599. %%% <pre>
  600. %%% proc:select_properties(
  601. %%% [{'_', proc:guards([{'not', is_counter}]), [true]}]).
  602. %%% </pre>
  603. %%% <p>will list all published properties that are not counters.</p>
  604. %%% @end
  605. %%%
  606. %%% Note that this function must work for both PROPS and PROPS_REV.
  607. %%% For now, this affects counters. The second element of a counter
  608. %%% must be an integer, in both PROPS and PROPS_REV.
  609. %%%
  610. guards([is_counter|Gs]) ->
  611. [{is_integer, {element, 2, '$_'}} | guards(Gs)];
  612. guards([{'not', G}|Gs]) ->
  613. [G1] = guards([G]),
  614. [{'not', G1} | guards(Gs)];
  615. guards([G|Gs]) when is_tuple(G), size(G) > 1 ->
  616. case element(1,G) of
  617. Op when Op == 'and';
  618. Op == 'or';
  619. Op == 'andalso';
  620. Op == 'xor';
  621. Op == 'orelse' ->
  622. Gs1 = tl(tuple_to_list(G)),
  623. Gs2 = guards(Gs1),
  624. [list_to_tuple([Op|Gs2]) | guards(Gs)];
  625. _ ->
  626. [G | guards(Gs)]
  627. end;
  628. guards([G|Gs]) ->
  629. [G | guards(Gs)];
  630. guards([]) ->
  631. [].
  632. %%% @spec select_properties(Patterns) -> {Objs, Continuation} | '$end_of_table'
  633. %%% @doc Returns `{Property,Pid}' objects one at a time based on
  634. %%% a given selection of all registered instances of `Key'.
  635. %%% <p>`Patterns' can be written as follows:</p>
  636. %%% <pre>
  637. %%% * Patterns = [MatchFunction] | '_'
  638. %%% * MatchFunction = {MatchHead, [Guard], [Result]}
  639. %%% * MatchHead = "Pattern as in ets:match"
  640. %%% * Guard = {"Guardtest name", ...}
  641. %%% * Result = true (if object is to be included)
  642. %%% </pre>
  643. %%% <p>or generated using {@link select_pattern/1} or
  644. %%% {@link select_pattern/2}.</p>
  645. %%% <p>For convenience, the special pattern '_' is also accepted.
  646. %%% It expands to <code>[{'_', [], [true]}]</code>.</p>
  647. %%% <p>The `Continuation' returned can be used when calling {@link select/1}
  648. %%% in order to continue processing.</p>
  649. %%% @end
  650. %%%
  651. select_properties(Patterns) ->
  652. select_properties(Patterns, 1).
  653. %%% @spec select_properties(Patterns, Limit::integer()) ->
  654. %%% {Objs, Continuation} | '$end_of_table'
  655. %%% @doc Like {@link select_properties/1} but works in batches of `Limit'
  656. %%% objects at a time in order to improve raw performance.
  657. %%%
  658. select_properties(Patterns0, Limit) ->
  659. Patterns = if Patterns0 == '_' ->
  660. [{'_',[],[true]}];
  661. true ->
  662. Patterns0
  663. end,
  664. Patterns1 = [{{{P,'_'},'_'},G,[{{{element,1,{element,1,'$_'}},
  665. {element,2,{element,1,'$_'}}}}]} ||
  666. {P,G,[true]} <- Patterns],
  667. ets:select(?PROPS, Patterns1, Limit).
  668. %%% @spec select_fold_properties(Fun, Acc, Patterns) -> NewAcc
  669. %%% @doc Like {@link fold_properties/3} but more flexible as it allows
  670. %%% for combining several patterns into one query.
  671. %%% <p>`Patterns' is composed as for {@link select_properties/1}.</p>
  672. %%% @end
  673. %%%
  674. select_fold_properties(Fun, Acc, Patterns) ->
  675. select_fold_properties(Fun, Acc, Patterns, 1).
  676. %%% @spec select_fold_properties(Fun, Acc, Patterns, Limit) -> NewAcc
  677. %%% @doc Like {@link select_fold_properties/3}, but works in batches of `Limit'
  678. %%% objects at a time, in order to improve raw performance.
  679. %%% <p>`select_fold_properties/3' uses a default limit of 1 object at a
  680. %%% time.</p>
  681. %%% <p>This function is equivalent to
  682. %%% `select_fold_properties(Fun,Acc,Patterns,Limit,fun(_) -> true end)',
  683. %%% (see {@link select_fold_properties/5}.)</p>
  684. %%% @end
  685. select_fold_properties(Fun, Acc, Patterns, Limit) ->
  686. select_fold_properties(Fun, Acc, Patterns, Limit, fun(_) -> true end).
  687. %%% @spec select_fold_properties(Fun::function(), Acc, Property,
  688. %%% Limit::integer(), Regulator::function()) -> NewAcc
  689. %%% @doc Like {@link select_fold_properties/4}, but applies a
  690. %%% "Regulator function"
  691. %%% after each batch of objects before proceeding with the next one.
  692. %%% <p>The `Regulator' function is expected to take the current accumulator
  693. %%% as an argument and return either `true' (in which case processing
  694. %%% continues), or `false' (in which case the `select_fold_properties/5'
  695. %%% function breaks and returns the current accumulator.</p>
  696. %%% @end
  697. select_fold_properties(Fun, Acc, Patterns, Limit, Regulator) ->
  698. Res = select_properties(Patterns, Limit),
  699. select_each_property(Res, Fun, Acc, Regulator).
  700. select_each_property({Objs, Continuation}, Fun, Acc, Regulator) ->
  701. NewAcc = lists:foldl(
  702. fun({_Prop,Pid} =X, Acc1) when is_pid(Pid) ->
  703. Fun(X, Acc1)
  704. end, Acc, Objs),
  705. case Regulator(NewAcc) of
  706. true ->
  707. select_each_property(ets:select(Continuation),
  708. Fun, NewAcc, Regulator);
  709. false ->
  710. NewAcc
  711. end;
  712. select_each_property('$end_of_table', _, Acc, _Regulator) ->
  713. Acc.
  714. %%% @spec select_names(Patterns) -> {Objs, Continuation} | '$end_of_table'
  715. %%% @doc Returns `{Name,Pid}' objects one at a time based on
  716. %%% a given selection of all registered unique names.
  717. %%% <p>`Patterns' can be written as follows:</p>
  718. %%% <pre>
  719. %%% * Patterns = [MatchFunction] | '_'
  720. %%% * MatchFunction = {MatchHead, [Guard], [Result]}
  721. %%% * MatchHead = "Pattern as in ets:match"
  722. %%% * Guard = {"Guardtest name", ...}
  723. %%% * Result = true (if object is to be included)
  724. %%% </pre>
  725. %%% <p>or generated using {@link select_pattern/1} or
  726. %%% {@link select_pattern/2}.</p>
  727. %%% <p>The function is <i>almost</i> equivalent to
  728. %%% `ets:select(Tab,Patterns, 100)' on an `ordered_set' table `Tab'
  729. %%% with the following representation:</p>
  730. %%% <pre>{{Key, Value}, Pid}</pre>
  731. %%% <p>The difference compared to `ets:select/3' is the `Result' expression.
  732. %%% In this function, `Result' is expected to be &apos;true&apos; for those
  733. %%% objects that are to be included.</p>
  734. %%% <p>For convenience, the special pattern '_' is accepted,
  735. %%% and expanded to <code>[{'_', [], [true]}]</code>.</p>
  736. %%% <p>Any other patterns are ignored.</p>
  737. %%% <p>The `Continuation' returned can be used when calling {@link select/1}
  738. %%% in order to continue processing.</p>
  739. %%% @end
  740. %%%
  741. select_names(Patterns) ->
  742. select_names(Patterns, 100).
  743. %%% @spec select_names(Patterns, Limit) ->
  744. %%% {Objs, Continuation} | '$end_of_table'
  745. %%% @doc Like {@link select_names/1}, but works in batches of `Limit'
  746. %%% objects at a time, in order to improve raw performance.
  747. %%%
  748. select_names(Patterns0, Limit) ->
  749. Patterns = if Patterns0 == '_' ->
  750. [{'_',[],[true]}];
  751. true ->
  752. Patterns0
  753. end,
  754. Patterns1 =
  755. [{P,[{is_pid,{element,2,'$_'}}|G],
  756. [{{{element,1,'$_'},
  757. {element,2,'$_'}}}]} ||
  758. {P,G,[true]} <- Patterns],
  759. ets:select(?REG, Patterns1, Limit).
  760. %%% @spec fold_names(Fun::function(), Acc, Patterns) -> NewAcc
  761. %%% @doc Similar to {@link lists:foldl/3}, but based on a list of select
  762. %%% patterns, identifying a sub-set of unique names.
  763. %%% <p>For each matching name, a call is made to
  764. %%% <pre>Fun({Name1,Pid1}, Acc1)</pre>, which is expected to
  765. %%% return `NewAcc'.</p>
  766. %%% <p>`Patterns' is a list of match specifications on the same form as
  767. %%% that used by {@link ets:select_delete/2} and {@link ets:select_count/2},
  768. %%% that is:</p>
  769. %%% <pre>
  770. %%% * Patterns = [MatchFunction] | '_'
  771. %%% * MatchFunction = {MatchHead, [Guard], [Result]}
  772. %%% * MatchHead = "Pattern as in ets:match"
  773. %%% * Guard = {"Guardtest name", ...}
  774. %%% * Result = true (if object is to be included)
  775. %%% </pre>
  776. %%% <p>The following expression would return a
  777. %%% list of all registered unique names together with the processes
  778. %%% that registered them:</p>
  779. %%% <pre>
  780. %%% proc:fold_names(fun(X,Acc) -> [X|Acc] end, [], [{'_',[],[true]}])
  781. %%% </pre>
  782. %%% <p>For convenience, the special pattern '_' is also allowed. It is
  783. %%% expanded to <code>[{'_',[],[true]}]</code>.</p>
  784. %%% <p>This function is equivalent to `fold_names(Fun,Acc,Patterns,100)',
  785. %%% (see {@link fold_names/5}.)</p>
  786. %%% @end
  787. %%%
  788. fold_names(Fun, Acc, Patterns) ->
  789. fold_names(Fun, Acc, Patterns, 100).
  790. %%% @spec fold_names(Fun, Acc, Patterns, Limit::integer()) -> NewAcc
  791. %%% @doc Like {@link fold_names/3}, but works in batches of `Limit' objects
  792. %%% at a time, in order to improve memory characteristics.
  793. %%% <p>`fold_names/3' uses a default limit of 1 object at a time.</p>
  794. %%% <p>This function is equivalent to
  795. %%% `fold_names(Fun,Acc,Patterns,Limit,fun(_) -> true end)',
  796. %%% (see {@link fold_names/5}.)</p>
  797. %%% @end
  798. %%%
  799. fold_names(Fun, Acc, Patterns, Limit) ->
  800. fold_names(Fun, Acc, Patterns, Limit, fun(_) -> true end).
  801. %%% @spec fold_names(Fun::function(), Acc, Patterns,
  802. %%% Limit::integer(), Regulator::function()) ->
  803. %%% NewAcc
  804. %%% @doc Like {@link fold_names/4}, but applies a "Regulator function"
  805. %%% after each batch of objects before proceeding with the next one.
  806. %%% <p>The `Regulator' function is expected to take the current accumulator
  807. %%% as an argument and return either `true' (in which case processing
  808. %%% continues), or `false' (in which case the `fold_names/5' function breaks
  809. %%% and returns the current accumulator.</p>
  810. %%% @end
  811. %%%
  812. fold_names(Fun, Acc, Patterns, Limit, Regulator) ->
  813. Res = select_names(Patterns, Limit),
  814. select_each_name(Res, Fun, Acc, Regulator).
  815. select_each_name({Objs, Continuation}, Fun, Acc, Regulator) ->
  816. NewAcc = lists:foldl(
  817. fun(Key, Acc1) ->
  818. Fun(Key, Acc1)
  819. end, Acc, Objs),
  820. case Regulator(NewAcc) of
  821. true ->
  822. select_each_name(ets:select(Continuation), Fun, NewAcc, Regulator);
  823. false ->
  824. NewAcc
  825. end;
  826. select_each_name('$end_of_table', _, Acc, _Regulator) ->
  827. Acc.
  828. %%% @spec select(Continuation) -> {[Obj], NewContinuation} | '$end_of_table'
  829. %%% @doc Analogous to {@link ets:select/1}.
  830. %%% <p>This function is intended to be called with a `Continuation'
  831. %%% returned from {@link select_names/1} or {@link select_properties/2}.</p>
  832. %%% @end
  833. %%%
  834. select(Continuation) ->
  835. ets:select(Continuation).
  836. %%% @spec first(Type:: names | properties) -> Key | '$end_of_table'
  837. %%% @doc Analogous to {@link ets:first/1}.
  838. %%% <p>The tables corresponding to `names' and `properties' both have
  839. %%% `ordered_set' semantics.</p>
  840. %%% @end
  841. first(properties) ->
  842. ets:first(?PROPS);
  843. first(names) ->
  844. ets:first(?REG).
  845. %%% @spec next(Type:: names | properties, Key) -> Key | '$end_of_table'
  846. %%% @doc Analogous to {@link ets:next/2}.
  847. %%% <p>The tables corresponding to `names' and `properties' both have
  848. %%% `ordered_set' semantics. The key format of the `properties' table is
  849. %%% `{Property, Pid}', while the key format of the `names' table is
  850. %%% simply the name. Note that `names' and `properties' are not likely
  851. %%% the physical names of the actual tables.</p>
  852. %%% @end
  853. next(properties, Prev) ->
  854. ets:next(?PROPS, Prev);
  855. next(names, Prev) ->
  856. ets:next(?REG, Prev).
  857. %%% @spec previous(Type:: names | properties, Key) -> Key | '$end_of_table'
  858. %%% @doc Analogous to {@link ets:previous/2}.
  859. %%% <p>The tables corresponding to `names' and `properties' both have
  860. %%% `ordered_set' semantics. The key format of the `properties' table is
  861. %%% `{Property, Pid}', while the key format of the `names' table is
  862. %%% simply the name. Note that `names' and `properties' are not likely
  863. %%% the physical names of the actual tables.</p>
  864. %%% @end
  865. previous(properties, {_Prop, Pid} = Next) when is_pid(Pid) ->
  866. ets:prev(?PROPS, Next);
  867. previous(names, Next) ->
  868. ets:prev(?REG, Next).
  869. %%% @spec last(Type:: names | properties) -> Key | '$end_of_table'
  870. %%% @doc Analogous to {@link ets:last/1}.
  871. %%% <p>The tables corresponding to `names' and `properties' both have
  872. %%% `ordered_set' semantics.</p>
  873. %%% @end
  874. last(properties) ->
  875. ets:last(?PROPS);
  876. last(names) ->
  877. ets:last(?REG).
  878. %%% --
  879. %%% @spec properties_by_pid(pid()) -> [Property]
  880. %%% @doc Lists all properties for a given process.
  881. %%%
  882. properties_by_pid(P) when is_pid(P) ->
  883. properties_by_pid(P, '_', []).
  884. %%% @spec properties_by_pid(P::pid(), KeyPat) ->
  885. %%% [Property]
  886. %%% @doc Lists all properties for a given process that match the pattern
  887. %%% KeyPat.
  888. %%% <p>Equivalend to `properties_by_pid(P, KeyPat, [])'.</p>
  889. %%% @end
  890. %%%
  891. properties_by_pid(P, KeyPat) ->
  892. properties_by_pid(P, KeyPat, []).
  893. %%% @spec properties_by_pid(P::pid(), KeyPat, Guards) -> [Property]
  894. %%% @doc Like {@link properties_by_pid/2}, but with an added list of guards.
  895. %%% <p>The Guards list may be one returned from the function {@link guards/1}.
  896. %%% </p>
  897. %%% @end
  898. %%%
  899. properties_by_pid(P, KeyPat, Guards) ->
  900. ets:select(?PROPS_REV, [{{{P,KeyPat},'_'}, Guards,
  901. [{element,2,{element,1,'$_'}}]}]).
  902. %%% @spec info(Process) -> [{Tag, Info}] | undefined
  903. %%% @doc Like `process_info(PidOfProcess)', but with added
  904. %%% proc-related attributes.
  905. %%% <p>This function calls `process_info(PidOfProcess)', and adds to the
  906. %%% result the following info items:</p>
  907. %%% <ul>
  908. %%% <li>`{pid, pid()}' -- the pid of the process</li>
  909. %%% <li>`{names, Names}' -- all unique proc names
  910. %%% registered by the process</li>
  911. %%% <li>`{properties, Props}' -- all non-unique properties</li>
  912. %%% </ul>
  913. %%% @end
  914. %%%
  915. info(NameOrPid) ->
  916. info(NameOrPid, all).
  917. %%% @spec info(Process, Attr) -> {Attr, Info}
  918. %%% @doc Like `process_info(PidOfProcess, Attr)' but accepts some
  919. %%% additional attributes.
  920. %%% <p>All attributes supported by {@link erlang:process_info/2} are
  921. %%% supported by this function. In addition, the following attributes are
  922. %%% handled:</p>
  923. %%% <ul>
  924. %%% <li>`pid' -- the pid of the process (mainly here for symmetry)</li>
  925. %%% <li>`names' -- all unique names registered in proc by `Process'</li>
  926. %%% <li>`properties' -- all non-unique properties of `Process'</li>
  927. %%% </ul>
  928. %%% @end
  929. info(NameOrPid, Attr) ->
  930. case NameOrPid of
  931. P when is_pid(P) -> pid_info(P, Attr);
  932. N -> case where(N) of
  933. undefined ->
  934. undefined;
  935. P ->
  936. pid_info(P, Attr)
  937. end
  938. end.
  939. pid_info(P, all) when is_pid(P) ->
  940. Names = case pid_info(P, names) of
  941. undefined -> [];
  942. {names, Ns} -> Ns
  943. end,
  944. Keys = properties_by_pid(P),
  945. [{pid, P},
  946. {names, Names},
  947. {properties, Keys} | process_info(P)];
  948. pid_info(P, pid) when is_pid(P) ->
  949. {pid, P};
  950. pid_info(P, names) when is_pid(P) ->
  951. {names,
  952. ets:select(?REG_REV, [{{{P,'$1'},'_'}, [], ['$1']}])};
  953. pid_info(P, properties) when is_pid(P) ->
  954. {properties, properties_by_pid(P)};
  955. pid_info(P, Attr) when is_pid(P), is_atom(Attr) ->
  956. process_info(P, Attr).
  957. %%% @spec i(Option) -> ok
  958. %%% Option = help | [{Op, Type, Patterns}]
  959. %%% Op = i | x
  960. %%% Type = names | properties
  961. %%% @doc Like {@link c:i/0}, but allows for filtering output with
  962. %%% `proc' names and properties.
  963. %%% <p>Usage: `i([{i | x, names | properties, Patterns}])'.<br/>
  964. %%% calls `c:i(Processes)' for a subset of all running processes.
  965. %%% `{i, Type, Pattern}' specifies by name or property which processes
  966. %%% to include. `{x, Type, Pattern}' specifies which processes to
  967. %%% exclude from the listing. If no `{i,T,P}' tuples are specified,
  968. %%% all processes are included per default, then reduced by the
  969. %%% `{x,T,P}' patterns. Multiple tuples are allowed.
  970. %%% `Pattern' is a match specification where the result head is `true'.</p>
  971. %%% <p>Note that currently, using {@link ets:fun2ms/1} may have severe impact
  972. %%% on performance if there is a large number of registered names or
  973. %%% keys.</p>
  974. %%% @end
  975. %%%
  976. i(help) ->
  977. io:format(
  978. "~s~n",
  979. ["------------------------------------------------------------------\n"
  980. "Usage: i([{i | x, names | properties, Patterns}]).\n\n"
  981. "calls c:i(Processes) for a subset of all running processes.\n"
  982. "{i, Type, Pattern} specifies by name or property which processes\n"
  983. "to include. {x, Type, Pattern} specifies which processes to\n"
  984. "exclude from the listing. If no {i,T,P} tuples are specified,\n"
  985. "all processes are included per default, then reduced by the\n"
  986. "{x,T,P} patterns. Multiple tuples are allowed.\n\n"
  987. "Pattern is a match specification where the result head is true.\n"
  988. "Note that currently, using ets:fun2ms/1 may have severe impact\n"
  989. "on performance if there is a large number of registered names or\n"
  990. "keys.\n\n"
  991. "(Note: 'name' uniquely identifies one process; 'property' is non-unique.)\n"
  992. "-------------------------------------------------------------------\n"]);
  993. i(Options) when is_list(Options) ->
  994. Temp = ets:new(temp, [ordered_set]),
  995. case lists:keymember(i,1,Options) of
  996. true ->
  997. i_names(Options, Temp),
  998. i_props(Options, Temp);
  999. false ->
  1000. lists:foreach(
  1001. fun(Pid) ->
  1002. ets:insert(Temp, {Pid, 1})
  1003. end, processes())
  1004. end,
  1005. x_names(Options, Temp),
  1006. x_props(Options, Temp),
  1007. Pids = ets:select(Temp, ets:fun2ms(fun({P,_}) ->
  1008. P
  1009. end)),
  1010. ets:delete(Temp),
  1011. c:i(Pids).
  1012. i_names(Opts, Temp) ->
  1013. Pats =
  1014. case lists:member({i,names,'_'}, Opts) of
  1015. true ->
  1016. [{'_',[],[true]}];
  1017. false ->
  1018. lists:concat([P || {i, names, P} <- Opts])
  1019. end,
  1020. case Pats of
  1021. [] ->
  1022. true;
  1023. _ ->
  1024. fold_names(
  1025. fun({_Name, Pid}, _) ->
  1026. ets:insert(Temp, {Pid,1})
  1027. end, [], Pats)
  1028. end.
  1029. i_props(Opts, Temp) ->
  1030. Pats =
  1031. case lists:member({i,properties,'_'}, Opts) of
  1032. true ->
  1033. [{'_',[],[true]}];
  1034. false ->
  1035. lists:concat([P || {i, properties, P} <- Opts])
  1036. end,
  1037. case Pats of
  1038. [] ->
  1039. true;
  1040. _ ->
  1041. select_fold_properties(
  1042. fun({_Prop, Pid}, _) ->
  1043. ets:insert(Temp, {Pid, 1})
  1044. end, [], Pats)
  1045. end.
  1046. x_names(Opts, Temp) ->
  1047. case lists:concat([P || {x, names, P} <- Opts]) of
  1048. [] ->
  1049. true;
  1050. Pats ->
  1051. fold_names(
  1052. fun({_Name, Pid}, _) ->
  1053. ets:delete(Temp, Pid)
  1054. end, [], Pats)
  1055. end.
  1056. x_props(Opts, Temp) ->
  1057. case lists:concat([P || {x, properties, P} <- Opts]) of
  1058. [] ->
  1059. true;
  1060. Pats ->
  1061. select_fold_properties(
  1062. fun({_Prop, Pid}, _) ->
  1063. ets:delete(Temp, Pid)
  1064. end, [], Pats)
  1065. end.
  1066. %%% Gen_server callbacks
  1067. %%% @spec start_link() -> {ok, pid()}
  1068. %%% @doc Starts the proc server.
  1069. %%% <p>`proc' assumes that the `proc_tabs' module is available, and that
  1070. %%% the `proc_tabs' process is running.</p>
  1071. %%% @end
  1072. %%%
  1073. start_link() ->
  1074. gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  1075. %%% @hidden
  1076. init([]) ->
  1077. ensure_tabs_created(),
  1078. set_monitors(),
  1079. {ok, []}.
  1080. %%% @hidden
  1081. handle_call(Req, From, S) ->
  1082. try handle_call1(Req, From, S)
  1083. catch
  1084. throw:Return ->
  1085. {reply, Return, S}
  1086. end.
  1087. %%% @hidden
  1088. handle_cast(Msg, S) ->
  1089. {stop, {unknown_cast, Msg}, S}.
  1090. %%% @hidden
  1091. handle_info({'DOWN', _MRef, process, Pid, _}, S) ->
  1092. process_is_down(Pid),
  1093. {noreply, S};
  1094. handle_info(_, S) ->
  1095. {noreply, S}.
  1096. process_is_down(Pid) ->
  1097. Pattern = {{Pid, '_'}, '_'},
  1098. Regs = ets:match_object(?REG_REV, Pattern),
  1099. lists:foreach(
  1100. fun({{_, I}, 1}) ->
  1101. ets:delete(?REG, I);
  1102. ({{_, I}, w}) ->
  1103. [{_Name, {awaiting_reg, Ps}}] = ets:lookup(?REG, I),
  1104. case lists:keydelete(Pid, 1, Ps) of
  1105. [] ->
  1106. ets:delete(?REG, I);
  1107. [_|_] = PsLeft ->
  1108. ets:insert(?REG, {I, {awaiting_reg, PsLeft}})
  1109. end
  1110. end, Regs),
  1111. ets:match_delete(?REG_REV, Pattern),
  1112. PPattern = {{Pid,'_'},'_'},
  1113. Properties = ets:match_object(?PROPS_REV, PPattern),
  1114. lists:foreach(
  1115. fun({{_, Prop}, _}) ->
  1116. ets:delete(?PROPS, {Prop,Pid})
  1117. end, Properties),
  1118. ets:match_delete(?PROPS_REV, PPattern),
  1119. ets:match_delete(?PRIVS, {{Pid,'_','_'}}),
  1120. ets:delete(?MONITORS, Pid).
  1121. %%% @hidden
  1122. terminate(_Reason, _S) ->
  1123. ok.
  1124. %%% @hidden
  1125. code_change(_FromVsn, State, _Extra) ->
  1126. {ok, State}.
  1127. %%% broken out from handle_call/3
  1128. handle_call1({reg, Name}, {Pid,_} = From, S) ->
  1129. CompleteReg = fun() ->
  1130. set_monitor(Pid),
  1131. ets:insert(?REG_REV, {{Pid, Name}, 1})
  1132. end,
  1133. case ets:insert_new(?REG, {Name, Pid}) of
  1134. false ->
  1135. case ets:lookup(?REG, Name) of
  1136. [{_, {awaiting_reg, Pids}}] ->
  1137. ets:insert(?REG, {Name, Pid}),
  1138. CompleteReg(),
  1139. gen:reply(From, true),
  1140. lists:foreach(
  1141. fun({WaitingPid, Ref}) ->
  1142. clear_monitor(WaitingPid),
  1143. ets:delete(?REG_REV, {WaitingPid,Name}),
  1144. WaitingPid ! {?MODULE, Ref, reg, Name, Pid}
  1145. end, Pids),
  1146. {noreply, S};
  1147. [{_,OtherPid}] ->
  1148. case is_process_alive(OtherPid) of
  1149. true ->
  1150. ?BADARG;
  1151. false ->
  1152. process_is_down(OtherPid),
  1153. ets:insert(?REG, {Name, Pid}),
  1154. CompleteReg(),
  1155. {reply, true, S}
  1156. end;
  1157. [] ->
  1158. io:format("what the f...?~n", []),
  1159. ?BADARG
  1160. end;
  1161. true ->
  1162. CompleteReg(),
  1163. {reply, true, S}
  1164. end;
  1165. handle_call1({unreg, Name}, {Pid,_}, S) ->
  1166. case ets:lookup(?REG, Name) of
  1167. [{_, Pid}] ->
  1168. clear_monitor(Pid),
  1169. ets:delete(?REG_REV, {Pid, Name}),
  1170. ets:delete(?REG, Name),
  1171. {reply, true, S};
  1172. _ ->
  1173. ?BADARG
  1174. end;
  1175. handle_call1({await_reg, Name}, {Pid,_}, S) ->
  1176. case ets:lookup(?REG, Name) of
  1177. [] ->
  1178. set_monitor(Pid),
  1179. Ref = make_ref(),
  1180. ets:insert(?REG, {Name, {awaiting_reg, [{Pid, Ref}]}}),
  1181. ets:insert(?REG_REV, {{Pid, Name}, w}),
  1182. {reply, {Ref,Name}, S};
  1183. [{_, RegPid}] when is_pid(RegPid) ->
  1184. {reply, {already_registered, RegPid}, S};
  1185. [{_, {awaiting_reg, OtherPids}}] ->
  1186. set_monitor(Pid),
  1187. Ref = make_ref(),
  1188. ets:insert(?REG, {Name, {awaiting_reg, [{Pid, Ref}|OtherPids]}}),
  1189. ets:insert(?REG_REV, {{Pid, Name}, w}),
  1190. {reply, {Ref,Name}, S}
  1191. end;
  1192. handle_call1({clear_await_reg, {Ref,Name}}, _From, S) when is_reference(Ref) ->
  1193. case ets:lookup(?REG, Name) of
  1194. [{Name, Pid}] when is_pid(Pid) ->
  1195. ignore;
  1196. [{Name, {awaiting_reg, Ps}}] ->
  1197. case lists:keysearch(Ref, 2, Ps) of
  1198. {value, {WPid, _}} when is_pid(WPid) ->
  1199. clear_monitor(WPid),
  1200. ets:delete(?REG_REV, {WPid, Name}),
  1201. case lists:keydelete(Ref, 2, Ps) of
  1202. [] ->
  1203. ets:delete(?REG, Name);
  1204. [_|_] = PsLeft ->
  1205. ets:insert(?REG, {Name, {awaiting_reg, PsLeft}})
  1206. end;
  1207. false ->
  1208. ignore
  1209. end
  1210. end,
  1211. %% due to possible race conditions, we reply true as long as it's
  1212. %% reasonable that the reference given was valid once. It would be
  1213. %% unreasonable to require the client to verify that Name is not
  1214. %% registered just before the registry server handles this request.
  1215. %% Therefore, whether the request would exit or return true could be
  1216. %% timing dependent, which would suck greatly. Therefore, don't worry,
  1217. %% be happy, and return 'true' each time. If the argument is not
  1218. %% on the form {reference(), Name}, there _will_ be a BADARG.
  1219. {reply, true, S};
  1220. handle_call1({add_property, Pid, Property}, {Pid,_}, S) ->
  1221. do_add_property(Pid, Property),
  1222. {reply, true, S};
  1223. handle_call1({add_property, Process, Property}, {Pid, _}, S) ->
  1224. OnPid = on_pid(Process),
  1225. verify_access(OnPid, add_property, Pid),
  1226. do_add_property(OnPid, Property),
  1227. {reply, true, S};
  1228. handle_call1({replace_property, Pid, OldProp, NewProp}, {Pid,_}, S) ->
  1229. do_replace_property(Pid, OldProp, NewProp),
  1230. {reply, true, S};
  1231. handle_call1({replace_property, Process, OldProp, NewProp}, {Pid,_}, S) ->
  1232. OnPid = on_pid(Process),
  1233. verify_access(OnPid, replace_property, Pid),
  1234. do_replace_property(OnPid, OldProp, NewProp),
  1235. {reply, true, S};
  1236. handle_call1({del_property, Pid, Property}, {Pid,_}, S) ->
  1237. do_del_property(Pid, Property),
  1238. {reply, true, S};
  1239. handle_call1({del_property, Process, Property}, {Pid, _}, S) ->
  1240. OnPid = on_pid(Process),
  1241. verify_access(OnPid, del_property, Pid),
  1242. do_del_property(OnPid, Property),
  1243. {reply, true, S};
  1244. handle_call1({add_counter, Pid, Name, Initial}, {Pid,_}, S) ->
  1245. do_add_counter(Pid, Name, Initial),
  1246. {reply, true, S};
  1247. handle_call1({add_counter, Process, Name, Initial}, {Pid,_}, S) ->
  1248. OnPid = on_pid(Process),
  1249. verify_access(OnPid, add_counter, Pid),
  1250. do_add_counter(OnPid, Name, Initial),
  1251. {reply, true, S};
  1252. handle_call1({del_counter, Pid, Name}, {Pid,_}, S) ->
  1253. do_del_counter(Pid, Name),
  1254. {reply, true, S};
  1255. handle_call1({del_counter, Process, Name}, {Pid,_}, S) ->
  1256. OnPid = on_pid(Process),
  1257. verify_access(OnPid, del_counter, Pid),
  1258. do_del_counter(Process, Name),
  1259. {reply, true, S};
  1260. handle_call1({set_access, Access}, {Pid,_}, S) ->
  1261. Objs =
  1262. lists:foldr(
  1263. fun({Action, Procs, Ops}, Acc) ->
  1264. Pids = [on_pid(P) || P <- Procs],
  1265. Ops1 = expand_ops(Ops),
  1266. [[{Action, {Pid, Op, P}} || P <- Pids,
  1267. Op <- Ops1] | Acc]
  1268. end, [], Access),
  1269. lists:foreach(
  1270. fun({grant, Key}) ->
  1271. ets:insert(?PRIVS, {Key});
  1272. ({revoke, Key}) ->
  1273. ets:delete(?PRIVS, Key)
  1274. end, lists:concat(Objs)),
  1275. {reply, true, S};
  1276. handle_call1(_Req, _From, _S) ->
  1277. ?BADARG.
  1278. expand_ops(Ops) ->
  1279. lists:foldl(
  1280. fun(properties, Acc) ->
  1281. [add_property, del_property, replace_property] ++ Acc;
  1282. (counters, Acc) ->
  1283. [add_counter, del_counter, update_counter] ++ Acc;
  1284. (Op, Acc) when Op == add_property;
  1285. Op == replace_property;
  1286. Op == del_property;
  1287. Op == add_counter;
  1288. Op == del_counter;
  1289. Op == update_counter ->
  1290. [Op|Acc]
  1291. end, [], Ops).
  1292. verify_access(OnPid, Op, Pid) ->
  1293. case ets:member(?PRIVS, {OnPid, Op, Pid}) of
  1294. true ->
  1295. true;
  1296. false ->
  1297. ?EACCESS
  1298. end.
  1299. do_add_property(Pid, Property) ->
  1300. case ets:member(?PROPS_REV, {Pid,Property}) of
  1301. true ->
  1302. ?BADARG;
  1303. false ->
  1304. set_monitor(Pid),
  1305. insert_property(Prop…

Large files files are truncated, but you can click here to view the full file