p1_utils-1.0.3/0000755000232200023220000000000012642704237013630 5ustar debalancedebalancep1_utils-1.0.3/rebar.config0000644000232200023220000000144512642704237016116 0ustar debalancedebalance{erl_opts, [ debug_info, {src_dirs, ["src"]}, {platform_define, "^(15|16|17)", 'NEED_TIME_FALLBACKS'} ]}. {cover_enabled, true}. {cover_export_enabled, true}. {xref_checks, [undefined_function_calls, undefined_functions, deprecated_function_calls, deprecated_functions]}. {edoc_opts, [{layout, p1_edoc_layout}, {stylesheet, "style.css"}, %% Settings for ProcessOne docs {image, "logo_p1.png"}, {link, "http://www.process-one.net"} %% Settings for ejabberd docs %% {image, "logo_ejabberd.png"}, %% {link, "http://www.ejabberd.im"} %% Settings for Erlang docs %% {image, "erlang.png"}, %% {link, "http://www.erlang.org"} ]}. {profiles, [{test, [{erl_opts, [{src_dirs, ["src", "test"]}]}]}]}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: p1_utils-1.0.3/CHANGELOG.md0000644000232200023220000000063012642704237015440 0ustar debalancedebalance# Version 1.0.3 * Added time related compatibility module, added API documentation (Paweł Chmielowski) * Improve documentation readability (Marek Foss) # Version 1.0.2 * Add p1_time_compat module to ease support for both R17 and R18 Erlang time features (Paweł Chmielowski) # Version 1.0.1 * Better Rebar3 support, remove warning about missing hex plugin when building with rebar (Mickaël Rémond) p1_utils-1.0.3/LICENSE.txt0000644000232200023220000002613612642704237015463 0ustar debalancedebalance Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. p1_utils-1.0.3/README.md0000644000232200023220000000223012642704237015104 0ustar debalancedebalance# p1_utils [![Build Status](https://travis-ci.org/processone/p1_utils.svg?branch=master)](https://travis-ci.org/processone/p1_utils) [![Coverage Status](https://coveralls.io/repos/processone/p1_utils/badge.svg?branch=master&service=github)](https://coveralls.io/github/processone/p1_utils?branch=master) [![Hex version](https://img.shields.io/hexpm/v/p1_utils.svg "Hex version")](https://hex.pm/packages/p1_utils) p1_utils is an application containing ProcessOne modules and tools that are leveraged in other development projects: * `p1_fsm` and `p1_server` are drop-in replacements of Erlang gen_fsm and gen_server, offering extra option for better reliability in production. They support mostly priority messages and message queue length controls. * `p1_nif_utils` is an helper utilities for handling NIF code. * `treap` is a treap algorithm implementation. It is a randomized binary search tree. See: https://en.wikipedia.org/wiki/Treap * `p1_time_compat` is a module to ease support and migration of Erlang time management function from Erlang R16/R17 to Erlang R18. If you have `rebar` binary, you can generate `p1_utils` documentation by running `rebar3 edoc`. p1_utils-1.0.3/rebar.config.script0000644000232200023220000000743512642704237017426 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : rebar.config.script %%% Author : Mickael Remond %%% Purpose : Rebar build script. Compliant with rebar and rebar3. %%% Created : 24 Nov 2015 by Mickael Remond %%% %%% Copyright (C) 2002-2015 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%---------------------------------------------------------------------- ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) -> {OldVal,PartCfg} = case lists:keytake(Key, 1, Cfg) of {value, {_, V1}, V2} -> {V1, V2}; false -> {if Tail == [] -> Default; true -> [] end, Cfg} end, case Tail of [] -> [{Key, Op(OldVal)} | PartCfg]; _ -> [{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg] end end, ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end. %% Rebar3 support for hex.pm support: %% - Transform dependencies specification to use hex.pm packages: %% deps of the form: {Name, _Vsn, {git, _URL, {tag, Version}}} %% are expected to refer to package and are rewritten for rebar3 as: %% {Name, Version} %% - Add rebar3_hex plugin IsRebar3 = case application:get_key(rebar, vsn) of {ok, VSN} -> [VSN1 | _] = string:tokens(VSN, "-"), [Maj, Min, Patch] = string:tokens(VSN1, "."), (list_to_integer(Maj) >= 3); undefined -> lists:keymember(mix, 1, application:loaded_applications()) end, Cfg2 = case IsRebar3 of true -> DepsFun = fun(DepsList) -> lists:map(fun({DepName,_, {git,_, {tag,Version}}}) -> {DepName, Version}; (Dep) -> Dep end, DepsList) end, RB1 = ModCfg(CONFIG, [deps], DepsFun, []), ModCfg(RB1, [plugins], fun(V) -> V ++ [rebar3_hex] end, []); false -> CONFIG end, %% When running Travis test, upload test coverage result to coveralls: Config = case os:getenv("TRAVIS") of "true" -> JobId = os:getenv("TRAVIS_JOB_ID"), Cfg3 = ModCfg(Cfg2, [deps], fun(V) -> [{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}|V] end, []), ModCfg(Cfg3, [post_hooks], fun(V) -> V ++ [{eunit, "echo '\n%%! -pa .eunit/ deps/coveralls/ebin\nmain(_)->{ok,F}=file:open(\"erlang.json\",[write]),io:fwrite(F,\"~s\",[coveralls:convert_file(\".eunit/cover.coverdata\", \""++JobId++"\", \"travis-ci\")]).' > getcover.erl"}, {eunit, "escript ./getcover.erl"}] end, []); _ -> Cfg2 end, Config. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: p1_utils-1.0.3/doc/0000755000232200023220000000000012642704237014375 5ustar debalancedebalancep1_utils-1.0.3/doc/p1_time_compat.html0000644000232200023220000001145312642704237020170 0ustar debalancedebalance Module p1_time_compat

Module p1_time_compat

Function Index

convert_time_unit/3
monitor/2
monotonic_time/0
monotonic_time/1
os_system_time/0
os_system_time/1
system_flag/2
system_info/1
system_time/0
system_time/1
time_offset/0
time_offset/1
timestamp/0
unique_integer/0
unique_integer/1

Function Details

convert_time_unit/3

convert_time_unit(Time, FromUnit, ToUnit) -> any()

monitor/2

monitor(Type, Item) -> any()

monotonic_time/0

monotonic_time() -> any()

monotonic_time/1

monotonic_time(Unit) -> any()

os_system_time/0

os_system_time() -> any()

os_system_time/1

os_system_time(Unit) -> any()

system_flag/2

system_flag(Flag, Value) -> any()

system_info/1

system_info(Item) -> any()

system_time/0

system_time() -> any()

system_time/1

system_time(Unit) -> any()

time_offset/0

time_offset() -> any()

time_offset/1

time_offset(Unit) -> any()

timestamp/0

timestamp() -> any()

unique_integer/0

unique_integer() -> any()

unique_integer/1

unique_integer(Modifiers) -> any()


Generated by EDoc, Dec 17 2015, 23:20:00.

p1_utils-1.0.3/doc/p1_fsm.html0000644000232200023220000001704112642704237016453 0ustar debalancedebalance Module p1_fsm

Module p1_fsm

This module defines the p1_fsm behaviour.
Required callback functions: init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4.

Function Index

cancel_timer/1
enter_loop/4
enter_loop/5
enter_loop/6
format_status/2
init_it/6
print_event/3
reply/2
send_all_state_event/2
send_event/2
send_event_after/2
start/3
start/4
start_link/3
start_link/4
start_timer/2
sync_send_all_state_event/2
sync_send_all_state_event/3
sync_send_event/2
sync_send_event/3
system_code_change/4
system_continue/3
system_terminate/4
wake_hib/7

Function Details

cancel_timer/1

cancel_timer(Ref) -> any()

enter_loop/4

enter_loop(Mod, Options, StateName, StateData) -> any()

enter_loop/5

enter_loop(Mod, Options, StateName, StateData, ServerName) -> any()

enter_loop/6

enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) -> any()

format_status/2

format_status(Opt, StatusData) -> any()

init_it/6

init_it(Starter, Parent, Name, Mod, Args, Options) -> any()

print_event/3

print_event(Dev, X2, Name) -> any()

reply/2

reply(X1, Reply) -> any()

send_all_state_event/2

send_all_state_event(Name, Event) -> any()

send_event/2

send_event(Name, Event) -> any()

send_event_after/2

send_event_after(Time, Event) -> any()

start/3

start(Mod, Args, Options) -> any()

start/4

start(Name, Mod, Args, Options) -> any()

start_link/3

start_link(Mod, Args, Options) -> any()

start_link/4

start_link(Name, Mod, Args, Options) -> any()

start_timer/2

start_timer(Time, Msg) -> any()

sync_send_all_state_event/2

sync_send_all_state_event(Name, Event) -> any()

sync_send_all_state_event/3

sync_send_all_state_event(Name, Event, Timeout) -> any()

sync_send_event/2

sync_send_event(Name, Event) -> any()

sync_send_event/3

sync_send_event(Name, Event, Timeout) -> any()

system_code_change/4

system_code_change(X1, Module, OldVsn, Extra) -> any()

system_continue/3

system_continue(Parent, Debug, X3) -> any()

system_terminate/4

system_terminate(Reason::term(), Parent::term(), Debug::term(), X4::[term(), ...]) -> no_return()

wake_hib/7

wake_hib(Parent, Name, StateName, StateData, Mod, Debug, Limits) -> any()


Generated by EDoc, Dec 17 2015, 23:20:00.

p1_utils-1.0.3/doc/index.html0000644000232200023220000000074712642704237016402 0ustar debalancedebalance The p1_utils application <h2>This page uses frames</h2> <p>Your browser does not accept frames. <br>You should go to the <a href="overview-summary.html">non-frame version</a> instead. </p> p1_utils-1.0.3/doc/treap.html0000644000232200023220000000751112642704237016402 0ustar debalancedebalance Module treap

Module treap

Data Types

hashkey()

hashkey() = {non_neg_integer(), any()}

treap()

treap() = {hashkey(), any(), any(), treap(), treap()} | nil

Function Index

delete/2
delete_root/1
empty/0
fold/3
from_list/1
get_root/1
insert/4
is_empty/1
lookup/2
to_list/1

Function Details

delete/2

delete(Key, Tree) -> any()

delete_root/1

delete_root(X1) -> any()

empty/0

empty() -> any()

fold/3

fold(F, Acc, X3) -> any()

from_list/1

from_list(List) -> any()

get_root/1

get_root(X1) -> any()

insert/4

insert(Key, Priority, Value, Tree) -> any()

is_empty/1

is_empty(X1) -> any()

lookup/2

lookup(Key, Tree) -> any()

to_list/1

to_list(Tree) -> any()


Generated by EDoc, Dec 17 2015, 23:20:00.

p1_utils-1.0.3/doc/p1_nif_utils.html0000644000232200023220000000323112642704237017656 0ustar debalancedebalance Module p1_nif_utils

Module p1_nif_utils

Function Index

get_so_path/3

Function Details

get_so_path/3

get_so_path(ModuleName, AppNames, SoName) -> any()


Generated by EDoc, Dec 17 2015, 23:20:00.

p1_utils-1.0.3/doc/style.css0000755000232200023220000001052512642704237016255 0ustar debalancedebalance /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ html { -webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0;padding:40px;}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0} html { font-family: sans-serif; } body { background: #000; margin: 0; padding: 1em; line-height: 1.25em; } body.mainpane { background: #fff } body > ul { background: #ffc none repeat scroll 0% 0%; padding: 30px; } body > ul > li a { color: #555; } li { margin-bottom: 15px; } .index li { margin-left: 30px; margin-bottom: 0; color: #888; list-style-type: decimal; } a { text-decoration: none; } a:not([name]):hover, a:not([name]):active { text-decoration: underline; } h2,h3 { margin-top: 2em; margin-bottom: 0.5em; } hr { height: 0px; border: 0px; } tt { color: #5B6380; font-size: 1em !important; font-family: monospace,monospace !important; } p > tt { color:green; font-family: monospace,monospace !important; font-size: 1em !important; } table td { padding:15px; } body > table tr:nth-child(odd) { background:#E7E7E7 !important; border:0px !important; } frame:nth-child(1) { background:#161518 !important; } frame:nth-child(2) { background:#F3F3F3 none repeat scroll 0% 0% !important; } pre { background: #F3F3F3; border: 1px solid #DDD; padding: 10px; border-left: 5px solid #B02539; } code { background: #eee; border: 1px solid #ddd; margin: 3px; padding: 1px 3px; font-size: 1em !important; } #main { padding: 40px !important; } .navbar { background: #161518 none repeat scroll 0% 0%; } .navbar a, .index li a{ color: #888; } .indextitle { margin-top: 0.5em; text-transform: uppercase; font-weight: normal; color: #fff; } .indextitle + table { margin: -1em; padding: 1em; background: #000; } .indextitle + table a { color: #888 !important; text-decoration: none !important; text-transform: uppercase; } .indextitle + table h2 { color: #F8F8F8; text-transform: uppercase; } .indextitle + table { margin-top: 1em; } .indextitle + table td { padding: 6px; margin: 0; } .indextitle + table td a { padding: 6px; border-left: 3px solid #000; display: block; } .indextitle + table td a:hover { color: #B02539 !important; border-left: 3px solid #B02539; } .indextitle + table tr:nth-child(odd) { background: none !important; } .index { background: #EEE none repeat scroll 0% 0%; padding: 15px; }p1_utils-1.0.3/doc/modules-frame.html0000644000232200023220000000145512642704237020030 0ustar debalancedebalance The p1_utils application

Modules

p1_fsm
p1_nif_utils
p1_server
p1_time_compat
treap
p1_utils-1.0.3/doc/erlang.png0000644000232200023220000000407512642704237016361 0ustar debalancedebalancePNG  IHDR2*~؟ pHYs  tIME ,,IDATX͘}PSW{x !y "bk"V](Zaґ iwpӮ+RGu#dIB@AxoHyq{s= u~׮AY E!)4I&A}n租uߺ㨓m%*:P΄tݥU]_߱(XM1 !HAQwdV- #{w+EYOmF5:6m|؟f2e2ih;唂9%8!:Bɪۏv80Ȕeڦ6JV8`b;lRJwgG,9 euR%oh1,7!9H1|'ʖe ] J7 "X(wE+CڌhLAp.ʜ[yO; V`y0v*aEX}',.!xMX܀n@QE,ӂNAiVBds{f2_/pȭ8bxAcy׏&8)`  +,]VszB Iȫ8<xfJI't~h˼B!  AH$088800zjdA򍏏srɤhVZFQT2sCDoooCC[vT*dff$IdBB]MrF0 )++r%%%Z;==MZ?u-A|> EQ7n$ l6A^p+UT66623gXg8LQΝX,œs=5e4 L&qL&UVV䤦566D :adFj %|}}q'ښzڵP `uuuL޽X">}j.Ka`HNN)--uٹs֤$tYT.]$ =T*ռ!!!%ht ktt4+++???33`VTTTUUc*72r֭#GzׯY֕+WԘf碆c00d2YV Zm}}}aa`s”JS?wުybX,h4ZvϞ=]]]ݛRg[暚 .^*r9 NKK۾} bFFFv{ <((ݻ<˶QQQ'N`085۽{w||+W:$ il6344488x֊\. L&aT*J֭X,: (a t:b7|"""RiDDn!pZZZPܷoN?JJIENDB`p1_utils-1.0.3/doc/logo_p1.png0000644000232200023220000000126412642704237016446 0ustar debalancedebalancePNG  IHDR2*GgAMA a cHRMz&u0`:pQ<bKGDC pHYs  ~IDAThq@f,,$L L*0H*T@/8;Ptߌsww 4!}B)͙q!@D#3STu <&"Ẵ66S̸)ɵ#W~"7i ui2,tlp3m8?W4p5MI]&K'~<=Z odW&XJ=i7Cri![:s,O'҆#exZVFģH<4o6BrnXkd$Js߱jD*\iY!HRGd`!Gn%(fu#whԍ M~F@7 y)"ވ2tT'{#"J]Dv'FgGmD䠂h=(G$CV%>X>g4nncuJ31\du z91² @y:NWhcR{d˲1 lU5; ;klL,{]>2M=^/IENDB`p1_utils-1.0.3/doc/p1_server.html0000644000232200023220000001522112642704237017172 0ustar debalancedebalance Module p1_server

Module p1_server

This module defines the p1_server behaviour.
Required callback functions: init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3.

Function Index

abcast/2
abcast/3
call/2
call/3
cast/2
enter_loop/3
enter_loop/4
enter_loop/5
format_status/2
init_it/6
multi_call/2
multi_call/3
multi_call/4
reply/2
start/3
start/4
start_link/3
start_link/4
system_code_change/4
system_continue/3
system_terminate/4
wake_hib/6

Function Details

abcast/2

abcast(Name, Request) -> any()

abcast/3

abcast(Nodes, Name, Request) -> any()

call/2

call(Name, Request) -> any()

call/3

call(Name, Request, Timeout) -> any()

cast/2

cast(Dest, Request) -> any()

enter_loop/3

enter_loop(Mod, Options, State) -> any()

enter_loop/4

enter_loop(Mod, Options, State, ServerName) -> any()

enter_loop/5

enter_loop(Mod, Options, State, ServerName, Timeout) -> any()

format_status/2

format_status(Opt, StatusData) -> any()

init_it/6

init_it(Starter, Parent, Name, Mod, Args, Options) -> any()

multi_call/2

multi_call(Name, Req) -> any()

multi_call/3

multi_call(Nodes, Name, Req) -> any()

multi_call/4

multi_call(Nodes, Name, Req, Timeout) -> any()

reply/2

reply(X1, Reply) -> any()

start/3

start(Mod, Args, Options) -> any()

start/4

start(Name, Mod, Args, Options) -> any()

start_link/3

start_link(Mod, Args, Options) -> any()

start_link/4

start_link(Name, Mod, Args, Options) -> any()

system_code_change/4

system_code_change(X1, Module, OldVsn, Extra) -> any()

system_continue/3

system_continue(Parent, Debug, X3) -> any()

system_terminate/4

system_terminate(Reason::term(), Parent::term(), Debug::term(), X4::[term()]) -> no_return()

wake_hib/6

wake_hib(Parent, Name, State, Mod, Debug, Limits) -> any()


Generated by EDoc, Dec 17 2015, 23:20:00.

p1_utils-1.0.3/doc/overview.edoc0000644000232200023220000000150012642704237017073 0ustar debalancedebalance@author ProcessOne [http://www.process-one.net] @copyright 2002-2015 ProcessOne @version 1.0.0 @title P1 Erlang Utils @doc This is a set of Erlang utilities used in other ProcessOne projects. p1_utils is an application containing ProcessOne modules and tools that are leveraged in other development projects:
  • p1_fsm and p1_server are drop-in replacements of Erlang gen_fsm and gen_server, offering extra option for better reliability in production. They support mostly priority messages and message queue length controls.
  • p1_nif_utils is an helper utilities for handling NIF code.
  • treap is a treap algorithm implementation. It is a randomized binary search tree. See: Treap on Wikipedia
p1_utils-1.0.3/doc/overview-summary.html0000644000232200023220000000401012642704237020617 0ustar debalancedebalance P1 Erlang Utils

P1 Erlang Utils

Copyright © 2002-2015 ProcessOne

Version: 1.0.0

Authors: ProcessOne [web site: http://www.process-one.net].

This is a set of Erlang utilities used in other ProcessOne projects.

p1_utils is an application containing ProcessOne modules and tools that are leveraged in other development projects:

  • p1_fsm and p1_server are drop-in replacements of Erlang gen_fsm and gen_server, offering extra option for better reliability in production. They support mostly priority messages and message queue length controls.
  • p1_nif_utils is an helper utilities for handling NIF code.
  • treap is a treap algorithm implementation. It is a randomized binary search tree. See: Treap on Wikipedia

Generated by EDoc, Dec 17 2015, 23:20:00.

p1_utils-1.0.3/doc/logo_ejabberd.png0000644000232200023220000000173312642704237017665 0ustar debalancedebalancePNG  IHDR2*GtEXtSoftwareAdobe ImageReadyqe<}IDATxԙMhA'vZ(416+Z C{5^r՛Lz0}4<[9ч2j/0KG' m$%0HE]ǙOGlXgda Ǧ $w9 H)]L&u~ kM 8*|8a@SzJR ]G%!l.\!>:wD@Xo8i7zʱ)aKA;v LY4ݏ(Ÿ{8 ECDG0b-u3mQhC]vݯ{i e"TWA/j" :;eݎqNC*5NE_cCA)9.7`:QXoo4&|NL' HKS@` ${?a!EIENDB`p1_utils-1.0.3/.travis.yml0000644000232200023220000000023112642704237015735 0ustar debalancedebalancesudo: false language: erlang install: - rebar get-deps script: rebar compile && rebar skip_deps=true eunit otp_release: - 17.1 - 17.5 - 18.1 p1_utils-1.0.3/src/0000755000232200023220000000000012642704237014417 5ustar debalancedebalancep1_utils-1.0.3/src/p1_fsm.erl0000644000232200023220000007733712642704237016331 0ustar debalancedebalance%% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% The code has been modified and improved by ProcessOne. %% %% Copyright 2007-2015 ProcessOne %% %% The change adds the following features: %% - You can send exit(priority_shutdown) to the p1_fsm process to %% terminate immediatetly. If the fsm trap_exit process flag has been %% set to true, the FSM terminate function will called. %% - You can pass the gen_fsm options to control resource usage. %% {max_queue, N} will exit the process with priority_shutdown %% - You can limit the time processing a message (TODO): If the %% message processing does not return in a given period of time, the %% process will be terminated. %% - You might customize the State data before sending it to error_logger %% in case of a crash (just export the function print_state/1) %% $Id$ %% -module(p1_fsm). %%%----------------------------------------------------------------- %%% %%% This state machine is somewhat more pure than state_lib. It is %%% still based on State dispatching (one function per state), but %%% allows a function handle_event to take care of events in all states. %%% It's not that pure anymore :( We also allow synchronized event sending. %%% %%% If the Parent process terminates the Module:terminate/2 %%% function is called. %%% %%% The user module should export: %%% %%% init(Args) %%% ==> {ok, StateName, StateData} %%% {ok, StateName, StateData, Timeout} %%% ignore %%% {stop, Reason} %%% %%% StateName(Msg, StateData) %%% %%% ==> {next_state, NewStateName, NewStateData} %%% {next_state, NewStateName, NewStateData, Timeout} %%% {stop, Reason, NewStateData} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% StateName(Msg, From, StateData) %%% %%% ==> {next_state, NewStateName, NewStateData} %%% {next_state, NewStateName, NewStateData, Timeout} %%% {reply, Reply, NewStateName, NewStateData} %%% {reply, Reply, NewStateName, NewStateData, Timeout} %%% {stop, Reason, NewStateData} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% handle_event(Msg, StateName, StateData) %%% %%% ==> {next_state, NewStateName, NewStateData} %%% {next_state, NewStateName, NewStateData, Timeout} %%% {stop, Reason, Reply, NewStateData} %%% {stop, Reason, NewStateData} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% handle_sync_event(Msg, From, StateName, StateData) %%% %%% ==> {next_state, NewStateName, NewStateData} %%% {next_state, NewStateName, NewStateData, Timeout} %%% {reply, Reply, NewStateName, NewStateData} %%% {reply, Reply, NewStateName, NewStateData, Timeout} %%% {stop, Reason, Reply, NewStateData} %%% {stop, Reason, NewStateData} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% handle_info(Info, StateName) (e.g. {'EXIT', P, R}, {nodedown, N}, ... %%% %%% ==> {next_state, NewStateName, NewStateData} %%% {next_state, NewStateName, NewStateData, Timeout} %%% {stop, Reason, NewStateData} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% terminate(Reason, StateName, StateData) Let the user module clean up %%% always called when server terminates %%% %%% ==> the return value is ignored %%% %%% %%% The work flow (of the fsm) can be described as follows: %%% %%% User module fsm %%% ----------- ------- %%% start -----> start %%% init <----- . %%% %%% loop %%% StateName <----- . %%% %%% handle_event <----- . %%% %%% handle__sunc_event <----- . %%% %%% handle_info <----- . %%% %%% terminate <----- . %%% %%% %%% --------------------------------------------------- -export([start/3, start/4, start_link/3, start_link/4, send_event/2, sync_send_event/2, sync_send_event/3, send_all_state_event/2, sync_send_all_state_event/2, sync_send_all_state_event/3, reply/2, start_timer/2,send_event_after/2,cancel_timer/1, enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/7]). %% Internal exports -export([init_it/6, print_event/3, system_continue/3, system_terminate/4, system_code_change/4, format_status/2]). -import(error_logger , [format/2]). %%% Internal gen_fsm state %%% This state is used to defined resource control values: -record(limits, {max_queue :: non_neg_integer()}). %%% --------------------------------------------------- %%% Interface functions. %%% --------------------------------------------------- -callback init(Args :: term()) -> {ok, StateName :: atom(), StateData :: term()} | {ok, StateName :: atom(), StateData :: term(), timeout() | hibernate} | {stop, Reason :: term()} | ignore. -callback handle_event(Event :: term(), StateName :: atom(), StateData :: term()) -> {next_state, NextStateName :: atom(), NewStateData :: term()} | {next_state, NextStateName :: atom(), NewStateData :: term(), timeout() | hibernate} | {migrate, NewStateData :: term(), {Node :: atom(), M :: atom(), F :: atom(), A :: list()}, Timeout :: timeout()} | {stop, Reason :: term(), NewStateData :: term()}. -callback handle_sync_event(Event :: term(), From :: {pid(), Tag :: term()}, StateName :: atom(), StateData :: term()) -> {reply, Reply :: term(), NextStateName :: atom(), NewStateData :: term()} | {reply, Reply :: term(), NextStateName :: atom(), NewStateData :: term(), timeout() | hibernate} | {next_state, NextStateName :: atom(), NewStateData :: term()} | {next_state, NextStateName :: atom(), NewStateData :: term(), timeout() | hibernate} | {migrate, NewStateData :: term(), {Node :: atom(), M :: atom(), F :: atom(), A :: list()}, Timeout :: timeout()} | {stop, Reason :: term(), Reply :: term(), NewStateData :: term()} | {stop, Reason :: term(), NewStateData :: term()}. -callback handle_info(Info :: term(), StateName :: atom(), StateData :: term()) -> {next_state, NextStateName :: atom(), NewStateData :: term()} | {next_state, NextStateName :: atom(), NewStateData :: term(), timeout() | hibernate} | {migrate, NewStateData :: term(), {Node :: atom(), M :: atom(), F :: atom(), A :: list()}, Timeout :: timeout()} | {stop, Reason :: normal | term(), NewStateData :: term()}. -callback terminate(Reason :: normal | shutdown | {shutdown, term()} | term(), StateName :: atom(), StateData :: term()) -> term(). -callback code_change(OldVsn :: term() | {down, term()}, StateName :: atom(), StateData :: term(), Extra :: term()) -> {ok, NextStateName :: atom(), NewStateData :: term()}. %%% --------------------------------------------------- %%% Starts a generic state machine. %%% start(Mod, Args, Options) %%% start(Name, Mod, Args, Options) %%% start_link(Mod, Args, Options) %%% start_link(Name, Mod, Args, Options) where: %%% Name ::= {local, atom()} | {global, atom()} %%% Mod ::= atom(), callback module implementing the 'real' fsm %%% Args ::= term(), init arguments (to Mod:init/1) %%% Options ::= [{debug, [Flag]}] %%% Flag ::= trace | log | {logfile, File} | statistics | debug %%% (debug == log && statistics) %%% Returns: {ok, Pid} | %%% {error, {already_started, Pid}} | %%% {error, Reason} %%% --------------------------------------------------- start(Mod, Args, Options) -> gen:start(?MODULE, nolink, Mod, Args, Options). start(Name, Mod, Args, Options) -> gen:start(?MODULE, nolink, Name, Mod, Args, Options). start_link(Mod, Args, Options) -> gen:start(?MODULE, link, Mod, Args, Options). start_link(Name, Mod, Args, Options) -> gen:start(?MODULE, link, Name, Mod, Args, Options). send_event({global, Name}, Event) -> catch global:send(Name, {'$gen_event', Event}), ok; send_event(Name, Event) -> Name ! {'$gen_event', Event}, ok. sync_send_event(Name, Event) -> case catch gen:call(Name, '$gen_sync_event', Event) of {ok,Res} -> Res; {'EXIT',Reason} -> exit({Reason, {?MODULE, sync_send_event, [Name, Event]}}) end. sync_send_event(Name, Event, Timeout) -> case catch gen:call(Name, '$gen_sync_event', Event, Timeout) of {ok,Res} -> Res; {'EXIT',Reason} -> exit({Reason, {?MODULE, sync_send_event, [Name, Event, Timeout]}}) end. send_all_state_event({global, Name}, Event) -> catch global:send(Name, {'$gen_all_state_event', Event}), ok; send_all_state_event(Name, Event) -> Name ! {'$gen_all_state_event', Event}, ok. sync_send_all_state_event(Name, Event) -> case catch gen:call(Name, '$gen_sync_all_state_event', Event) of {ok,Res} -> Res; {'EXIT',Reason} -> exit({Reason, {?MODULE, sync_send_all_state_event, [Name, Event]}}) end. sync_send_all_state_event(Name, Event, Timeout) -> case catch gen:call(Name, '$gen_sync_all_state_event', Event, Timeout) of {ok,Res} -> Res; {'EXIT',Reason} -> exit({Reason, {?MODULE, sync_send_all_state_event, [Name, Event, Timeout]}}) end. %% Designed to be only callable within one of the callbacks %% hence using the self() of this instance of the process. %% This is to ensure that timers don't go astray in global %% e.g. when straddling a failover, or turn up in a restarted %% instance of the process. %% Returns Ref, sends event {timeout,Ref,Msg} after Time %% to the (then) current state. start_timer(Time, Msg) -> erlang:start_timer(Time, self(), {'$gen_timer', Msg}). %% Returns Ref, sends Event after Time to the (then) current state. send_event_after(Time, Event) -> erlang:start_timer(Time, self(), {'$gen_event', Event}). %% Returns the remaing time for the timer if Ref referred to %% an active timer/send_event_after, false otherwise. cancel_timer(Ref) -> case erlang:cancel_timer(Ref) of false -> receive {timeout, Ref, _} -> 0 after 0 -> false end; RemainingTime -> RemainingTime end. %% enter_loop/4,5,6 %% Makes an existing process into a gen_fsm. %% The calling process will enter the gen_fsm receive loop and become a %% gen_fsm process. %% The process *must* have been started using one of the start functions %% in proc_lib, see proc_lib(3). %% The user is responsible for any initialization of the process, %% including registering a name for it. enter_loop(Mod, Options, StateName, StateData) -> enter_loop(Mod, Options, StateName, StateData, self(), infinity). enter_loop(Mod, Options, StateName, StateData, ServerName = {_,_}) -> enter_loop(Mod, Options, StateName, StateData, ServerName,infinity); enter_loop(Mod, Options, StateName, StateData, Timeout) -> enter_loop(Mod, Options, StateName, StateData, self(), Timeout). enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) -> Name = get_proc_name(ServerName), Parent = get_parent(), Debug = gen:debug_options(Options), Limits = limit_options(Options), Queue = queue:new(), QueueLen = 0, loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug, Limits, Queue, QueueLen). get_proc_name(Pid) when is_pid(Pid) -> Pid; get_proc_name({local, Name}) -> case process_info(self(), registered_name) of {registered_name, Name} -> Name; {registered_name, _Name} -> exit(process_not_registered); [] -> exit(process_not_registered) end; get_proc_name({global, Name}) -> case global:whereis_name(Name) of undefined -> exit(process_not_registered_globally); Pid when Pid==self() -> Name; _Pid -> exit(process_not_registered_globally) end. get_parent() -> case get('$ancestors') of [Parent | _] when is_pid(Parent) -> Parent; [Parent | _] when is_atom(Parent) -> name_to_pid(Parent); _ -> exit(process_was_not_started_by_proc_lib) end. name_to_pid(Name) -> case whereis(Name) of undefined -> case global:whereis_name(Name) of undefined -> exit(could_not_find_registerd_name); Pid -> Pid end; Pid -> Pid end. %%% --------------------------------------------------- %%% Initiate the new process. %%% Register the name using the Rfunc function %%% Calls the Mod:init/Args function. %%% Finally an acknowledge is sent to Parent and the main %%% loop is entered. %%% --------------------------------------------------- init_it(Starter, self, Name, Mod, Args, Options) -> init_it(Starter, self(), Name, Mod, Args, Options); init_it(Starter, Parent, Name0, Mod, Args, Options) -> Name = name(Name0), Debug = gen:debug_options(Options), Limits = limit_options(Options), Queue = queue:new(), QueueLen = 0, case catch Mod:init(Args) of {ok, StateName, StateData} -> proc_lib:init_ack(Starter, {ok, self()}), loop(Parent, Name, StateName, StateData, Mod, infinity, Debug, Limits, Queue, QueueLen); {ok, StateName, StateData, Timeout} -> proc_lib:init_ack(Starter, {ok, self()}), loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug, Limits, Queue, QueueLen); {stop, Reason} -> proc_lib:init_ack(Starter, {error, Reason}), exit(Reason); ignore -> proc_lib:init_ack(Starter, ignore), exit(normal); {'EXIT', Reason} -> proc_lib:init_ack(Starter, {error, Reason}), exit(Reason); Else -> Error = {bad_return_value, Else}, proc_lib:init_ack(Starter, {error, Error}), exit(Error) end. name({local,Name}) -> Name; name({global,Name}) -> Name; name(Pid) when is_pid(Pid) -> Pid. %%----------------------------------------------------------------- %% The MAIN loop %%----------------------------------------------------------------- loop(Parent, Name, StateName, StateData, Mod, hibernate, Debug, Limits, Queue, QueueLen) when QueueLen > 0 -> case queue:out(Queue) of {{value, Msg}, Queue1} -> decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, Debug, Limits, Queue1, QueueLen - 1, false); {empty, _} -> Reason = internal_queue_error, error_info(Mod, Reason, Name, hibernate, StateName, StateData, Debug), exit(Reason) end; loop(Parent, Name, StateName, StateData, Mod, hibernate, Debug, Limits, _Queue, _QueueLen) -> proc_lib:hibernate(?MODULE,wake_hib, [Parent, Name, StateName, StateData, Mod, Debug, Limits]); %% First we test if we have reach a defined limit ... loop(Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue, QueueLen) -> try message_queue_len(Limits, QueueLen) %% TODO: We can add more limit checking here... catch {process_limit, Limit} -> Reason = {process_limit, Limit}, Msg = {'EXIT', Parent, {error, {process_limit, Limit}}}, terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug, queue:new()) end, process_message(Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue, QueueLen). %% ... then we can process a new message: process_message(Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue, QueueLen) -> {Msg, Queue1, QueueLen1} = collect_messages(Queue, QueueLen, Time), decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue1, QueueLen1, false). collect_messages(Queue, QueueLen, Time) -> receive Input -> case Input of {'EXIT', _Parent, priority_shutdown} -> {Input, Queue, QueueLen}; _ -> collect_messages( queue:in(Input, Queue), QueueLen + 1, Time) end after 0 -> case queue:out(Queue) of {{value, Msg}, Queue1} -> {Msg, Queue1, QueueLen - 1}; {empty, _} -> receive Input -> {Input, Queue, QueueLen} after Time -> {{'$gen_event', timeout}, Queue, QueueLen} end end end. wake_hib(Parent, Name, StateName, StateData, Mod, Debug, Limits) -> Msg = receive Input -> Input end, Queue = queue:new(), QueueLen = 0, decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, Debug, Limits, Queue, QueueLen, true). decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue, QueueLen, Hib) -> put('$internal_queue_len', QueueLen), case Msg of {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, [Name, StateName, StateData, Mod, Time, Limits, Queue, QueueLen], Hib); {'EXIT', Parent, Reason} -> terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug, Queue); _Msg when Debug == [] -> handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, Limits, Queue, QueueLen); _Msg -> Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, StateName}, {in, Msg}), handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, Debug1, Limits, Queue, QueueLen) end. %%----------------------------------------------------------------- %% Callback functions for system messages handling. %%----------------------------------------------------------------- system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time, Limits, Queue, QueueLen]) -> loop(Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue, QueueLen). -spec system_terminate(term(), _, _, [term(),...]) -> no_return(). system_terminate(Reason, _Parent, Debug, [Name, StateName, StateData, Mod, _Time, _Limits, Queue, _QueueLen]) -> terminate(Reason, Name, [], Mod, StateName, StateData, Debug, Queue). system_code_change([Name, StateName, StateData, Mod, Time, Limits, Queue, QueueLen], _Module, OldVsn, Extra) -> case catch Mod:code_change(OldVsn, StateName, StateData, Extra) of {ok, NewStateName, NewStateData} -> {ok, [Name, NewStateName, NewStateData, Mod, Time, Limits, Queue, QueueLen]}; Else -> Else end. %%----------------------------------------------------------------- %% Format debug messages. Print them as the call-back module sees %% them, not as the real erlang messages. Use trace for that. %%----------------------------------------------------------------- print_event(Dev, {in, Msg}, {Name, StateName}) -> case Msg of {'$gen_event', Event} -> io:format(Dev, "*DBG* ~p got event ~p in state ~w~n", [Name, Event, StateName]); {'$gen_all_state_event', Event} -> io:format(Dev, "*DBG* ~p got all_state_event ~p in state ~w~n", [Name, Event, StateName]); {timeout, Ref, {'$gen_timer', Message}} -> io:format(Dev, "*DBG* ~p got timer ~p in state ~w~n", [Name, {timeout, Ref, Message}, StateName]); {timeout, _Ref, {'$gen_event', Event}} -> io:format(Dev, "*DBG* ~p got timer ~p in state ~w~n", [Name, Event, StateName]); _ -> io:format(Dev, "*DBG* ~p got ~p in state ~w~n", [Name, Msg, StateName]) end; print_event(Dev, {out, Msg, To, StateName}, Name) -> io:format(Dev, "*DBG* ~p sent ~p to ~w~n" " and switched to state ~w~n", [Name, Msg, To, StateName]); print_event(Dev, return, {Name, StateName}) -> io:format(Dev, "*DBG* ~p switched to state ~w~n", [Name, StateName]). relay_messages(MRef, TRef, Clone, Queue) -> lists:foreach( fun(Msg) -> Clone ! Msg end, queue:to_list(Queue)), relay_messages(MRef, TRef, Clone). relay_messages(MRef, TRef, Clone) -> receive {'DOWN', MRef, process, Clone, _Reason} -> normal; {'EXIT', _Parent, _Reason} -> {migrated, Clone}; {timeout, TRef, timeout} -> {migrated, Clone}; Msg -> Clone ! Msg, relay_messages(MRef, TRef, Clone) end. handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Limits, Queue, QueueLen) -> %No debug here From = from(Msg), case catch dispatch(Msg, Mod, StateName, StateData) of {next_state, NStateName, NStateData} -> loop(Parent, Name, NStateName, NStateData, Mod, infinity, [], Limits, Queue, QueueLen); {next_state, NStateName, NStateData, Time1} -> loop(Parent, Name, NStateName, NStateData, Mod, Time1, [], Limits, Queue, QueueLen); {reply, Reply, NStateName, NStateData} when From =/= undefined -> reply(From, Reply), loop(Parent, Name, NStateName, NStateData, Mod, infinity, [], Limits, Queue, QueueLen); {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined -> reply(From, Reply), loop(Parent, Name, NStateName, NStateData, Mod, Time1, [], Limits, Queue, QueueLen); {migrate, NStateData, {Node, M, F, A}, Time1} -> RPCTimeout = if Time1 == 0 -> %% We don't care about a delay, %% so we set it one minute 60000; true -> Time1 end, Now = p1_time_compat:monotonic_time(milli_seconds), Reason = case catch rpc_call(Node, M, F, A, RPCTimeout) of {ok, Clone} -> process_flag(trap_exit, true), MRef = erlang:monitor(process, Clone), NowDiff = p1_time_compat:monotonic_time(milli_seconds) - Now, TimeLeft = lists:max([Time1 - NowDiff, 0]), TRef = erlang:start_timer(TimeLeft, self(), timeout), relay_messages(MRef, TRef, Clone, Queue); _ -> normal end, Queue1 = case Reason of normal -> Queue; _ -> queue:new() end, terminate(Reason, Name, Msg, Mod, StateName, NStateData, [], Queue1); {stop, Reason, NStateData} -> terminate(Reason, Name, Msg, Mod, StateName, NStateData, [], Queue); {stop, Reason, Reply, NStateData} when From =/= undefined -> {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod, StateName, NStateData, [], Queue)), reply(From, Reply), exit(R); {'EXIT', What} -> terminate(What, Name, Msg, Mod, StateName, StateData, [], Queue); Reply -> terminate({bad_return_value, Reply}, Name, Msg, Mod, StateName, StateData, [], Queue) end. handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Debug, Limits, Queue, QueueLen) -> From = from(Msg), case catch dispatch(Msg, Mod, StateName, StateData) of {next_state, NStateName, NStateData} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1, Limits, Queue, QueueLen); {next_state, NStateName, NStateData, Time1} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1, Limits, Queue, QueueLen); {reply, Reply, NStateName, NStateData} when From =/= undefined -> Debug1 = reply(Name, From, Reply, Debug, NStateName), loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1, Limits, Queue, QueueLen); {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined -> Debug1 = reply(Name, From, Reply, Debug, NStateName), loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1, Limits, Queue, QueueLen); {migrate, NStateData, {Node, M, F, A}, Time1} -> RPCTimeout = if Time1 == 0 -> %% We don't care about a delay, %% so we set it one minute 60000; true -> Time1 end, Now = p1_time_compat:monotonic_time(milli_seconds), Reason = case catch rpc_call(Node, M, F, A, RPCTimeout) of {ok, Clone} -> process_flag(trap_exit, true), MRef = erlang:monitor(process, Clone), NowDiff = p1_time_compat:monotonic_time(milli_seconds) - Now, TimeLeft = lists:max([Time1 - NowDiff, 0]), TRef = erlang:start_timer(TimeLeft, self(), timeout), relay_messages(MRef, TRef, Clone, Queue); _ -> normal end, Queue1 = case Reason of normal -> Queue; _ -> queue:new() end, terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug, Queue1); {stop, Reason, NStateData} -> terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug, Queue); {stop, Reason, Reply, NStateData} when From =/= undefined -> {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug, Queue)), reply(Name, From, Reply, Debug, StateName), exit(R); {'EXIT', What} -> terminate(What, Name, Msg, Mod, StateName, StateData, Debug, Queue); Reply -> terminate({bad_return_value, Reply}, Name, Msg, Mod, StateName, StateData, Debug, Queue) end. dispatch({'$gen_event', Event}, Mod, StateName, StateData) -> Mod:StateName(Event, StateData); dispatch({'$gen_all_state_event', Event}, Mod, StateName, StateData) -> Mod:handle_event(Event, StateName, StateData); dispatch({'$gen_sync_event', From, Event}, Mod, StateName, StateData) -> Mod:StateName(Event, From, StateData); dispatch({'$gen_sync_all_state_event', From, Event}, Mod, StateName, StateData) -> Mod:handle_sync_event(Event, From, StateName, StateData); dispatch({timeout, Ref, {'$gen_timer', Msg}}, Mod, StateName, StateData) -> Mod:StateName({timeout, Ref, Msg}, StateData); dispatch({timeout, _Ref, {'$gen_event', Event}}, Mod, StateName, StateData) -> Mod:StateName(Event, StateData); dispatch(Info, Mod, StateName, StateData) -> Mod:handle_info(Info, StateName, StateData). from({'$gen_sync_event', From, _Event}) -> From; from({'$gen_sync_all_state_event', From, _Event}) -> From; from(_) -> undefined. %% Send a reply to the client. reply({To, Tag}, Reply) -> catch To ! {Tag, Reply}. reply(Name, {To, Tag}, Reply, Debug, StateName) -> reply({To, Tag}, Reply), sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, StateName}). %%% --------------------------------------------------- %%% Terminate the server. %%% --------------------------------------------------- terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug, Queue) -> lists:foreach( fun(Message) -> self() ! Message end, queue:to_list(Queue)), case catch Mod:terminate(Reason, StateName, StateData) of {'EXIT', R} -> error_info(Mod, R, Name, Msg, StateName, StateData, Debug), exit(R); _ -> case Reason of normal -> exit(normal); shutdown -> exit(shutdown); priority_shutdown -> %% Priority shutdown should be considered as %% shutdown by SASL exit(shutdown); {process_limit, _Limit} -> exit(Reason); {migrated, _Clone} -> exit(normal); _ -> error_info(Mod, Reason, Name, Msg, StateName, StateData, Debug), exit(Reason) end end. error_info(Mod, Reason, Name, Msg, StateName, StateData, Debug) -> Reason1 = case Reason of {undef,[{M,F,A}|MFAs]} -> case code:is_loaded(M) of false -> {'module could not be loaded',[{M,F,A}|MFAs]}; _ -> case erlang:function_exported(M, F, length(A)) of true -> Reason; false -> {'function not exported',[{M,F,A}|MFAs]} end end; _ -> Reason end, StateToPrint = case erlang:function_exported(Mod, print_state, 1) of true -> (catch Mod:print_state(StateData)); false -> StateData end, Str = "** State machine ~p terminating \n" ++ get_msg_str(Msg) ++ "** When State == ~p~n" "** Data == ~p~n" "** Reason for termination = ~n** ~p~n", format(Str, [Name, get_msg(Msg), StateName, StateToPrint, Reason1]), sys:print_log(Debug), ok. get_msg_str({'$gen_event', _Event}) -> "** Last event in was ~p~n"; get_msg_str({'$gen_sync_event', _Event}) -> "** Last sync event in was ~p~n"; get_msg_str({'$gen_all_state_event', _Event}) -> "** Last event in was ~p (for all states)~n"; get_msg_str({'$gen_sync_all_state_event', _Event}) -> "** Last sync event in was ~p (for all states)~n"; get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) -> "** Last timer event in was ~p~n"; get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) -> "** Last timer event in was ~p~n"; get_msg_str(_Msg) -> "** Last message in was ~p~n". get_msg({'$gen_event', Event}) -> Event; get_msg({'$gen_sync_event', Event}) -> Event; get_msg({'$gen_all_state_event', Event}) -> Event; get_msg({'$gen_sync_all_state_event', Event}) -> Event; get_msg({timeout, Ref, {'$gen_timer', Msg}}) -> {timeout, Ref, Msg}; get_msg({timeout, _Ref, {'$gen_event', Event}}) -> Event; get_msg(Msg) -> Msg. %%----------------------------------------------------------------- %% Status information %%----------------------------------------------------------------- format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time, _Limits, _Queue, _QueueLen]] = StatusData, NameTag = if is_pid(Name) -> pid_to_list(Name); is_atom(Name) -> Name end, Header = lists:concat(["Status for state machine ", NameTag]), Log = sys:get_debug(log, Debug, []), Specfic = case erlang:function_exported(Mod, format_status, 2) of true -> case catch Mod:format_status(Opt,[PDict,StateData]) of {'EXIT', _} -> [{data, [{"StateData", StateData}]}]; Else -> Else end; _ -> [{data, [{"StateData", StateData}]}] end, [{header, Header}, {data, [{"Status", SysState}, {"Parent", Parent}, {"Logged events", Log}, {"StateName", StateName}]} | Specfic]. %%----------------------------------------------------------------- %% Resources limit management %%----------------------------------------------------------------- %% Extract know limit options limit_options(Options) -> limit_options(Options, #limits{}). limit_options([], Limits) -> Limits; %% Maximum number of messages allowed in the process message queue limit_options([{max_queue,N}|Options], Limits) when is_integer(N) -> NewLimits = Limits#limits{max_queue=N}, limit_options(Options, NewLimits); limit_options([_|Options], Limits) -> limit_options(Options, Limits). %% Throw max_queue if we have reach the max queue size %% Returns ok otherwise message_queue_len(#limits{max_queue = undefined}, _QueueLen) -> ok; message_queue_len(#limits{max_queue = MaxQueue}, QueueLen) -> Pid = self(), case process_info(Pid, message_queue_len) of {message_queue_len, N} when N + QueueLen > MaxQueue -> throw({process_limit, {max_queue, N + QueueLen}}); _ -> ok end. rpc_call(Node, Mod, Fun, Args, Timeout) -> Ref = make_ref(), Caller = self(), F = fun() -> group_leader(whereis(user), self()), case catch apply(Mod, Fun, Args) of {'EXIT', _} = Err -> Caller ! {Ref, {badrpc, Err}}; Result -> Caller ! {Ref, Result} end end, Pid = spawn(Node, F), MRef = erlang:monitor(process, Pid), receive {Ref, Result} -> erlang:demonitor(MRef, [flush]), Result; {'DOWN', MRef, _, _, noconnection = Reason} -> {badrpc, Reason} after Timeout -> erlang:demonitor(MRef, [flush]), catch exit(Pid, kill), receive {Ref, Result} -> Result after 0 -> {badrpc, timeout} end end. p1_utils-1.0.3/src/treap.erl0000644000232200023220000001354312642704237016244 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : treap.erl %%% Author : Alexey Shchepin %%% Purpose : Treaps implementation %%% Created : 22 Apr 2008 by Alexey Shchepin %%% %%% %%% Copyright (C) 2002-2015 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%---------------------------------------------------------------------- -module(treap). -export([empty/0, insert/4, delete/2, delete_root/1, get_root/1, lookup/2, is_empty/1, fold/3, from_list/1, to_list/1, delete_higher_priorities/2, priority_from_current_time/0, priority_from_current_time/1]). -type hashkey() :: {non_neg_integer(), any()}. -type treap() :: {hashkey(), any(), any(), treap(), treap()} | nil. -export_type([treap/0]). empty() -> nil. insert(Key, Priority, Value, Tree) -> HashKey = {erlang:phash2(Key), Key}, insert1(Tree, HashKey, Priority, Value). insert1(nil, HashKey, Priority, Value) -> {HashKey, Priority, Value, nil, nil}; insert1({HashKey1, Priority1, Value1, Left, Right} = Tree, HashKey, Priority, Value) -> if HashKey < HashKey1 -> heapify({HashKey1, Priority1, Value1, insert1(Left, HashKey, Priority, Value), Right}); HashKey > HashKey1 -> heapify({HashKey1, Priority1, Value1, Left, insert1(Right, HashKey, Priority, Value)}); Priority == Priority1 -> {HashKey, Priority, Value, Left, Right}; true -> insert1(delete_root(Tree), HashKey, Priority, Value) end. heapify({_HashKey, _Priority, _Value, nil, nil} = Tree) -> Tree; heapify({HashKey, Priority, Value, nil = Left, {HashKeyR, PriorityR, ValueR, LeftR, RightR}} = Tree) -> if PriorityR > Priority -> {HashKeyR, PriorityR, ValueR, {HashKey, Priority, Value, Left, LeftR}, RightR}; true -> Tree end; heapify({HashKey, Priority, Value, {HashKeyL, PriorityL, ValueL, LeftL, RightL}, nil = Right} = Tree) -> if PriorityL > Priority -> {HashKeyL, PriorityL, ValueL, LeftL, {HashKey, Priority, Value, RightL, Right}}; true -> Tree end; heapify({HashKey, Priority, Value, {HashKeyL, PriorityL, ValueL, LeftL, RightL} = Left, {HashKeyR, PriorityR, ValueR, LeftR, RightR} = Right} = Tree) -> if PriorityR > Priority -> {HashKeyR, PriorityR, ValueR, {HashKey, Priority, Value, Left, LeftR}, RightR}; PriorityL > Priority -> {HashKeyL, PriorityL, ValueL, LeftL, {HashKey, Priority, Value, RightL, Right}}; true -> Tree end. delete(Key, Tree) -> HashKey = {erlang:phash2(Key), Key}, delete1(HashKey, Tree). delete1(_HashKey, nil) -> nil; delete1(HashKey, {HashKey1, Priority1, Value1, Left, Right} = Tree) -> if HashKey < HashKey1 -> {HashKey1, Priority1, Value1, delete1(HashKey, Left), Right}; HashKey > HashKey1 -> {HashKey1, Priority1, Value1, Left, delete1(HashKey, Right)}; true -> delete_root(Tree) end. delete_root({HashKey, Priority, Value, Left, Right}) -> case {Left, Right} of {nil, nil} -> nil; {_, nil} -> Left; {nil, _} -> Right; {{HashKeyL, PriorityL, ValueL, LeftL, RightL}, {HashKeyR, PriorityR, ValueR, LeftR, RightR}} -> if PriorityL > PriorityR -> {HashKeyL, PriorityL, ValueL, LeftL, delete_root({HashKey, Priority, Value, RightL, Right})}; true -> {HashKeyR, PriorityR, ValueR, delete_root({HashKey, Priority, Value, Left, LeftR}), RightR} end end. delete_higher_priorities(Treap, DeletePriority) -> case treap:is_empty(Treap) of true -> Treap; false -> {_Key, Priority, _Value} = treap:get_root(Treap), if Priority > DeletePriority -> delete_higher_priorities(treap:delete_root(Treap), DeletePriority); true -> Treap end end. priority_from_current_time() -> priority_from_current_time(0). -ifdef(NEED_TIME_FALLBACKS). priority_from_current_time(MsOffset) -> {MS, S, US} = now(), -(MS*1000000+S)*1000000+US. -else. priority_from_current_time(MsOffset) -> case MsOffset of 0 -> {-erlang:monotonic_time(micro_seconds), -erlang:unique_integer([positive])}; _ -> {-erlang:monotonic_time(micro_seconds)+MsOffset, 0} end. -endif. is_empty(nil) -> true; is_empty({_HashKey, _Priority, _Value, _Left, _Right}) -> false. get_root({{_Hash, Key}, Priority, Value, _Left, _Right}) -> {Key, Priority, Value}. lookup(Key, Tree) -> HashKey = {erlang:phash2(Key), Key}, lookup1(Tree, HashKey). lookup1(nil, _HashKey) -> error; lookup1({HashKey1, Priority1, Value1, Left, Right}, HashKey) -> if HashKey < HashKey1 -> lookup1(Left, HashKey); HashKey > HashKey1 -> lookup1(Right, HashKey); true -> {ok, Priority1, Value1} end. fold(_F, Acc, nil) -> Acc; fold(F, Acc, {{_Hash, Key}, Priority, Value, Left, Right}) -> Acc1 = F({Key, Priority, Value}, Acc), Acc2 = fold(F, Acc1, Left), fold(F, Acc2, Right). to_list(Tree) -> to_list(Tree, []). to_list(nil, Acc) -> Acc; to_list(Tree, Acc) -> Root = get_root(Tree), to_list(delete_root(Tree), [Root | Acc]). from_list(List) -> from_list(List, nil). from_list([{Key, Priority, Value} | Tail], Tree) -> from_list(Tail, insert(Key, Priority, Value, Tree)); from_list([], Tree) -> Tree. p1_utils-1.0.3/src/p1_edoc_layout.erl0000644000232200023220000010072112642704237020033 0ustar debalancedebalance%% ===================================================================== %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as %% published by the Free Software Foundation; either version 2 of the %% License, or (at your option) any later version. %% %% This library is distributed in the hope that it will be useful, but %% WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %% Lesser General Public License for more details. %% %% You should have received a copy of the GNU Lesser General Public %% License along with this library; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% %% @author Richard Carlsson %% @copyright 2001-2006 Richard Carlsson %% @see edoc %% @end %% ===================================================================== %% @hidden %% @doc The standard HTML layout module for EDoc. See the {@link edoc} %% module for details on usage. %% Note that this is written so that it is *not* depending on edoc.hrl! -module(p1_edoc_layout). -export([module/2, overview/2, type/1]). -import(edoc_report, [report/2]). -include_lib("xmerl/include/xmerl.hrl"). -define(HTML_EXPORT, xmerl_html). -define(DEFAULT_XML_EXPORT, ?HTML_EXPORT). -define(OVERVIEW_SUMMARY, "overview-summary.html"). -define(STYLESHEET, "stylesheet.css"). -define(NL, "\n"). -define(DESCRIPTION_TITLE, "Description"). -define(DESCRIPTION_LABEL, "description"). -define(DATA_TYPES_TITLE, "Data Types"). -define(DATA_TYPES_LABEL, "types"). -define(FUNCTION_INDEX_TITLE, "Function Index"). -define(FUNCTION_INDEX_LABEL, "index"). -define(FUNCTIONS_TITLE, "Function Details"). -define(FUNCTIONS_LABEL, "functions"). %% @doc The layout function. %% %% Options to the standard layout: %%
%%
{@type {index_columns, integer()@}} %%
%%
Specifies the number of column pairs used for the function %% index tables. The default value is 1. %%
%%
{@type {pretty_printer, atom()@}} %%
%%
Specifies how types and specifications are pretty printed. %% If the value `erl_pp' is specified the Erlang pretty printer %% (the module `erl_pp') will be used. The default is to do %% no pretty printing which implies that lines can be very long. %%
%%
{@type {stylesheet, string()@}} %%
%%
Specifies the URI used for referencing the stylesheet. The %% default value is `"stylesheet.css"'. If an empty string is %% specified, no stylesheet reference will be generated. %%
%%
{@type {sort_functions, boolean()@}} %%
%%
If `true', the detailed function descriptions are listed by %% name, otherwise they are listed in the order of occurrence in %% the source file. The default value is `true'. %%
%%
{@type {xml_export, Module::atom()@}} %%
%%
Specifies an {@link //xmerl. `xmerl'} callback module to be %% used for exporting the documentation. See {@link %% //xmerl/xmerl:export_simple/3} for details. %%
%%
%% %% @see edoc:layout/2 %% NEW-OPTIONS: xml_export, index_columns, stylesheet module(Element, Options) -> XML = layout_module(Element, init_opts(Element, Options)), Export = proplists:get_value(xml_export, Options, ?DEFAULT_XML_EXPORT), xmerl:export_simple(XML, Export, []). % Put layout options in a data structure for easier access. %% %Commented out until it can be made private %% %@type opts() = #opts{root = string(), %% % stylesheet = string(), %% % index_columns = integer()} -record(opts, {root, stylesheet, image, link, index_columns, sort_functions, pretty_printer}). init_opts(Element, Options) -> R = #opts{root = get_attrval(root, Element), index_columns = proplists:get_value(index_columns, Options, 1), sort_functions = proplists:get_value(sort_functions, Options, true), pretty_printer = proplists:get_value(pretty_printer, Options, ''), image = proplists:get_value(image, Options, "erlang.png"), link = proplists:get_value(link, Options, "http://www.erlang.org/") }, case proplists:get_value(stylesheet, Options) of undefined -> S = edoc_lib:join_uri(R#opts.root, ?STYLESHEET), R#opts{stylesheet = S}; "" -> R; % don't use any stylesheet S when is_list(S) -> R#opts{stylesheet = S}; _ -> report("bad value for option `stylesheet'.", []), exit(error) end. %% ===================================================================== %% XML-BASED LAYOUT ENGINE %% ===================================================================== %% We assume that we have expanded XML data. %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% TODO: improve layout of parameterized modules layout_module(#xmlElement{name = module, content = Es}=E, Opts) -> Args = module_params(get_content(args, Es)), Name = get_attrval(name, E), Title = case get_elem(args, Es) of [] -> ["Module ", Name]; _ -> ["Abstract module ", Name, " [", {Args}, "]"] end, Desc = get_content(description, Es), ShortDesc = get_content(briefDescription, Desc), FullDesc = get_content(fullDescription, Desc), Functions = [{function_name(E1), E1} || E1 <- get_content(functions, Es)], Types = [{type_name(E1), E1} || E1 <- get_content(typedecls, Es)], SortedFs = lists:sort(Functions), Body = (navigation("top", Opts) ++ [?NL, hr, ?NL, ?NL, {h1, Title}, ?NL] ++ doc_index(FullDesc, Functions, Types) ++ ShortDesc ++ [?NL] ++ copyright(Es) ++ deprecated(Es, "module") ++ [?NL] ++ version(Es) ++ since(Es) ++ behaviours(Es, Name) ++ authors(Es) ++ references(Es) ++ sees(Es) ++ todos(Es) ++ if FullDesc == [] -> []; true -> [?NL, {h2, [{a, [{name, "description"}], ["Description"]}]} | FullDesc] end ++ types(lists:sort(Types), Opts) ++ function_index(SortedFs, Opts#opts.index_columns) ++ if Opts#opts.sort_functions -> functions(SortedFs, Opts); true -> functions(Functions, Opts) end ++ [hr, ?NL] ++ navigation("bottom", Opts) ++ timestamp()), Encoding = get_attrval(encoding, E), xhtml(Title, stylesheet(Opts), Body, Encoding). module_params(Es) -> As = [{get_text(argName, Es1), get_content(fullDescription, get_content(description, Es1))} || #xmlElement{content = Es1} <- Es], case As of [] -> []; [First | Rest] -> [element(1, First) | [ {[", ",A]} || {A, _D} <- Rest]] end. timestamp() -> [?NL, {p, [{i, [io_lib:fwrite("Generated by EDoc, ~s, ~s.", [edoc_lib:datestr(date()), edoc_lib:timestr(time())]) ]}]}, ?NL]. stylesheet(Opts) -> case Opts#opts.stylesheet of undefined -> []; CSS -> [{link, [{rel, "stylesheet"}, {type, "text/css"}, {href, CSS}, {title, "EDoc"}], []}, ?NL] end. image(Opts) -> {td, [{a, [{href, Opts#opts.link}, {target, "_top"}], [{img, [{src, Opts#opts.image}, {align, "right"}, {border, 0}, {alt, "Logo"}], []}]} ]}. navigation(Where, Opts) -> [?NL, {'div', [{class, "navbar"}], [{a, [{name, "#navbar_" ++ Where}], []}, {table, [{width, "100%"}, {border,0}, {cellspacing, 0}, {cellpadding, 2}, {summary, "navigation bar"}], [{tr, [{td, [{a, [{href, ?OVERVIEW_SUMMARY}, {target,"overviewFrame"}], ["Overview"]}]}, image(Opts) ]} ]} ]} ]. doc_index(FullDesc, Functions, Types) -> case doc_index_rows(FullDesc, Functions, Types) of [] -> []; Rs -> [{ul, [{class, "index"}], [{li, [{a, [{href, local_label(R)}], [T]}]} || {T, R} <- Rs]}] end. doc_index_rows(FullDesc, Functions, Types) -> (if FullDesc == [] -> []; true -> [{?DESCRIPTION_TITLE, ?DESCRIPTION_LABEL}] end ++ if Types == [] -> []; true -> [{?DATA_TYPES_TITLE, ?DATA_TYPES_LABEL}] end ++ if Functions == [] -> []; true -> [{?FUNCTION_INDEX_TITLE, ?FUNCTION_INDEX_LABEL}, {?FUNCTIONS_TITLE, ?FUNCTIONS_LABEL}] end). function_index(Fs, Cols) -> case function_index_rows(Fs, Cols, []) of [] -> []; Rows -> [?NL, {h2, [{a, [{name, ?FUNCTION_INDEX_LABEL}], [?FUNCTION_INDEX_TITLE]}]}, ?NL, {table, [{width, "100%"}, {border, 0}, {cellspacing,0}, {cellpadding,2}, {summary, "function index"}], Rows}, ?NL] end. function_index_rows(Fs, Cols, Title) -> Rows = (length(Fs) + (Cols - 1)) div Cols, (if Title == [] -> []; true -> [{tr, [{th, [{colspan, Cols * 2}, {align, left}], [Title]}]}, ?NL] end ++ lists:flatmap(fun index_row/1, edoc_lib:transpose(edoc_lib:segment(Fs, Rows)))). index_row(Fs) -> [{tr, lists:flatmap(fun index_col/1, Fs)}, ?NL]. index_col({Name, F=#xmlElement{content = Es}}) -> [{td, [{valign, "top"}], label_href(function_header(Name, F, "*"), F)}, {td, index_desc(Es)}]. index_desc(Es) -> Desc = get_content(description, Es), (case get_content(deprecated, Es) of [] -> []; _ -> ["(", {em, ["Deprecated"]}, ".) "] end ++ case get_content(briefDescription, Desc) of [] -> equiv(Es); % no description at all if no equiv ShortDesc -> ShortDesc end). label_href(Content, F) -> case get_attrval(label, F) of "" -> Content; Ref -> [{a, [{href, local_label(Ref)}], Content}] end. %% %% %% %% %% functions(Fs, Opts) -> Es = lists:flatmap(fun ({Name, E}) -> function(Name, E, Opts) end, Fs), if Es == [] -> []; true -> [?NL, {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]}, ?NL | Es] end. function(Name, E=#xmlElement{content = Es}, Opts) -> ([?NL, {h3, [{class, "function"}], label_anchor(function_header(Name, E, " *"), E)}, ?NL] ++ [{'div', [{class, "spec"}], [?NL, {p, case typespec(get_content(typespec, Es), Opts) of [] -> signature(get_content(args, Es), get_attrval(name, E)); Spec -> Spec end}, ?NL] ++ case params(get_content(args, Es)) of [] -> []; Ps -> [{p, Ps}, ?NL] end ++ case returns(get_content(returns, Es)) of [] -> []; Rs -> [{p, Rs}, ?NL] end}] ++ throws(Es, Opts) ++ equiv_p(Es) ++ deprecated(Es, "function") ++ fulldesc(Es) ++ since(Es) ++ sees(Es) ++ todos(Es)). function_name(E) -> atom(get_attrval(name, E)) ++ "/" ++ get_attrval(arity, E). function_header(Name, E, Private) -> case is_exported(E) of true -> [Name]; false -> [Name, Private] end. is_exported(E) -> case get_attrval(exported, E) of "yes" -> true; _ -> false end. label_anchor(Content, E) -> case get_attrval(label, E) of "" -> Content; Ref -> [{a, [{name, Ref}], Content}] end. %% %% %% %% This is currently only done for functions without type spec. signature(Es, Name) -> [{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> any()"]}]. arg(#xmlElement{content = Es}) -> [get_text(argName, Es)]. %% parameter and return value descriptions (if any) params(Es) -> As = [{get_text(argName, Es1), get_content(fullDescription, get_content(description, Es1))} || #xmlElement{content = Es1} <- Es], As1 = [A || A <- As, element(2, A) /= []], if As1 == [] -> []; true -> [ { [{tt, [A]}, ": "] ++ D ++ [br, ?NL] } || {A, D} <- As1] end. returns(Es) -> case get_content(fullDescription, get_content(description, Es)) of [] -> []; D -> ["returns: "] ++ D end. %% throws(Es, Opts) -> case get_content(throws, Es) of [] -> []; Es1 -> %% Doesn't use format_type; keep it short! [{p, (["throws ", {tt, t_utype(get_elem(type, Es1))}] ++ local_defs(get_elem(localdef, Es1), Opts))}, ?NL] end. %% typespec([], _Opts) -> []; typespec(Es, Opts) -> Name = t_name(get_elem(erlangName, Es)), Defs = get_elem(localdef, Es), [Type] = get_elem(type, Es), format_spec(Name, Type, Defs, Opts) ++ local_defs(Defs, Opts). %% %% types([], _Opts) -> []; types(Ts, Opts) -> Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E, Opts) end, Ts), [?NL, {h2, [{a, [{name, ?DATA_TYPES_LABEL}], [?DATA_TYPES_TITLE]}]}, ?NL | Es]. typedecl(Name, E=#xmlElement{content = Es}, Opts) -> ([?NL, {h3, [{class, "typedecl"}], label_anchor([Name, "()"], E)}, ?NL] ++ [{p, typedef(get_content(typedef, Es), Opts)}, ?NL] ++ fulldesc(Es)). type_name(#xmlElement{content = Es}) -> t_name(get_elem(erlangName, get_content(typedef, Es))). typedef(Es, Opts) -> Name = ([t_name(get_elem(erlangName, Es)), "("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), (case get_elem(type, Es) of [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}]; Type -> format_type(Name, Name, Type, [], Opts) end ++ local_defs(get_elem(localdef, Es), Opts)). local_defs(Es, Opts) -> local_defs(Es, [], Opts). local_defs([], _, _Opts) -> []; local_defs(Es0, Last, Opts) -> [E | Es] = lists:reverse(Es0), [?NL, {ul, [{class, "definitions"}], lists:reverse(lists:append([localdef(E1, [], Opts) || E1 <- Es]), localdef(E, Last, Opts))}]. localdef(E = #xmlElement{content = Es}, Last, Opts) -> Name = case get_elem(typevar, Es) of [] -> label_anchor(N0 = t_abstype(get_content(abstype, Es)), E); [V] -> N0 = t_var(V) end, [{li, format_type(Name, N0, get_elem(type, Es), Last, Opts)}]. %% Use the default formatting of EDoc, which creates references, and %% then insert newlines and indentation according to erl_pp (the %% (fast) Erlang pretty printer). format_spec(Name, Type, Defs, #opts{pretty_printer = erl_pp}=Opts) -> try L = t_clause(Name, Type), O = pp_clause(Name, Type), {R, ".\n"} = etypef(L, O), [{pre, R}] catch _:_ -> %% Should not happen. format_spec(Name, Type, Defs, Opts#opts{pretty_printer=''}) end; format_spec(Sep, Type, Defs, _Opts) -> %% Very limited formatting. Br = if Defs =:= [] -> br; true -> [] end, [{tt, t_clause(Sep, Type)}, Br]. t_clause(Name, Type) -> #xmlElement{content = [#xmlElement{name = 'fun', content = C}]} = Type, [Name] ++ t_fun(C). pp_clause(Pre, Type) -> Types = ot_utype([Type]), Atom = lists:duplicate(iolist_size(Pre), $a), Attr = {attribute,0,spec,{{list_to_atom(Atom),0},[Types]}}, L1 = erl_pp:attribute(erl_parse:new_anno(Attr)), "-spec " ++ L2 = lists:flatten(L1), L3 = Pre ++ lists:nthtail(length(Atom), L2), re:replace(L3, "\n ", "\n", [{return,list},global]). format_type(Prefix, Name, Type, Last, #opts{pretty_printer = erl_pp}=Opts) -> try L = t_utype(Type), O = pp_type(Name, Type), {R, ".\n"} = etypef(L, O), [{pre, Prefix ++ [" = "] ++ R ++ Last}] catch _:_ -> %% Example: "t() = record(a)." format_type(Prefix, Name, Type, Last, Opts#opts{pretty_printer =''}) end; format_type(Prefix, _Name, Type, Last, _Opts) -> [{tt, Prefix ++ [" = "] ++ t_utype(Type) ++ Last}]. pp_type(Prefix, Type) -> Atom = list_to_atom(lists:duplicate(iolist_size(Prefix), $a)), Attr = {attribute,0,type,{Atom,ot_utype(Type),[]}}, L1 = erl_pp:attribute(erl_parse:new_anno(Attr)), {L2,N} = case lists:dropwhile(fun(C) -> C =/= $: end, lists:flatten(L1)) of ":: " ++ L3 -> {L3,9}; % compensation for extra "()" and ":" "::\n" ++ L3 -> {"\n"++L3,6} end, Ss = lists:duplicate(N, $\s), re:replace(L2, "\n"++Ss, "\n", [{return,list},global]). etypef(L, O0) -> {R, O} = etypef(L, [], O0, []), {lists:reverse(R), O}. etypef([C | L], St, [C | O], R) -> etypef(L, St, O, [[C] | R]); etypef(" "++L, St, O, R) -> etypef(L, St, O, R); etypef("", [Cs | St], O, R) -> etypef(Cs, St, O, R); etypef("", [], O, R) -> {R, O}; etypef(L, St, " "++O, R) -> etypef(L, St, O, [" " | R]); etypef(L, St, "\n"++O, R) -> Ss = lists:takewhile(fun(C) -> C =:= $\s end, O), etypef(L, St, lists:nthtail(length(Ss), O), ["\n"++Ss | R]); etypef([{a, HRef, S0} | L], St, O0, R) -> {S, O} = etypef(S0, app_fix(O0)), etypef(L, St, O, [{a, HRef, S} | R]); etypef("="++L, St, "::"++O, R) -> %% EDoc uses "=" for record field types; Erlang types use "::". %% Maybe there should be an option for this, possibly affecting %% other similar discrepancies. etypef(L, St, O, ["=" | R]); etypef([Cs | L], St, O, R) -> etypef(Cs, [L | St], O, R). app_fix(L) -> try {"//" ++ R1,L2} = app_fix(L, 1), [App, Mod] = string:tokens(R1, "/"), "//" ++ atom(App) ++ "/" ++ atom(Mod) ++ L2 catch _:_ -> L end. app_fix(L, I) -> % a bit slow {L1, L2} = lists:split(I, L), case erl_scan:tokens([], L1 ++ ". ", 1) of {done, {ok,[{atom,_,Atom}|_],_}, _} -> {atom_to_list(Atom), L2}; _ -> app_fix(L, I+1) end. fulldesc(Es) -> case get_content(fullDescription, get_content(description, Es)) of [] -> [?NL]; Desc -> [{p, Desc}, ?NL] end. sees(Es) -> case get_elem(see, Es) of [] -> []; Es1 -> [{p, [{b, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])}, ?NL] end. see(E=#xmlElement{content = Es}) -> see(E, Es). see(E, Es) -> case href(E) of [] -> Es; Ref -> [{a, Ref, Es}] end. href(E) -> case get_attrval(href, E) of "" -> []; URI -> T = case get_attrval(target, E) of "" -> []; S -> [{target, S}] end, [{href, URI} | T] end. equiv_p(Es) -> equiv(Es, true). equiv(Es) -> equiv(Es, false). equiv(Es, P) -> case get_content(equiv, Es) of [] -> []; Es1 -> case get_content(expr, Es1) of [] -> []; [Expr] -> Expr1 = [{tt, [Expr]}], Expr2 = case get_elem(see, Es1) of [] -> Expr1; [E=#xmlElement{}] -> see(E, Expr1) end, Txt = ["Equivalent to "] ++ Expr2 ++ ["."], (case P of true -> [{p, Txt}]; false -> Txt end ++ [?NL]) end end. copyright(Es) -> case get_content(copyright, Es) of [] -> []; Es1 -> [{p, ["Copyright \251 " | Es1]}, ?NL] end. version(Es) -> case get_content(version, Es) of [] -> []; Es1 -> [{p, [{b, ["Version:"]}, " " | Es1]}, ?NL] end. since(Es) -> case get_content(since, Es) of [] -> []; Es1 -> [{p, [{b, ["Introduced in:"]}, " " | Es1]}, ?NL] end. deprecated(Es, S) -> Es1 = get_content(description, get_content(deprecated, Es)), case get_content(fullDescription, Es1) of [] -> []; Es2 -> [{p, [{b, ["This " ++ S ++ " is deprecated:"]}, " " | Es2]}, ?NL] end. behaviours(Es, Name) -> CBs = get_content(callbacks, Es), OCBs = get_content(optional_callbacks, Es), (case get_elem(behaviour, Es) of [] -> []; Es1 -> [{p, ([{b, ["Behaviours:"]}, " "] ++ seq(fun behaviour/1, Es1, ["."]))}, ?NL] end ++ if CBs =:= [], OCBs =:= [] -> []; true -> Req = if CBs =:= [] -> []; true -> [br, " Required callback functions: "] ++ seq(fun callback/1, CBs, ["."]) end, Opt = if OCBs =:= [] -> []; true -> [br, " Optional callback functions: "] ++ seq(fun callback/1, OCBs, ["."]) end, [{p, ([{b, ["This module defines the ", {tt, [Name]}, " behaviour."]}] ++ Req ++ Opt)}, ?NL] end). behaviour(E=#xmlElement{content = Es}) -> see(E, [{tt, Es}]). callback(E=#xmlElement{}) -> Name = get_attrval(name, E), Arity = get_attrval(arity, E), [{tt, [Name, "/", Arity]}]. authors(Es) -> case get_elem(author, Es) of [] -> []; Es1 -> [{p, [{b, ["Authors:"]}, " "] ++ seq(fun author/1, Es1, ["."])}, ?NL] end. atom(String) -> io_lib:write_atom(list_to_atom(String)). %% author(E=#xmlElement{}) -> Name = get_attrval(name, E), Mail = get_attrval(email, E), URI = get_attrval(website, E), (if Name == Mail -> [{a, [{href, "mailto:" ++ Mail}],[{tt, [Mail]}]}]; true -> if Mail == "" -> [Name]; true -> [Name, " (", {a, [{href, "mailto:" ++ Mail}], [{tt, [Mail]}]}, ")"] end end ++ if URI == "" -> []; true -> [" [", {em, ["web site:"]}, " ", {tt, [{a, [{href, URI}, {target, "_top"}], [URI]}]}, "]"] end). references(Es) -> case get_elem(reference, Es) of [] -> []; Es1 -> [{p, [{b, ["References"]}, {ul, [{li, C} || #xmlElement{content = C} <- Es1]}]}, ?NL] end. todos(Es) -> case get_elem(todo, Es) of [] -> []; Es1 -> Todos = [{li, [{font, [{color,red}], C}]} || #xmlElement{content = C} <- Es1], [{p, [{b, [{font, [{color,red}], ["To do"]}]}, {ul, Todos}]}, ?NL] end. t_name([E]) -> N = get_attrval(name, E), case get_attrval(module, E) of "" -> atom(N); M -> S = atom(M) ++ ":" ++ atom(N), case get_attrval(app, E) of "" -> S; A -> "//" ++ atom(A) ++ "/" ++ S end end. t_utype([E]) -> t_utype_elem(E). t_utype_elem(E=#xmlElement{content = Es}) -> case get_attrval(name, E) of "" -> t_type(Es); Name -> T = t_type(Es), case T of [Name] -> T; % avoid generating "Foo::Foo" T -> [Name] ++ ["::"] ++ T end end. t_type([E=#xmlElement{name = typevar}]) -> t_var(E); t_type([E=#xmlElement{name = atom}]) -> t_atom(E); t_type([E=#xmlElement{name = integer}]) -> t_integer(E); t_type([E=#xmlElement{name = range}]) -> t_range(E); t_type([E=#xmlElement{name = binary}]) -> t_binary(E); t_type([E=#xmlElement{name = float}]) -> t_float(E); t_type([#xmlElement{name = nil}]) -> t_nil(); t_type([#xmlElement{name = paren, content = Es}]) -> t_paren(Es); t_type([#xmlElement{name = list, content = Es}]) -> t_list(Es); t_type([#xmlElement{name = nonempty_list, content = Es}]) -> t_nonempty_list(Es); t_type([#xmlElement{name = map, content = Es}]) -> t_map(Es); t_type([#xmlElement{name = tuple, content = Es}]) -> t_tuple(Es); t_type([#xmlElement{name = 'fun', content = Es}]) -> ["fun("] ++ t_fun(Es) ++ [")"]; t_type([E = #xmlElement{name = record, content = Es}]) -> t_record(E, Es); t_type([E = #xmlElement{name = abstype, content = Es}]) -> t_abstype(E, Es); t_type([#xmlElement{name = union, content = Es}]) -> t_union(Es). t_var(E) -> [get_attrval(name, E)]. t_atom(E) -> [get_attrval(value, E)]. t_integer(E) -> [get_attrval(value, E)]. t_range(E) -> [get_attrval(value, E)]. t_binary(E) -> [get_attrval(value, E)]. t_float(E) -> [get_attrval(value, E)]. t_nil() -> ["[]"]. t_paren(Es) -> ["("] ++ t_utype(get_elem(type, Es)) ++ [")"]. t_list(Es) -> ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"]. t_nonempty_list(Es) -> ["["] ++ t_utype(get_elem(type, Es)) ++ [", ...]"]. t_tuple(Es) -> ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). t_fun(Es) -> ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [") -> "] ++ t_utype(get_elem(type, Es))). t_map(Es) -> Fs = get_elem(map_field, Es), ["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]). t_map_field(#xmlElement{content = [K,V]}) -> t_utype_elem(K) ++ [" => "] ++ t_utype_elem(V). t_record(E, Es) -> Name = ["#"] ++ t_type(get_elem(atom, Es)), case get_elem(field, Es) of [] -> see(E, [Name, "{}"]); Fs -> see(E, Name) ++ ["{"] ++ seq(fun t_field/1, Fs, ["}"]) end. t_field(#xmlElement{content = Es}) -> t_type(get_elem(atom, Es)) ++ [" = "] ++ t_utype(get_elem(type, Es)). t_abstype(E, Es) -> Name = t_name(get_elem(erlangName, Es)), case get_elem(type, Es) of [] -> see(E, [Name, "()"]); Ts -> see(E, [Name]) ++ ["("] ++ seq(fun t_utype_elem/1, Ts, [")"]) end. t_abstype(Es) -> ([t_name(get_elem(erlangName, Es)), "("] ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])). t_union(Es) -> seq(fun t_utype_elem/1, Es, " | ", []). seq(F, Es) -> seq(F, Es, []). seq(F, Es, Tail) -> seq(F, Es, ", ", Tail). seq(F, [E], _Sep, Tail) -> F(E) ++ Tail; seq(F, [E | Es], Sep, Tail) -> F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail); seq(_F, [], _Sep, Tail) -> Tail. get_elem(Name, [#xmlElement{name = Name} = E | Es]) -> [E | get_elem(Name, Es)]; get_elem(Name, [_ | Es]) -> get_elem(Name, Es); get_elem(_, []) -> []. get_attr(Name, [#xmlAttribute{name = Name} = A | As]) -> [A | get_attr(Name, As)]; get_attr(Name, [_ | As]) -> get_attr(Name, As); get_attr(_, []) -> []. get_attrval(Name, #xmlElement{attributes = As}) -> case get_attr(Name, As) of [#xmlAttribute{value = V}] -> V; [] -> "" end. get_content(Name, Es) -> case get_elem(Name, Es) of [#xmlElement{content = Es1}] -> Es1; [] -> [] end. get_text(Name, Es) -> case get_content(Name, Es) of [#xmlText{value = Text}] -> Text; [] -> "" end. local_label(R) -> "#" ++ R. xhtml(Title, CSS, Body, Encoding) -> EncString = case Encoding of "latin1" -> "ISO-8859-1"; _ -> "UTF-8" end, [{html, [?NL, {head, [?NL, {meta, [{'http-equiv',"Content-Type"}, {content, "text/html; charset="++EncString}], []}, ?NL, {title, Title}, ?NL] ++ CSS}, ?NL, {body, [{bgcolor, "white"},{class, "mainpane"}], Body}, ?NL] }, ?NL]. %% --------------------------------------------------------------------- type(E) -> type(E, []). type(E, Ds) -> Opts = [], xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds, Opts), ?HTML_EXPORT). overview(E=#xmlElement{name = overview, content = Es}, Options) -> Opts = init_opts(E, Options), Title = [get_text(title, Es)], Desc = get_content(description, Es), % ShortDesc = get_content(briefDescription, Desc), FullDesc = get_content(fullDescription, Desc), Body = (navigation("top", Opts) ++ [?NL, {h1, [Title]}, ?NL] % ++ ShortDesc ++ copyright(Es) ++ version(Es) ++ since(Es) ++ authors(Es) ++ references(Es) ++ sees(Es) ++ todos(Es) ++ FullDesc ++ [?NL, hr] ++ navigation("bottom", Opts) ++ timestamp()), Encoding = get_attrval(encoding, E), XML = xhtml(Title, stylesheet(Opts), Body, Encoding), xmerl:export_simple(XML, ?HTML_EXPORT, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% NYTT ot_utype([E]) -> ot_utype_elem(E). ot_utype_elem(E=#xmlElement{content = Es}) -> case get_attrval(name, E) of "" -> ot_type(Es); N -> Name = {var,0,list_to_atom(N)}, T = ot_type(Es), case T of Name -> T; T -> {ann_type,0,[Name, T]} end end. ot_type([E=#xmlElement{name = typevar}]) -> ot_var(E); ot_type([E=#xmlElement{name = atom}]) -> ot_atom(E); ot_type([E=#xmlElement{name = integer}]) -> ot_integer(E); ot_type([E=#xmlElement{name = range}]) -> ot_range(E); ot_type([E=#xmlElement{name = binary}]) -> ot_binary(E); ot_type([E=#xmlElement{name = float}]) -> ot_float(E); ot_type([#xmlElement{name = nil}]) -> ot_nil(); ot_type([#xmlElement{name = paren, content = Es}]) -> ot_paren(Es); ot_type([#xmlElement{name = list, content = Es}]) -> ot_list(Es); ot_type([#xmlElement{name = nonempty_list, content = Es}]) -> ot_nonempty_list(Es); ot_type([#xmlElement{name = tuple, content = Es}]) -> ot_tuple(Es); ot_type([#xmlElement{name = map, content = Es}]) -> ot_map(Es); ot_type([#xmlElement{name = 'fun', content = Es}]) -> ot_fun(Es); ot_type([#xmlElement{name = record, content = Es}]) -> ot_record(Es); ot_type([#xmlElement{name = abstype, content = Es}]) -> ot_abstype(Es); ot_type([#xmlElement{name = union, content = Es}]) -> ot_union(Es). ot_var(E) -> {var,0,list_to_atom(get_attrval(name, E))}. ot_atom(E) -> {ok, [{atom,A,Name}], _} = erl_scan:string(get_attrval(value, E), 0), {atom,erl_anno:line(A),Name}. ot_integer(E) -> {integer,0,list_to_integer(get_attrval(value, E))}. ot_range(E) -> [I1, I2] = string:tokens(get_attrval(value, E), "."), {type,0,range,[{integer,0,list_to_integer(I1)}, {integer,0,list_to_integer(I2)}]}. ot_binary(E) -> {Base, Unit} = case string:tokens(get_attrval(value, E), ",:*><") of [] -> {0, 0}; ["_",B] -> {list_to_integer(B), 0}; ["_","_",U] -> {0, list_to_integer(U)}; ["_",B,_,"_",U] -> {list_to_integer(B), list_to_integer(U)} end, {type,0,binary,[{integer,0,Base},{integer,0,Unit}]}. ot_float(E) -> {float,0,list_to_float(get_attrval(value, E))}. ot_nil() -> {nil,0}. ot_paren(Es) -> {paren_type,0,[ot_utype(get_elem(type, Es))]}. ot_list(Es) -> {type,0,list,[ot_utype(get_elem(type, Es))]}. ot_nonempty_list(Es) -> {type,0,nonempty_list,[ot_utype(get_elem(type, Es))]}. ot_tuple(Es) -> {type,0,tuple,[ot_utype_elem(E) || E <- Es]}. ot_map(Es) -> {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}. ot_map_field(#xmlElement{content=[K,V]}) -> {type,0,map_field_assoc,ot_utype_elem(K), ot_utype_elem(V)}. ot_fun(Es) -> Range = ot_utype(get_elem(type, Es)), Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)], {type,0,'fun',[{type,0,product,Args},Range]}. ot_record(Es) -> {type,0,record,[ot_type(get_elem(atom, Es)) | [ot_field(F) || F <- get_elem(field, Es)]]}. ot_field(#xmlElement{content = Es}) -> {type,0,field_type, [ot_type(get_elem(atom, Es)), ot_utype(get_elem(type, Es))]}. ot_abstype(Es) -> ot_name(get_elem(erlangName, Es), [ot_utype_elem(Elem) || Elem <- get_elem(type, Es)]). ot_union(Es) -> {type,0,union,[ot_utype_elem(E) || E <- Es]}. ot_name(Es, T) -> case ot_name(Es) of [Mod, ":", Atom] -> {remote_type,0,[{atom,0,list_to_atom(Mod)}, {atom,0,list_to_atom(Atom)},T]}; "tuple" when T =:= [] -> {type,0,tuple,any}; Atom -> {type,0,list_to_atom(Atom),T} end. ot_name([E]) -> Atom = get_attrval(name, E), case get_attrval(module, E) of "" -> Atom; M -> case get_attrval(app, E) of "" -> [M, ":", Atom]; A -> ["//"++A++"/" ++ M, ":", Atom] % EDoc only! end end. p1_utils-1.0.3/src/p1_utils.app.src0000644000232200023220000000062312642704237017450 0ustar debalancedebalance{application, p1_utils, [ {description, "Erlang utility modules from ProcessOne"}, {vsn, "1.0.3"}, {modules, []}, {registered, []}, {applications, [ kernel, stdlib ]}, {env, []}, %% hex.pm packaging: {licenses, ["Apache 2.0"]}, {maintainers, ["ProcessOne"]}, {links, [{"Github", "https://github.com/processone/p1_utils"}]} ]}. p1_utils-1.0.3/src/p1_nif_utils.erl0000644000232200023220000000554112642704237017524 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : p1_nif_utils.erl %%% Author : Paweł Chmielowski %%% Description : Helper utilities for handling nif code %%% %%% Created : 7 Oct 2015 by Paweł Chmielowski %%% %%% %%% Copyright (C) 2002-2015 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%------------------------------------------------------------------- -module(p1_nif_utils). -export([get_so_path/3]). get_so_path(ModuleName, AppNames, SoName) -> PrivDir = first_match(fun(App) -> case code:priv_dir(App) of {error, _} -> none; V -> V end end, AppNames), case PrivDir of none -> Ext = case os:type() of {win32, _} -> ".dll"; _ -> ".so" end, SoFName = filename:join(["priv", "lib", SoName ++ Ext]), LPath = first_match(fun(Path) -> P = case filename:basename(Path) of ebin -> filename:dirname(Path); _ -> Path end, case filelib:is_file(filename:join([P, SoFName])) of true -> filename:join([P, "priv", "lib", SoName]); _ -> none end end, code:get_path()), case LPath of none -> EbinDir = filename:dirname(code:which(ModuleName)), AppDir = filename:dirname(EbinDir), filename:join([AppDir, "priv", "lib", SoName]); Val -> Val end; V -> filename:join([V, "lib", SoName]) end. first_match(_Fun, []) -> none; first_match(Fun, [H|T]) -> case Fun(H) of none -> first_match(Fun, T); V -> V end. p1_utils-1.0.3/src/p1_server.erl0000644000232200023220000010720112642704237017032 0ustar debalancedebalance%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %% The code has been modified and improved by ProcessOne. %% %% Copyright 2007-2015 ProcessOne %% %% The change adds the following features: %% - You can send exit(priority_shutdown) to the p1_fsm process to %% terminate immediatetly. If the fsm trap_exit process flag has been %% set to true, the FSM terminate function will called. %% - You can pass the gen_fsm options to control resource usage. %% {max_queue, N} will exit the process with priority_shutdown %% - You can limit the time processing a message (TODO): If the %% message processing does not return in a given period of time, the %% process will be terminated. %% - You might customize the State data before sending it to error_logger %% in case of a crash (just export the function print_state/1) %% -module(p1_server). %%% --------------------------------------------------- %%% %%% The idea behind THIS server is that the user module %%% provides (different) functions to handle different %%% kind of inputs. %%% If the Parent process terminates the Module:terminate/2 %%% function is called. %%% %%% The user module should export: %%% %%% init(Args) %%% ==> {ok, State} %%% {ok, State, Timeout} %%% ignore %%% {stop, Reason} %%% %%% handle_call(Msg, {From, Tag}, State) %%% %%% ==> {reply, Reply, State} %%% {reply, Reply, State, Timeout} %%% {noreply, State} %%% {noreply, State, Timeout} %%% {stop, Reason, Reply, State} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% handle_cast(Msg, State) %%% %%% ==> {noreply, State} %%% {noreply, State, Timeout} %%% {stop, Reason, State} %%% Reason = normal | shutdown | Term terminate(State) is called %%% %%% handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ... %%% %%% ==> {noreply, State} %%% {noreply, State, Timeout} %%% {stop, Reason, State} %%% Reason = normal | shutdown | Term, terminate(State) is called %%% %%% terminate(Reason, State) Let the user module clean up %%% always called when server terminates %%% %%% ==> ok %%% %%% %%% The work flow (of the server) can be described as follows: %%% %%% User module Generic %%% ----------- ------- %%% start -----> start %%% init <----- . %%% %%% loop %%% handle_call <----- . %%% -----> reply %%% %%% handle_cast <----- . %%% %%% handle_info <----- . %%% %%% terminate <----- . %%% %%% -----> reply %%% %%% %%% --------------------------------------------------- %% API -export([start/3, start/4, start_link/3, start_link/4, call/2, call/3, cast/2, reply/2, abcast/2, abcast/3, multi_call/2, multi_call/3, multi_call/4, enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/6]). %% System exports -export([system_continue/3, system_terminate/4, system_code_change/4, format_status/2]). %% Internal exports -export([init_it/6]). -import(error_logger, [format/2]). %%% Internal gen_fsm state %%% This state is used to defined resource control values: -record(limits, {max_queue :: non_neg_integer()}). %%%========================================================================= %%% API %%%========================================================================= -callback init(Args :: term()) -> {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | {stop, Reason :: term()} | ignore. -callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, State :: term()) -> {reply, Reply :: term(), NewState :: term()} | {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} | {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate} | {stop, Reason :: term(), Reply :: term(), NewState :: term()} | {stop, Reason :: term(), NewState :: term()}. -callback handle_cast(Request :: term(), State :: term()) -> {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate} | {stop, Reason :: term(), NewState :: term()}. -callback handle_info(Info :: timeout | term(), State :: term()) -> {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate} | {stop, Reason :: term(), NewState :: term()}. -callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), State :: term()) -> term(). -callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), Extra :: term()) -> {ok, NewState :: term()} | {error, Reason :: term()}. %%% ----------------------------------------------------------------- %%% Starts a generic server. %%% start(Mod, Args, Options) %%% start(Name, Mod, Args, Options) %%% start_link(Mod, Args, Options) %%% start_link(Name, Mod, Args, Options) where: %%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()} %%% Mod ::= atom(), callback module implementing the 'real' server %%% Args ::= term(), init arguments (to Mod:init/1) %%% Options ::= [{timeout, Timeout} | {debug, [Flag]}] %%% Flag ::= trace | log | {logfile, File} | statistics | debug %%% (debug == log && statistics) %%% Returns: {ok, Pid} | %%% {error, {already_started, Pid}} | %%% {error, Reason} %%% ----------------------------------------------------------------- start(Mod, Args, Options) -> gen:start(?MODULE, nolink, Mod, Args, Options). start(Name, Mod, Args, Options) -> gen:start(?MODULE, nolink, Name, Mod, Args, Options). start_link(Mod, Args, Options) -> gen:start(?MODULE, link, Mod, Args, Options). start_link(Name, Mod, Args, Options) -> gen:start(?MODULE, link, Name, Mod, Args, Options). %% ----------------------------------------------------------------- %% Make a call to a generic server. %% If the server is located at another node, that node will %% be monitored. %% If the client is trapping exits and is linked server termination %% is handled here (? Shall we do that here (or rely on timeouts) ?). %% ----------------------------------------------------------------- call(Name, Request) -> case catch gen:call(Name, '$gen_call', Request) of {ok,Res} -> Res; {'EXIT',Reason} -> exit({Reason, {?MODULE, call, [Name, Request]}}) end. call(Name, Request, Timeout) -> case catch gen:call(Name, '$gen_call', Request, Timeout) of {ok,Res} -> Res; {'EXIT',Reason} -> exit({Reason, {?MODULE, call, [Name, Request, Timeout]}}) end. %% ----------------------------------------------------------------- %% Make a cast to a generic server. %% ----------------------------------------------------------------- cast({global,Name}, Request) -> catch global:send(Name, cast_msg(Request)), ok; cast({via, Mod, Name}, Request) -> catch Mod:send(Name, cast_msg(Request)), ok; cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) -> do_cast(Dest, Request); cast(Dest, Request) when is_atom(Dest) -> do_cast(Dest, Request); cast(Dest, Request) when is_pid(Dest) -> do_cast(Dest, Request). do_cast(Dest, Request) -> do_send(Dest, cast_msg(Request)), ok. cast_msg(Request) -> {'$gen_cast',Request}. %% ----------------------------------------------------------------- %% Send a reply to the client. %% ----------------------------------------------------------------- reply({To, Tag}, Reply) -> catch To ! {Tag, Reply}. %% ----------------------------------------------------------------- %% Asynchronous broadcast, returns nothing, it's just send 'n' pray %%----------------------------------------------------------------- abcast(Name, Request) when is_atom(Name) -> do_abcast([node() | nodes()], Name, cast_msg(Request)). abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) -> do_abcast(Nodes, Name, cast_msg(Request)). do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) -> do_send({Name,Node},Msg), do_abcast(Nodes, Name, Msg); do_abcast([], _,_) -> abcast. %%% ----------------------------------------------------------------- %%% Make a call to servers at several nodes. %%% Returns: {[Replies],[BadNodes]} %%% A Timeout can be given %%% %%% A middleman process is used in case late answers arrives after %%% the timeout. If they would be allowed to glog the callers message %%% queue, it would probably become confused. Late answers will %%% now arrive to the terminated middleman and so be discarded. %%% ----------------------------------------------------------------- multi_call(Name, Req) when is_atom(Name) -> do_multi_call([node() | nodes()], Name, Req, infinity). multi_call(Nodes, Name, Req) when is_list(Nodes), is_atom(Name) -> do_multi_call(Nodes, Name, Req, infinity). multi_call(Nodes, Name, Req, infinity) -> do_multi_call(Nodes, Name, Req, infinity); multi_call(Nodes, Name, Req, Timeout) when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 -> do_multi_call(Nodes, Name, Req, Timeout). %%----------------------------------------------------------------- %% enter_loop(Mod, Options, State, , ) ->_ %% %% Description: Makes an existing process into a gen_server. %% The calling process will enter the gen_server receive %% loop and become a gen_server process. %% The process *must* have been started using one of the %% start functions in proc_lib, see proc_lib(3). %% The user is responsible for any initialization of the %% process, including registering a name for it. %%----------------------------------------------------------------- enter_loop(Mod, Options, State) -> enter_loop(Mod, Options, State, self(), infinity). enter_loop(Mod, Options, State, ServerName = {Scope, _}) when Scope == local; Scope == global -> enter_loop(Mod, Options, State, ServerName, infinity); enter_loop(Mod, Options, State, ServerName = {via, _, _}) -> enter_loop(Mod, Options, State, ServerName, infinity); enter_loop(Mod, Options, State, Timeout) -> enter_loop(Mod, Options, State, self(), Timeout). enter_loop(Mod, Options, State, ServerName, Timeout) -> Name = get_proc_name(ServerName), Parent = get_parent(), Debug = debug_options(Name, Options), Limits = limit_options(Options), Queue = queue:new(), QueueLen = 0, loop(Parent, Name, State, Mod, Timeout, Debug, Limits, Queue, QueueLen). %%%======================================================================== %%% Gen-callback functions %%%======================================================================== %%% --------------------------------------------------- %%% Initiate the new process. %%% Register the name using the Rfunc function %%% Calls the Mod:init/Args function. %%% Finally an acknowledge is sent to Parent and the main %%% loop is entered. %%% --------------------------------------------------- init_it(Starter, self, Name, Mod, Args, Options) -> init_it(Starter, self(), Name, Mod, Args, Options); init_it(Starter, Parent, Name0, Mod, Args, Options) -> Name = name(Name0), Debug = debug_options(Name, Options), Limits = limit_options(Options), Queue = queue:new(), QueueLen = 0, case catch Mod:init(Args) of {ok, State} -> proc_lib:init_ack(Starter, {ok, self()}), loop(Parent, Name, State, Mod, infinity, Debug, Limits, Queue, QueueLen); {ok, State, Timeout} -> proc_lib:init_ack(Starter, {ok, self()}), loop(Parent, Name, State, Mod, Timeout, Debug, Limits, Queue, QueueLen); {stop, Reason} -> %% For consistency, we must make sure that the %% registered name (if any) is unregistered before %% the parent process is notified about the failure. %% (Otherwise, the parent process could get %% an 'already_started' error if it immediately %% tried starting the process again.) unregister_name(Name0), proc_lib:init_ack(Starter, {error, Reason}), exit(Reason); ignore -> unregister_name(Name0), proc_lib:init_ack(Starter, ignore), exit(normal); {'EXIT', Reason} -> unregister_name(Name0), proc_lib:init_ack(Starter, {error, Reason}), exit(Reason); Else -> Error = {bad_return_value, Else}, proc_lib:init_ack(Starter, {error, Error}), exit(Error) end. name({local,Name}) -> Name; name({global,Name}) -> Name; name({via,_, Name}) -> Name; name(Pid) when is_pid(Pid) -> Pid. unregister_name({local,Name}) -> _ = (catch unregister(Name)); unregister_name({global,Name}) -> _ = global:unregister_name(Name); unregister_name({via, Mod, Name}) -> _ = Mod:unregister_name(Name); unregister_name(Pid) when is_pid(Pid) -> Pid. %%%======================================================================== %%% Internal functions %%%======================================================================== %%% --------------------------------------------------- %%% The MAIN loop. %%% --------------------------------------------------- loop(Parent, Name, State, Mod, hibernate, Debug, Limits, Queue, QueueLen) when QueueLen > 0 -> case queue:out(Queue) of {{value, Msg}, Queue1} -> decode_msg(Msg, Parent, Name, State, Mod, hibernate, Debug, Limits, Queue1, QueueLen - 1, false); {empty, _} -> Reason = internal_queue_error, error_info(Mod, Reason, Name, hibernate, State, Debug), exit(Reason) end; loop(Parent, Name, State, Mod, hibernate, Debug, Limits, _Queue, _QueueLen) -> proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug, Limits]); %% First we test if we have reach a defined limit ... loop(Parent, Name, State, Mod, Time, Debug, Limits, Queue, QueueLen) -> try message_queue_len(Limits, QueueLen) %% TODO: We can add more limit checking here... catch {process_limit, Limit} -> Reason = {process_limit, Limit}, Msg = {'EXIT', Parent, {error, {process_limit, Limit}}}, terminate(Reason, Name, Msg, Mod, State, Debug, queue:new()) end, process_message(Parent, Name, State, Mod, Time, Debug, Limits, Queue, QueueLen). %% ... then we can process a new message: process_message(Parent, Name, State, Mod, Time, Debug, Limits, Queue, QueueLen) -> {Msg, Queue1, QueueLen1} = collect_messages(Queue, QueueLen, Time), decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Limits, Queue1, QueueLen1, false). collect_messages(Queue, QueueLen, Time) -> receive Input -> case Input of {'EXIT', _Parent, priority_shutdown} -> {Input, Queue, QueueLen}; _ -> collect_messages( queue:in(Input, Queue), QueueLen + 1, Time) end after 0 -> case queue:out(Queue) of {{value, Msg}, Queue1} -> {Msg, Queue1, QueueLen - 1}; {empty, _} -> receive Input -> {Input, Queue, QueueLen} after Time -> {{'$gen_event', timeout}, Queue, QueueLen} end end end. wake_hib(Parent, Name, State, Mod, Debug, Limits) -> Msg = receive Input -> Input end, Queue = queue:new(), QueueLen = 0, decode_msg(Msg, Parent, Name, State, Mod, hibernate, Debug, Limits, Queue, QueueLen, true). decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Limits, Queue, QueueLen, Hib) -> put('$internal_queue_len', QueueLen), case Msg of {system, From, get_state} -> sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug, {State, [Name, State, Mod, Time, Limits, Queue, QueueLen]}, Hib); {system, From, {replace_state, StateFun}} -> NState = try StateFun(State) catch _:_ -> State end, sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug, {NState, [Name, NState, Mod, Time, Limits, Queue, QueueLen]}, Hib); {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, [Name, State, Mod, Time, Limits, Queue, QueueLen], Hib); {'EXIT', Parent, Reason} -> terminate(Reason, Name, Msg, Mod, State, Debug, Queue); _Msg when Debug =:= [] -> handle_msg(Msg, Parent, Name, State, Mod, Limits, Queue, QueueLen); _Msg -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {in, Msg}), handle_msg(Msg, Parent, Name, State, Mod, Debug1, Limits, Queue, QueueLen) end. %%% --------------------------------------------------- %%% Send/receive functions %%% --------------------------------------------------- do_send(Dest, Msg) -> case catch erlang:send(Dest, Msg, [noconnect]) of noconnect -> spawn(erlang, send, [Dest,Msg]); Other -> Other end. do_multi_call(Nodes, Name, Req, infinity) -> Tag = make_ref(), Monitors = send_nodes(Nodes, Name, Tag, Req), rec_nodes(Tag, Monitors, Name, undefined); do_multi_call(Nodes, Name, Req, Timeout) -> Tag = make_ref(), Caller = self(), Receiver = spawn( fun() -> %% Middleman process. Should be unsensitive to regular %% exit signals. The sychronization is needed in case %% the receiver would exit before the caller started %% the monitor. process_flag(trap_exit, true), Mref = erlang:monitor(process, Caller), receive {Caller,Tag} -> Monitors = send_nodes(Nodes, Name, Tag, Req), TimerId = erlang:start_timer(Timeout, self(), ok), Result = rec_nodes(Tag, Monitors, Name, TimerId), exit({self(),Tag,Result}); {'DOWN',Mref,_,_,_} -> %% Caller died before sending us the go-ahead. %% Give up silently. exit(normal) end end), Mref = erlang:monitor(process, Receiver), Receiver ! {self(),Tag}, receive {'DOWN',Mref,_,_,{Receiver,Tag,Result}} -> Result; {'DOWN',Mref,_,_,Reason} -> %% The middleman code failed. Or someone did %% exit(_, kill) on the middleman process => Reason==killed exit(Reason) end. send_nodes(Nodes, Name, Tag, Req) -> send_nodes(Nodes, Name, Tag, Req, []). send_nodes([Node|Tail], Name, Tag, Req, Monitors) when is_atom(Node) -> Monitor = start_monitor(Node, Name), %% Handle non-existing names in rec_nodes. catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req}, send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]); send_nodes([_Node|Tail], Name, Tag, Req, Monitors) -> %% Skip non-atom Node send_nodes(Tail, Name, Tag, Req, Monitors); send_nodes([], _Name, _Tag, _Req, Monitors) -> Monitors. %% Against old nodes: %% If no reply has been delivered within 2 secs. (per node) check that %% the server really exists and wait for ever for the answer. %% %% Against contemporary nodes: %% Wait for reply, server 'DOWN', or timeout from TimerId. rec_nodes(Tag, Nodes, Name, TimerId) -> rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId). rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) -> receive {'DOWN', R, _, _, _} -> rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId); {{Tag, N}, Reply} -> %% Tag is bound !!! erlang:demonitor(R, [flush]), rec_nodes(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies], Time, TimerId); {timeout, TimerId, _} -> erlang:demonitor(R, [flush]), %% Collect all replies that already have arrived rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) end; rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) -> %% R6 node receive {nodedown, N} -> monitor_node(N, false), rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId); {{Tag, N}, Reply} -> %% Tag is bound !!! receive {nodedown, N} -> ok after 0 -> ok end, monitor_node(N, false), rec_nodes(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies], 2000, TimerId); {timeout, TimerId, _} -> receive {nodedown, N} -> ok after 0 -> ok end, monitor_node(N, false), %% Collect all replies that already have arrived rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies) after Time -> case rpc:call(N, erlang, whereis, [Name]) of Pid when is_pid(Pid) -> % It exists try again. rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, infinity, TimerId); _ -> % badnode receive {nodedown, N} -> ok after 0 -> ok end, monitor_node(N, false), rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId) end end; rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) -> case catch erlang:cancel_timer(TimerId) of false -> % It has already sent it's message receive {timeout, TimerId, _} -> ok after 0 -> ok end; _ -> % Timer was cancelled, or TimerId was 'undefined' ok end, {Replies, Badnodes}. %% Collect all replies that already have arrived rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) -> receive {'DOWN', R, _, _, _} -> rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); {{Tag, N}, Reply} -> %% Tag is bound !!! erlang:demonitor(R, [flush]), rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) after 0 -> erlang:demonitor(R, [flush]), rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) end; rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) -> %% R6 node receive {nodedown, N} -> monitor_node(N, false), rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); {{Tag, N}, Reply} -> %% Tag is bound !!! receive {nodedown, N} -> ok after 0 -> ok end, monitor_node(N, false), rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) after 0 -> receive {nodedown, N} -> ok after 0 -> ok end, monitor_node(N, false), rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) end; rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) -> {Replies, Badnodes}. %%% --------------------------------------------------- %%% Monitor functions %%% --------------------------------------------------- start_monitor(Node, Name) when is_atom(Node), is_atom(Name) -> if node() =:= nonode@nohost, Node =/= nonode@nohost -> Ref = make_ref(), self() ! {'DOWN', Ref, process, {Name, Node}, noconnection}, {Node, Ref}; true -> case catch erlang:monitor(process, {Name, Node}) of {'EXIT', _} -> %% Remote node is R6 monitor_node(Node, true), Node; Ref when is_reference(Ref) -> {Node, Ref} end end. %%% --------------------------------------------------- %%% Message handling functions %%% --------------------------------------------------- dispatch({'$gen_cast', Msg}, Mod, State) -> Mod:handle_cast(Msg, State); dispatch(Info, Mod, State) -> Mod:handle_info(Info, State). handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Limits, Queue, QueueLen) -> case catch Mod:handle_call(Msg, From, State) of {reply, Reply, NState} -> reply(From, Reply), loop(Parent, Name, NState, Mod, infinity, [], Limits, Queue, QueueLen); {reply, Reply, NState, Time1} -> reply(From, Reply), loop(Parent, Name, NState, Mod, Time1, [], Limits, Queue, QueueLen); {noreply, NState} -> loop(Parent, Name, NState, Mod, infinity, [], Limits, Queue, QueueLen); {noreply, NState, Time1} -> loop(Parent, Name, NState, Mod, Time1, [], Limits, Queue, QueueLen); {stop, Reason, Reply, NState} -> {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod, NState, [], Queue)), reply(From, Reply), exit(R); Other -> handle_common_reply(Other, Parent, Name, Msg, Mod, State, Limits, Queue, QueueLen) end; handle_msg(Msg, Parent, Name, State, Mod, Limits, Queue, QueueLen) -> Reply = (catch dispatch(Msg, Mod, State)), handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Limits, Queue, QueueLen). handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug, Limits, Queue, QueueLen) -> case catch Mod:handle_call(Msg, From, State) of {reply, Reply, NState} -> Debug1 = reply(Name, From, Reply, NState, Debug), loop(Parent, Name, NState, Mod, infinity, Debug1, Limits, Queue, QueueLen); {reply, Reply, NState, Time1} -> Debug1 = reply(Name, From, Reply, NState, Debug), loop(Parent, Name, NState, Mod, Time1, Debug1, Limits, Queue, QueueLen); {noreply, NState} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, infinity, Debug1, Limits, Queue, QueueLen); {noreply, NState, Time1} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, Time1, Debug1, Limits, Queue, QueueLen); {stop, Reason, Reply, NState} -> {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod, NState, Debug, Queue)), reply(Name, From, Reply, NState, Debug), exit(R); Other -> handle_common_reply(Other, Parent, Name, Msg, Mod, State, Debug, Limits, Queue, QueueLen) end; handle_msg(Msg, Parent, Name, State, Mod, Debug, Limits, Queue, QueueLen) -> Reply = (catch dispatch(Msg, Mod, State)), handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug, Limits, Queue, QueueLen). handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Limits, Queue, QueueLen) -> case Reply of {noreply, NState} -> loop(Parent, Name, NState, Mod, infinity, [], Limits, Queue, QueueLen); {noreply, NState, Time1} -> loop(Parent, Name, NState, Mod, Time1, [], Limits, Queue, QueueLen); {stop, Reason, NState} -> terminate(Reason, Name, Msg, Mod, NState, [], Queue); {'EXIT', What} -> terminate(What, Name, Msg, Mod, State, [], Queue); _ -> terminate({bad_return_value, Reply}, Name, Msg, Mod, State, [], Queue) end. handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug, Limits, Queue, QueueLen) -> case Reply of {noreply, NState} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, infinity, Debug1, Limits, Queue, QueueLen); {noreply, NState, Time1} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, Time1, Debug1, Limits, Queue, QueueLen); {stop, Reason, NState} -> terminate(Reason, Name, Msg, Mod, NState, Debug, Queue); {'EXIT', What} -> terminate(What, Name, Msg, Mod, State, Debug, Queue); _ -> terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug, Queue) end. reply(Name, {To, Tag}, Reply, State, Debug) -> reply({To, Tag}, Reply), sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, State} ). %%----------------------------------------------------------------- %% Callback functions for system messages handling. %%----------------------------------------------------------------- system_continue(Parent, Debug, [Name, State, Mod, Time, Limits, Queue, QueueLen]) -> loop(Parent, Name, State, Mod, Time, Debug, Limits, Queue, QueueLen). -spec system_terminate(_, _, _, [_]) -> no_return(). system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _Limits, Queue, _QueueLen]) -> terminate(Reason, Name, [], Mod, State, Debug, Queue). system_code_change([Name, State, Mod, Time, Limits, Queue, QueueLen], _Module, OldVsn, Extra) -> case catch Mod:code_change(OldVsn, State, Extra) of {ok, NewState} -> {ok, [Name, NewState, Mod, Time, Limits, Queue, QueueLen]}; Else -> Else end. %%----------------------------------------------------------------- %% Format debug messages. Print them as the call-back module sees %% them, not as the real erlang messages. Use trace for that. %%----------------------------------------------------------------- print_event(Dev, {in, Msg}, Name) -> case Msg of {'$gen_call', {From, _Tag}, Call} -> io:format(Dev, "*DBG* ~p got call ~p from ~w~n", [Name, Call, From]); {'$gen_cast', Cast} -> io:format(Dev, "*DBG* ~p got cast ~p~n", [Name, Cast]); _ -> io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg]) end; print_event(Dev, {out, Msg, To, State}, Name) -> io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n", [Name, Msg, To, State]); print_event(Dev, {noreply, State}, Name) -> io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]); print_event(Dev, Event, Name) -> io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]). %%% --------------------------------------------------- %%% Terminate the server. %%% --------------------------------------------------- terminate(Reason, Name, Msg, Mod, State, Debug, Queue) -> lists:foreach( fun(Message) -> self() ! Message end, queue:to_list(Queue)), case catch Mod:terminate(Reason, State) of {'EXIT', R} -> error_info(Mod, R, Name, Msg, State, Debug), exit(R); _ -> case Reason of normal -> exit(normal); shutdown -> exit(shutdown); {shutdown,_}=Shutdown -> exit(Shutdown); priority_shutdown -> %% Priority shutdown should be considered as %% shutdown by SASL exit(shutdown); {process_limit, _Limit} -> exit(Reason); _ -> FmtState = case erlang:function_exported(Mod, format_status, 2) of true -> Args = [get(), State], case catch Mod:format_status(terminate, Args) of {'EXIT', _} -> State; Else -> Else end; _ -> State end, error_info(Mod, Reason, Name, Msg, FmtState, Debug), exit(Reason) end end. error_info(_Mod, _Reason, application_controller, _Msg, _State, _Debug) -> %% OTP-5811 Don't send an error report if it's the system process %% application_controller which is terminating - let init take care %% of it instead ok; error_info(Mod, Reason, Name, Msg, State, Debug) -> Reason1 = case Reason of {undef,[{M,F,A,L}|MFAs]} -> case code:is_loaded(M) of false -> {'module could not be loaded',[{M,F,A,L}|MFAs]}; _ -> case erlang:function_exported(M, F, length(A)) of true -> Reason; false -> {'function not exported',[{M,F,A,L}|MFAs]} end end; _ -> Reason end, StateToPrint = case erlang:function_exported(Mod, print_state, 1) of true -> (catch Mod:print_state(State)); false -> State end, format("** Generic server ~p terminating \n" "** Last message in was ~p~n" "** When Server state == ~p~n" "** Reason for termination == ~n** ~p~n", [Name, Msg, StateToPrint, Reason1]), sys:print_log(Debug), ok. %%% --------------------------------------------------- %%% Misc. functions. %%% --------------------------------------------------- opt(Op, [{Op, Value}|_]) -> {ok, Value}; opt(Op, [_|Options]) -> opt(Op, Options); opt(_, []) -> false. debug_options(Name, Opts) -> case opt(debug, Opts) of {ok, Options} -> dbg_options(Name, Options); _ -> dbg_options(Name, []) end. dbg_options(Name, []) -> Opts = case init:get_argument(generic_debug) of error -> []; _ -> [log, statistics] end, dbg_opts(Name, Opts); dbg_options(Name, Opts) -> dbg_opts(Name, Opts). dbg_opts(Name, Opts) -> case catch sys:debug_options(Opts) of {'EXIT',_} -> format("~p: ignoring erroneous debug options - ~p~n", [Name, Opts]), []; Dbg -> Dbg end. get_proc_name(Pid) when is_pid(Pid) -> Pid; get_proc_name({local, Name}) -> case process_info(self(), registered_name) of {registered_name, Name} -> Name; {registered_name, _Name} -> exit(process_not_registered); [] -> exit(process_not_registered) end; get_proc_name({global, Name}) -> case global:whereis_name(Name) of undefined -> exit(process_not_registered_globally); Pid when Pid =:= self() -> Name; _Pid -> exit(process_not_registered_globally) end; get_proc_name({via, Mod, Name}) -> case Mod:whereis_name(Name) of undefined -> exit({process_not_registered_via, Mod}); Pid when Pid =:= self() -> Name; _Pid -> exit({process_not_registered_via, Mod}) end. get_parent() -> case get('$ancestors') of [Parent | _] when is_pid(Parent)-> Parent; [Parent | _] when is_atom(Parent)-> name_to_pid(Parent); _ -> exit(process_was_not_started_by_proc_lib) end. name_to_pid(Name) -> case whereis(Name) of undefined -> case global:whereis_name(Name) of undefined -> exit(could_not_find_registered_name); Pid -> Pid end; Pid -> Pid end. %%----------------------------------------------------------------- %% Status information %%----------------------------------------------------------------- format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, _Limits, _Queue, _QueueLen]] = StatusData, Header = gen:format_status_header("Status for generic server", Name), Log = sys:get_debug(log, Debug, []), DefaultStatus = [{data, [{"State", State}]}], Specfic = case erlang:function_exported(Mod, format_status, 2) of true -> case catch Mod:format_status(Opt, [PDict, State]) of {'EXIT', _} -> DefaultStatus; StatusList when is_list(StatusList) -> StatusList; Else -> [Else] end; _ -> DefaultStatus end, [{header, Header}, {data, [{"Status", SysState}, {"Parent", Parent}, {"Logged events", Log}]} | Specfic]. %%----------------------------------------------------------------- %% Resources limit management %%----------------------------------------------------------------- %% Extract know limit options limit_options(Options) -> limit_options(Options, #limits{}). limit_options([], Limits) -> Limits; %% Maximum number of messages allowed in the process message queue limit_options([{max_queue,N}|Options], Limits) when is_integer(N) -> NewLimits = Limits#limits{max_queue=N}, limit_options(Options, NewLimits); limit_options([_|Options], Limits) -> limit_options(Options, Limits). %% Throw max_queue if we have reach the max queue size %% Returns ok otherwise message_queue_len(#limits{max_queue = undefined}, _QueueLen) -> ok; message_queue_len(#limits{max_queue = MaxQueue}, QueueLen) -> Pid = self(), case process_info(Pid, message_queue_len) of {message_queue_len, N} when N + QueueLen > MaxQueue -> throw({process_limit, {max_queue, N + QueueLen}}); _ -> ok end. p1_utils-1.0.3/src/p1_time_compat.erl0000644000232200023220000001501512642704237020026 0ustar debalancedebalance%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2014-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% %% If your code need to be able to execute on ERTS versions both %% earlier and later than 7.0, the best approach is to use the new %% time API introduced in ERTS 7.0 and implement a fallback %% solution using the old primitives to be used on old ERTS %% versions. This way your code can automatically take advantage %% of the improvements in the API when available. This is an %% example of how to implement such an API, but it can be used %% as is if you want to. Just add (a preferrably renamed version of) %% this module to your project, and call the API via this module %% instead of calling the BIFs directly. %% -module(p1_time_compat). %% We don't want warnings about the use of erlang:now/0 in %% this module. -compile(nowarn_deprecated_function). %% %% We don't use %% -compile({nowarn_deprecated_function, [{erlang, now, 0}]}). %% since this will produce warnings when compiled on systems %% where it has not yet been deprecated. %% -export([monotonic_time/0, monotonic_time/1, system_time/0, system_time/1, os_system_time/0, os_system_time/1, time_offset/0, time_offset/1, convert_time_unit/3, timestamp/0, unique_integer/0, unique_integer/1, monitor/2, system_info/1, system_flag/2]). -ifdef(NEED_TIME_FALLBACKS). monotonic_time() -> erlang_system_time_fallback(). monotonic_time(Unit) -> STime = erlang_system_time_fallback(), convert_time_unit_fallback(STime, native, Unit). system_time() -> erlang_system_time_fallback(). system_time(Unit) -> STime = erlang_system_time_fallback(), convert_time_unit_fallback(STime, native, Unit). os_system_time() -> os_system_time_fallback(). os_system_time(Unit) -> STime = os_system_time_fallback(), try convert_time_unit_fallback(STime, native, Unit) catch error:bad_time_unit -> erlang:error(badarg, [Unit]) end. time_offset() -> %% Erlang system time and Erlang monotonic %% time are always aligned 0. time_offset(Unit) -> _ = integer_time_unit(Unit), %% Erlang system time and Erlang monotonic %% time are always aligned 0. convert_time_unit(Time, FromUnit, ToUnit) -> try convert_time_unit_fallback(Time, FromUnit, ToUnit) catch _:_ -> erlang:error(badarg, [Time, FromUnit, ToUnit]) end. timestamp() -> erlang:now(). unique_integer() -> {MS, S, US} = erlang:now(), (MS*1000000+S)*1000000+US. unique_integer(Modifiers) -> case is_valid_modifier_list(Modifiers) of true -> %% now() converted to an integer %% fullfill the requirements of %% all modifiers: unique, positive, %% and monotonic... {MS, S, US} = erlang:now(), (MS*1000000+S)*1000000+US; false -> erlang:error(badarg, [Modifiers]) end. monitor(Type, Item) -> try erlang:monitor(Type, Item) catch error:Error -> case {Error, Type, Item} of {badarg, time_offset, clock_service} -> %% Time offset is final and will never change. %% Return a dummy reference, there will never %% be any need for 'CHANGE' messages... make_ref(); _ -> erlang:error(Error, [Type, Item]) end end. system_info(Item) -> try erlang:system_info(Item) catch error:badarg -> case Item of time_correction -> case erlang:system_info(tolerant_timeofday) of enabled -> true; disabled -> false end; time_warp_mode -> no_time_warp; time_offset -> final; NotSupArg when NotSupArg == os_monotonic_time_source; NotSupArg == os_system_time_source; NotSupArg == start_time; NotSupArg == end_time -> %% Cannot emulate this... erlang:error(notsup, [NotSupArg]); _ -> erlang:error(badarg, [Item]) end; error:Error -> erlang:error(Error, [Item]) end. system_flag(Flag, Value) -> try erlang:system_flag(Flag, Value) catch error:Error -> case {Error, Flag, Value} of {badarg, time_offset, finalize} -> %% Time offset is final final; _ -> erlang:error(Error, [Flag, Value]) end end. %% %% Internal functions %% integer_time_unit(native) -> 1000*1000; integer_time_unit(nano_seconds) -> 1000*1000*1000; integer_time_unit(micro_seconds) -> 1000*1000; integer_time_unit(milli_seconds) -> 1000; integer_time_unit(seconds) -> 1; integer_time_unit(I) when is_integer(I), I > 0 -> I; integer_time_unit(BadRes) -> erlang:error(badarg, [BadRes]). erlang_system_time_fallback() -> {MS, S, US} = erlang:now(), (MS*1000000+S)*1000000+US. os_system_time_fallback() -> {MS, S, US} = os:timestamp(), (MS*1000000+S)*1000000+US. convert_time_unit_fallback(Time, FromUnit, ToUnit) -> FU = integer_time_unit(FromUnit), TU = integer_time_unit(ToUnit), case Time < 0 of true -> TU*Time - (FU - 1); false -> TU*Time end div FU. is_valid_modifier_list([positive|Ms]) -> is_valid_modifier_list(Ms); is_valid_modifier_list([monotonic|Ms]) -> is_valid_modifier_list(Ms); is_valid_modifier_list([]) -> true; is_valid_modifier_list(_) -> false. -else. monotonic_time() -> erlang:monotonic_time(). monotonic_time(Unit) -> erlang:monotonic_time(Unit). system_time() -> erlang:system_time(). system_time(Unit) -> erlang:system_time(Unit). os_system_time() -> os:system_time(). os_system_time(Unit) -> os:system_time(Unit). time_offset() -> erlang:time_offset(). time_offset(Unit) -> erlang:time_offset(Unit). convert_time_unit(Time, FromUnit, ToUnit) -> erlang:convert_time_unit(Time, FromUnit, ToUnit). timestamp() -> erlang:timestamp(). unique_integer() -> erlang:unique_integer(). unique_integer(Modifiers) -> erlang:unique_integer(Modifiers). monitor(Type, Item) -> erlang:monitor(Type, Item). system_info(Item) -> erlang:system_info(Item). system_flag(Flag, Value) -> erlang:system_flag(Flag, Value). -endif. p1_utils-1.0.3/Makefile0000644000232200023220000000015112642704237015265 0ustar debalancedebalanceall: src src: rebar compile clean: rebar clean test: rebar skip_deps=true eunit .PHONY: clean src