diff options
149 files changed, 1976 insertions, 2589 deletions
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam Binary files differindex e648a70b2f..111f85acd2 100644 --- a/bootstrap/lib/compiler/ebin/compile.beam +++ b/bootstrap/lib/compiler/ebin/compile.beam diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app index 39635bd447..dfe077d5b9 100644 --- a/bootstrap/lib/compiler/ebin/compiler.app +++ b/bootstrap/lib/compiler/ebin/compiler.app @@ -69,5 +69,5 @@ {registered, []}, {applications, [kernel, stdlib]}, {env, []}, - {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-7.0", - "crypto-3.3"]}]}. + {runtime_dependencies, ["stdlib-2.5","kernel-4.0","hipe-3.12","erts-7.0", + "crypto-3.6"]}]}. diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam Binary files differindex 6d04559d9a..0c5b6c73e1 100644 --- a/bootstrap/lib/kernel/ebin/inet_dns.beam +++ b/bootstrap/lib/kernel/ebin/inet_dns.beam diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam Binary files differindex c322eb9fdc..a3635e5dde 100644 --- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam +++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app index e2cc4bac63..bfeea49e91 100644 --- a/bootstrap/lib/kernel/ebin/kernel.app +++ b/bootstrap/lib/kernel/ebin/kernel.app @@ -115,6 +115,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-7.0", "stdlib-2.0", "sasl-2.4"]} + {runtime_dependencies, ["erts-7.0", "stdlib-2.5", "sasl-2.4"]} ] }. diff --git a/bootstrap/lib/stdlib/ebin/c.beam b/bootstrap/lib/stdlib/ebin/c.beam Binary files differindex a08df44eb7..0e07dc1531 100644 --- a/bootstrap/lib/stdlib/ebin/c.beam +++ b/bootstrap/lib/stdlib/ebin/c.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam Binary files differindex f30442bc06..a07f730e99 100644 --- a/bootstrap/lib/stdlib/ebin/erl_anno.beam +++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex 7c80cbe624..5a8bc2a7f1 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam Binary files differindex 6f2085cf22..7d30fc9fc1 100644 --- a/bootstrap/lib/stdlib/ebin/ets.beam +++ b/bootstrap/lib/stdlib/ebin/ets.beam diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam Binary files differindex cf763f51a0..71da2376ba 100644 --- a/bootstrap/lib/stdlib/ebin/gb_sets.beam +++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam diff --git a/bootstrap/lib/stdlib/ebin/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam Binary files differindex 6660dcb787..db59d5af19 100644 --- a/bootstrap/lib/stdlib/ebin/gb_trees.beam +++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam Binary files differindex 5209c7cfd8..d1aa8bb9dd 100644 --- a/bootstrap/lib/stdlib/ebin/maps.beam +++ b/bootstrap/lib/stdlib/ebin/maps.beam diff --git a/bootstrap/lib/stdlib/ebin/shell_default.beam b/bootstrap/lib/stdlib/ebin/shell_default.beam Binary files differindex fa64e33080..f295c9f116 100644 --- a/bootstrap/lib/stdlib/ebin/shell_default.beam +++ b/bootstrap/lib/stdlib/ebin/shell_default.beam diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app index 50eb39d712..92ecc16b4c 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.app +++ b/bootstrap/lib/stdlib/ebin/stdlib.app @@ -104,7 +104,7 @@ dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-7.0","crypto-3.3", + {runtime_dependencies, ["sasl-2.4","kernel-4.0","erts-7.0","crypto-3.3", "compiler-5.0"]} ]}. diff --git a/erts/configure.in b/erts/configure.in index 62515fe081..39d3c51e3f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -649,6 +649,7 @@ case $chk_arch_ in powerpc) ARCH=ppc;; ppc) ARCH=ppc;; ppc64) ARCH=ppc64;; + ppc64le) ARCH=ppc64;; "Power Macintosh") ARCH=ppc;; armv5b) ARCH=arm;; armv5teb) ARCH=arm;; diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 6ca57566aa..3fea64cef5 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -539,55 +539,94 @@ <name name="cancel_timer" arity="2"/> <fsummary>Cancel a timer</fsummary> <desc> - <p>Cancels a timer. <c><anno>TimerRef</anno></c> needs to refer to - a timer that was created by either - <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>, - or <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>.</p> - <p>Currently available <c><anno>Option</anno>s</c>:</p> + <p> + Cancels a timer that has been created by either + <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>, + or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>. + <c><anno>TimerRef</anno></c> identifies the timer, and + was returned by the BIF that created the timer. + </p> + <p>Currently available <c><anno>Option</anno></c>s:</p> <taglist> <tag><c>{async, Async}</c></tag> <item> - <p>Asynchronous request for cancellation. <c>Async</c> - defaults to <c>false</c>. That is the operation will be - performed synchronously. When <c>Async</c> is set to - <c>true</c> the cancel operation will be performed - asynchronously. That is, <c>cancel_timer()</c> will send - a request for cancellation to the timer service that - manages the timer, and then return <c>ok</c>.</p></item> + <p> + Asynchronous request for cancellation. <c>Async</c> + defaults to <c>false</c> which will cause the + cancellation to be performed synchronously. When + <c>Async</c> is set to <c>true</c>, the cancel + operation will be performed asynchronously. That is, + <c>erlang:cancel_timer()</c> will send an asynchronous + request for cancellation to the timer service that + manages the timer, and then return <c>ok</c>. + </p> + </item> <tag><c>{info, Info}</c></tag> <item> - <p>Request information about the <c>Result</c> of the - cancellation. <c>Info</c> defaults to <c>true</c>. That - is information will be given. When <c>Info</c> is set to - <c>false</c> no information about the result of the cancel - operation will be given. When the operation is performed - synchronously the <c>Result</c> will returned from - <c>cancel_timer()</c>. When the operation is performed - asynchronously, a message on the form - <c>{cancel_timer, <anno>TimerRef</anno>, <anno>Result</anno>}</c> - will be sent to the caller of <c>cancel_timer()</c> when - the operation has been performed.</p></item> + <p> + Request information about the <c><anno>Result</anno></c> + of the cancellation. <c>Info</c> defaults to <c>true</c> + which means that the <c><anno>Result</anno></c> will + be given. When <c>Info</c> is set to <c>false</c>, no + information about the result of the cancellation + will be given. When the operation is performed</p> + <taglist> + <tag>synchronously</tag> + <item> + <p> + If <c>Info</c> is <c>true</c>, the <c>Result</c> will + returned by <c>erlang:cancel_timer()</c>; otherwise, + <c>ok</c> will be returned. + </p> + </item> + <tag>asynchronously</tag> + <item> + <p> + If <c>Info</c> is <c>true</c>, a message on the form + <c>{cancel_timer, <anno>TimerRef</anno>, + <anno>Result</anno>}</c> will be sent to the + caller of <c>erlang:cancel_timer()</c> when the + cancellation operation has been performed; otherwise, + no message will be sent. + </p> + </item> + </taglist> + </item> </taglist> - <p>When the <c><anno>Result</anno></c> equals <c>false</c> a timer - corresponding to <c><anno>TimerRef</anno></c> could not be found. This - can be either because the timer had expired, been canceled, or because - <c><anno>TimerRef</anno></c> do not correspond to a timer. When the - <c><anno>Result</anno></c> is an integer, it represents - the time in milli seconds left before the timer will expire.</p> - <note><p>The timer service that manages the timer may be co-located - with another scheduler than the scheduler that the calling process - is executing on. In this case communication with the timer - service will be performed using asynchronous signals. If the calling - process is in critical path and can do other things while waiting - for the result of this operation, you want to use the <c>{async, true}</c> - option.</p></note> + <p> + More <c><anno>Option</anno></c>s may be added in the future. + </p> + <p> + When the <c><anno>Result</anno></c> equals <c>false</c>, a + timer corresponding to <c><anno>TimerRef</anno></c> could not + be found. This can be either because the timer had expired, + already had been canceled, or because <c><anno>TimerRef</anno></c> + never has corresponded to a timer. If the timer has expired, + the timeout message has been sent, but it does not tell you + whether or not it has arrived at its destination yet. When the + <c><anno>Result</anno></c> is an integer, it represents the + time in milli-seconds left until the timer will expire. + </p> + <note> + <p> + The timer service that manages the timer may be co-located + with another scheduler than the scheduler that the calling + process is executing on. If this is the case, communication + with the timer service will take much longer time than if it + is located locally. If the calling process is in critical + path, and can do other things while waiting for the result + of this operation, or is not interested in the result of + the operation, you want to use the <c>{async, true}</c> + option. If using the <c>{async, false}</c> option, the calling + process will be blocked until the operation has been + performed. + </p> + </note> <p>See also <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>, <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>, and <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p> - <p>Note: Cancelling a timer does not guarantee that the message - has not already been delivered to the message queue.</p> </desc> </func> <func> @@ -596,7 +635,7 @@ <desc> <p>Cancels a timer. The same as calling <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer(TimerRef, - [{async, false}, {info, true}])</c></seealso>.</p> + [])</c></seealso>.</p> </desc> </func> <func> @@ -4548,37 +4587,60 @@ os_prompt% </pre> <name name="read_timer" arity="2"/> <fsummary>Read the state of a timer</fsummary> <desc> - <p>Read the state of a timer. <c><anno>TimerRef</anno></c> - needs to refer to a timer that was created by either - <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>, - or <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>.</p> + <p> + Read the state of a timer that has been created by either + <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>, + or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>. + <c><anno>TimerRef</anno></c> identifies the timer, and + was returned by the BIF that created the timer. + </p> <p>Currently available <c><anno>Option</anno>s</c>:</p> <taglist> <tag><c>{async, Async}</c></tag> <item> - <p>Asynchronous request. <c>Async</c> defaults to <c>false</c>. That - is the operation will be performed synchronously, and the <c>Result</c> - will returned from <c>read_timer()</c>. When <c>Async</c> is set to - <c>true</c>, <c>read_timer()</c> will send a request for the - <c>Result</c> to a timer service that manages the timer and then - return <c>ok</c>. A message on the format - <c>{read_timer, <anno>TimerRef</anno>, <anno>Result</anno>}</c> - will be sent to the caller of <c>read_timer()</c> when - the operation has been processed.</p></item> + <p> + Asynchronous request for state information. <c>Async</c> + defaults to <c>false</c> which will cause the operation + to be performed synchronously. In this case, the <c>Result</c> + will be returned by <c>erlang:read_timer()</c>. When + <c>Async</c> is set to <c>true</c>, <c>erlang:read_timer()</c> + will send an asynchronous request for the state information + to the timer service that manages the timer, and then return + <c>ok</c>. A message on the format <c>{read_timer, + <anno>TimerRef</anno>, <anno>Result</anno>}</c> will be + sent to the caller of <c>erlang:read_timer()</c> when the + operation has been processed. + </p> + </item> </taglist> - <p>When the <c><anno>Result</anno></c> equals <c>false</c> a timer - corresponding to <c><anno>TimerRef</anno></c> could not be found. This - can be either because the timer had expired, been canceled, or because - <c><anno>TimerRef</anno></c> do not correspond to a timer. When the - <c><anno>Result</anno></c> is an integer, it represents - the time in milli seconds left before the timer will expire.</p> - <note><p>The timer service that manages the timer may be co-located - with another scheduler than the scheduler that the calling process - is executing on. In this case communication with the timer - service will be performed using asynchronous signals. If the calling - process is in critical path and can do other things while waiting - for the result of this operation, you want to use the <c>{async, true}</c> - option.</p></note> + <p> + More <c><anno>Option</anno></c>s may be added in the future. + </p> + <p> + When the <c><anno>Result</anno></c> equals <c>false</c>, a + timer corresponding to <c><anno>TimerRef</anno></c> could not + be found. This can be either because the timer had expired, + had been canceled, or because <c><anno>TimerRef</anno></c> + never has corresponded to a timer. If the timer has expired, + the timeout message has been sent, but it does not tell you + whether or not it has arrived at its destination yet. When the + <c><anno>Result</anno></c> is an integer, it represents the + time in milli-seconds left until the timer will expire. + </p> + <note> + <p> + The timer service that manages the timer may be co-located + with another scheduler than the scheduler that the calling + process is executing on. If this is the case, communication + with the timer service will take much longer time than if it + is located locally. If the calling process is in critical + path, and can do other things while waiting for the result + of this operation you want to use the <c>{async, true}</c> + option. If using the <c>{async, false}</c> option, the calling + process will be blocked until the operation has been + performed. + </p> + </note> <p>See also <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>, <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>, @@ -4592,7 +4654,7 @@ os_prompt% </pre> <desc> <p>Read the state of a timer. The same as calling <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef, - [{async, false}])</c></seealso>.</p> + [])</c></seealso>.</p> </desc> </func> <func> @@ -4744,48 +4806,14 @@ true</pre> <name name="send_after" arity="4"/> <fsummary>Start a timer</fsummary> <desc> - <p>Starts a timer. When the timer expires, the message - <c><anno>Msg</anno></c> will be sent to - <c><anno>Dest</anno></c>.</p> - <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to - be a <c>pid()</c> of a local process, dead or alive.</p> - <p>Currently available <c><anno>Option</anno>s</c>:</p> - <taglist> - <tag><c>{abs, Abs}</c></tag> - <item> - <p>Absolute timeout. When <c>Abs</c> is <c>false</c> - the <c><anno>Time</anno></c> value will be interpreted - as a time in milli-seconds relative current - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso>. When <c>Abs</c> is <c>true</c> the - <c><anno>Time</anno></c> value will be interpreted as an absolute - Erlang monotonic time of milli second time unit. <c>Abs</c> - defaults to <c>false</c>.</p> - </item> - </taglist> - <p>The absolute time when the timer is set to expire needs - to be in the range between - <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso> - and - <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>. - If a negative relative time is specified the time is not - allowed to be negative.</p> - <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of - a registered process. The process referred to by the name is - looked up at the time of delivery. No error is given if - the name does not refer to a process.</p> - <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically - canceled if the process referred to by the <c>pid()</c> is not alive, - or when the process exits. This feature was introduced in - erts version 5.4.11. Note that timers will not be - automatically canceled when <c><anno>Dest</anno></c> is an <c>atom()</c>.</p> - <p>See also - <seealso marker="#start_timer/4"><c>erlang:send_timer/4</c></seealso>, - <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>, - and - <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p> - <p>Failure: <c>badarg</c> if the arguments does not satisfy - the requirements specified above.</p> + <p> + Starts a timer. When the timer expires, the message + <c><anno>Msg</anno></c> will be sent to the process + identified by <c><anno>Dest</anno></c>. Appart from + the format of the message sent to + <c><anno>Dest</anno></c> when the timer expires + <c>erlang:send_after/4</c> works exactly as + <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>.</p> </desc> </func> <func> @@ -4793,36 +4821,8 @@ true</pre> <fsummary>Start a timer</fsummary> <desc> <p>Starts a timer. The same as calling - <seealso marker="#send_timer/4"><c>erlang:send_after(<anno>Time</anno>, - <anno>Dest</anno>, <anno>Msg</anno>, [{abs, false}])</c></seealso>.</p> - </desc> - </func> - <func> - <name name="send_after" arity="3"/> - <type_desc variable="Time">0 <= Time <= 4294967295</type_desc> - <fsummary>Start a timer</fsummary> - <desc> - <p>Starts a timer which will send the message <c>Msg</c> - to <c><anno>Dest</anno></c> after <c><anno>Time</anno></c> milliseconds.</p> - <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to be a <c>pid()</c> of a local process, dead or alive.</p> - <p>The <c><anno>Time</anno></c> value can, in the current implementation, not be greater than 4294967295.</p> - <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of - a registered process. The process referred to by the name is - looked up at the time of delivery. No error is given if - the name does not refer to a process.</p> - - <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically - canceled if the process referred to by the <c>pid()</c> is not alive, - or when the process exits. This feature was introduced in - erts version 5.4.11. Note that timers will not be - automatically canceled when <c><anno>Dest</anno></c> is an <c>atom</c>.</p> - <p>See also - <seealso marker="#start_timer/3">erlang:start_timer/3</seealso>, - <seealso marker="#cancel_timer/2">erlang:cancel_timer/2</seealso>, - and - <seealso marker="#read_timer/2">erlang:read_timer/2</seealso>.</p> - <p>Failure: <c>badarg</c> if the arguments does not satisfy - the requirements specified above.</p> + <seealso marker="#send_after/4"><c>erlang:send_after(<anno>Time</anno>, + <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p> </desc> </func> <func> @@ -5231,41 +5231,59 @@ true</pre> <name name="start_timer" arity="4"/> <fsummary>Start a timer</fsummary> <desc> - <p>Starts a timer. When the timer expires, the message + <p> + Starts a timer. When the timer expires, the message <c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c> - will be sent to <c><anno>Dest</anno></c>.</p> - <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to - be a <c>pid()</c> of a local process, dead or alive.</p> - <p>Currently available <c><anno>Option</anno>s</c>:</p> + will be sent to the process identified by + <c><anno>Dest</anno></c>. + </p> + <p>Currently available <c><anno>Option</anno></c>s:</p> <taglist> <tag><c>{abs, Abs}</c></tag> <item> - <p>Absolute timeout. When <c>Abs</c> is <c>false</c> - the <c><anno>Time</anno></c> value will be interpreted - as a time in milli-seconds relative current - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso>. When <c>Abs</c> is <c>true</c> the - <c><anno>Time</anno></c> value will be interpreted as an absolute - Erlang monotonic time of milli second time unit. <c>Abs</c> - defaults to <c>false</c>.</p> + <p> + Absolute <c><anno>Time</anno></c> value. <c>Abs</c> + defaults to <c>false</c> which means that the + <c><anno>Time</anno></c> value will be interpreted + as a time in milli-seconds relative current + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso>. When <c>Abs</c> is set to + <c>true</c>, the <c><anno>Time</anno></c> value will + be interpreted as an absolute Erlang monotonic time of + milli-seconds + <seealso marker="#type_time_unit">time unit</seealso>. + </p> </item> </taglist> - <p>The absolute time when the timer is set to expire needs - to be in the range between - <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso> - and - <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>. - If a negative relative time is specified the time is not - allowed to be negative.</p> - <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of - a registered process. The process referred to by the name is - looked up at the time of delivery. No error is given if - the name does not refer to a process.</p> - <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically - canceled if the process referred to by the <c>pid()</c> is not alive, - or when the process exits. This feature was introduced in - erts version 5.4.11. Note that timers will not be - automatically canceled when <c><anno>Dest</anno></c> is an <c>atom()</c>.</p> + <p> + More <c><anno>Option</anno></c>s may be added in the future. + </p> + <p> + The absolute point in time that the timer is set to expire on + has to be in the interval + <c>[</c><seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso><c>, + </c><seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso><c>]</c>. + Further, if a relative time is specified, the <c><anno>Time</anno></c> value + is not allowed to be negative. + </p> + <p> + If <c><anno>Dest</anno></c> is a <c>pid()</c>, it has to + be a <c>pid()</c> of a process created on the current + runtime system instance. This process may or may not + have terminated. If <c><anno>Dest</anno></c> is an + <c>atom()</c>, it will be interpreted as the name of a + locally registered process. The process referred to by the + name is looked up at the time of timer expiration. No error + is given if the name does not refer to a process. + </p> + <p> + If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will + be automatically canceled if the process referred to by the + <c>pid()</c> is not alive, or when the process exits. This + feature was introduced in erts version 5.4.11. Note that + timers will not be automatically canceled when + <c><anno>Dest</anno></c> is an <c>atom()</c>. + </p> <p>See also <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>, <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>, @@ -5281,7 +5299,7 @@ true</pre> <desc> <p>Starts a timer. The same as calling <seealso marker="#start_timer/4"><c>erlang:start_timer(<anno>Time</anno>, - <anno>Dest</anno>, <anno>Msg</anno>, [{abs, false}])</c></seealso>.</p> + <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p> </desc> </func> <func> @@ -6845,7 +6863,9 @@ ok <item><p>The <seealso marker="#monotonic_time/0">Erlang monotonic time</seealso> in <c>native</c> <seealso marker="#type_time_unit">time unit</seealso> at the - time when current Erlang runtime system instance started.</p></item> + time when current Erlang runtime system instance started. See also + <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>. + </p></item> <tag><c>system_version</c></tag> <item> <p>Returns a string containing version number and diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index c85cbe543d..35e6e55e72 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,508 +30,6 @@ </header> <p>This document describes the changes made to the ERTS application.</p> -<section><title>Erts 7.0</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix issuing with spaces and quoting in the arguments when - using erlang:open_port spawn_executable on windows. The - behavior now mimics how unix works. This change implies a - backwards incompatibility for how spawn_executable works - on windows.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-11905</p> - </item> - <item> - <p> - Fix global call trace when hipe compiled code call beam - compiled functions. Tracing of beam functions should now - alway work regardless who the caller is.</p> - <p> - Own Id: OTP-11939</p> - </item> - <item> - <p> - Correct cache alignment for ETS <c>write_concurrency</c> - locks to improve performance by reduced false sharing. - May increase memory footprint for tables with - <c>write_concurrency</c>.</p> - <p> - Own Id: OTP-11974</p> - </item> - <item> - <p> - All possibly blocking operations in the fd/spawn and - terminal driver have been converted to non-blocking - operations. Before this fix it was possible for the VM to - be blocked for a long time if the entity consuming - stdout/stderr did not consume it fast enough.</p> - <p> - Own Id: OTP-12239</p> - </item> - <item> - <p> - Add missing overhead for offheap binaries created from - external format. This fix can improve the garbage - collection of large binaries originating from - <c>binary_to_term</c> or messages from remote nodes.</p> - <p> - Own Id: OTP-12554</p> - </item> - <item> - <p> - Ensure hashing of zero is consistent</p> - <p> Erlang treats positive and negative zero as - equal:</p> - <p> - <c>true = 0.0 =:= 0.0/-1</c></p> - <p>However, Erlangs hash functions: hash, phash and - phash2 did not reflect this behaviour. The hash values - produced by the different hash functions would not be - identical for positive and negative zero.</p> <p>This - change ensures that hash value of positive zero is always - produced regardless of the signedness of the zero float, - i.e.,</p> - <p> - <c>true = erlang:phash2(0.0) =:= - erlang:phash2(0.0/-1)</c></p> - <p> - Own Id: OTP-12641</p> - </item> - <item> - <p> - Ensure NIF term creation disallows illegal floating point - values and too long atoms. Such values will cause a NIF - to throw badarg exception when it returns.</p> - <p> - Own Id: OTP-12655</p> - </item> - <item> - <p> - Fixed building of Map results from match_specs</p> - <p> - A faulty "box-value" entered into the heap which could - cause a segmentation fault in the garbage collector if it - was written on a heap fragment.</p> - <p> - Own Id: OTP-12656</p> - </item> - <item> - <p> - Fix hipe bug when matching a "writable" binary. The bug - has been seen to sometimes cause a failed binary matching - of a correct utf8 character, but other symptoms are also - possible.</p> - <p> - Own Id: OTP-12667</p> - </item> - <item> - <p> - Keep dirty schedulers from waking other schedulers.</p> - <p> - Own Id: OTP-12685</p> - </item> - <item> - <p> - Disable floating point exceptions if the VM is compiled - by clang/llvm. This is a known long-standing problem in - clang/llvm.</p> - <p> - Own Id: OTP-12717</p> - </item> - <item> - <p> - Fix bug in <c>file:sendfile</c> for FreeBSD causing not - the entire file to be sent.</p> - <p> - Own Id: OTP-12720</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Add <c>md5</c> and <c>module</c> entries to - <c>?MODULE:module_info/0/1</c> and remove obsolete entry - 'import'.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-11940</p> - </item> - <item> - <p> - Debug function <c>erlang:display/1</c> shows content of - binaries and bitstrings, not only the length.</p> - <p> - Own Id: OTP-11941</p> - </item> - <item> - <p>The time functionality of Erlang has been extended. - This both includes a <seealso - marker="time_correction#The_New_Time_API">new - API</seealso> for time, as well as <seealso - marker="time_correction#Time_Warp_Modes">time warp - modes</seealso> which alters the behavior of the system - when system time changes. <em>You are strongly encouraged - to use the new API</em> instead of the old API based on - <seealso - marker="erlang#now/0"><c>erlang:now/0</c></seealso>. - <c>erlang:now/0</c> has been deprecated since it is and - forever will be a scalability bottleneck. For more - information see the <seealso - marker="time_correction">Time and Time - Correction</seealso> chapter of the ERTS User's - Guide.</p> - <p>Besides the API changes and time warp modes a lot of - scalability and performance improvements regarding time - management has been made internally in the runtime - system. Examples of such improvements are scheduler - specific timer wheels, scheduler specific BIF timer - management, parallel retrieval of monotonic time and - system time on systems with primitives that are not - buggy.</p> - <p> - Own Id: OTP-11997</p> - </item> - <item> - <p><c>erlang:function_exported(M, F, A)</c> will now - return <c>true</c> if <c>M:F/A</c> refers to a BIF.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12099</p> - </item> - <item> - <p> - New BIF: <c>erlang:get_keys/0</c>, lists all keys - associated with the process dictionary. Note: - <c>erlang:get_keys/0</c> is auto-imported.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12151 Aux Id: seq12521 </p> - </item> - <item> - <p> - Make distributed send of large messages yield to improve - real-time characteristics.</p> - <p> - Own Id: OTP-12232</p> - </item> - <item> - <p> - Use high accuracy poll timeouts</p> - <p> - Where available, use poll/select API's that can handle - time resolutions less than 1ms. In the cases where such - API's are not available the timeout is rounded up to the - nearest ms.</p> - <p> - Own Id: OTP-12236</p> - </item> - <item> - <p> - The internal group to user_drv protocol has been changed - to be synchronous in order to guarantee that output sent - to a process implementing the user_drv protocol is - printed before replying. This protocol is used by the - standard_output device and the ssh application when - acting as a client. </p> - <p> - This change changes the previous unlimited buffer when - printing to standard_io and other devices that end up in - user_drv to 1KB.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12240</p> - </item> - <item> - <p>The previously introduced "eager check I/O" feature is - now enabled by default.</p> - <p>Eager check I/O can be disabled using the <c>erl</c> - command line argument: <seealso - marker="erl#+secio"><c>+secio false</c></seealso></p> - <p>Characteristics impact compared to previous - default:</p> <list> <item>Lower latency and smoother - management of externally triggered I/O operations.</item> - <item>A slightly reduced priority of externally triggered - I/O operations.</item> </list> - <p> - Own Id: OTP-12254 Aux Id: OTP-12117 </p> - </item> - <item> - <p> - Properly support maps in match_specs</p> - <p> - Own Id: OTP-12270</p> - </item> - <item> - <p> - The notice that a crashdump has been written has been - moved to be printed before the crashdump is generated - instead of afterwords. The wording of the notice has also - been changed.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12292</p> - </item> - <item> - <p> - New function <c>ets:take/2</c>. Works the same as - <c>ets:delete/2</c> but also returns the deleted - object(s).</p> - <p> - Own Id: OTP-12309</p> - </item> - <item> - <p> - Tracing with cpu_timestamp option has been enabled on - Linux.</p> - <p> - Own Id: OTP-12366</p> - </item> - <item> - <p> - ets:info/1,2 now contains information about whether - write_concurrency or read_concurrency is enabled.</p> - <p> - Own Id: OTP-12376</p> - </item> - <item> - <p> - Improved usage of <c>gcc</c>'s builtins for atomic memory - access. These are used when no other implementation of - atomic memory operations is available. For example, when - compiling for ARM when <c>libatomic_ops</c> is not - available.</p> - <p> - The largest improvement will be seen when compiling with - a <c>gcc</c> with support for the <c>__atomic_*</c> - builtins (using a <c>gcc</c> of at least version 4.7), - but also when only the legacy <c>__sync_*</c> builtins - are available (using a <c>gcc</c> of at least version - 4.1) an improvement can be seen.</p> - <p> - For more information see the "<seealso - marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP_Configuring_Atomic-Memory-Operations-and-the-VM">Atomic - Memory Operations and the VM</seealso>" section of - <c>$ERL_TOP/HOWTO/INSTALL.md</c>.</p> - <p> - Own Id: OTP-12383</p> - </item> - <item> - <p> - Introduce <c>math:log2/1</c> function to math module.</p> - <p> - Own Id: OTP-12411</p> - </item> - <item> - <p> - Remove perfctr support</p> - <p> - Development of perfctr in the linux kernel ceased in - 2010. The perfctr support code in the Erlang VM is thus - effectively dead code and therefor removed.</p> - <p> - Own Id: OTP-12508</p> - </item> - <item> - <p><c>zlib:inflateChunk/2</c> has been added. It works - like <c>zlib:inflate/2</c>, but decompresses no more data - than will fit in the buffer configured by - <c>zlib:setBufSize/2</c>.</p> - <p> - Own Id: OTP-12548</p> - </item> - <item> - <p> - Use linear search for small select_val arrays</p> - <p> - Own Id: OTP-12555</p> - </item> - <item> - <p> - New BIF ets:update_counter/4 with a default object as - argument, which will be inserted in the table if the key - was not found.</p> - <p> - Own Id: OTP-12563</p> - </item> - <item> - <p> - Export missing types from zlib module</p> - <p> - Own Id: OTP-12584</p> - </item> - <item> - <p> - Use persistent hashmaps for large Maps <p>Maps will use a - persistent hashmap implementation when the number of - pairs in a Map becomes sufficiently large. The change - will occur when a Map reaches 33 pairs in size but this - limit might change in the future.</p></p> - <p>The most significant impact for the user by this - change is speed, and to a lesser degree memory - consumption and introspection of Maps. Memory consumption - size is probalistic but lesser than <c>gb_trees</c> or - <c>dict</c> for instance. Any other impacts will be - transparent for the user except for the following - changes.</p> - <p>Semantics of Maps have changed in two incompatible - ways compared to the experimental implementation in OTP - 17:</p> <list> <item>Hashing of maps is done different by - <c>erlang:phash2/1,2</c>, <c>erlang:phash/1</c> and - <c>erlang:hash/2</c>.</item> <item>Comparing two maps - with ==, /=, =<, <, >= and >, is done - different if the keys contain floating point - numbers.</item> </list> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12585</p> - </item> - <item> - <p> - Scalability improvement for <seealso - marker="erlang#make_ref/0">erlang:make_ref/0</seealso>, - and other functionality that create references. Each - scheduler now manage its own set of references. By this - no communication at all is needed when creating - references.</p> - <p> - Previous implementation generated a strictly - monotonically increasing sequence of references - corresponding to creation time on the runtime system - instance. This is <em>not</em> the case with current - implementation. You can only expect reference to be - unique. The Erlang/OTP documentation has never mentioned - anything else but the uniqueness property, so this change - <em>is</em> fully compatible. The only reason we've - marked this as a potential incompatibility is since an - early draft for an Erlang specification mentions strict - monotonicity as a property.</p> - <p> - If you need to create data with a strict monotonicity - property use <seealso - marker="erlang#unique_integer/1">erlang:unique_integer([monotonic])</seealso>. - Do <em>not</em> use the deprecated <seealso - marker="erlang:now/0">erlang:now()</seealso>.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12610</p> - </item> - <item> - <p> - Enable different abort signal from heart</p> - <p>By using environment variable HEART_KILL_SIGNAL, heart - can now use a different signal to kill the old running - Erlang.</p> - <p>By default the signal is SIGKILL but SIGABRT may also - be used by setting environment variable: - HEART_KILL_SIGNAL=SIGABRT</p> - <p> - Own Id: OTP-12613 Aux Id: seq12826 </p> - </item> - <item> - <p> - Update autconf to latest version 2015-03-04</p> - <p> - Own Id: OTP-12646</p> - </item> - <item> - <p> - Optimization of timers internally in the VM. This include - process timers (<c>receive ... after</c>), port timers - (<c>driver_set_timer()</c>) as well as BIF timers - (<c>erlang:send_after()</c>/<c>erlang:start_timer()</c>).</p> - <p> - Each scheduler thread now has its own lock-free timer - service instead of one locked central service. This - dramatically improves performance of timer management on - systems with a large amount of schedulers and timers.</p> - <p> - The timer service internal data structure has also been - optimized to be able to handle more timers than before. - That is, each timer service is by its self able to handle - more timers without dramatic performance loss than the - old centralized timer service.</p> - <p> - The API of BIF timers has also been extended. Timeout - values are for example no longer limited to 32-bit - integers. For more information see the documentation of - <seealso - marker="erlang#start_timer/4"><c>erlang:start_timer/4</c></seealso>, - <seealso - marker="erlang#send_after/4"><c>erlang:send_after/4</c></seealso>, - <seealso - marker="erlang#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>, - and <seealso - marker="erlang#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p> - <p> - Own Id: OTP-12650 Aux Id: OTP-11997 </p> - </item> - <item> - <p> - Specialize instructions from common assembler patterns</p> - <p>Specialize common instructions of <c>rem</c>, - <c>band</c>, <c>minus</c> and <c>plus</c> in the beam - loader. This will reduce the number of fetches and thus - lessen the instruction dispatch pressure during runtime - and speed up those operations in some common cases.</p> - <p>Specialize move patterns from x-registers to the stack - with a new <c>move_window</c> instruction. This change - will reduce instruction dispatch pressure.</p> - <p> - Own Id: OTP-12690</p> - </item> - <item> - <p> - Fix cross compilation for Android.</p> - <p> - Own Id: OTP-12693</p> - </item> - <item> - <p> - Fix incorrect use of autoconf macro AC_EGREP_CPP, which - could cause faulty configuration if run from a path - containing the string 'yes'.</p> - <p> - Own Id: OTP-12706</p> - </item> - <item> - <p> - Minimal Java version is now 1.6</p> - <p> - Own Id: OTP-12718</p> - </item> - <item> - <p> - Send format and args on process exit to error_logger</p> - <p> - Previously, the emulator would generate a whole string - with values and call the error_logger passing - <c>"~s~n"</c>. This changes it to a format string - containing <c>~p</c> with the respective values as - arguments.</p> - <p> - Own Id: OTP-12735</p> - </item> - </list> - </section> - -</section> - <section><title>Erts 6.4.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 2f277690e4..b92533f228 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -718,7 +718,7 @@ static void make_name_atoms(Allctr_t *allctr); static Block_t *create_carrier(Allctr_t *, Uint, UWord); static void destroy_carrier(Allctr_t *, Block_t *, Carrier_t **); static void mbc_free(Allctr_t *allctr, void *p, Carrier_t **busy_pcrr_pp); -static void dealloc_block(Allctr_t *, void *, int); +static void dealloc_block(Allctr_t *, void *, ErtsAlcFixList_t *, int); /* internal data... */ @@ -1067,17 +1067,21 @@ typedef struct { } ErtsAllctrFixDDBlock_t; #endif +#define ERTS_ALC_FIX_NO_UNUSE (((ErtsAlcType_t) 1) << ERTS_ALC_N_BITS) + static ERTS_INLINE void dealloc_fix_block(Allctr_t *allctr, ErtsAlcType_t type, void *ptr, + ErtsAlcFixList_t *fix, int dec_cc_on_redirect) { #ifdef ERTS_SMP /* May be redirected... */ - ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type = type; + ASSERT((type & ERTS_ALC_FIX_NO_UNUSE) == 0); + ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type = type | ERTS_ALC_FIX_NO_UNUSE; #endif - dealloc_block(allctr, ptr, dec_cc_on_redirect); + dealloc_block(allctr, ptr, fix, dec_cc_on_redirect); } static ERTS_INLINE void @@ -1123,8 +1127,7 @@ fix_cpool_check_shrink(Allctr_t *allctr, if (fix->u.cpool.min_list_size > fix->list_size) fix->u.cpool.min_list_size = fix->list_size; - fix->u.cpool.allocated--; - dealloc_fix_block(allctr, type, p, 0); + dealloc_fix_block(allctr, type, p, fix, 0); } } } @@ -1170,7 +1173,8 @@ static ERTS_INLINE void fix_cpool_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, - Carrier_t **busy_pcrr_pp) + Carrier_t **busy_pcrr_pp, + int unuse) { ErtsAlcFixList_t *fix; @@ -1178,8 +1182,9 @@ fix_cpool_free(Allctr_t *allctr, && type <= ERTS_ALC_N_MAX_A_FIXED_SIZE); fix = &allctr->fix[type - ERTS_ALC_N_MIN_A_FIXED_SIZE]; - - fix->u.cpool.used--; + + if (unuse) + fix->u.cpool.used--; if ((!busy_pcrr_pp || !*busy_pcrr_pp) && !fix->u.cpool.shrink_list @@ -1237,8 +1242,7 @@ fix_cpool_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs) fix->list = *((void **) ptr); fix->list_size--; fix->u.cpool.shrink_list--; - fix->u.cpool.allocated--; - dealloc_fix_block(allctr, type, ptr, 0); + dealloc_fix_block(allctr, type, ptr, fix, 0); } if (fix->u.cpool.min_list_size > fix->list_size) fix->u.cpool.min_list_size = fix->list_size; @@ -1399,7 +1403,7 @@ fix_nocpool_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs) ptr = fix->list; fix->list = *((void **) ptr); fix->list_size--; - dealloc_block(allctr, ptr, 0); + dealloc_block(allctr, ptr, NULL, 0); fix->u.nocpool.allocated--; } if (fix->list_size != 0) { @@ -1746,11 +1750,13 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr) type = ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type; - ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE <= type - && type <= ERTS_ALC_N_MAX_A_FIXED_SIZE); + ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE + <= (type & ~ERTS_ALC_FIX_NO_UNUSE)); + ASSERT((type & ~ERTS_ALC_FIX_NO_UNUSE) + <= ERTS_ALC_N_MAX_A_FIXED_SIZE); if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr)) - fix_nocpool_free(allctr, type, ptr); + fix_nocpool_free(allctr, (type & ~ERTS_ALC_FIX_NO_UNUSE), ptr); else { Block_t *blk = UMEM2BLK(ptr); Carrier_t *busy_pcrr_p; @@ -1765,7 +1771,9 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr) NULL, &busy_pcrr_p); if (used_allctr == allctr) { doit: - fix_cpool_free(allctr, type, ptr, &busy_pcrr_p); + fix_cpool_free(allctr, (type & ~ERTS_ALC_FIX_NO_UNUSE), + ptr, &busy_pcrr_p, + !(type & ERTS_ALC_FIX_NO_UNUSE)); clear_busy_pool_carrier(allctr, busy_pcrr_p); } else { @@ -1885,7 +1893,7 @@ handle_delayed_dealloc(Allctr_t *allctr, if (fix) handle_delayed_fix_dealloc(allctr, ptr); else - dealloc_block(allctr, ptr, 1); + dealloc_block(allctr, ptr, NULL, 1); } } @@ -1991,15 +1999,24 @@ erts_alcu_check_delayed_dealloc(Allctr_t *allctr, ERTS_ALCU_DD_OPS_LIM_LOW, NULL, NULL, NULL) static void -dealloc_block(Allctr_t *allctr, void *ptr, int dec_cc_on_redirect) +dealloc_block(Allctr_t *allctr, void *ptr, ErtsAlcFixList_t *fix, int dec_cc_on_redirect) { Block_t *blk = UMEM2BLK(ptr); ERTS_SMP_LC_ASSERT(!allctr->thread_safe || erts_lc_mtx_is_locked(&allctr->mutex)); - if (IS_SBC_BLK(blk)) + if (IS_SBC_BLK(blk)) { destroy_carrier(allctr, blk, NULL); +#ifdef ERTS_SMP + if (fix && ERTS_ALC_IS_CPOOL_ENABLED(allctr)) { + ErtsAlcType_t type = ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type; + if (!(type & ERTS_ALC_FIX_NO_UNUSE)) + fix->u.cpool.used--; + fix->u.cpool.allocated--; + } +#endif + } #ifndef ERTS_SMP else mbc_free(allctr, ptr, NULL); @@ -2012,6 +2029,12 @@ dealloc_block(Allctr_t *allctr, void *ptr, int dec_cc_on_redirect) used_allctr = get_used_allctr(allctr, ERTS_ALC_TS_PREF_LOCK_NO, ptr, NULL, &busy_pcrr_p); if (used_allctr == allctr) { + if (fix) { + ErtsAlcType_t type = ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type; + if (!(type & ERTS_ALC_FIX_NO_UNUSE)) + fix->u.cpool.used--; + fix->u.cpool.allocated--; + } mbc_free(allctr, ptr, &busy_pcrr_p); clear_busy_pool_carrier(allctr, busy_pcrr_p); } @@ -5215,7 +5238,7 @@ do_erts_alcu_free(ErtsAlcType_t type, void *extra, void *p, if (allctr->fix) { if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) - fix_cpool_free(allctr, type, p, busy_pcrr_pp); + fix_cpool_free(allctr, type, p, busy_pcrr_pp, 1); else fix_nocpool_free(allctr, type, p); } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index af8db519d4..b64a7f8902 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2887,22 +2887,29 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } if (aux_work) { - flgs = erts_smp_atomic32_read_acqb(&ssi->flags); - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + erts_bump_timers(esdp->timer_wheel, current_time); } - erts_bump_timers(esdp->timer_wheel, current_time); } } else { ErtsMonotonicTime timeout_time; - timeout_time = erts_check_next_timeout_time(esdp); - current_time = erts_get_monotonic_time(esdp); - if (current_time >= timeout_time) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { + int do_timeout = 0; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout_time = erts_check_next_timeout_time(esdp); + current_time = erts_get_monotonic_time(esdp); + do_timeout = (current_time >= timeout_time); + } else + timeout_time = ERTS_MONOTONIC_TIME_MAX; + if (do_timeout) { + if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -2926,23 +2933,28 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) int res; ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); do { Sint64 timeout; if (current_time >= timeout_time) break; - timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time - - current_time - - 1) + 1; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + } else + timeout = -1; res = erts_tse_twait(ssi->event, timeout); - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); } while (res == EINTR); } } if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) erts_thr_progress_finalize_wait(esdp); } - if (current_time >= timeout_time) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time) erts_bump_timers(esdp->timer_wheel, current_time); } @@ -3010,9 +3022,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(!erts_port_task_have_outstanding_io_tasks()); erl_sys_schedule(1); /* Might give us something to do */ - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) - erts_bump_timers(esdp->timer_wheel, current_time); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) + erts_bump_timers(esdp->timer_wheel, current_time); + } sys_aux_work: #ifndef ERTS_SMP @@ -3021,15 +3035,18 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { - if (!working) - sched_wall_time_change(esdp, working = 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (!working) + sched_wall_time_change(esdp, working = 1); #ifdef ERTS_SMP - if (!thr_prgr_active) - erts_thr_progress_active(esdp, thr_prgr_active = 1); + if (!thr_prgr_active) + erts_thr_progress_active(esdp, thr_prgr_active = 1); #endif + } aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); #ifdef ERTS_SMP - if (aux_work && erts_thr_progress_update(esdp)) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && aux_work && + erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); #endif } @@ -3127,7 +3144,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erl_sys_schedule(0); - { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { ErtsMonotonicTime current_time = erts_get_monotonic_time(esdp); if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) erts_bump_timers(esdp->timer_wheel, current_time); @@ -6790,7 +6807,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) } } - (void) erts_get_monotonic_time(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + (void) erts_get_monotonic_time(esdp); erts_smp_runq_lock(esdp->run_queue); non_empty_runq(esdp->run_queue); @@ -6906,7 +6924,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) & ERTS_RUNQ_FLGS_QMASK); aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work|qmask) { - if (!thr_prgr_active) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -6914,7 +6932,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); - if (aux_work && erts_thr_progress_update(esdp)) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && aux_work && + erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); if (qmask) { erts_smp_runq_lock(esdp->run_queue); @@ -6924,32 +6943,40 @@ suspend_scheduler(ErtsSchedulerData *esdp) } if (aux_work) { - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + erts_bump_timers(esdp->timer_wheel, current_time); } - erts_bump_timers(esdp->timer_wheel, current_time); } } else { ErtsMonotonicTime timeout_time; - timeout_time = erts_check_next_timeout_time(esdp); - current_time = erts_get_monotonic_time(esdp); - - if (current_time >= timeout_time) { + int do_timeout = 0; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout_time = erts_check_next_timeout_time(esdp); + current_time = erts_get_monotonic_time(esdp); + do_timeout = (current_time >= timeout_time); + } else + timeout_time = ERTS_MONOTONIC_TIME_MAX; + if (do_timeout) { if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } } - else { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); + else { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); } - erts_thr_progress_prepare_wait(esdp); flgs = sched_spin_suspended(ssi, ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); if (flgs == (ERTS_SSI_FLG_SLEEPING @@ -6962,23 +6989,29 @@ suspend_scheduler(ErtsSchedulerData *esdp) | ERTS_SSI_FLG_SUSPENDED)) { int res; - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); do { Sint64 timeout; if (current_time >= timeout_time) break; - timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time - - current_time - - 1) + 1; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + } else + timeout = -1; res = erts_tse_twait(ssi->event, timeout); - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); } while (res == EINTR); } } - erts_thr_progress_finalize_wait(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + erts_thr_progress_finalize_wait(esdp); } - if (current_time >= timeout_time) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time) erts_bump_timers(esdp->timer_wheel, current_time); } @@ -9196,13 +9229,15 @@ Process *schedule(Process *p, int calls) ERTS_SMP_CHK_NO_PROC_LOCKS; - if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS) - (void) erts_get_monotonic_time(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS) + (void) erts_get_monotonic_time(esdp); - if (esdp->last_monotonic_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - erts_smp_runq_unlock(rq); - erts_bump_timers(esdp->timer_wheel, esdp->last_monotonic_time); - erts_smp_runq_lock(rq); + if (esdp->last_monotonic_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + erts_smp_runq_unlock(rq); + erts_bump_timers(esdp->timer_wheel, esdp->last_monotonic_time); + erts_smp_runq_lock(rq); + } } BM_STOP_TIMER(system); @@ -9649,7 +9684,7 @@ Process *schedule(Process *p, int calls) ASSERT(erts_proc_read_refc(p) > 0); - if (ERTS_PTMR_IS_TIMED_OUT(p)) { + if (!(state & ERTS_PSFLG_EXITING) && ERTS_PTMR_IS_TIMED_OUT(p)) { BeamInstr** pi; #ifdef ERTS_SMP ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 5ad92dad02..d050748703 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -55,7 +55,7 @@ void sys_sigrelease(int sig) #endif /* !SIG_SIGSET */ #if defined(__ANDROID__) -int __system_properties_fd(void); +static int system_properties_fd(void); #endif /* __ANDROID__ */ #if defined(__ANDROID__) @@ -104,9 +104,12 @@ main(int argc, char *argv[]) #if defined(HAVE_CLOSEFROM) closefrom(from); #elif defined(__ANDROID__) - for (i = from; i <= to; i++) { - if (i!=__system_properties_fd) - (void) close(i); + if (from <= to) { + int spfd = system_properties_fd(); + for (i = from; i <= to; i++) { + if (i != spfd) + (void) close(i); + } } #else for (i = from; i <= to; i++) @@ -143,9 +146,9 @@ main(int argc, char *argv[]) } #if defined(__ANDROID__) -int __system_properties_fd(void) +static int system_properties_fd(void) { - int s, fd; + int fd; char *env; env = getenv("ANDROID_PROPERTY_WORKSPACE"); @@ -156,4 +159,3 @@ int __system_properties_fd(void) return fd; } #endif /* __ANDROID__ */ - diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index 345a6ae3be..cf9a06599a 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -35,7 +35,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]} + {runtime_dependencies, ["stdlib-2.5", "kernel-4.0", "sasl-2.4"]} ]}. %% vim: ft=erlang diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index f73d21b9e3..9feb673c04 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -31,23 +31,6 @@ <p>This document describes the changes made to the asn1 application.</p> -<section><title>Asn1 4.0</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Many bugs have been eliminated in the the ASN.1 compiler - so that it can now successfully compile many more ASN.1 - specifications. Error messages have also been improved.</p> - <p> - Own Id: OTP-12395</p> - </item> - </list> - </section> - -</section> - <section><title>Asn1 3.0.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index d4c46863a3..c909c908d6 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1 +1 @@ -ASN1_VSN = 4.0 +ASN1_VSN = 3.0.4 diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index 218f76d403..472e3b7833 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -32,79 +32,6 @@ <file>notes.xml</file> </header> -<section><title>Common_Test 1.11</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - The status of an aborted test due to test suite - compilation error has changed from 'auto_skipped' to - 'failed'. This affects both the textual log file, event - handling and CT hook callbacks. The logging of - compilation failures has also been improved, especially - in the case of multiple test suites failing compilation.</p> - <p> - Own Id: OTP-10816</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Earlier there was no way to add optional parameters like - default-operation to an edit-config request sent with - ct_netconfc:edit_config/3,4, you had to use - ct_netconfc:send_rpc/2,3. For simplicity and completion, - a new optional argument, OptParams, is now added to the - edit_config function.</p> - <p> - Own Id: OTP-10446 Aux Id: kunagi-266 [177] </p> - </item> - <item> - <p> - When running OTP tests using the ts interface, it is now - possible to specify so called test categories per OTP - application. A test category is represented by a CT test - specification and defines an arbitrary subset of existing - test suites, groups and cases. Examples of test - categories are 'smoke' (smoke tests) and 'bench' - (benchmarks). (Call ts:help() for more info). Also, - functions for reading terms from the current test - specification during test, ct:get_testspec_terms/0 and - ct:get_testspec_terms/1, have been implemented.</p> - <p> - Own Id: OTP-11962</p> - </item> - <item> - <p> - Obsolete scripts and make file operations have been - removed and the installation chapter in the Common Test - User's Guide has been updated.</p> - <p> - Own Id: OTP-12421</p> - </item> - <item> - <p> - The 'keep_alive' interval has been reduced to 8 seconds, - which is two seconds shorter than the default - 'idle_timeout' value for ct_telnet:expect/3. This way, - the telnet server receives a NOP message (which might - trigger an action) before the operation times out. Also - the TCP option 'nodelay' has been enabled per default for - all telnet connections, in order to reduce the risk for - communication timeouts.</p> - <p> - Own Id: OTP-12678 Aux Id: seq12818 </p> - </item> - </list> - </section> - -</section> - <section><title>Common_Test 1.10.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 0654738247..9b5b44f3e1 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -31,94 +31,6 @@ <p>This document describes the changes made to the Compiler application.</p> -<section><title>Compiler 6.0</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - The compiler optimizes away building of terms that are - never actually used. As a result, the compiler in OTP 18 - may produce more warnings for terms that are built but - not used than the compiler in OTP 17.</p> - <p> - Own Id: OTP-12453</p> - </item> - <item> - <p> - Using a map could incorrectly suppress warnings for - unused variables.</p> - <p> - Own Id: OTP-12515</p> - </item> - <item> - <p> - The compiler now properly reports unknown parse - transforms. That is, <c>undef</c> exceptions coming from - the parse transform itself is reported differently from - the absence of the parse transform.</p> - <p> - Own Id: OTP-12723</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The <c>cerl</c> and <c>cerl_trees</c> modules in the - <c>compiler</c> application are now documented.</p> - <p> - Own Id: OTP-11978</p> - </item> - <item> - <p> - The deprecated '<c>asm</c>' option has been removed.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12100</p> - </item> - <item> - <p> - Support variables as Map keys in expressions and patterns</p> - <p>Erlang will accept any expression as keys in Map - expressions and it will accept literals or bound - variables as keys in Map patterns.</p> - <p> - Own Id: OTP-12218</p> - </item> - <item> - <p> - Infer Map type information in beam_type compiler - optimization pass.</p> - <p> - Own Id: OTP-12253</p> - </item> - <item> - <p> - Compiler optimizations have been improved.</p> - <p> - Own Id: OTP-12393</p> - </item> - <item> - <p> - Five undocumented functions in the module <c>core_lib</c> - have been deprecated and will be removed in the next - major release. The functions are: <c>get_anno/{1,2}</c>, - <c>is_literal/1</c>, <c>is_literal_list/1</c>, and - <c>literal_value</c>. Use the appropriate functions in - the <c>cerl</c> module instead.</p> - <p> - Own Id: OTP-12497</p> - </item> - </list> - </section> - -</section> - <section><title>Compiler 5.0.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl index 2a15c1ddf3..ee3e88959d 100644 --- a/lib/compiler/src/beam_bsm.erl +++ b/lib/compiler/src/beam_bsm.erl @@ -242,6 +242,12 @@ btb_reaches_match_2([{bif,_,{f,F},Ss,Dst}=I|Is], Regs0, D0) -> Regs = btb_kill([Dst], Regs0), D = btb_follow_branch(F, Regs, D0), btb_reaches_match_1(Is, Regs, D); +btb_reaches_match_2([{get_map_elements,{f,F},Src,{list,Ls}}=I|Is], Regs0, D0) -> + {Ss,Ds} = beam_utils:split_even(Ls), + btb_ensure_not_used([Src|Ss], I, Regs0), + Regs = btb_kill(Ds, Regs0), + D = btb_follow_branch(F, Regs, D0), + btb_reaches_match_1(Is, Regs, D); btb_reaches_match_2([{test,bs_start_match2,{f,F},Live,[Ctx,_],Ctx}=I|Is], Regs0, D0) -> CtxRegs = btb_context_regs(Regs0), diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 22810c910c..0158cf64db 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -342,10 +342,10 @@ run_tc({Name,Fun}, St) -> run_eprof({Name,Fun}, Name, St) -> io:format("~p: Running eprof\n", [Name]), - eprof:start_profiling([self()]), + c:appcall(tools, eprof, start_profiling, [[self()]]), Val = (catch Fun(St)), - eprof:stop_profiling(), - eprof:analyze(), + c:appcall(tools, eprof, stop_profiling, []), + c:appcall(tools, eprof, analyze, []), Val; run_eprof({_,Fun}, _, St) -> catch Fun(St). diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src index 2a40c1c379..17d1bd91ce 100644 --- a/lib/compiler/src/compiler.app.src +++ b/lib/compiler/src/compiler.app.src @@ -69,5 +69,5 @@ {registered, []}, {applications, [kernel, stdlib]}, {env, []}, - {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-7.0", - "crypto-3.3"]}]}. + {runtime_dependencies, ["stdlib-2.5","kernel-4.0","hipe-3.12","erts-7.0", + "crypto-3.6"]}]}. diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index f6ba75577d..5742b7e6cf 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -38,7 +38,8 @@ -export([pattern/1,pattern2/1,pattern3/1,pattern4/1, guard/1,bad_arith/1,bool_cases/1,bad_apply/1, files/1,effect/1,bin_opt_info/1,bin_construction/1, - comprehensions/1,maps/1,redundant_boolean_clauses/1, + comprehensions/1,maps/1,maps_bin_opt_info/1, + redundant_boolean_clauses/1, latin1_fallback/1,underscore/1,no_warnings/1]). % Default timetrap timeout (set in init_per_testcase). @@ -64,6 +65,7 @@ groups() -> [pattern,pattern2,pattern3,pattern4,guard, bad_arith,bool_cases,bad_apply,files,effect, bin_opt_info,bin_construction,comprehensions,maps, + maps_bin_opt_info, redundant_boolean_clauses,latin1_fallback, underscore,no_warnings]}]. @@ -619,6 +621,19 @@ maps(Config) when is_list(Config) -> run(Config, Ts), ok. +maps_bin_opt_info(Config) when is_list(Config) -> + Ts = [{map_bsm, + <<" + t1(<<0:8,7:8,T/binary>>,#{val := I}=M) -> + t1(T, M#{val := I+1}); + t1(<<_:8>>,M) -> + M. + ">>, + [bin_opt_info], + {warnings,[{2,beam_bsm,bin_opt}]}}], + [] = run(Config, Ts), + ok. + redundant_boolean_clauses(Config) when is_list(Config) -> Ts = [{redundant_boolean_clauses, <<" diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 69f71ba5dd..05e682c893 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 6.0 +COMPILER_VSN = 5.0.4 diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml index 229e0c9945..8f519447fc 100644 --- a/lib/cosEvent/doc/src/notes.xml +++ b/lib/cosEvent/doc/src/notes.xml @@ -32,23 +32,7 @@ <file>notes.xml</file> </header> - <section><title>cosEvent 2.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosEvent 2.1.15</title> + <section><title>cosEvent 2.1.15</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml index 72408b52f4..2c3bf16411 100644 --- a/lib/cosEventDomain/doc/src/notes.xml +++ b/lib/cosEventDomain/doc/src/notes.xml @@ -31,23 +31,7 @@ <file>notes.xml</file> </header> - <section><title>cosEventDomain 1.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosEventDomain 1.1.14</title> + <section><title>cosEventDomain 1.1.14</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosFileTransfer/doc/src/notes.xml b/lib/cosFileTransfer/doc/src/notes.xml index a3cce2a7d8..1d0c826d40 100644 --- a/lib/cosFileTransfer/doc/src/notes.xml +++ b/lib/cosFileTransfer/doc/src/notes.xml @@ -30,23 +30,7 @@ <file>notes.xml</file> </header> - <section><title>cosFileTransfer 1.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosFileTransfer 1.1.16</title> + <section><title>cosFileTransfer 1.1.16</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml index b16571a1cf..2e4f922142 100644 --- a/lib/cosNotification/doc/src/notes.xml +++ b/lib/cosNotification/doc/src/notes.xml @@ -31,23 +31,7 @@ <file>notes.xml</file> </header> - <section><title>cosNotification 1.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosNotification 1.1.21</title> + <section><title>cosNotification 1.1.21</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml index 1885a8fd6b..739f41617f 100644 --- a/lib/cosProperty/doc/src/notes.xml +++ b/lib/cosProperty/doc/src/notes.xml @@ -31,23 +31,7 @@ <file>notes.xml</file> </header> - <section><title>cosProperty 1.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosProperty 1.1.17</title> + <section><title>cosProperty 1.1.17</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml index 6c948c8c2b..f218f19a6b 100644 --- a/lib/cosTime/doc/src/notes.xml +++ b/lib/cosTime/doc/src/notes.xml @@ -32,23 +32,7 @@ <file>notes.xml</file> </header> - <section><title>cosTime 1.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosTime 1.1.14</title> + <section><title>cosTime 1.1.14</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosTransactions/doc/src/notes.xml b/lib/cosTransactions/doc/src/notes.xml index ae3b9eb6f2..4b20f23efb 100644 --- a/lib/cosTransactions/doc/src/notes.xml +++ b/lib/cosTransactions/doc/src/notes.xml @@ -32,23 +32,7 @@ <file>notes.xml</file> </header> - <section><title>cosTransactions 1.3</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>cosTransactions 1.2.14</title> + <section><title>cosTransactions 1.2.14</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index 6ab109e349..a0ebc4b3dd 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -30,49 +30,6 @@ </header> <p>This document describes the changes made to the Crypto application.</p> -<section><title>Crypto 3.6</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Enhance crypto:generate_key to calculate ECC public keys - from private key.</p> - <p> - Own Id: OTP-12394</p> - </item> - <item> - <p> - Fix bug in <c>crypto:generate_key</c> for <c>ecdh</c> - that could cause VM crash for faulty input.</p> - <p> - Own Id: OTP-12733</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Use the EVP API for AES-CBC crypto to enables the use of - hardware acceleration for AES-CBC crypto on newer Intel - CPUs (AES-NI), among other platforms.</p> - <p> - Own Id: OTP-12380</p> - </item> - <item> - <p> - Add AES ECB block encryption.</p> - <p> - Own Id: OTP-12403</p> - </item> - </list> - </section> - -</section> - <section><title>Crypto 3.5</title> <section><title>Improvements and New Features</title> diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 55b1b3e8c4..8489b59562 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 3.6 +CRYPTO_VSN = 3.5 diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml index 2bf80597b5..7384189a6f 100644 --- a/lib/debugger/doc/src/notes.xml +++ b/lib/debugger/doc/src/notes.xml @@ -32,24 +32,6 @@ <p>This document describes the changes made to the Debugger application.</p> -<section><title>Debugger 4.1</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Support variables as Map keys in expressions and patterns</p> - <p>Erlang will accept any expression as keys in Map - expressions and it will accept literals or bound - variables as keys in Map patterns.</p> - <p> - Own Id: OTP-12218</p> - </item> - </list> - </section> - -</section> - <section><title>Debugger 4.0.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk index b6fd4e8e44..b82f0f4e37 100644 --- a/lib/debugger/vsn.mk +++ b/lib/debugger/vsn.mk @@ -1 +1 @@ -DEBUGGER_VSN = 4.1 +DEBUGGER_VSN = 4.0.3 diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 2a8bf6edcc..5f52906625 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -368,6 +368,7 @@ Option :: {files, [Filename :: string()]} | {include_dirs, [DirName :: string()]} | {output_file, FileName :: string()} | {output_plt, FileName :: string()} + | {check_plt, boolean()}, | {analysis_type, 'succ_typings' | 'plt_add' | 'plt_build' | diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index d30882b5b9..8976679c1d 100644 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -31,92 +31,6 @@ <p>This document describes the changes made to the Dialyzer application.</p> -<section><title>Dialyzer 2.8</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> The translation of Erlang forms to the type - representation used by Dialyzer has been improved in - several ways. The most important change is that deeply - nested records can be handled. </p> - <p> - Own Id: OTP-12350</p> - </item> - <item> - <p> Update the PLT properly when a module is changed. - (Thanks to James Fish for the bug report, and to Stavros - Aronis for fixing the bug.) </p> - <p> - Own Id: OTP-12637</p> - </item> - <item> - <p> - An argument of '*'/2 is not constraind if the other - operand can be zero.</p> - <p> - Own Id: OTP-12725</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> The <c>-dialyzer()</c> attribute can be used for - suppressing warnings in a module by specifying functions - or warning options. It can also be used for requesting - warnings in a module. </p> - <p> - Own Id: OTP-10280</p> - </item> - <item> - <p> The pre-defined types <c>array()</c>, <c>dict()</c>, - <c>digraph()</c>, <c>gb_set()</c>, <c>gb_tree()</c>, - <c>queue()</c>, <c>set()</c>, and <c>tid()</c> have been - removed. </p> - <p> - Own Id: OTP-11445 Aux Id: OTP-10342, OTP-9352 </p> - </item> - <item> - <p> A few type names that have been used for representing - certain predefined types can now be used for user-defined - types. This affects the types <c>product/_</c>, - <c>union/_</c>, and <c>range/2</c> as well as - <c>tuple/N</c> (N > 0), <c>map/N</c> (N > 0), - <c>atom/1</c>, <c>integer/1</c>, <c>binary/2</c>, - <c>record/_,</c> and <c>'fun'/_</c>. A consequence is - that, for example, it is no longer possible to refer to a - record type with <c>record(r)</c>; instead the usual - record notation, <c>#r{}</c>, is to be used. </p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-11851</p> - </item> - <item> - <p> When implementing user-defined behaviours it is now - possible to specify optional callback functions. See OTP - Design Principles User's Guide, Sys and Proc_Lib, - User-Defined Behaviours, for details. </p> - <p> - Own Id: OTP-11861</p> - </item> - <item> - <p>Add two options to the Dialyzer: - <c>no_missing_calls</c> suppresses warnings about calls - to missing or unexported functions; <c>unknown</c> lets - warnings about unknown functions or types affect the exit - status. See also dialyzer(3). </p> - <p> - Own Id: OTP-12682</p> - </item> - </list> - </section> - -</section> - <section><title>Dialyzer 2.7.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index c9e7da9ef0..c8537e3bd8 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2014. All Rights Reserved. +%% Copyright Ericsson AB 2006-2015. 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 @@ -162,14 +162,7 @@ run(Opts) -> {error, Msg} -> throw({dialyzer_error, Msg}); OptsRecord -> - case OptsRecord#options.check_plt of - true -> - case cl_check_init(OptsRecord) of - {ok, ?RET_NOTHING_SUSPICIOUS} -> ok; - {error, ErrorMsg1} -> throw({dialyzer_error, ErrorMsg1}) - end; - false -> ok - end, + ok = check_init(OptsRecord), case dialyzer_cl:start(OptsRecord) of {?RET_DISCREPANCIES, Warnings} -> Warnings; {?RET_NOTHING_SUSPICIOUS, _} -> [] @@ -179,6 +172,16 @@ run(Opts) -> erlang:error({dialyzer_error, lists:flatten(ErrorMsg)}) end. +check_init(#options{analysis_type = plt_check}) -> + ok; +check_init(#options{check_plt = true} = OptsRecord) -> + case cl_check_init(OptsRecord) of + {ok, _} -> ok; + {error, Msg} -> throw({dialyzer_error, Msg}) + end; +check_init(#options{check_plt = false}) -> + ok. + internal_gui(OptsRecord) -> F = fun() -> dialyzer_gui_wx:start(OptsRecord), @@ -199,17 +202,13 @@ gui(Opts) -> throw({dialyzer_error, Msg}); OptsRecord -> ok = check_gui_options(OptsRecord), - case cl_check_init(OptsRecord) of - {ok, ?RET_NOTHING_SUSPICIOUS} -> - F = fun() -> - dialyzer_gui_wx:start(OptsRecord) - end, - case doit(F) of - {ok, _} -> ok; - {error, Msg} -> throw({dialyzer_error, Msg}) - end; - {error, ErrorMsg1} -> - throw({dialyzer_error, ErrorMsg1}) + ok = check_init(OptsRecord), + F = fun() -> + dialyzer_gui_wx:start(OptsRecord) + end, + case doit(F) of + {ok, _} -> ok; + {error, Msg} -> throw({dialyzer_error, Msg}) end catch throw:{dialyzer_error, ErrorMsg} -> diff --git a/lib/dialyzer/test/plt_SUITE.erl b/lib/dialyzer/test/plt_SUITE.erl index ef4cdc57f0..ecbac14e5d 100644 --- a/lib/dialyzer/test/plt_SUITE.erl +++ b/lib/dialyzer/test/plt_SUITE.erl @@ -6,12 +6,13 @@ -include_lib("common_test/include/ct.hrl"). -include("dialyzer_test_constants.hrl"). --export([suite/0, all/0, build_plt/1, beam_tests/1, update_plt/1]). +-export([suite/0, all/0, build_plt/1, beam_tests/1, update_plt/1, + run_plt_check/1, run_succ_typings/1]). suite() -> [{timetrap, ?plt_timeout}]. -all() -> [build_plt, beam_tests, update_plt]. +all() -> [build_plt, beam_tests, update_plt, run_plt_check, run_succ_typings]. build_plt(Config) -> OutDir = ?config(priv_dir, Config), @@ -37,14 +38,76 @@ beam_tests(Config) when is_list(Config) -> ">>, Opts = [no_auto_import], {ok, BeamFile} = compile(Config, Prog, no_auto_import, Opts), - [] = run_dialyzer([BeamFile]), + [] = run_dialyzer(plt_build, [BeamFile], []), ok. -run_dialyzer(Files) -> - dialyzer:run([{analysis_type, plt_build}, - {files, Files}, - {from, byte_code}, - {check_plt, false}]). +run_plt_check(Config) when is_list(Config) -> + Mod1 = <<" + -module(run_plt_check1). + ">>, + + Mod2A = <<" + -module(run_plt_check2). + ">>, + + {ok, BeamFile1} = compile(Config, Mod1, run_plt_check1, []), + {ok, BeamFile2} = compile(Config, Mod2A, run_plt_check2, []), + [] = run_dialyzer(plt_build, [BeamFile1, BeamFile2], []), + + Mod2B = <<" + -module(run_plt_check2). + + -export([call/1]). + + call(X) -> run_plt_check1:call(X). + ">>, + + {ok, BeamFile2} = compile(Config, Mod2B, run_plt_check2, []), + + % callgraph warning as run_plt_check2:call/1 makes a call to unexported + % function run_plt_check1:call/1. + [_] = run_dialyzer(plt_check, [], []), + + ok. + +run_succ_typings(Config) when is_list(Config) -> + Mod1A = <<" + -module(run_succ_typings1). + + -export([call/0]). + + call() -> a. + ">>, + + {ok, BeamFile1} = compile(Config, Mod1A, run_succ_typings1, []), + [] = run_dialyzer(plt_build, [BeamFile1], []), + + Mod1B = <<" + -module(run_succ_typings1). + + -export([call/0]). + + call() -> b. + ">>, + + Mod2 = <<" + -module(run_succ_typings2). + + -export([call/0]). + + -spec call() -> b. + call() -> run_succ_typings1:call(). + ">>, + + {ok, BeamFile1} = compile(Config, Mod1B, run_succ_typings1, []), + {ok, BeamFile2} = compile(Config, Mod2, run_succ_typings2, []), + % contract types warning as run_succ_typings2:call/0 makes a call to + % run_succ_typings1:call/0, which returns a (not b) in the PLT. + [_] = run_dialyzer(succ_typings, [BeamFile2], [{check_plt, false}]), + % warning not returned as run_succ_typings1 is updated in the PLT. + [] = run_dialyzer(succ_typings, [BeamFile2], [{check_plt, true}]), + + ok. %%% [James Fish:] %%% If a function is removed from a module and the module has previously @@ -103,3 +166,9 @@ compile(Config, Prog, Module, CompileOpts) -> Opts = [{outdir, PrivDir}, debug_info | CompileOpts], {ok, Module} = compile:file(Filename, Opts), {ok, filename:join([PrivDir, lists:concat([Module, ".beam"])])}. + +run_dialyzer(Analysis, Files, Opts) -> + dialyzer:run([{analysis_type, Analysis}, + {files, Files}, + {from, byte_code} | + Opts]). diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 48e0830109..527afaf4ef 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 2.8 +DIALYZER_VSN = 2.7.4 diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml index 48bb415ab3..e350adb540 100644 --- a/lib/edoc/doc/src/notes.xml +++ b/lib/edoc/doc/src/notes.xml @@ -31,21 +31,6 @@ <p>This document describes the changes made to the EDoc application.</p> -<section><title>Edoc 0.7.17</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Remove functionality related to packages</p> - <p> - Own Id: OTP-12431</p> - </item> - </list> - </section> - -</section> - <section><title>Edoc 0.7.16</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk index 49a73331c6..24cfbf16d5 100644 --- a/lib/edoc/vsn.mk +++ b/lib/edoc/vsn.mk @@ -1 +1 @@ -EDOC_VSN = 0.7.17 +EDOC_VSN = 0.7.16 diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index a6fad8a857..253ba7c2ff 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -121,7 +121,7 @@ filter() See present/1, substrings/2, <item>Any error responded from ssl:connect/3</item> </taglist> <p>The <c>Timeout</c> parameter is for the actual tls upgrade (phase 2) while the timeout in - <seealso marker="#open/2">erl_tar:open/2</seealso> is used for the initial negotiation about + <seealso marker="#open/2">eldap:open/2</seealso> is used for the initial negotiation about upgrade (phase 1). </p> </desc> @@ -298,7 +298,7 @@ filter() See present/1, substrings/2, search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]), </pre> <p>The <c>timeout</c> option in the <c>SearchOptions</c> is for the ldap server, while - the timeout in <seealso marker="#open/2">erl_tar:open/2</seealso> is used for each + the timeout in <seealso marker="#open/2">eldap:open/2</seealso> is used for each individual request in the search operation. </p> </desc> diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml index a7aefefb4a..e76101c30e 100644 --- a/lib/eldap/doc/src/notes.xml +++ b/lib/eldap/doc/src/notes.xml @@ -30,22 +30,6 @@ </header> <p>This document describes the changes made to the Eldap application.</p> -<section><title>Eldap 1.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Support added for LDAP Password Modify Extended Operation - (RFC 3062). Thanks to danielwhite.</p> - <p> - Own Id: OTP-12282</p> - </item> - </list> - </section> - -</section> - <section><title>Eldap 1.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk index 5823c96253..2abd3d2b7e 100644 --- a/lib/erl_docgen/vsn.mk +++ b/lib/erl_docgen/vsn.mk @@ -1 +1 @@ -ERL_DOCGEN_VSN = 0.3.8 +ERL_DOCGEN_VSN = 0.4 diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc index df716cdeea..eb60f673ef 100644 --- a/lib/eunit/doc/overview.edoc +++ b/lib/eunit/doc/overview.edoc @@ -572,6 +572,21 @@ Examples: <dt>`assertNotMatch(GuardedPattern, Expr)'</dt> <dd>The inverse case of assertMatch, for convenience. </dd> +<dt>`assertReceive(GuardedPattern, Timeout)'</dt> +<dd>Waits for up to the `Timeout' milliseconds for a message to arrive +in the mailbox of the current process that matches against the +`GuardedPattern' if testing is enabled. +If no message matching the `GuardedPattern' is received in the specified +`Timeout' interval, the assertion fails and an informative exception will +be generated; see the `assert' macro for further details. `GuardedPattern' +can be anything that you can write on the left hand side of the `->' +symbol in a case-clause, except that it cannot contain comma-separated +guard tests. + +Examples: +```?assertReceive(done, 1000)''' +```?assertReceive(Bin when byte_size(Bin) > 10, 1000)''' +</dd> <dt>`assertEqual(Expect, Expr)'</dt> <dd>Evaluates the expressions `Expect' and `Expr' and compares the results for equality, if testing is enabled. If the values are not diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml index 3c6daf142e..6b76e097b6 100644 --- a/lib/eunit/doc/src/notes.xml +++ b/lib/eunit/doc/src/notes.xml @@ -32,20 +32,6 @@ </header> <p>This document describes the changes made to the EUnit application.</p> -<section><title>Eunit 2.2.10</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p>The <c>eunit</c> application is now unicode safe.</p> - <p> - Own Id: OTP-11660</p> - </item> - </list> - </section> - -</section> - <section><title>Eunit 2.2.9</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index 53d291430d..8a829396ec 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -166,6 +166,26 @@ %% This is mostly a convenience which gives more detailed reports. %% Note: Guard is a guarded pattern, and can not be used for value. -ifdef(NOASSERT). +-define(assertReceive(Guard, Timeout), ok). +-else. +-define(assertReceive(Guard, Timeout), + begin + ((fun () -> + receive (Guard) -> ok + after Timeout -> erlang:error({assertReceive_timedout, + [{module, ?MODULE}, + {line, ?LINE}, + {pattern, (??Guard)}, + {timeout, __V}]}) + end + end)()) + end). +-endif. +-define(_assertReceive(Guard, Timeout), ?_test(?assertReceive(Guard, Timeout))). + +%% This is mostly a convenience which gives more detailed reports. +%% Note: Guard is a guarded pattern, and can not be used for value. +-ifdef(NOASSERT). -define(assertMatch(Guard, Expr), ok). -else. -define(assertMatch(Guard, Expr), diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index 8b489bdc04..dca8b3ece0 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.2.10 +EUNIT_VSN = 2.2.9 diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index 1cc8558fe8..8d3358533b 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -30,42 +30,6 @@ </header> <p>This document describes the changes made to HiPE.</p> -<section><title>Hipe 3.12</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> Fix a minor bug in the handling of opaque types. </p> - <p> - Own Id: OTP-12666</p> - </item> - <item> - <p> - Fix hipe bug when matching a "writable" binary. The bug - has been seen to sometimes cause a failed binary matching - of a correct utf8 character, but other symptoms are also - possible.</p> - <p> - Own Id: OTP-12667</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Improved error handling when memory allocation for HiPE - code fails.</p> - <p> - Own Id: OTP-12448</p> - </item> - </list> - </section> - -</section> - <section><title>Hipe 3.11.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index e507ae933f..60b4e0559b 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.12 +HIPE_VSN = 3.11.3 diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 7939787601..bae8e327a3 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,19 +32,15 @@ <file>notes.xml</file> </header> - <section><title>Inets 6.0</title> + <section><title>Inets 5.10.8</title> - <section><title>Improvements and New Features</title> + <section><title>Fixed Bugs and Malfunctions</title> <list> <item> <p> - Remove Server Side Include support from inets, as this is - an old technic that has security issues and was not well - tested.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + Reject messages with a Content-Length less than 0</p> <p> - Own Id: OTP-12156</p> + Own Id: OTP-12739 Aux Id: seq12860 </p> </item> </list> </section> diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl index 6985065c3e..3ff07616f9 100644 --- a/lib/inets/src/http_server/httpd_request.erl +++ b/lib/inets/src/http_server/httpd_request.erl @@ -417,8 +417,12 @@ check_header({"content-length", Value}, Maxsizes) -> case length(Value) =< MaxLen of true -> try - _ = list_to_integer(Value), - ok + list_to_integer(Value) + of + I when I>= 0 -> + ok; + _ -> + {error, {size_error, Max, 411, "negative content-length"}} catch _:_ -> {error, {size_error, Max, 411, "content-length not an integer"}} end; diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index d5cb460404..ecb84e447c 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 6.0 +INETS_VSN = 5.10.8 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml index f854fa1f3a..fc5f8be53e 100644 --- a/lib/jinterface/doc/src/notes.xml +++ b/lib/jinterface/doc/src/notes.xml @@ -30,59 +30,6 @@ </header> <p>This document describes the changes made to the Jinterface application.</p> -<section><title>Jinterface 1.6</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Reformat the sources for JInterface uniformly and - according to the standard Java style guidelines. Provide - description of the rules applied in Eclipse format (for - other editors one can check the settings against these).</p> - <p> - In short, the formatting style is: * indentation uses - only spaces; each level is 4 positions * no trailing - whitespace * mostly default Java style formatting (any - difference is minor) * always use {} blocks * use 'final' - as much as possible</p> - <p> - Own Id: OTP-12333</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Add basic transport factory implementation. This makes - possible creating connections between nodes using ssh - channels for example.</p> - <p> - Own Id: OTP-12686</p> - </item> - <item> - <p> - Add Jinterface generic match and bind methods to provide - low level interface base methods sufficient for variety - of higher level pattern matching/variable binding - implementations.</p> - <p> - Own Id: OTP-12691</p> - </item> - <item> - <p> - Minimal Java version is now 1.6</p> - <p> - Own Id: OTP-12718</p> - </item> - </list> - </section> - -</section> - <section><title>Jinterface 1.5.12</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java index 5b9d13ad81..74afbbcca6 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java @@ -153,9 +153,6 @@ public class OtpSelf extends OtpLocalNode { * the port number you wish to use for incoming connections. * Specifying 0 lets the system choose an available port. * - * @param transportFactory - * the transport factory to use when creating connections. - * * @exception IOException * in case of server transport failure */ diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk index 4df01d1151..72ad316333 100644 --- a/lib/jinterface/vsn.mk +++ b/lib/jinterface/vsn.mk @@ -1 +1 @@ -JINTERFACE_VSN = 1.6 +JINTERFACE_VSN = 1.5.12 diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 0bd9a067ca..6f7f18a8e7 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -30,79 +30,6 @@ </header> <p>This document describes the changes made to the Kernel application.</p> -<section><title>Kernel 4.0</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix error handling in <c>file:read_line/1</c> for Unicode - contents.</p> - <p> - Own Id: OTP-12144</p> - </item> - <item> - <p> - Introduce <c>os:getenv/2</c> which is similar to - <c>os:getenv/1</c> but returns the passed default value - if the required environment variable is undefined.</p> - <p> - Own Id: OTP-12342</p> - </item> - <item> - <p> - It is now possible to paste text in JCL mode (using - Ctrl-Y) that has been copied in the previous shell - session. Also a bug that caused the JCL mode to crash - when pasting text has been fixed.</p> - <p> - Own Id: OTP-12673</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - New BIF: <c>erlang:get_keys/0</c>, lists all keys - associated with the process dictionary. Note: - <c>erlang:get_keys/0</c> is auto-imported.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12151 Aux Id: seq12521 </p> - </item> - <item> - <p> - The internal group to user_drv protocol has been changed - to be synchronous in order to guarantee that output sent - to a process implementing the user_drv protocol is - printed before replying. This protocol is used by the - standard_output device and the ssh application when - acting as a client. </p> - <p> - This change changes the previous unlimited buffer when - printing to standard_io and other devices that end up in - user_drv to 1KB.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12240</p> - </item> - <item> - <p>The <c>inflateInit/2</c> and <c>deflateInit/6</c> - functions now accepts a WindowBits argument equal to 8 - and -8.</p> - <p> - Own Id: OTP-12564</p> - </item> - </list> - </section> - -</section> - <section><title>Kernel 3.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index 6635885aaf..a1a99a4e18 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -490,7 +490,8 @@ init(Init, Kernel) -> %% called during start-up of any app. case check_conf_data(ConfData) of ok -> - _ = ets:new(ac_tab, [set, public, named_table]), + _ = ets:new(ac_tab, [set, public, named_table, + {read_concurrency,true}]), S = #state{conf_data = ConfData}, {ok, KAppl} = make_appl(Kernel), case catch load(S, KAppl) of diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index d73d1ff281..5cc38325b1 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -107,7 +107,7 @@ is_module_native(_) -> -spec make_stub_module(Module, Beam, Info) -> Module when Module :: module(), Beam :: binary(), - Info :: {list(), list()}. + Info :: {list(), list(), binary()}. make_stub_module(_, _, _) -> erlang:nif_error(undef). diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index 835dcf2705..fb60a14afb 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -112,7 +112,6 @@ listen_options(Opts0) -> end, case application:get_env(kernel, inet_dist_listen_options) of {ok,ListenOpts} -> - erlang:display({inet_dist_listen_options, ListenOpts}), ListenOpts ++ Opts1; _ -> Opts1 @@ -340,7 +339,6 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> connect_options(Opts) -> case application:get_env(kernel, inet_dist_connect_options) of {ok,ConnectOpts} -> - erlang:display({inet_dist_listen_options, ConnectOpts}), ConnectOpts ++ Opts; _ -> Opts diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 0cb10791d7..9787dca162 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -115,6 +115,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-7.0", "stdlib-2.0", "sasl-2.4"]} + {runtime_dependencies, ["erts-7.0", "stdlib-2.5", "sasl-2.4"]} ] }. diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index c912da0091..e1d447a465 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 4.0 +KERNEL_VSN = 3.2 diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 7f6ff1e655..dc98efbff3 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -38,37 +38,7 @@ thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> - <section><title>Mnesia 4.13</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Mnesia's dirty functions did not always exit with - <c>{aborted, Reason}</c> as documented when an error - occurred.</p> - <p> - Own Id: OTP-12714</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Make Mnesia DCD dump behavior at start up optional, when - turned off mnesia loads large disc_copies tables faster.</p> - <p> - Own Id: OTP-12481</p> - </item> - </list> - </section> - -</section> - -<section><title>Mnesia 4.12.5</title> + <section><title>Mnesia 4.12.5</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index 79dd495c4b..b23339e408 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.13 +MNESIA_VSN = 4.12.5 diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml index 338708db43..a9ec68fc9e 100644 --- a/lib/observer/doc/src/notes.xml +++ b/lib/observer/doc/src/notes.xml @@ -31,28 +31,6 @@ <p>This document describes the changes made to the Observer application.</p> -<section><title>Observer 2.1</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Added the possibility to view sasl log entries for - processes.</p> - <p> - Own Id: OTP-12504</p> - </item> - <item> - <p> - Add memory allocator usage and utilization graphs.</p> - <p> - Own Id: OTP-12631</p> - </item> - </list> - </section> - -</section> - <section><title>Observer 2.0.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk index 7e7e32099b..10ed3bdfe5 100644 --- a/lib/observer/vsn.mk +++ b/lib/observer/vsn.mk @@ -1 +1 @@ -OBSERVER_VSN = 2.1 +OBSERVER_VSN = 2.0.4 diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml index 1ffd86a1fb..2167a43eee 100644 --- a/lib/orber/doc/src/notes.xml +++ b/lib/orber/doc/src/notes.xml @@ -33,23 +33,7 @@ </header> - <section><title>Orber 3.8</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Remove the usage of erlang:now() from all Corba - applications and use the new rand module instead of - random. </p> - <p> - Own Id: OTP-12687</p> - </item> - </list> - </section> - -</section> - -<section><title>Orber 3.7.1</title> + <section><title>Orber 3.7.1</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/orber/src/orber.app.src b/lib/orber/src/orber.app.src index 5dda63982f..217c1b247f 100644 --- a/lib/orber/src/orber.app.src +++ b/lib/orber/src/orber.app.src @@ -104,7 +104,7 @@ {applications, [stdlib, kernel, mnesia]}, {env, []}, {mod, {orber, []}}, - {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","mnesia-4.12","kernel-3.0", + {runtime_dependencies, ["stdlib-2.5","ssl-5.3.4","mnesia-4.12","kernel-3.0", "inets-5.10","erts-7.0"]} ]}. diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml index bd4206d748..d3acc1effc 100644 --- a/lib/os_mon/doc/src/notes.xml +++ b/lib/os_mon/doc/src/notes.xml @@ -30,25 +30,6 @@ </header> <p>This document describes the changes made to the OS_Mon application.</p> -<section><title>Os_Mon 2.4</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - cpu_sup should use native sysctl/libkvm calls on BSD</p> - <p> - This avoids forking off with os:cmd every time we just - want to collect the load averages. riak does this every - second, which results in a lot of unnecessary load.</p> - <p> - Own Id: OTP-12730</p> - </item> - </list> - </section> - -</section> - <section><title>Os_Mon 2.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk index 7f2667e40a..833e855e0e 100644 --- a/lib/os_mon/vsn.mk +++ b/lib/os_mon/vsn.mk @@ -1 +1 @@ -OS_MON_VSN = 2.4 +OS_MON_VSN = 2.3.1 diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml index 1751238d20..c8cb70b6d2 100644 --- a/lib/parsetools/doc/src/notes.xml +++ b/lib/parsetools/doc/src/notes.xml @@ -30,21 +30,6 @@ </header> <p>This document describes the changes made to the Parsetools application.</p> -<section><title>Parsetools 2.1</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> The new <c>-dialyzer()</c> attribute is used for - suppressing Dialyzer warnings in generated code. </p> - <p> - Own Id: OTP-12271</p> - </item> - </list> - </section> - -</section> - <section><title>Parsetools 2.0.12</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk index b99b3bb713..dd9cc2991c 100644 --- a/lib/parsetools/vsn.mk +++ b/lib/parsetools/vsn.mk @@ -1 +1 @@ -PARSETOOLS_VSN = 2.1 +PARSETOOLS_VSN = 2.0.12 diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml index 19d6cd6d01..b51c8fcb4d 100644 --- a/lib/percept/doc/src/notes.xml +++ b/lib/percept/doc/src/notes.xml @@ -32,21 +32,6 @@ </header> <p>This document describes the changes made to the Percept application.</p> -<section><title>Percept 0.8.11</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix http server configuration</p> - <p> - Own Id: OTP-12662</p> - </item> - </list> - </section> - -</section> - <section><title>Percept 0.8.10</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk index 833ab35aa5..4451354e21 100644 --- a/lib/percept/vsn.mk +++ b/lib/percept/vsn.mk @@ -1 +1 @@ -PERCEPT_VSN = 0.8.11 +PERCEPT_VSN = 0.8.10 diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index df65ad7004..f241a91eb0 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -34,37 +34,6 @@ <file>notes.xml</file> </header> -<section><title>Public_Key 1.0</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - public_key: Remove legacy switch compact_bit_string</p> - <p> - E.i bitstrings will not be decode as {Unused, Binary}, - they are now Erlang bitstrings.</p> - <p> - Also the compact_bit_string implies the - legacy_erlang_types switch So removing the switch will - also make OCTET STRING values be represented as binaries.</p> - <p> - Undecoded open type will now be wrapped in a - asn1_OPENTYPE tuple.</p> - <p> - This will change some values in records returned by the - public_key API making this change a potentiall - incompatibility.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12110</p> - </item> - </list> - </section> - -</section> - <section><title>Public_Key 0.23</title> <section><title>Improvements and New Features</title> diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml index c0ac0cf79c..95d7c6fa50 100644 --- a/lib/sasl/doc/src/notes.xml +++ b/lib/sasl/doc/src/notes.xml @@ -30,30 +30,6 @@ </header> <p>This document describes the changes made to the SASL application.</p> -<section><title>SASL 2.4.2</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - The undocumented upgrade instruction - <c>{remove_module,PrePurge,PostPurge,DepMods}</c> is - removed. This instruction was added for symmetry reasons - in OTP R7B, but was never documented or tested.</p> - <p> - The existing instruction <c>{add_module,Mod,DepMods}</c> - is now documented, and the complementing instruction - <c>{delete_module,Mod,DepMods}</c> is added.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-11540</p> - </item> - </list> - </section> - -</section> - <section><title>SASL 2.4.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk index 8d1a043410..4259a2d76c 100644 --- a/lib/sasl/vsn.mk +++ b/lib/sasl/vsn.mk @@ -1 +1 @@ -SASL_VSN = 2.4.2 +SASL_VSN = 2.4.1 diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index af5b78bff2..579a3ae4a8 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,66 +29,19 @@ <file>notes.xml</file> </header> -<section><title>Ssh 4.0</title> +<section><title>Ssh 3.2.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> <item> <p> - Included test of the 'e' and 'f' parameters in dh key - exchange as specified in rfc 4253 section 8.</p> + A new option for handling the SSH_MSG_DEBUG message's + printouts. A fun could be given in the options that will + be called whenever the SSH_MSG_DEBUG message arrives. + This enables the user to format the printout or just + discard it.</p> <p> - Own Id: OTP-12649</p> - </item> - <item> - <p> - Fixes the bug that once the rekey_limit bytes (by - default, 1GB) had been transmitted the connection was - rekeyed every minute, not after the next 'rekey_limit'.</p> - <p> - Thanks to Simon Cornish for the report and the fix!</p> - <p> - Own Id: OTP-12692</p> - </item> - <item> - <p> - Fixes a bug that causes an SFTP connection to always fail - when {timeout, Timeout} option is used with - ssh_sftp:start_channel.</p> - <p> - Thanks to Simon Cornish</p> - <p> - Own Id: OTP-12708</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The internal group to user_drv protocol has been changed - to be synchronous in order to guarantee that output sent - to a process implementing the user_drv protocol is - printed before replying. This protocol is used by the - standard_output device and the ssh application when - acting as a client. </p> - <p> - This change changes the previous unlimited buffer when - printing to standard_io and other devices that end up in - user_drv to 1KB.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12240</p> - </item> - <item> - <p> - If ssh_connection:subsystem/4 fails we do not want to - crash but rather terminate gracefully.</p> - <p> - Own Id: OTP-12648 Aux Id: seq12834 </p> + Own Id: OTP-12738 Aux Id: seq12860 </p> </item> </list> </section> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index d49d3ac2a7..df13442fc6 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -57,29 +57,28 @@ this module, or abstractions to indicate the intended use of the data type, or both:</p> <taglist> - <tag><c>boolean()</c></tag> - <item><p>= <c>true | false</c></p></item> - <tag><c>string()</c></tag> - <item><p>= <c>[byte()]</c></p></item> - <tag><c>ssh_daemon_ref()</c></tag> - <item><p>Opaque to the user, - returned by <c>ssh:daemon/[1,2,3]</c></p></item> - <tag><c>ssh_connection_ref()</c></tag> - <item><p>Opaque to the user, - returned by <c>ssh:connect/3</c></p></item> - <tag><c>ip_address()</c></tag> + <tag><c>boolean() =</c></tag> + <item><p><c>true | false</c></p></item> + <tag><c>string() =</c></tag> + <item><p><c>[byte()]</c></p></item> + <tag><c>ssh_daemon_ref() =</c></tag> + <item><p>opaque() - + as returned by <c>ssh:daemon/[1,2,3]</c></p></item> + <tag><c>ssh_connection_ref() =</c></tag> + <item><p>opaque() - as returned by <c>ssh:connect/3</c></p></item> + <tag><c>ip_address() =</c></tag> <item><p><c>inet::ip_address</c></p></item> - <tag><c>subsystem_spec()</c></tag> - <item><p>= <c>{subsystem_name(), - {channel_callback(), channel_init_args()}}</c></p></item> - <tag><c>subsystem_name()</c></tag> - <item><p>= <c>string()</c></p></item> - <tag><c>channel_callback()</c></tag> - <item><p>= <c>atom()</c> - Name of the Erlang module - implementing the subsystem using the <c>ssh_channel</c> behavior, see - <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item> - <tag><c>channel_init_args()</c></tag> - <item><p>= <c>list()</c></p></item> + <tag><c>subsystem_spec() =</c></tag> + <item><p><c>{subsystem_name(), + {channel_callback(), channel_init_args()}}</c></p></item> + <tag><c>subsystem_name() =</c></tag> + <item><p><c>string()</c></p></item> + <tag><c>channel_callback() =</c></tag> + <item><p><c>atom()</c> - Name of the Erlang module + implementing the subsystem using the <c>ssh_channel</c> behavior, see + <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item> + <tag><c>channel_init_args() =</c></tag> + <item><p><c>list()</c></p></item> </taglist> </section> @@ -227,6 +226,13 @@ <item> <p>Sets a time-out on a connection when no channels are active. Defaults to <c>infinity</c>.</p></item> + <tag><c><![CDATA[{ssh_msg_debug_fun, fun(ConnectionRef::ssh_connection_ref(), AlwaysDisplay::boolean(), Msg::binary(), LanguageTag::binary()) -> _}]]></c></tag> + <item> + <p>Provide a fun to implement your own logging of the SSH message SSH_MSG_DEBUG. The last three parameters are from the message, see RFC4253, section 11.3. The <c>ConnectionRef</c> is the reference to the connection on which the message arrived. The return value from the fun is not checked.</p> + <p>The default behaviour is ignore the message. + To get a printout for each message with <c>AlwaysDisplay = true</c>, use for example <c>{ssh_msg_debug_fun, fun(_,true,M,_)-> io:format("DEBUG: ~p~n", [M]) end}</c></p> + </item> + </taglist> </desc> </func> @@ -427,8 +433,16 @@ <item> <p>Provides a fun to implement your own logging when a user disconnects from the server.</p> </item> - </taglist> - </desc> + + <tag><c><![CDATA[{ssh_msg_debug_fun, fun(ConnectionRef::ssh_connection_ref(), AlwaysDisplay::boolean(), Msg::binary(), LanguageTag::binary()) -> _}]]></c></tag> + <item> + <p>Provide a fun to implement your own logging of the SSH message SSH_MSG_DEBUG. The last three parameters are from the message, see RFC4253, section 11.3. The <c>ConnectionRef</c> is the reference to the connection on which the message arrived. The return value from the fun is not checked.</p> + <p>The default behaviour is ignore the message. + To get a printout for each message with <c>AlwaysDisplay = true</c>, use for example <c>{ssh_msg_debug_fun, fun(_,true,M,_)-> io:format("DEBUG: ~p~n", [M]) end}</c></p> + </item> + + </taglist> + </desc> </func> diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml index b8a03c350a..2fdecf9072 100644 --- a/lib/ssh/doc/src/ssh_channel.xml +++ b/lib/ssh/doc/src/ssh_channel.xml @@ -62,22 +62,22 @@ type, or both:</p> <taglist> - <tag><c>boolean()</c></tag> - <item><p>= <c>true | false</c></p></item> - <tag><c>string()</c></tag> - <item><p>= list of ASCII characters</p></item> - <tag><c>timeout()</c></tag> - <item><p>= <c>infinity | integer()</c> in milliseconds</p></item> - <tag><c>ssh_connection_ref()</c></tag> - <item><p>Opaque to the user, returned by - <c>ssh:connect/3</c> or sent to an SSH channel process</p></item> - <tag><c>ssh_channel_id()</c></tag> - <item><p>= <c>integer()</c></p></item> - <tag><c>ssh_data_type_code()</c></tag> - <item><p>= <c>1</c> ("stderr") | <c>0</c> ("normal") are - the valid values, - see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> - Section 5.2</p></item> + <tag><c>boolean() =</c></tag> + <item><p><c>true | false</c></p></item> + <tag><c>string() =</c></tag> + <item><p>list of ASCII characters</p></item> + <tag><c>timeout() =</c></tag> + <item><p><c>infinity | integer()</c> in milliseconds</p></item> + <tag><c>ssh_connection_ref() =</c></tag> + <item><p>opaque() -as returned by + <c>ssh:connect/3</c> or sent to an SSH channel process</p></item> + <tag><c>ssh_channel_id() =</c></tag> + <item><p><c>integer()</c></p></item> + <tag><c>ssh_data_type_code() =</c></tag> + <item><p><c>1</c> ("stderr") | <c>0</c> ("normal") are + the valid values, + see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> + Section 5.2</p></item> </taglist> </section> diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml index a8dda042c9..9a892d71fd 100644 --- a/lib/ssh/doc/src/ssh_client_key_api.xml +++ b/lib/ssh/doc/src/ssh_client_key_api.xml @@ -50,16 +50,16 @@ <seealso marker="public_key:public_key_records"> public_key user's guide:</seealso> </p> <taglist> - <tag><c>boolean()</c></tag> - <item><p>= <c>true | false</c></p></item> - <tag><c>string()</c></tag> - <item><p>= <c>[byte()]</c></p></item> - <tag><c>public_key()</c></tag> - <item><p>= <c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item> - <tag><c>private_key()</c></tag> - <item><p>= <c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item> - <tag><c>public_key_algorithm()</c></tag> - <item><p>= <c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item> + <tag><c>boolean() =</c></tag> + <item><p><c>true | false</c></p></item> + <tag><c>string() =</c></tag> + <item><p><c>[byte()]</c></p></item> + <tag><c>public_key() =</c></tag> + <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item> + <tag><c>private_key() =</c></tag> + <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item> + <tag><c>public_key_algorithm() =</c></tag> + <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item> </taglist> </section> diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml index 669a361db9..5422633dc3 100644 --- a/lib/ssh/doc/src/ssh_connection.xml +++ b/lib/ssh/doc/src/ssh_connection.xml @@ -56,29 +56,29 @@ type, or both:</p> <taglist> - <tag><c>boolean()</c></tag> - <item><p>= <c>true | false </c></p></item> - <tag><c>string()</c></tag> - <item><p>= list of ASCII characters</p></item> - <tag><c>timeout()</c></tag> - <item><p>= <c>infinity | integer()</c> in milliseconds</p></item> - <tag><c>ssh_connection_ref()</c></tag> - <item><p>Opaque to the user, returned by - <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item> - <tag><c>ssh_channel_id()</c></tag> - <item><p>= <c>integer()</c></p></item> - <tag><c>ssh_data_type_code()</c></tag> - <item><p>= <c>1</c> ("stderr") | <c>0</c> ("normal") are + <tag><c>boolean() =</c></tag> + <item><p><c>true | false </c></p></item> + <tag><c>string() =</c></tag> + <item><p>list of ASCII characters</p></item> + <tag><c>timeout() =</c></tag> + <item><p><c>infinity | integer()</c> in milliseconds</p></item> + <tag><c>ssh_connection_ref() =</c></tag> + <item><p>opaque() -as returned by + <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item> + <tag><c>ssh_channel_id() =</c></tag> + <item><p><c>integer()</c></p></item> + <tag><c>ssh_data_type_code() =</c></tag> + <item><p><c>1</c> ("stderr") | <c>0</c> ("normal") are valid values, see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> Section 5.2.</p></item> - <tag><c>ssh_request_status() ssh_request_status()</c></tag> - <item><p>= <c>success | failure</c></p></item> - <tag><c>event()</c></tag> - <item><p>= <c>{ssh_cm, ssh_connection_ref(), ssh_event_msg()}</c></p></item> - <tag><c>ssh_event_msg()</c></tag> - <item><p>= <c>data_events() | status_events() | terminal_events()</c></p></item> - <tag><c>reason()</c></tag> - <item><p>= <c>timeout | closed</c></p></item> + <tag><c>ssh_request_status() =</c></tag> + <item><p> <c>success | failure</c></p></item> + <tag><c>event() =</c></tag> + <item><p><c>{ssh_cm, ssh_connection_ref(), ssh_event_msg()}</c></p></item> + <tag><c>ssh_event_msg() =</c></tag> + <item><p><c>data_events() | status_events() | terminal_events()</c></p></item> + <tag><c>reason() =</c></tag> + <item><p><c>timeout | closed</c></p></item> </taglist> <taglist> diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml index 34ce7f7660..73dd90c962 100644 --- a/lib/ssh/doc/src/ssh_server_key_api.xml +++ b/lib/ssh/doc/src/ssh_server_key_api.xml @@ -50,20 +50,20 @@ <seealso marker="public_key:public_key_records"> public_key user's guide</seealso>. </p> -<taglist> - <tag><c>boolean()</c></tag> - <item><p>= <c>true | false</c></p></item> - <tag><c>string()</c></tag> - <item><p>= <c>[byte()]</c></p></item> - <tag><c>public_key()</c></tag> - <item><p>= <c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item> - <tag><c>private_key()</c></tag> - <item><p>= <c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item> - <tag><c>public_key_algorithm()</c></tag> - <item><p>= <c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item> + <taglist> + <tag><c>boolean() =</c></tag> + <item><p><c>true | false</c></p></item> + <tag><c>string() =</c></tag> + <item><p><c>[byte()]</c></p></item> + <tag><c>public_key() =</c></tag> + <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item> + <tag><c>private_key() =</c></tag> + <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item> + <tag><c>public_key_algorithm() =</c></tag> + <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item> </taglist> </section> - + <funcs> <func> <name>Module:host_key(Algorithm, DaemonOptions) -> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index 643130fe6b..fc418bc934 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -43,8 +43,8 @@ </p> <taglist> - <tag><c>ssh_connection_ref()</c></tag> - <item><p>Opaque to the user, returned by <c>ssh:connect/3</c></p></item> + <tag><c>ssh_connection_ref() =</c></tag> + <item><p>opaque() - as returned by <c>ssh:connect/3</c></p></item> <tag><c>timeout()</c></tag> <item><p>= <c>infinity | integer() in milliseconds. Default infinity.</c></p></item> </taglist> diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml index bc2660f595..8b2497e6a3 100644 --- a/lib/ssh/doc/src/ssh_sftpd.xml +++ b/lib/ssh/doc/src/ssh_sftpd.xml @@ -37,16 +37,16 @@ <section> <title>DATA TYPES</title> <taglist> - <tag><c>subsystem_spec()</c></tag> - <item><p>= <c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item> - <tag><c>subsystem_name()</c></tag> - <item><p>= <c>"sftp"</c></p></item> - <tag><c>channel_callback()</c></tag> - <item><p>= <c>atom()</c> - Name of the Erlang module implementing the subsystem using the + <tag><c>subsystem_spec() =</c></tag> + <item><p><c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item> + <tag><c>subsystem_name() =</c></tag> + <item><p><c>"sftp"</c></p></item> + <tag><c>channel_callback() =</c></tag> + <item><p><c>atom()</c> - Name of the Erlang module implementing the subsystem using the <c>ssh_channel</c> behavior, see the <seealso marker="ssh_channel">ssh_channel(3)</seealso> manual page.</p></item> - <tag><c>channel_init_args()</c></tag> - <item><p>= <c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item> + <tag><c>channel_init_args() =</c></tag> + <item><p><c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item> </taglist> </section> <funcs> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index d4b02a024e..71e7d77475 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -312,6 +312,8 @@ handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{ssh_msg_debug_fun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); %%Backwards compatibility should not be underscore between ip and v6 in API handle_option([{ip_v6_disabled, Value} | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option({ipv6_disabled, Value}) | SshOptions]); @@ -417,6 +419,8 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) -> Opt; handle_ssh_option({failfun, Value} = Opt) when is_function(Value) -> Opt; +handle_ssh_option({ssh_msg_debug_fun, Value} = Opt) when is_function(Value,4) -> + Opt; handle_ssh_option({ipv6_disabled, Value} = Opt) when is_boolean(Value) -> throw({error, {{ipv6_disabled, Opt}, option_no_longer_valid_use_inet_option_instead}}); diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 4dea284071..65208ae158 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -71,6 +71,7 @@ key_exchange_init_msg, % #ssh_msg_kexinit{} renegotiate = false, % boolean() last_size_rekey = 0, + event_queue = [], connection_queue, address, port, @@ -83,6 +84,11 @@ {next_state, state_name(), term(), timeout()} | {stop, term(), term()}. +-type gen_fsm_sync_return() :: {next_state, state_name(), term()} | + {next_state, state_name(), term(), timeout()} | + {reply, term(), state_name(), term()} | + {stop, term(), term(), term()}. + %%==================================================================== %% Internal application API %%==================================================================== @@ -433,9 +439,7 @@ key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg, new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) -> {ok, Ssh} = ssh_transport:handle_new_keys(Msg, Ssh0), - {NextStateName, State} = - after_new_keys(State0#state{ssh_params = Ssh}), - {next_state, NextStateName, next_packet(State)}. + after_new_keys(next_packet(State0#state{ssh_params = Ssh})). %%-------------------------------------------------------------------- -spec userauth(#ssh_msg_service_request{} | #ssh_msg_service_accept{} | @@ -559,11 +563,13 @@ userauth(#ssh_msg_userauth_banner{message = Msg}, -spec connected({#ssh_msg_kexinit{}, binary()}, %%| %% #ssh_msg_kexdh_init{}, #state{}) -> gen_fsm_state_return(). %%-------------------------------------------------------------------- -connected({#ssh_msg_kexinit{}, _Payload} = Event, State) -> - kexinit(Event, State#state{renegotiate = true}). -%% ; -%% connected(#ssh_msg_kexdh_init{} = Event, State) -> -%% key_exchange(Event, State#state{renegotiate = true}). +connected({#ssh_msg_kexinit{}, _Payload} = Event, #state{ssh_params = Ssh0} = State0) -> + {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0), + State = State0#state{ssh_params = Ssh, + key_exchange_init_msg = KeyInitMsg, + renegotiate = true}, + send_msg(SshPacket, State), + kexinit(Event, State). %%-------------------------------------------------------------------- -spec handle_event(#ssh_msg_disconnect{} | #ssh_msg_ignore{} | #ssh_msg_debug{} | @@ -581,44 +587,17 @@ handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName handle_event(#ssh_msg_ignore{}, StateName, State) -> {next_state, StateName, next_packet(State)}; -handle_event(#ssh_msg_debug{always_display = true, message = DbgMsg}, - StateName, State) -> - io:format("DEBUG: ~p\n", [DbgMsg]), - {next_state, StateName, next_packet(State)}; - -handle_event(#ssh_msg_debug{}, StateName, State) -> +handle_event(#ssh_msg_debug{always_display = Display, message = DbgMsg, language=Lang}, + StateName, #state{opts = Opts} = State) -> + F = proplists:get_value(ssh_msg_debug_fun, Opts, + fun(_ConnRef, _AlwaysDisplay, _Msg, _Language) -> ok end + ), + catch F(self(), Display, DbgMsg, Lang), {next_state, StateName, next_packet(State)}; handle_event(#ssh_msg_unimplemented{}, StateName, State) -> {next_state, StateName, next_packet(State)}; -handle_event({adjust_window, ChannelId, Bytes}, StateName, - #state{connection_state = - #connection{channel_cache = Cache}} = State0) -> - State = - case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{recv_window_size = WinSize, remote_id = Id} = Channel -> - ssh_channel:cache_update(Cache, Channel#channel{recv_window_size = - WinSize + Bytes}), - Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes), - send_replies([{connection_reply, Msg}], State0); - undefined -> - State0 - end, - {next_state, StateName, next_packet(State)}; - -handle_event({reply_request, success, ChannelId}, StateName, - #state{connection_state = - #connection{channel_cache = Cache}} = State0) -> - State = case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{remote_id = RemoteId} -> - Msg = ssh_connection:channel_success_msg(RemoteId), - send_replies([{connection_reply, Msg}], State0); - undefined -> - State0 - end, - {next_state, StateName, State}; - handle_event(renegotiate, connected, #state{ssh_params = Ssh0} = State) -> {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0), @@ -630,8 +609,7 @@ handle_event(renegotiate, connected, #state{ssh_params = Ssh0} renegotiate = true})}; handle_event(renegotiate, StateName, State) -> - timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]), - %% Allready in keyexcahange so ignore + %% Already in key-exchange so safe to ignore {next_state, StateName, State}; %% Rekey due to sent data limit reached? @@ -653,6 +631,38 @@ handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) -> {next_state, connected, next_packet(State)} end; handle_event(data_size, StateName, State) -> + %% Already in key-exchange so safe to ignore + {next_state, StateName, State}; + +handle_event(Event, StateName, State) when StateName /= connected -> + Events = [{event, Event} | State#state.event_queue], + {next_state, StateName, State#state{event_queue = Events}}; + +handle_event({adjust_window, ChannelId, Bytes}, StateName, + #state{connection_state = + #connection{channel_cache = Cache}} = State0) -> + State = + case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{recv_window_size = WinSize, remote_id = Id} = Channel -> + ssh_channel:cache_update(Cache, Channel#channel{recv_window_size = + WinSize + Bytes}), + Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes), + send_replies([{connection_reply, Msg}], State0); + undefined -> + State0 + end, + {next_state, StateName, next_packet(State)}; + +handle_event({reply_request, success, ChannelId}, StateName, + #state{connection_state = + #connection{channel_cache = Cache}} = State0) -> + State = case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{remote_id = RemoteId} -> + Msg = ssh_connection:channel_success_msg(RemoteId), + send_replies([{connection_reply, Msg}], State0); + undefined -> + State0 + end, {next_state, StateName, State}; handle_event({request, ChannelPid, ChannelId, Type, Data}, StateName, State0) -> @@ -683,8 +693,65 @@ handle_event({unknown, Data}, StateName, State) -> sockname]} | {channel_info, channel_id(), [recv_window | send_window]} | {close, channel_id()} | stop, term(), state_name(), #state{}) - -> gen_fsm_state_return(). + -> gen_fsm_sync_return(). %%-------------------------------------------------------------------- +handle_sync_event(get_print_info, _From, StateName, State) -> + Reply = + try + {inet:sockname(State#state.socket), + inet:peername(State#state.socket) + } + of + {{ok,Local}, {ok,Remote}} -> {{Local,Remote},io_lib:format("statename=~p",[StateName])}; + _ -> {{"-",0},"-"} + catch + _:_ -> {{"?",0},"?"} + end, + {reply, Reply, StateName, State}; + +handle_sync_event({connection_info, Options}, _From, StateName, State) -> + Info = ssh_info(Options, State, []), + {reply, Info, StateName, State}; + +handle_sync_event({channel_info, ChannelId, Options}, _From, StateName, + #state{connection_state = #connection{channel_cache = Cache}} = State) -> + case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{} = Channel -> + Info = ssh_channel_info(Options, Channel, []), + {reply, Info, StateName, State}; + undefined -> + {reply, [], StateName, State} + end; + +handle_sync_event({info, ChannelPid}, _From, StateName, + #state{connection_state = + #connection{channel_cache = Cache}} = State) -> + Result = ssh_channel:cache_foldl( + fun(Channel, Acc) when ChannelPid == all; + Channel#channel.user == ChannelPid -> + [Channel | Acc]; + (_, Acc) -> + Acc + end, [], Cache), + {reply, {ok, Result}, StateName, State}; + +handle_sync_event(stop, _, _StateName, #state{connection_state = Connection0, + role = Role, + opts = Opts} = State0) -> + {disconnect, Reason, {{replies, Replies}, Connection}} = + ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, + description = "User closed down connection", + language = "en"}, Connection0, Role), + State = send_replies(Replies, State0), + SSHOpts = proplists:get_value(ssh_opts, Opts), + disconnect_fun(Reason, SSHOpts), + {stop, normal, ok, State#state{connection_state = Connection}}; + + +handle_sync_event(Event, From, StateName, State) when StateName /= connected -> + Events = [{sync, Event, From} | State#state.event_queue], + {next_state, StateName, State#state{event_queue = Events}}; + handle_sync_event({request, ChannelPid, ChannelId, Type, Data, Timeout}, From, StateName, State0) -> {{replies, Replies}, State1} = handle_request(ChannelPid, ChannelId, Type, Data, @@ -787,46 +854,6 @@ handle_sync_event({recv_window, ChannelId}, _From, StateName, end, {reply, Reply, StateName, next_packet(State)}; -handle_sync_event(get_print_info, _From, StateName, State) -> - Reply = - try - {inet:sockname(State#state.socket), - inet:peername(State#state.socket) - } - of - {{ok,Local}, {ok,Remote}} -> {{Local,Remote},io_lib:format("statename=~p",[StateName])}; - _ -> {{"-",0},"-"} - catch - _:_ -> {{"?",0},"?"} - end, - {reply, Reply, StateName, State}; - -handle_sync_event({connection_info, Options}, _From, StateName, State) -> - Info = ssh_info(Options, State, []), - {reply, Info, StateName, State}; - -handle_sync_event({channel_info, ChannelId, Options}, _From, StateName, - #state{connection_state = #connection{channel_cache = Cache}} = State) -> - case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{} = Channel -> - Info = ssh_channel_info(Options, Channel, []), - {reply, Info, StateName, State}; - undefined -> - {reply, [], StateName, State} - end; - -handle_sync_event({info, ChannelPid}, _From, StateName, - #state{connection_state = - #connection{channel_cache = Cache}} = State) -> - Result = ssh_channel:cache_foldl( - fun(Channel, Acc) when ChannelPid == all; - Channel#channel.user == ChannelPid -> - [Channel | Acc]; - (_, Acc) -> - Acc - end, [], Cache), - {reply, {ok, Result}, StateName, State}; - handle_sync_event({close, ChannelId}, _, StateName, #state{connection_state = #connection{channel_cache = Cache}} = State0) -> @@ -841,19 +868,7 @@ handle_sync_event({close, ChannelId}, _, StateName, undefined -> State0 end, - {reply, ok, StateName, next_packet(State)}; - -handle_sync_event(stop, _, _StateName, #state{connection_state = Connection0, - role = Role, - opts = Opts} = State0) -> - {disconnect, Reason, {{replies, Replies}, Connection}} = - ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, - description = "User closed down connection", - language = "en"}, Connection0, Role), - State = send_replies(Replies, State0), - SSHOpts = proplists:get_value(ssh_opts, Opts), - disconnect_fun(Reason, SSHOpts), - {stop, normal, ok, State#state{connection_state = Connection}}. + {reply, ok, StateName, next_packet(State)}. %%-------------------------------------------------------------------- -spec handle_info({atom(), port(), binary()} | {atom(), port()} | @@ -1282,8 +1297,17 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName, ConnectionMsg = ssh_message:decode(Msg), State1 = generate_event_new_state(State0, EncData), try ssh_connection:handle_msg(ConnectionMsg, Connection0, Role) of - {{replies, Replies}, Connection} -> - State = send_replies(Replies, State1#state{connection_state = Connection}), + {{replies, Replies0}, Connection} -> + if StateName == connected -> + Replies = Replies0, + State2 = State1; + true -> + {ConnReplies, Replies} = + lists:splitwith(fun not_connected_filter/1, Replies0), + Q = State1#state.event_queue ++ ConnReplies, + State2 = State1#state{ event_queue = Q } + end, + State = send_replies(Replies, State2#state{connection_state = Connection}), {next_state, StateName, next_packet(State)}; {noreply, Connection} -> {next_state, StateName, next_packet(State1#state{connection_state = Connection})}; @@ -1456,15 +1480,43 @@ next_packet(#state{socket = Socket} = State) -> State. after_new_keys(#state{renegotiate = true} = State) -> - {connected, State#state{renegotiate = false}}; + State1 = State#state{renegotiate = false, event_queue = []}, + lists:foldr(fun after_new_keys_events/2, {next_state, connected, State1}, State#state.event_queue); after_new_keys(#state{renegotiate = false, ssh_params = #ssh{role = client} = Ssh0} = State) -> {Msg, Ssh} = ssh_auth:service_request_msg(Ssh0), send_msg(Msg, State), - {userauth, State#state{ssh_params = Ssh}}; + {next_state, userauth, State#state{ssh_params = Ssh}}; after_new_keys(#state{renegotiate = false, ssh_params = #ssh{role = server}} = State) -> - {userauth, State}. + {next_state, userauth, State}. + +after_new_keys_events({sync, _Event, From}, {stop, _Reason, _StateData}=Terminator) -> + gen_fsm:reply(From, {error, closed}), + Terminator; +after_new_keys_events(_, {stop, _Reason, _StateData}=Terminator) -> + Terminator; +after_new_keys_events({sync, Event, From}, {next_state, StateName, StateData}) -> + case handle_sync_event(Event, From, StateName, StateData) of + {reply, Reply, NextStateName, NewStateData} -> + gen_fsm:reply(From, Reply), + {next_state, NextStateName, NewStateData}; + {next_state, NextStateName, NewStateData}-> + {next_state, NextStateName, NewStateData}; + {stop, Reason, Reply, NewStateData} -> + gen_fsm:reply(From, Reply), + {stop, Reason, NewStateData} + end; +after_new_keys_events({event, Event}, {next_state, StateName, StateData}) -> + case handle_event(Event, StateName, StateData) of + {next_state, NextStateName, NewStateData}-> + {next_state, NextStateName, NewStateData}; + {stop, Reason, NewStateData} -> + {stop, Reason, NewStateData} + end; +after_new_keys_events({connection_reply, _Data} = Reply, {StateName, State}) -> + NewState = send_replies([Reply], State), + {next_state, StateName, NewState}. handle_ssh_packet_data(RemainingSshPacketLen, DecData, EncData, StateName, State) -> @@ -1625,6 +1677,11 @@ log_error(Reason) -> error_logger:error_report(Report), "Internal error". +not_connected_filter({connection_reply, _Data}) -> + true; +not_connected_filter(_) -> + false. + send_replies([], State) -> State; send_replies([{connection_reply, Data} | Rest], #state{ssh_params = Ssh0} = State) -> diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 740dbd0235..39b2f57d26 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -40,7 +40,8 @@ MODULES= \ ssh_connection_SUITE \ ssh_echo_server \ ssh_peername_sockname_server \ - ssh_test_cli + ssh_test_cli \ + ssh_relay HRL_FILES_NEEDED_IN_TEST= \ $(ERL_TOP)/lib/ssh/src/ssh.hrl \ diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index d55d09f2a2..aaf0fa9905 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -29,6 +29,7 @@ -define(NEWLINE, <<"\r\n">>). +-define(REKEY_DATA_TMO, 65000). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -44,6 +45,7 @@ all() -> {group, dsa_pass_key}, {group, rsa_pass_key}, {group, internal_error}, + {group, renegotiate}, daemon_already_started, server_password_option, server_userpassword_option, @@ -52,6 +54,8 @@ all() -> ssh_connect_arg4_timeout, packet_size_zero, ssh_daemon_minimal_remote_max_packet_size_option, + ssh_msg_debug_fun_option_client, + ssh_msg_debug_fun_option_server, id_string_no_opt_client, id_string_own_string_client, id_string_random_client, @@ -67,6 +71,7 @@ groups() -> {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {internal_error, [], [internal_error]}, + {renegotiate, [], [rekey, rekey_limit, renegotiate1, renegotiate2]}, {hardening_tests, [], [ssh_connect_nonegtimeout_connected_parallel, ssh_connect_nonegtimeout_connected_sequential, ssh_connect_negtimeout_parallel, @@ -82,8 +87,7 @@ groups() -> basic_tests() -> [send, close, peername_sockname, exec, exec_compressed, shell, cli, known_hosts, - idle_time, rekey, openssh_zlib_basic_test, - misc_ssh_options, inet_option]. + idle_time, openssh_zlib_basic_test, misc_ssh_options, inet_option]. %%-------------------------------------------------------------------- @@ -331,25 +335,175 @@ idle_time(Config) -> rekey() -> [{doc, "Idle timeout test"}]. rekey(Config) -> - SystemDir = filename:join(?config(priv_dir, Config), system), + SystemDir = ?config(data_dir, Config), UserDir = ?config(priv_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, + {user_dir, UserDir}, {failfun, fun ssh_test_lib:failfun/2}, + {user_passwords, + [{"simon", "says"}]}, {rekey_limit, 0}]), + ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, + {user, "simon"}, + {password, "says"}, {user_interaction, false}, {rekey_limit, 0}]), receive - after 200000 -> + after ?REKEY_DATA_TMO -> %%By this time rekeying would have been done ssh:close(ConnectionRef), ssh:stop_daemon(Pid) end. %%-------------------------------------------------------------------- +rekey_limit() -> + [{doc, "Test rekeying by data volume"}]. +rekey_limit(Config) -> + SystemDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + DataFile = filename:join(UserDir, "rekey.data"), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, + [{"simon", "says"}]}]), + {ok, SftpPid, ConnectionRef} = + ssh_sftp:start_channel(Host, Port, [{system_dir, SystemDir}, + {user_dir, UserDir}, + {user, "simon"}, + {password, "says"}, + {rekey_limit, 2500}, + {user_interaction, false}, + {silently_accept_hosts, true}]), + + Kex1 = get_kex_init(ConnectionRef), + + ct:sleep(?REKEY_DATA_TMO), + Kex1 = get_kex_init(ConnectionRef), + + Data = lists:duplicate(9000,1), + ok = ssh_sftp:write_file(SftpPid, DataFile, Data), + + ct:sleep(?REKEY_DATA_TMO), + Kex2 = get_kex_init(ConnectionRef), + + false = (Kex2 == Kex1), + + ct:sleep(?REKEY_DATA_TMO), + Kex2 = get_kex_init(ConnectionRef), + + ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"), + + ct:sleep(?REKEY_DATA_TMO), + Kex2 = get_kex_init(ConnectionRef), + + false = (Kex2 == Kex1), + + ct:sleep(?REKEY_DATA_TMO), + Kex2 = get_kex_init(ConnectionRef), + + + ssh_sftp:stop_channel(SftpPid), + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +renegotiate1() -> + [{doc, "Test rekeying with simulataneous send request"}]. +renegotiate1(Config) -> + SystemDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + DataFile = filename:join(UserDir, "renegotiate1.data"), + + {Pid, Host, DPort} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, + [{"simon", "says"}]}]), + RPort = ssh_test_lib:inet_port(), + + {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort), + + {ok, SftpPid, ConnectionRef} = + ssh_sftp:start_channel(Host, RPort, [{system_dir, SystemDir}, + {user_dir, UserDir}, + {user, "simon"}, + {password, "says"}, + {user_interaction, false}, + {silently_accept_hosts, true}]), + + Kex1 = get_kex_init(ConnectionRef), + + {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]), + + ok = ssh_sftp:write(SftpPid, Handle, "hi\n"), + + ssh_relay:hold(RelayPid, rx, 20, 1000), + ssh_connection_handler:renegotiate(ConnectionRef), + spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end), + + ct:sleep(2000), + + Kex2 = get_kex_init(ConnectionRef), + + false = (Kex2 == Kex1), + + ssh_relay:stop(RelayPid), + ssh_sftp:stop_channel(SftpPid), + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +renegotiate2() -> + [{doc, "Test rekeying with inflight messages from peer"}]. +renegotiate2(Config) -> + SystemDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + DataFile = filename:join(UserDir, "renegotiate1.data"), + + {Pid, Host, DPort} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, + [{"simon", "says"}]}]), + RPort = ssh_test_lib:inet_port(), + + {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort), + + {ok, SftpPid, ConnectionRef} = + ssh_sftp:start_channel(Host, RPort, [{system_dir, SystemDir}, + {user_dir, UserDir}, + {user, "simon"}, + {password, "says"}, + {user_interaction, false}, + {silently_accept_hosts, true}]), + + Kex1 = get_kex_init(ConnectionRef), + + {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]), + + ok = ssh_sftp:write(SftpPid, Handle, "hi\n"), + + ssh_relay:hold(RelayPid, rx, 20, infinity), + spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end), + %% need a small pause here to ensure ssh_sftp:write is executed + ct:sleep(10), + ssh_connection_handler:renegotiate(ConnectionRef), + ssh_relay:release(RelayPid, rx), + + ct:sleep(2000), + + Kex2 = get_kex_init(ConnectionRef), + + false = (Kex2 == Kex1), + + ssh_relay:stop(RelayPid), + ssh_sftp:stop_channel(SftpPid), + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- shell() -> [{doc, "Test that ssh:shell/2 works"}]. shell(Config) when is_list(Config) -> @@ -494,6 +648,94 @@ server_userpassword_option(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +ssh_msg_debug_fun_option_client() -> + [{doc, "validate client that uses the 'ssh_msg_debug_fun' option"}]. +ssh_msg_debug_fun_option_client(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}, + {failfun, fun ssh_test_lib:failfun/2}]), + Parent = self(), + DbgFun = fun(ConnRef,Displ,Msg,Lang) -> Parent ! {msg_dbg,{ConnRef,Displ,Msg,Lang}} end, + + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_dir, UserDir}, + {user_interaction, false}, + {ssh_msg_debug_fun,DbgFun}]), + %% Beware, implementation knowledge: + gen_fsm:send_all_state_event(ConnectionRef,{ssh_msg_debug,false,<<"Hello">>,<<>>}), + receive + {msg_dbg,X={ConnectionRef,false,<<"Hello">>,<<>>}} -> + ct:log("Got expected dbg msg ~p",[X]), + ssh:stop_daemon(Pid); + {msg_dbg,X={_,false,<<"Hello">>,<<>>}} -> + ct:log("Got dbg msg but bad ConnectionRef (~p expected) ~p",[ConnectionRef,X]), + ssh:stop_daemon(Pid), + {fail, "Bad ConnectionRef received"}; + {msg_dbg,X} -> + ct:log("Got bad dbg msg ~p",[X]), + ssh:stop_daemon(Pid), + {fail,"Bad msg received"} + after 1000 -> + ssh:stop_daemon(Pid), + {fail,timeout} + end. + +%%-------------------------------------------------------------------- +ssh_msg_debug_fun_option_server() -> + [{doc, "validate client that uses the 'ssh_msg_debug_fun' option"}]. +ssh_msg_debug_fun_option_server(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + + Parent = self(), + DbgFun = fun(ConnRef,Displ,Msg,Lang) -> Parent ! {msg_dbg,{ConnRef,Displ,Msg,Lang}} end, + ConnFun = fun(_,_,_) -> Parent ! {connection_pid,self()} end, + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}, + {failfun, fun ssh_test_lib:failfun/2}, + {connectfun, ConnFun}, + {ssh_msg_debug_fun, DbgFun}]), + _ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_dir, UserDir}, + {user_interaction, false}]), + receive + {connection_pid,Server} -> + %% Beware, implementation knowledge: + gen_fsm:send_all_state_event(Server,{ssh_msg_debug,false,<<"Hello">>,<<>>}), + receive + {msg_dbg,X={_,false,<<"Hello">>,<<>>}} -> + ct:log("Got expected dbg msg ~p",[X]), + ssh:stop_daemon(Pid); + {msg_dbg,X} -> + ct:log("Got bad dbg msg ~p",[X]), + ssh:stop_daemon(Pid), + {fail,"Bad msg received"} + after 3000 -> + ssh:stop_daemon(Pid), + {fail,timeout2} + end + after 3000 -> + ssh:stop_daemon(Pid), + {fail,timeout1} + end. + +%%-------------------------------------------------------------------- known_hosts() -> [{doc, "check that known_hosts is updated correctly"}]. known_hosts(Config) when is_list(Config) -> @@ -1210,3 +1452,18 @@ fake_daemon(_Config) -> {sockname,Server,ServerHost,ServerPort} -> {Server, ServerHost, ServerPort} end. +%% get_kex_init - helper function to get key_exchange_init_msg +get_kex_init(Conn) -> + %% First, validate the key exchange is complete (StateName == connected) + {connected,S} = sys:get_state(Conn), + %% Next, walk through the elements of the #state record looking + %% for the #ssh_msg_kexinit record. This method is robust against + %% changes to either record. The KEXINIT message contains a cookie + %% unique to each invocation of the key exchange procedure (RFC4253) + SL = tuple_to_list(S), + case lists:keyfind(ssh_msg_kexinit, 1, SL) of + false -> + throw(not_found); + KexInit -> + KexInit + end. diff --git a/lib/ssh/test/ssh_relay.erl b/lib/ssh/test/ssh_relay.erl new file mode 100644 index 0000000000..a4f2bad2e2 --- /dev/null +++ b/lib/ssh/test/ssh_relay.erl @@ -0,0 +1,407 @@ +%%%------------------------------------------------------------------- +%%% @author Simon Cornish <[email protected]> +%%% @copyright (C) 2015, Simon Cornish +%%% @doc +%%% Provide manipulatable TCP-level relaying for testing SSH +%%% @end +%%% Created : 7 May 2015 by Simon Cornish <[email protected]> +%%%------------------------------------------------------------------- +-module(ssh_relay). + +-behaviour(gen_server). + +%% API +-export([start_link/4]). +-export([stop/1]). +-export([hold/4, release/2, release_next/3]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-record(hold, { + port, + n, + tmo, + tref, + q = [] + }). + +-record(state, { + local_addr, + local_port, + peer_addr, + peer_port, + lpid, + local, + peer, + tx_hold, + rx_hold + }). + +-define(ACCEPT_TMO, 200). +%%%=================================================================== +%%% API +%%%=================================================================== +%%-------------------------------------------------------------------- +%% @doc +%% Hold N (or 'all') messages in given direction. +%% Messages will be released after the N+1th message or +%% Tmo ms or 'infinity' +%% +%% Dir is 'tx' for direction local -> peer +%% and 'rx' for direction peer -> local +%% +%% An Error, ealready, is returned if there is already a hold +%% in the given direction +%% +%% @spec hold(Srv, Dir, N, Tmo) -> ok | {error, Error} +%% @end +%%-------------------------------------------------------------------- +hold(Srv, Dir, N, Tmo) -> + gen_server:call(Srv, {hold, Dir, N, Tmo}). + +%%-------------------------------------------------------------------- +%% @doc +%% Release all held messages in given direction. +%% +%% An Error, enoent, is returned if there is no hold +%% in the given direction +%% +%% @spec release(Srv, Dir) -> ok | {error, Error} +%% @end +%%-------------------------------------------------------------------- +release(Srv, Dir) -> + gen_server:call(Srv, {release, Dir}). + +%%-------------------------------------------------------------------- +%% @doc +%% Release all held messages in given direction after the +%% next message in the trigger direction +%% +%% An Error, enoent, is returned if there is no hold +%% in the given direction +%% +%% @spec release_next(Srv, Dir, TriggerDir) -> ok | {error, Error} +%% @end +%%-------------------------------------------------------------------- +release_next(Srv, Dir, TriggerDir) -> + gen_server:call(Srv, {release_next, Dir, TriggerDir}). + +%%-------------------------------------------------------------------- +%% @doc +%% Starts the server +%% +%% @spec start_link() -> {ok, Pid} | ignore | {error, Error} +%% @end +%%-------------------------------------------------------------------- +start_link(ListenAddr, ListenPort, PeerAddr, PeerPort) -> + gen_server:start_link(?MODULE, [ListenAddr, ListenPort, PeerAddr, PeerPort], []). + +stop(Srv) -> + unlink(Srv), + Srv ! stop. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Initializes the server +%% +%% @spec init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% @end +%%-------------------------------------------------------------------- +init([ListenAddr, ListenPort, PeerAddr, PeerPort | Options]) -> + IfAddr = case ListenAddr of + {0,0,0,0} -> + []; + _ -> + [{ifaddr, ListenAddr}] + end, + case gen_tcp:listen(ListenPort, [{reuseaddr, true}, {backlog, 1}, {active, false}, binary | IfAddr]) of + {ok, LSock} -> + Parent = self(), + {LPid, _LMod} = spawn_monitor(fun() -> listen(Parent, LSock) end), + S = #state{local_addr = ListenAddr, + local_port = ListenPort, + lpid = LPid, + peer_addr = PeerAddr, + peer_port = PeerPort + }, + {ok, S}; + Error -> + {stop, Error} + end. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling call messages +%% +%% @spec handle_call(Request, From, State) -> +%% {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_call({hold, Dir, N, Tmo}, _From, State) -> + case Dir of + tx -> + do_hold(#state.tx_hold, State#state.peer, N, Tmo, State); + rx -> + do_hold(#state.rx_hold, State#state.local, N, Tmo, State); + _ -> + {reply, {error, einval}, State} + end; +handle_call({release, Dir}, _From, State) -> + case Dir of + tx -> + do_release(#state.tx_hold, State); + rx -> + do_release(#state.rx_hold, State); + _ -> + {reply, {error, einval}, State} + end; +handle_call({release_next, _Dir, _TriggerDir}, _From, State) -> + {reply, {error, nyi}, State}; + +handle_call(Request, _From, State) -> + Reply = {unhandled, Request}, + {reply, Reply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling cast messages +%% +%% @spec handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_cast(_Msg, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling all non call/cast messages +%% +%% @spec handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_info({tcp, Local, Data}, S) when S#state.local == Local -> + S1 = do_local(Data, S), + {noreply, S1}; + +handle_info({tcp_error, Local, Error}, S) when S#state.local == Local -> + S1 = do_local({error, Error}, S), + {noreply, S1}; + +handle_info({tcp_closed, Local}, S) when S#state.local == Local -> + S1 = do_local(closed, S), + {noreply, S1}; + +handle_info({tcp, Peer, Data}, S) when S#state.peer == Peer -> + S1 = do_peer(Data, S), + {noreply, S1}; + +handle_info({tcp_error, Peer, Error}, S) when S#state.peer == Peer -> + S1 = do_peer({error, Error}, S), + {noreply, S1}; + +handle_info({tcp_closed, Peer}, S) when S#state.peer == Peer -> + S1 = do_peer(closed, S), + {noreply, S1}; + +handle_info({accept, Local}, S) -> + S1 = do_accept(Local, S), + {noreply, S1}; + +handle_info({activate, Local}, State) -> + inet:setopts(Local, [{active, true}]), + {noreply, State}; + +handle_info({release, Pos}, S) -> + {reply, _, S1} = do_release(Pos,S), + {noreply, S1}; + +handle_info(stop, State) -> + {stop, normal, State}; + +handle_info({'DOWN', _Ref, _process, LPid, Reason}, S) when S#state.lpid == LPid -> + io:format("Acceptor has finished: ~p~n", [Reason]), + {noreply, S}; + +handle_info(_Info, State) -> + io:format("Unhandled info: ~p~n", [_Info]), + {noreply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +%% +%% @spec terminate(Reason, State) -> void() +%% @end +%%-------------------------------------------------------------------- +terminate(_Reason, _State) -> + ok. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Convert process state when code is changed +%% +%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} +%% @end +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +do_hold(Pos, _Port, _N, _Tmo, S) when element(Pos, S) /= undefined -> + {reply, {error, ealready}, S}; +do_hold(Pos, Port, N, Tmo, S) -> + TRef = if is_integer(Tmo) andalso Tmo > 0 -> + erlang:send_after(Tmo, self(), {release, Pos}); + true -> + undefined + end, + Hold = #hold{port = Port, n = N, tmo = Tmo, tref = TRef}, + {reply, ok, setelement(Pos, S, Hold)}. + +do_release(HPos, S) when element(HPos, S) == undefined -> + {reply, {error, enoent}, S}; +do_release(HPos, S) -> + #hold{port = Port, tref = TRef, q = Q} = element(HPos, S), + lists:foreach(fun(M) -> gen_tcp:send(Port, M), erlang:yield() end, Q), + catch erlang:cancel_timer(TRef), + receive + {release, HPos} -> ok + after 0 -> + ok + end, + {reply, ok, setelement(HPos, S, undefined)}. + +listen(Parent, LSock) -> + monitor(process, Parent), + do_listen(Parent, LSock). + +do_listen(Parent, LSock) -> + %% So annoying there is no select-like sematic for this + case gen_tcp:accept(LSock, ?ACCEPT_TMO) of + {ok, Sock} -> + Parent ! {accept, Sock}, + gen_tcp:controlling_process(Sock, Parent), + Parent ! {activate, Sock}, + do_flush(Parent, Sock), + gen_tcp:close(LSock); + {error, timeout} -> + receive + DOWN when element(1, DOWN) == 'DOWN' -> + ok; + stop -> + ok + after 1 -> + do_listen(Parent, LSock) + end; + Error -> + gen_tcp:close(LSock), + exit({accept,Error}) + end. + +do_flush(Parent, Sock) -> + receive + {Tcp, Sock, _} = Msg when Tcp == tcp; Tcp == tcp_error -> + Parent ! Msg, + do_flush(Parent, Sock); + {tcp_closed, Sock} = Msg -> + Parent ! Msg, + do_flush(Parent, Sock) + after 1 -> + ok + end. + +do_accept(Local, S) -> + case gen_tcp:connect(S#state.peer_addr, S#state.peer_port, [{active, true}, binary]) of + {ok, Peer} -> + S#state{local = Local, peer = Peer}; + Error -> + exit({connect, Error}) + end. + +do_local(Data, S) when is_binary(Data) -> + TxH = S#state.tx_hold, + if TxH == undefined -> + gen_tcp:send(S#state.peer, Data), + S; + TxH#hold.n == 0 -> + lists:foreach(fun(M) -> gen_tcp:send(S#state.peer, M) end, TxH#hold.q), + gen_tcp:send(S#state.peer, Data), + catch erlang:cancel_timer(TxH#hold.tref), + TxP = #state.tx_hold, + receive + {release, TxP} -> + ok + after 0 -> + ok + end, + S#state{tx_hold = undefined}; + true -> + Q = TxH#hold.q ++ [Data], + N = if is_integer(TxH#hold.n) -> + TxH#hold.n -1; + true -> + TxH#hold.n + end, + S#state{tx_hold = TxH#hold{q = Q, n = N}} + end; +do_local(Error, _S) -> + exit({local, Error}). + +do_peer(Data, S) when is_binary(Data) -> + RxH = S#state.rx_hold, + if RxH == undefined -> + gen_tcp:send(S#state.local, Data), + S; + RxH#hold.n == 0 -> + lists:foreach(fun(M) -> gen_tcp:send(S#state.local, M) end, RxH#hold.q), + gen_tcp:send(S#state.local, Data), + catch erlang:cancel_timer(RxH#hold.tref), + RxP = #state.rx_hold, + receive + {release, RxP} -> + ok + after 0 -> + ok + end, + S#state{rx_hold = undefined}; + true -> + Q = RxH#hold.q ++ [Data], + N = if is_integer(RxH#hold.n) -> + RxH#hold.n -1; + true -> + RxH#hold.n + end, + S#state{rx_hold = RxH#hold{q = Q, n = N}} + end; +do_peer(Error, _S) -> + exit({peer, Error}). + diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index e0992d317c..352563700b 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -25,68 +25,7 @@ <file>notes.xml</file> </header> <p>This document describes the changes made to the SSL application.</p> - <section><title>SSL 7.0</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Ignore signature_algorithm (TLS 1.2 extension) sent to - TLS 1.0 or TLS 1.1 server</p> - <p> - Own Id: OTP-12670</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Add new API functions to handle CRL-verification</p> - <p> - Own Id: OTP-10362 Aux Id: kunagi-215 [126] </p> - </item> - <item> - <p> - Remove default support for SSL-3.0, due to Poodle - vunrability in protocol specification.</p> - <p> - Add padding check for TLS-1.0 to remove Poodle - vunrability from TLS 1.0, also add the option - padding_check. This option only affects TLS-1.0 - connections and if set to false it disables the block - cipher padding check to be able to interoperate with - legacy software.</p> - <p> - Remove default support for RC4 cipher suites, as they are - consider too weak.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12390</p> - </item> - <item> - <p> - Add support for TLS ALPN (Application-Layer Protocol - Negotiation) extension.</p> - <p> - Own Id: OTP-12580</p> - </item> - <item> - <p> - Add SNI (Server Name Indication) support for the server - side.</p> - <p> - Own Id: OTP-12736</p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 6.0</title> + <section><title>SSL 6.0</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 8a0bf69be4..18d98e5efb 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -123,13 +123,13 @@ The callback <c>gen_tcp</c> is treated specially and calls <c>inet</c> directly.</p> <taglist> - <tag><c>CallbackModule</c></tag> - <item><p><c>= atom()</c></p></item> - <tag><c>DataTag</c></tag> - <item><p><c>= atom()</c></p> + <tag><c>CallbackModule =</c></tag> + <item><p><c>atom()</c></p></item> + <tag><c>DataTag =</c></tag> + <item><p><c>atom()</c></p> <p>Used in socket data message.</p></item> - <tag><c>ClosedTag</c></tag> - <item><p><c>= atom()</c></p> + <tag><c>ClosedTag =</c></tag> + <item><p><c>atom()</c></p> <p>Used in socket close message.</p></item> </taglist> </item> diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml index 90aa895aff..9230442ae0 100644 --- a/lib/ssl/doc/src/ssl_crl_cache_api.xml +++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml @@ -47,10 +47,10 @@ <taglist> - <tag><c>cache_ref()</c></tag> - <item> = opaque()</item> - <tag><c>dist_point()</c></tag> - <item><p> = #'DistributionPoint'{} see <seealso + <tag><c>cache_ref() =</c></tag> + <item>opaque()</item> + <tag><c>dist_point() =</c></tag> + <item><p>#'DistributionPoint'{} see <seealso marker="public_key:public_key_records"> X509 certificates records</seealso></p></item> </taglist> diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml index c89d3874a1..28b5f4ce23 100644 --- a/lib/ssl/doc/src/ssl_session_cache_api.xml +++ b/lib/ssl/doc/src/ssl_session_cache_api.xml @@ -40,20 +40,20 @@ <c>ssl_session_cache_api</c>:</p> <taglist> - <tag><c>cache_ref()</c></tag> - <item><p>= <c>opaque()</c></p></item> + <tag><c>cache_ref() =</c></tag> + <item><p><c>opaque()</c></p></item> - <tag><c>key()</c></tag> - <item><p>= <c>{partialkey(), session_id()}</c></p></item> + <tag><c>key() =</c></tag> + <item><p><c>{partialkey(), session_id()}</c></p></item> - <tag><c>partialkey()</c></tag> - <item><p>= <c>opaque()</c></p></item> + <tag><c>partialkey() =</c></tag> + <item><p><c>opaque()</c></p></item> - <tag><c>session_id()</c></tag> - <item><p>= <c>binary()</c></p></item> + <tag><c>session_id() =</c></tag> + <item><p><c>binary()</c></p></item> - <tag><c>session()</c></tag> - <item><p>= <c>opaque()</c></p></item> + <tag><c>session()</c> =</tag> + <item><p><c>opaque()</c></p></item> </taglist> </section> diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl index ccd70fa605..ae76f5849e 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl @@ -384,7 +384,7 @@ ssl_receive_and_assert_alpn(Socket, Protocol, Data) -> ssl_send(Socket, Data) -> ct:log("Connection info: ~p~n", - [ssl:connection_info(Socket)]), + [ssl:connection_information(Socket)]), ssl:send(Socket, Data). ssl_receive(Socket, Data) -> @@ -392,7 +392,7 @@ ssl_receive(Socket, Data) -> ssl_receive(Socket, Data, Buffer) -> ct:log("Connection info: ~p~n", - [ssl:connection_info(Socket)]), + [ssl:connection_information(Socket)]), receive {ssl, Socket, MoreData} -> ct:log("Received ~p~n",[MoreData]), @@ -411,4 +411,4 @@ ssl_receive(Socket, Data, Buffer) -> end. connection_info_result(Socket) -> - ssl:connection_info(Socket). + ssl:connection_information(Socket). diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 50d5fb411f..e1a36dbbd4 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -384,7 +384,7 @@ new_options_in_accept(Config) when is_list(Config) -> %%-------------------------------------------------------------------- connection_info() -> - [{doc,"Test the API function ssl:connection_info/1"}]. + [{doc,"Test the API function ssl:connection_information/1"}]. connection_info(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2831,7 +2831,7 @@ listen_socket(Config) -> {error, enotconn} = ssl:send(ListenSocket, <<"data">>), {error, enotconn} = ssl:recv(ListenSocket, 0), - {error, enotconn} = ssl:connection_info(ListenSocket), + {error, enotconn} = ssl:connection_information(ListenSocket), {error, enotconn} = ssl:peername(ListenSocket), {error, enotconn} = ssl:peercert(ListenSocket), {error, enotconn} = ssl:session_info(ListenSocket), @@ -3445,7 +3445,7 @@ renegotiate_immediately(Socket) -> end, ok = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), - ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), + ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP), ok = ssl:renegotiate(Socket), ct:log("Renegotiated again"), ssl:send(Socket, "Hello world"), @@ -3836,10 +3836,10 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> end. connection_info_result(Socket) -> - ssl:connection_info(Socket). - + {ok, Info} = ssl:connection_information(Socket, [protocol, cipher_suite]), + {ok, {proplists:get_value(protocol, Info), proplists:get_value(cipher_suite, Info)}}. version_info_result(Socket) -> - {ok, {Version, _}} = ssl:connection_info(Socket), + {ok, [{version, Version}]} = ssl:connection_information(Socket, [version]), {ok, Version}. connect_dist_s(S) -> diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 326f907e66..8e95679306 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -332,7 +332,7 @@ ssl_receive_and_assert_npn(Socket, Protocol, Data) -> ssl_send(Socket, Data) -> ct:log("Connection info: ~p~n", - [ssl:connection_info(Socket)]), + [ssl:connection_information(Socket)]), ssl:send(Socket, Data). ssl_receive(Socket, Data) -> @@ -340,7 +340,7 @@ ssl_receive(Socket, Data) -> ssl_receive(Socket, Data, Buffer) -> ct:log("Connection info: ~p~n", - [ssl:connection_info(Socket)]), + [ssl:connection_information(Socket)]), receive {ssl, Socket, MoreData} -> ct:log("Received ~p~n",[MoreData]), @@ -360,4 +360,4 @@ ssl_receive(Socket, Data, Buffer) -> connection_info_result(Socket) -> - ssl:connection_info(Socket). + ssl:connection_information(Socket). diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index 46cd644e4d..b059ff991b 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2015. All Rights Reserved. +%% Copyright Ericsson AB 2015-2015. 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 @@ -16,7 +16,6 @@ %% %% %CopyrightEnd% %% - %% -module(ssl_sni_SUITE). @@ -31,7 +30,12 @@ %%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> [no_sni_header, sni_match, sni_no_match] ++ [no_sni_header_fun, sni_match_fun, sni_no_match_fun]. +all() -> [no_sni_header, + sni_match, + sni_no_match, + no_sni_header_fun, + sni_match_fun, + sni_no_match_fun]. init_per_suite(Config0) -> catch crypto:stop(), @@ -39,11 +43,11 @@ init_per_suite(Config0) -> ok -> ssl:start(), Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), ct:log("Make certs ~p~n", [Result]), ssl_test_lib:cert_options(Config0) - catch _:_ -> + catch _:_ -> {skip, "Crypto did not start"} end. @@ -76,8 +80,6 @@ sni_no_match_fun(Config) -> %%-------------------------------------------------------------------- %% Internal Functions ------------------------------------------------ %%-------------------------------------------------------------------- - - ssl_recv(SSLSocket, Expect) -> ssl_recv(SSLSocket, "", Expect). @@ -93,20 +95,21 @@ ssl_recv(SSLSocket, CurrentData, ExpectedData) -> end; Other -> ct:fail({unexpected_message, Other}) - after 4000 -> + after 4000 -> ct:fail({timeout, CurrentData, ExpectedData}) end. - - send_and_hostname(SSLSocket) -> ssl:send(SSLSocket, "OK"), {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]), Hostname. -rdnPart([[#'AttributeTypeAndValue'{type=Type, value=Value} | _] | _], Type) -> Value; -rdnPart([_ | Tail], Type) -> rdnPart(Tail, Type); -rdnPart([], _) -> unknown. +rdnPart([[#'AttributeTypeAndValue'{type=Type, value=Value} | _] | _], Type) -> + Value; +rdnPart([_ | Tail], Type) -> + rdnPart(Tail, Type); +rdnPart([], _) -> + unknown. rdn_to_string({utf8String, Binary}) -> erlang:binary_to_list(Binary); @@ -116,12 +119,15 @@ rdn_to_string({printableString, String}) -> recv_and_certificate(SSLSocket) -> ssl_recv(SSLSocket, "OK"), {ok, PeerCert} = ssl:peercert(SSLSocket), - #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = {rdnSequence, Subject}}} = public_key:pkix_decode_cert(PeerCert, otp), + #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = {rdnSequence, Subject}}} + = public_key:pkix_decode_cert(PeerCert, otp), ct:log("Subject of certificate received from server: ~p", [Subject]), rdn_to_string(rdnPart(Subject, ?'id-at-commonName')). run_sni_fun_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> - ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), + ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, " + "ExpectedSNIHostname: ~p, ExpectedCN: ~p", + [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), [{sni_hosts, ServerSNIConf}] = ?config(sni_server_opts, Config), SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end, ServerOptions = ?config(server_opts, Config) ++ [{sni_fun, SNIFun}], @@ -142,11 +148,14 @@ run_sni_fun_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> {host, Hostname}, {from, self()}, {mfa, {?MODULE, recv_and_certificate, []}}, {options, ClientOptions}]), - ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN). - + ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). run_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> - ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), + ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, " + "ExpectedSNIHostname: ~p, ExpectedCN: ~p", + [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), ServerOptions = ?config(sni_server_opts, Config) ++ ?config(server_opts, Config), ClientOptions = case SNIHostname of @@ -165,4 +174,6 @@ run_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> {host, Hostname}, {from, self()}, {mfa, {?MODULE, recv_and_certificate, []}}, {options, ClientOptions}]), - ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN). + ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 8b98e6f16b..a3bfdf8893 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -949,7 +949,8 @@ der_to_pem(File, Entries) -> file:write_file(File, PemBin). cipher_result(Socket, Result) -> - Result = ssl:connection_info(Socket), + {ok, Info} = ssl:connection_information(Socket), + Result = {ok, {proplists:get_value(protocol, Info), proplists:get_value(cipher_suite, Info)}}, ct:log("~p:~p~nSuccessfull connect: ~p~n", [?MODULE,?LINE, Result]), %% Importante to send two packets here %% to properly test "cipher state" handling diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 0413415e49..aca34cb6e9 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1243,15 +1243,16 @@ run_suites(Ciphers, Version, Config, Type) -> ct:fail(cipher_suite_failed_see_test_case_log) end. -client_read_check([], _NewData) -> ok; -client_read_check([Hd | T], NewData) -> - case binary:match(NewData, list_to_binary(Hd)) of +client_read_check([], _Data) -> + ok; +client_read_check([Hd | T], Data) -> + case binary:match(Data, list_to_binary(Hd)) of nomatch -> nomatch; _ -> - client_read_check(T, NewData) + client_read_check(T, Data) end. -client_read_bulk(Port, DataExpected, DataReceived) -> +client_check_result(Port, DataExpected, DataReceived) -> receive {Port, {data, TheData}} -> Data = list_to_binary(TheData), @@ -1261,15 +1262,14 @@ client_read_bulk(Port, DataExpected, DataReceived) -> ok -> ok; _ -> - client_read_bulk(Port, DataExpected, NewData) - end; - _ -> - ct:fail("unexpected_message") - after 4000 -> - ct:fail("timeout") + client_check_result(Port, DataExpected, NewData) + end + after 3000 -> + ct:fail({"Time out on opensssl Client", {expected, DataExpected}, + {got, DataReceived}}) end. -client_read_bulk(Port, DataExpected) -> - client_read_bulk(Port, DataExpected, <<"">>). +client_check_result(Port, DataExpected) -> + client_check_result(Port, DataExpected, <<"">>). send_and_hostname(SSLSocket) -> ssl:send(SSLSocket, "OK"), @@ -1292,9 +1292,12 @@ erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, end, ct:log("Options: ~p", [[ServerOptions, ClientCommand]]), ClientPort = open_port({spawn, ClientCommand}, [stderr_to_stdout]), - ssl_test_lib:check_result(Server, ExpectedSNIHostname), + + %% Client check needs to be done befor server check, + %% or server check might consume client messages ExpectedClientOutput = ["OK", "/CN=" ++ ExpectedCN ++ "/"], - ok = client_read_bulk(ClientPort, ExpectedClientOutput), + client_check_result(ClientPort, ExpectedClientOutput), + ssl_test_lib:check_result(Server, ExpectedSNIHostname), ssl_test_lib:close_port(ClientPort), ssl_test_lib:close(Server), ok. @@ -1318,12 +1321,14 @@ erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHo end, ct:log("Options: ~p", [[ServerOptions, ClientCommand]]), ClientPort = open_port({spawn, ClientCommand}, [stderr_to_stdout]), - ssl_test_lib:check_result(Server, ExpectedSNIHostname), + + %% Client check needs to be done befor server check, + %% or server check might consume client messages ExpectedClientOutput = ["OK", "/CN=" ++ ExpectedCN ++ "/"], - ok = client_read_bulk(ClientPort, ExpectedClientOutput), + client_check_result(ClientPort, ExpectedClientOutput), + ssl_test_lib:check_result(Server, ExpectedSNIHostname), ssl_test_lib:close_port(ClientPort), - ssl_test_lib:close(Server), - ok. + ssl_test_lib:close(Server). cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> @@ -1664,7 +1669,7 @@ erlang_ssl_receive_and_assert_negotiated_protocol(Socket, Protocol, Data) -> erlang_ssl_receive(Socket, Data) -> ct:log("Connection info: ~p~n", - [ssl:connection_info(Socket)]), + [ssl:connection_information(Socket)]), receive {ssl, Socket, Data} -> io:format("Received ~p~n",[Data]), @@ -1683,16 +1688,16 @@ erlang_ssl_receive(Socket, Data) -> end. connection_info(Socket, Version) -> - case ssl:connection_info(Socket) of - {ok, {Version, _} = Info} -> + case ssl:connection_information(Socket, [version]) of + {ok, [{version, Version}] = Info} -> ct:log("Connection info: ~p~n", [Info]), ok; - {ok, {OtherVersion, _}} -> + {ok, [{version, OtherVersion}]} -> {wrong_version, OtherVersion} end. connection_info_result(Socket) -> - ssl:connection_info(Socket). + ssl:connection_information(Socket). delayed_send(Socket, [ErlData, OpenSslData]) -> diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml index b49fa6ad67..b43d4786ae 100644 --- a/lib/stdlib/doc/src/c.xml +++ b/lib/stdlib/doc/src/c.xml @@ -232,6 +232,14 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </desc> </func> <func> + <name name="uptime" arity="0"/> + <fsummary>Print node uptime</fsummary> + <desc> + <p>Prints the node uptime (as given by + <c>erlang:statistics(wall_clock)</c>), in human-readable form.</p> + </desc> + </func> + <func> <name>xm(ModSpec) -> void()</name> <fsummary>Cross reference check a module</fsummary> <type> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 6b9524ef63..2bfe074c3e 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -1435,7 +1435,9 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> <p>Whenever the <c>extended_info</c> option is used, it results in a file not readable by versions of ets prior to that in stdlib-1.15.1</p> - + <p>The <c>sync</c> option, if set to <c>true</c>, ensures that + the content of the file is actually written to the disk before + <c>tab2file</c> returns. Default is <c>{sync, false}</c>.</p> </desc> </func> <func> diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml index ea96c14472..405bae5698 100644 --- a/lib/stdlib/doc/src/gb_sets.xml +++ b/lib/stdlib/doc/src/gb_sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2014</year> + <year>2001</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -306,6 +306,17 @@ </desc> </func> <func> + <name name="iterator_from" arity="2"/> + <fsummary>Return an iterator for a set starting from a specified element</fsummary> + <desc> + <p>Returns an iterator that can be used for traversing the + entries of <c><anno>Set</anno></c>; see <c>next/1</c>. + The difference as compared to the iterator returned by + <c>iterator/1</c> is that the first element greater than + or equal to <c><anno>Element</anno></c> is returned.</p> + </desc> + </func> + <func> <name name="largest" arity="1"/> <fsummary>Return largest element</fsummary> <desc> diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml index b2f237e1d7..82167e1083 100644 --- a/lib/stdlib/doc/src/gb_trees.xml +++ b/lib/stdlib/doc/src/gb_trees.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2014</year> + <year>2001</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -183,6 +183,17 @@ </desc> </func> <func> + <name name="iterator_from" arity="2"/> + <fsummary>Return an iterator for a tree starting from specified key</fsummary> + <desc> + <p>Returns an iterator that can be used for traversing the + entries of <c><anno>Tree</anno></c>; see <c>next/1</c>. + The difference as compared to the iterator returned by + <c>iterator/1</c> is that the first key greater than + or equal to <c><anno>Key</anno></c> is returned.</p> + </desc> + </func> + <func> <name name="keys" arity="1"/> <fsummary>Return a list of the keys in a tree</fsummary> <desc> diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml index e46068230a..7345a9357a 100644 --- a/lib/stdlib/doc/src/maps.xml +++ b/lib/stdlib/doc/src/maps.xml @@ -33,6 +33,28 @@ <funcs> <func> + <name name="filter" arity="2"/> + <fsummary>Choose pairs which satisfy a predicate</fsummary> + <desc> + <p> + Returns a map <c><anno>Map2</anno></c> for which predicate + <c><anno>Pred</anno></c> holds true in <c><anno>Map1</anno></c>. + </p> + <p> + The call will fail with a <c>{badmap,Map}</c> exception if + <c><anno>Map1</anno></c> is not a map or with <c>badarg</c> if + <c><anno>Pred</anno></c> is not a function of arity 2. + </p> + <p>Example:</p> + <code type="none"> +> M = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "c" => 4}, + Pred = fun(K,V) -> is_atom(K) andalso (V rem 2) =:= 0 end, + maps:filter(Pred,M). +#{a => 2,c => 4} </code> + </desc> + </func> + + <func> <name name="find" arity="2"/> <fsummary></fsummary> <desc> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 3914a9bc0e..301a5ee2e8 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -30,224 +30,6 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> -<section><title>STDLIB 2.5</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix handling of single dot in filename:join/2</p> - <p> - The reference manual says that filename:join(A,B) is - equivalent to filename:join([A,B]). In some rare cases - this turns out not to be true. For example:</p> - <p> - <c>filename:join("/a/.","b") -> "/a/./b"</c> vs - <c>filename:join(["/a/.","b"]) -> "/a/b"</c>.</p> - <p> - This has been corrected. A single dot is now only kept if - it occurs at the very beginning or the very end of the - resulting path.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12158</p> - </item> - <item> - <p> - The undocumented option <c>generic_debug</c> for - <c>gen_server</c> has been removed.</p> - <p> - Own Id: OTP-12183</p> - </item> - <item> - <p> - erl_lint:icrt_export/4 has been rewritten to make the - code really follow the scoping rules of Erlang, and not - just in most situations by accident.</p> - <p> - Own Id: OTP-12186</p> - </item> - <item> - <p> - Add 'trim_all' option to binary:split/3</p> - <p> - This option can be set to remove _ALL_ empty parts of the - result of a call to binary:split/3.</p> - <p> - Own Id: OTP-12301</p> - </item> - <item> - <p> Correct orddict(3) regarding evaluation order of - <c>fold()</c> and <c>map()</c>. </p> - <p> - Own Id: OTP-12651 Aux Id: seq12832 </p> - </item> - <item> - <p> - Correct <c>maps</c> module error exceptions </p> - <p> - Bad input to maps module function will now yield the - following exceptions: <list> <item>{badmap,NotMap} - or,</item> <item>badarg</item> </list></p> - <p> - Own Id: OTP-12657</p> - </item> - <item> - <p> - It is now possible to paste text in JCL mode (using - Ctrl-Y) that has been copied in the previous shell - session. Also a bug that caused the JCL mode to crash - when pasting text has been fixed.</p> - <p> - Own Id: OTP-12673</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Allow maps for supervisor flags and child specs</p> - <p> - Earlier, supervisor flags and child specs were given as - tuples. While this is kept for backwards compatibility, - it is now also allowed to give these parameters as maps, - see <seealso - marker="stdlib:supervisor#sup_flags">sup_flags</seealso> - and <seealso - marker="stdlib:supervisor#child_spec">child_spec</seealso>.</p> - <p> - Own Id: OTP-11043</p> - </item> - <item> - <p> - A new system message, <c>terminate</c>, is added. This - can be sent with <c>sys:terminate/2,3</c>. If the - receiving process handles system messages properly it - will terminate shortly after receiving this message.</p> - <p> - The new function <c>proc_lib:stop/1,3</c> utilizes this - new system message and monitors the receiving process in - order to facilitate a synchronous stop mechanism for - 'special processes'.</p> - <p> - <c>proc_lib:stop/1,3</c> is used by the following - functions:</p> - <p> - <list> <item><c>gen_server:stop/1,3</c> (new)</item> - <item><c>gen_fsm:stop/1,3</c> (new)</item> - <item><c>gen_event:stop/1,3</c> (modified to be - synchronous)</item> <item><c>wx_object:stop/1,3</c> - (new)</item> </list></p> - <p> - Own Id: OTP-11173 Aux Id: seq12353 </p> - </item> - <item> - <p> - Remove the <c>pg</c> module, which has been deprecated - through OTP-17, is now removed from the STDLIB - application. This module has been marked experimental for - more than 15 years, and has largely been superseded by - the <c>pg2</c> module from the Kernel application.</p> - <p> - Own Id: OTP-11907</p> - </item> - <item> - <p> - New BIF: <c>erlang:get_keys/0</c>, lists all keys - associated with the process dictionary. Note: - <c>erlang:get_keys/0</c> is auto-imported.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12151 Aux Id: seq12521 </p> - </item> - <item> - <p> Add three new functions to <c>io_lib</c>-- - <c>scan_format/2</c>, <c>unscan_format/1</c>, and - <c>build_text/1</c>-- which expose the parsed form of the - format control sequences to make it possible to easily - modify or filter the input to <c>io_lib:format/2</c>. - This can e.g. be used in order to replace unbounded-size - control sequences like <c>~w</c> or <c>~p</c> with - corresponding depth-limited <c>~W</c> and <c>~P</c> - before doing the actual formatting. </p> - <p> - Own Id: OTP-12167</p> - </item> - <item> - <p> Introduce the <c>erl_anno</c> module, an abstraction - of the second element of tokens and tuples in the - abstract format. </p> - <p> - Own Id: OTP-12195</p> - </item> - <item> - <p> - Support variables as Map keys in expressions and patterns</p> - <p>Erlang will accept any expression as keys in Map - expressions and it will accept literals or bound - variables as keys in Map patterns.</p> - <p> - Own Id: OTP-12218</p> - </item> - <item> - <p> The last traces of Mnemosyne Rules have been removed. - </p> - <p> - Own Id: OTP-12257</p> - </item> - <item> - <p> - Properly support maps in match_specs</p> - <p> - Own Id: OTP-12270</p> - </item> - <item> - <p> - New function <c>ets:take/2</c>. Works the same as - <c>ets:delete/2</c> but also returns the deleted - object(s).</p> - <p> - Own Id: OTP-12309</p> - </item> - <item> - <p><c>string:tokens/2</c> is somewhat faster, especially - if the list of separators only contains one separator - character.</p> - <p> - Own Id: OTP-12422 Aux Id: seq12774 </p> - </item> - <item> - <p> - Prevent zip:zip_open/[12] from leaking file descriptors - if parent process dies.</p> - <p> - Own Id: OTP-12566</p> - </item> - <item> - <p> - Add a new random number generator, see <c>rand</c> - module. It have better characteristics and an improved - interface.</p> - <p> - Own Id: OTP-12586 Aux Id: OTP-12501, OTP-12502 </p> - </item> - <item> - <p><c>filename:split/1</c> when given an empty binary - will now return an empty list, to make it consistent with - return value when given an empty list.</p> - <p> - Own Id: OTP-12716</p> - </item> - </list> - </section> - -</section> - <section><title>STDLIB 2.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 9860adf04d..d5b24d3c32 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -27,7 +27,7 @@ lc_batch/0, lc_batch/1, i/3,pid/3,m/0,m/1, bt/1, q/0, - erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0, + erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0, uptime/0, nregs/0,pwd/0,ls/0,ls/1,cd/1,memory/1,memory/0, xm/1]). -export([display_info/1]). @@ -65,6 +65,7 @@ help() -> "q() -- quit - shorthand for init:stop()\n" "regs() -- information about registered processes\n" "nregs() -- information about all registered processes\n" + "uptime() -- print node uptime\n" "xm(M) -- cross reference check a module\n" "y(File) -- generate a Yecc parser\n">>). @@ -774,6 +775,26 @@ memory() -> erlang:memory(). memory(TypeSpec) -> erlang:memory(TypeSpec). %% +%% uptime/0 +%% + +-spec uptime() -> 'ok'. + +uptime() -> + io:format("~s~n", [uptime(get_uptime())]). + +uptime({D, {H, M, S}}) -> + lists:flatten( + [[ io_lib:format("~p days, ", [D]) || D > 0 ], + [ io_lib:format("~p hours, ", [H]) || D+H > 0 ], + [ io_lib:format("~p minutes and ", [M]) || D+H+M > 0 ], + io_lib:format("~p seconds", [S])]). + +get_uptime() -> + {UpTime, _} = erlang:statistics(wall_clock), + calendar:seconds_to_daystime(UpTime div 1000). + +%% %% Cross Reference Check %% %%-spec xm(module() | file:filename()) -> xref:m/1 return diff --git a/lib/stdlib/src/erl_anno.erl b/lib/stdlib/src/erl_anno.erl index 963b7278a6..9fb767fc93 100644 --- a/lib/stdlib/src/erl_anno.erl +++ b/lib/stdlib/src/erl_anno.erl @@ -147,7 +147,7 @@ is_anno2(_, _) -> false. is_filename(T) -> - is_string(T) orelse is_binary(T). + is_list(T) orelse is_binary(T). is_string(T) -> try lists:all(fun(C) when is_integer(C), C >= 0 -> true end, T) diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 821d81a6b4..714c260bda 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -120,13 +120,13 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> func=[], %Current function warn_format=0, %Warn format calls enabled_warnings=[], %All enabled warnings (ordset). + nowarn_bif_clash=[], %All no warn bif clashes (ordset). errors=[], %Current errors warnings=[], %Current warnings file = "" :: string(), %From last file attribute recdef_top=false :: boolean(), %true in record initialisation %outside any fun or lc xqlc= false :: boolean(), %true if qlc.hrl included - new = false :: boolean(), %Has user-defined 'new/N' called= [] :: [{fa(),line()}], %Called functions usage = #usage{} :: #usage{}, specs = dict:new() %Type specifications @@ -569,6 +569,7 @@ start(File, Opts) -> warn_format = value_option(warn_format, 1, warn_format, 1, nowarn_format, 0, Opts), enabled_warnings = Enabled, + nowarn_bif_clash = nowarn_function(nowarn_bif_clash, Opts), file = File }. @@ -640,8 +641,6 @@ forms(Forms0, St0) -> St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms), post_traversal_check(Forms, St4). -pre_scan([{function,_L,new,_A,_Cs} | Fs], St) -> - pre_scan(Fs, St#lint{new=true}); pre_scan([{attribute,L,compile,C} | Fs], St) -> case is_warn_enabled(export_all, St) andalso member(export_all, lists:flatten([C])) of @@ -772,8 +771,7 @@ eof(_Line, St0) -> %% bif_clashes(Forms, State0) -> State. -bif_clashes(Forms, St) -> - Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile), +bif_clashes(Forms, #lint{nowarn_bif_clash=Nowarn} = St) -> Clashes0 = [{Name,Arity} || {function,_L,Name,Arity,_Cs} <- Forms, erl_internal:bif(Name, Arity)], Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn), @@ -3781,8 +3779,7 @@ is_autoimport_suppressed(NoAutoSet,{Func,Arity}) -> gb_sets:is_element({Func,Arity},NoAutoSet). %% Predicate to find out if a function specific bif-clash suppression (old deprecated) is present bif_clash_specifically_disabled(St,{F,A}) -> - Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile), - lists:member({F,A},Nowarn). + lists:member({F,A},St#lint.nowarn_bif_clash). %% Predicate to find out if an autoimported guard_bif is not overriden in some way %% Guard Bif without module name is disallowed if diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 1df069755d..0e2d59d0c3 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -739,7 +739,8 @@ do_filter(Tab, Key, F, A, Ack) -> -record(filetab_options, { object_count = false :: boolean(), - md5sum = false :: boolean() + md5sum = false :: boolean(), + sync = false :: boolean() }). -spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when @@ -754,7 +755,7 @@ tab2file(Tab, File) -> Tab :: tab(), Filename :: file:name(), Options :: [Option], - Option :: {'extended_info', [ExtInfo]}, + Option :: {'extended_info', [ExtInfo]} | {'sync', boolean()}, ExtInfo :: 'md5sum' | 'object_count', Reason :: term(). @@ -835,6 +836,15 @@ tab2file(Tab, File, Options) -> List -> LogFun(NewState1,[['$end_of_table',List]]) end, + case FtOptions#filetab_options.sync of + true -> + case disk_log:sync(Name) of + ok -> ok; + {error, Reason2} -> throw(Reason2) + end; + false -> + ok + end, disk_log:close(Name) catch throw:TReason -> @@ -887,23 +897,24 @@ md5terms(State, [H|T]) -> {FinState, [B|TL]}. parse_ft_options(Options) when is_list(Options) -> - {Opt,Rest} = case (catch lists:keytake(extended_info,1,Options)) of - false -> - {[],Options}; - {value,{extended_info,L},R} when is_list(L) -> - {L,R} - end, - case Rest of - [] -> - parse_ft_info_options(#filetab_options{}, Opt); - Other -> - throw({unknown_option, Other}) - end; -parse_ft_options(Malformed) -> + {ok, parse_ft_options(Options, #filetab_options{}, false)}. + +parse_ft_options([], FtOpt, _) -> + FtOpt; +parse_ft_options([{sync,true} | Rest], FtOpt, EI) -> + parse_ft_options(Rest, FtOpt#filetab_options{sync = true}, EI); +parse_ft_options([{sync,false} | Rest], FtOpt, EI) -> + parse_ft_options(Rest, FtOpt, EI); +parse_ft_options([{extended_info,L} | Rest], FtOpt0, false) -> + FtOpt1 = parse_ft_info_options(FtOpt0, L), + parse_ft_options(Rest, FtOpt1, true); +parse_ft_options([Other | _], _, _) -> + throw({unknown_option, Other}); +parse_ft_options(Malformed, _, _) -> throw({malformed_option, Malformed}). parse_ft_info_options(FtOpt,[]) -> - {ok,FtOpt}; + FtOpt; parse_ft_info_options(FtOpt,[object_count | T]) -> parse_ft_info_options(FtOpt#filetab_options{object_count = true}, T); parse_ft_info_options(FtOpt,[md5sum | T]) -> diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index 393fb07229..d3fbd542f7 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -137,6 +137,10 @@ %% approach is that it does not require the complete list of all %% elements to be built in memory at one time. %% +%% - iterator_from(X, S): returns an iterator that can be used for +%% traversing the elements of set S greater than or equal to X; +%% see `next'. +%% %% - next(T): returns {X, T1} where X is the smallest element referred %% to by the iterator T, and T1 is the new iterator to be used for %% traversing the remaining elements, or the atom `none' if no @@ -157,8 +161,8 @@ insert/2, add/2, delete/2, delete_any/2, balance/1, union/2, union/1, intersection/2, intersection/1, is_disjoint/2, difference/2, is_subset/2, to_list/1, from_list/1, from_ordset/1, smallest/1, - largest/1, take_smallest/1, take_largest/1, iterator/1, next/1, - filter/2, fold/3, is_set/1]). + largest/1, take_smallest/1, take_largest/1, iterator/1, + iterator_from/2, next/1, filter/2, fold/3, is_set/1]). %% `sets' compatibility aliases: @@ -500,6 +504,22 @@ iterator({_, L, _} = T, As) -> iterator(nil, As) -> As. +-spec iterator_from(Element, Set) -> Iter when + Set :: set(Element), + Iter :: iter(Element). + +iterator_from(S, {_, T}) -> + iterator_from(S, T, []). + +iterator_from(S, {K, _, T}, As) when K < S -> + iterator_from(S, T, As); +iterator_from(_, {_, nil, _} = T, As) -> + [T | As]; +iterator_from(S, {_, L, _} = T, As) -> + iterator_from(S, L, [T | As]); +iterator_from(_, nil, As) -> + As. + -spec next(Iter1) -> {Element, Iter2} | 'none' when Iter1 :: iter(Element), Iter2 :: iter(Element). diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index 7069b61873..259e8f718b 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2014. All Rights Reserved. +%% Copyright Ericsson AB 2001-2015. 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 @@ -102,6 +102,10 @@ %% approach is that it does not require the complete list of all %% elements to be built in memory at one time. %% +%% - iterator_from(K, T): returns an iterator that can be used for +%% traversing the entries of tree T with key greater than or +%% equal to K; see `next'. +%% %% - next(S): returns {X, V, S1} where X is the smallest key referred to %% by the iterator S, and S1 is the new iterator to be used for %% traversing the remaining entries, or the atom `none' if no entries @@ -117,7 +121,7 @@ update/3, enter/3, delete/2, delete_any/2, balance/1, is_defined/2, keys/1, values/1, to_list/1, from_orddict/1, smallest/1, largest/1, take_smallest/1, take_largest/1, - iterator/1, next/1, map/2]). + iterator/1, iterator_from/2, next/1, map/2]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -529,6 +533,29 @@ iterator({_, _, L, _} = T, As) -> iterator(nil, As) -> As. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec iterator_from(Key, Tree) -> Iter when + Tree :: tree(Key, Value), + Iter :: iter(Key, Value). + +iterator_from(S, {_, T}) -> + iterator_1_from(S, T). + +iterator_1_from(S, T) -> + iterator_from(S, T, []). + +iterator_from(S, {K, _, _, T}, As) when K < S -> + iterator_from(S, T, As); +iterator_from(_, {_, _, nil, _} = T, As) -> + [T | As]; +iterator_from(S, {_, _, L, _} = T, As) -> + iterator_from(S, L, [T | As]); +iterator_from(_, nil, As) -> + As. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + -spec next(Iter1) -> 'none' | {Key, Value, Iter2} when Iter1 :: iter(Key, Value), Iter2 :: iter(Key, Value). diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 3877c150ec..533ff08726 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -19,7 +19,8 @@ -module(maps). --export([get/3,fold/3, map/2, size/1, +-export([get/3,filter/2,fold/3, map/2, + size/1, without/2, with/2]). @@ -145,6 +146,19 @@ get(Key,Map,Default) -> erlang:error({badmap,Map},[Key,Map,Default]). +-spec filter(Pred,Map1) -> Map2 when + Pred :: fun((Key, Value) -> boolean()), + Key :: term(), + Value :: term(), + Map1 :: map(), + Map2 :: map(). + +filter(Pred,Map) when is_function(Pred,2), is_map(Map) -> + maps:from_list([{K,V}||{K,V}<-maps:to_list(Map),Pred(K,V)]); +filter(Pred,Map) -> + erlang:error(error_type(Map),[Pred,Map]). + + -spec fold(Fun,Init,Map) -> Acc when Fun :: fun((K, V, AccIn) -> AccOut), Init :: term(), @@ -169,10 +183,7 @@ fold(Fun,Init,Map) -> V2 :: term(). map(Fun,Map) when is_function(Fun, 2), is_map(Map) -> - maps:from_list(lists:map(fun - ({K,V}) -> - {K,Fun(K,V)} - end,maps:to_list(Map))); + maps:from_list([{K,Fun(K,V)}||{K,V}<-maps:to_list(Map)]); map(Fun,Map) -> erlang:error(error_type(Map),[Fun,Map]). diff --git a/lib/stdlib/src/shell_default.erl b/lib/stdlib/src/shell_default.erl index 3fe359af0e..0fca7ff8c7 100644 --- a/lib/stdlib/src/shell_default.erl +++ b/lib/stdlib/src/shell_default.erl @@ -23,7 +23,7 @@ -module(shell_default). -export([help/0,lc/1,c/1,c/2,nc/1,nl/1,l/1,i/0,pid/3,i/3,m/0,m/1, - memory/0,memory/1, + memory/0,memory/1,uptime/0, erlangrc/1,bi/1, regs/0, flush/0,pwd/0,ls/0,ls/1,cd/1, y/1, y/2, xm/1, bt/1, q/0, @@ -92,6 +92,7 @@ pid(X,Y,Z) -> c:pid(X,Y,Z). pwd() -> c:pwd(). q() -> c:q(). regs() -> c:regs(). +uptime() -> c:uptime(). xm(Mod) -> c:xm(Mod). y(File) -> c:y(File). y(File, Opts) -> c:y(File, Opts). diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index a27a35dca2..c33130cf8c 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -104,7 +104,7 @@ dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-7.0","crypto-3.3", + {runtime_dependencies, ["sasl-2.4","kernel-4.0","erts-7.0","crypto-3.3", "compiler-5.0"]} ]}. diff --git a/lib/stdlib/test/dict_SUITE.erl b/lib/stdlib/test/dict_SUITE.erl index 69814e12ce..ab624e8dd2 100644 --- a/lib/stdlib/test/dict_SUITE.erl +++ b/lib/stdlib/test/dict_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2015. 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 @@ -25,16 +25,16 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - create/1,store/1]). + create/1,store/1,iterate/1]). -include_lib("test_server/include/test_server.hrl"). --import(lists, [foldl/3,reverse/1]). +-import(lists, [foldl/3]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [create, store]. + [create, store, iterate]. groups() -> []. @@ -93,6 +93,48 @@ store_1(List, M) -> D0. %%% +%%% Test specifics for gb_trees. +%%% + +iterate(Config) when is_list(Config) -> + test_all(fun iterate_1/1). + +iterate_1(M) -> + case M(module, []) of + gb_trees -> iterate_2(M); + _ -> ok + end, + M(empty, []). + +iterate_2(M) -> + random:seed(1, 2, 42), + iter_tree(M, 1000). + +iter_tree(_M, 0) -> + ok; +iter_tree(M, N) -> + L = [{I, I} || I <- lists:seq(1, N)], + T = M(from_list, L), + L = lists:reverse(iterate_tree(M, T)), + R = random:uniform(N), + KV = lists:reverse(iterate_tree_from(M, R, T)), + KV = [P || P={K,_} <- L, K >= R], + iter_tree(M, N-1). + +iterate_tree(M, Tree) -> + I = M(iterator, Tree), + iterate_tree_1(M, M(next, I), []). + +iterate_tree_from(M, Start, Tree) -> + I = M(iterator_from, {Start, Tree}), + iterate_tree_1(M, M(next, I), []). + +iterate_tree_1(_, none, R) -> + R; +iterate_tree_1(M, {K, V, I}, R) -> + iterate_tree_1(M, M(next, I), [{K, V} | R]). + +%%% %%% Helper functions. %%% diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl index 4fdb4fa0bd..81d26ce5f8 100644 --- a/lib/stdlib/test/dict_test_lib.erl +++ b/lib/stdlib/test/dict_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2015. 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 @@ -29,6 +29,9 @@ new(Mod, Eq) -> (module, []) -> Mod; (size, D) -> Mod:size(D); (is_empty, D) -> Mod:is_empty(D); + (iterator, S) -> Mod:iterator(S); + (iterator_from, {Start, S}) -> Mod:iterator_from(Start, S); + (next, I) -> Mod:next(I); (to_list, D) -> to_list(Mod, D) end. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 41bd4af241..a88843bb6e 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -4078,12 +4078,22 @@ tab2file(doc) -> ["Check the ets:tab2file function on an empty " "ets table."]; tab2file(suite) -> []; tab2file(Config) when is_list(Config) -> + ?line FName = filename:join([?config(priv_dir, Config),"tab2file_case"]), + tab2file_do(FName, []), + tab2file_do(FName, [{sync,true}]), + tab2file_do(FName, [{sync,false}]), + {'EXIT',{{badmatch,{error,_}},_}} = (catch tab2file_do(FName, [{sync,yes}])), + {'EXIT',{{badmatch,{error,_}},_}} = (catch tab2file_do(FName, [sync])), + ok. + +tab2file_do(FName, Opts) -> %% Write an empty ets table to a file, read back and check properties. ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, set, private, {keypos, 2}]), - ?line FName = filename:join([?config(priv_dir, Config),"tab2file_case"]), - ?line ok = ets:tab2file(Tab, FName), - ?line true = ets:delete(Tab), + catch file:delete(FName), + Res = ets:tab2file(Tab, FName, Opts), + true = ets:delete(Tab), + ok = Res, % ?line EtsMem = etsmem(), ?line {ok, Tab2} = ets:file2tab(FName), @@ -4093,6 +4103,7 @@ tab2file(Config) when is_list(Config) -> ?line set = ets:info(Tab2, type), ?line true = ets:delete(Tab2), ?line verify_etsmem(EtsMem). + tab2file2(doc) -> ["Check the ets:tab2file function on a ", "filled set/bag type ets table."]; diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl index 1d9c041a74..f8f241d834 100644 --- a/lib/stdlib/test/maps_SUITE.erl +++ b/lib/stdlib/test/maps_SUITE.erl @@ -34,18 +34,21 @@ -export([init_per_testcase/2]). -export([end_per_testcase/2]). --export([t_get_3/1, +-export([t_get_3/1, t_filter_2/1, t_fold_3/1,t_map_2/1,t_size_1/1, t_with_2/1,t_without_2/1]). --define(badmap(V,F,Args), {'EXIT', {{badmap,V}, [{maps,F,Args,_}|_]}}). --define(badarg(F,Args), {'EXIT', {badarg, [{maps,F,Args,_}|_]}}). +%-define(badmap(V,F,Args), {'EXIT', {{badmap,V}, [{maps,F,Args,_}|_]}}). +%-define(badarg(F,Args), {'EXIT', {badarg, [{maps,F,Args,_}|_]}}). +% silly broken hipe +-define(badmap(V,F,_Args), {'EXIT', {{badmap,V}, [{maps,F,_,_}|_]}}). +-define(badarg(F,_Args), {'EXIT', {badarg, [{maps,F,_,_}|_]}}). suite() -> [{ct_hooks, [ts_install_cth]}]. all() -> - [t_get_3, + [t_get_3,t_filter_2, t_fold_3,t_map_2,t_size_1, t_with_2,t_without_2]. @@ -99,6 +102,16 @@ t_with_2(_Config) -> ?badarg(with,[a,#{}]) = (catch maps:with(a,#{})), ok. +t_filter_2(Config) when is_list(Config) -> + M = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "c" => 4}, + Pred1 = fun(K,V) -> is_atom(K) andalso (V rem 2) =:= 0 end, + Pred2 = fun(K,V) -> is_list(K) andalso (V rem 2) =:= 0 end, + #{a := 2,c := 4} = maps:filter(Pred1,M), + #{"b" := 2,"c" := 4} = maps:filter(Pred2,M), + %% error case + ?badmap(a,filter,[_,a]) = (catch maps:filter(fun(_,_) -> ok end,id(a))), + ?badarg(filter,[<<>>,#{}]) = (catch maps:filter(id(<<>>),#{})), + ok. t_fold_3(Config) when is_list(Config) -> Vs = lists:seq(1,200), diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl index c0cf1fc7e8..24f5d65f82 100644 --- a/lib/stdlib/test/sets_SUITE.erl +++ b/lib/stdlib/test/sets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2015. 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 @@ -28,7 +28,7 @@ create/1,add_element/1,del_element/1, subtract/1,intersection/1,union/1,is_subset/1, is_set/1,fold/1,filter/1, - take_smallest/1,take_largest/1]). + take_smallest/1,take_largest/1, iterate/1]). -include_lib("test_server/include/test_server.hrl"). @@ -48,7 +48,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [create, add_element, del_element, subtract, intersection, union, is_subset, is_set, fold, filter, - take_smallest, take_largest]. + take_smallest, take_largest, iterate]. groups() -> []. @@ -426,6 +426,44 @@ take_largest_3(S0, List0, M) -> take_largest_3(S, List, M) end. +iterate(Config) when is_list(Config) -> + test_all(fun iterate_1/1). + +iterate_1(M) -> + case M(module, []) of + gb_sets -> iterate_2(M); + _ -> ok + end, + M(empty, []). + +iterate_2(M) -> + random:seed(1, 2, 42), + iter_set(M, 1000). + +iter_set(_M, 0) -> + ok; +iter_set(M, N) -> + L = [I || I <- lists:seq(1, N)], + T = M(from_list, L), + L = lists:reverse(iterate_set(M, T)), + R = random:uniform(N), + S = lists:reverse(iterate_set(M, R, T)), + S = [E || E <- L, E >= R], + iter_set(M, N-1). + +iterate_set(M, Set) -> + I = M(iterator, Set), + iterate_set_1(M, M(next, I), []). + +iterate_set(M, Start, Set) -> + I = M(iterator_from, {Start, Set}), + iterate_set_1(M, M(next, I), []). + +iterate_set_1(_, none, R) -> + R; +iterate_set_1(M, {E, I}, R) -> + iterate_set_1(M, M(next, I), [E | R]). + %%% %%% Helper functions. %%% diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl index 86f009a8f9..772139406d 100644 --- a/lib/stdlib/test/sets_test_lib.erl +++ b/lib/stdlib/test/sets_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2015. 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 @@ -34,7 +34,10 @@ new(Mod, Eq) -> (is_empty, S) -> is_empty(Mod, S); (is_set, S) -> Mod:is_set(S); (is_subset, {S,Set}) -> is_subset(Mod, Eq, S, Set); + (iterator, S) -> Mod:iterator(S); + (iterator_from, {Start, S}) -> Mod:iterator_from(Start, S); (module, []) -> Mod; + (next, I) -> Mod:next(I); (singleton, E) -> singleton(Mod, E); (size, S) -> Mod:size(S); (subtract, {S1,S2}) -> subtract(Mod, S1, S2); diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index a1f2a946b1..f57f31c8de 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 2.5 +STDLIB_VSN = 2.4 diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index 8f245083c4..408f6d5bac 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -31,20 +31,6 @@ <p>This document describes the changes made to the Syntax_Tools application.</p> -<section><title>Syntax_Tools 1.7</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>Use the new <c>erl_anno</c> module.</p> - <p> - Own Id: OTP-12732</p> - </item> - </list> - </section> - -</section> - <section><title>Syntax_Tools 1.6.18</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk index 403e90196e..1c42ef0ddb 100644 --- a/lib/syntax_tools/vsn.mk +++ b/lib/syntax_tools/vsn.mk @@ -1 +1 @@ -SYNTAX_TOOLS_VSN = 1.7 +SYNTAX_TOOLS_VSN = 1.6.18 diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml index 6cf7a2b52d..e996d2b4a3 100644 --- a/lib/test_server/doc/src/notes.xml +++ b/lib/test_server/doc/src/notes.xml @@ -32,55 +32,6 @@ <file>notes.xml</file> </header> -<section><title>Test_Server 3.9</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - The status of an aborted test due to test suite - compilation error has changed from 'auto_skipped' to - 'failed'. This affects both the textual log file, event - handling and CT hook callbacks. The logging of - compilation failures has also been improved, especially - in the case of multiple test suites failing compilation.</p> - <p> - Own Id: OTP-10816</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The Test Server application has been marked as obsolete - and will be removed from OTP in the next major release - (OTP 19.0).</p> - <p> - Own Id: OTP-10923 Aux Id: OTP-12705 </p> - </item> - <item> - <p> - When running OTP tests using the ts interface, it is now - possible to specify so called test categories per OTP - application. A test category is represented by a CT test - specification and defines an arbitrary subset of existing - test suites, groups and cases. Examples of test - categories are 'smoke' (smoke tests) and 'bench' - (benchmarks). (Call ts:help() for more info). Also, - functions for reading terms from the current test - specification during test, ct:get_testspec_terms/0 and - ct:get_testspec_terms/1, have been implemented.</p> - <p> - Own Id: OTP-11962</p> - </item> - </list> - </section> - -</section> - <section><title>Test_Server 3.8.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl index 9101212852..b0b5c40965 100644 --- a/lib/test_server/src/erl2html2.erl +++ b/lib/test_server/src/erl2html2.erl @@ -170,7 +170,11 @@ get_line(Anno) -> %%% Find the line number of the last expression in the function find_clause_lines([{clause,CL,_Params,_Op,Exprs}], CLs) -> % last clause try tuple_to_list(lists:last(Exprs)) of - [_Type,ExprLine | _] -> + [_Type,ExprLine | _] when is_integer(ExprLine) -> + {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(ExprLine)}; + [tree,_ | Exprs1] -> + find_clause_lines([{clause,CL,undefined,undefined,Exprs1}], CLs); + [macro,{_var,ExprLine,_MACRO} | _] when is_integer(ExprLine) -> {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(ExprLine)}; _ -> {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(CL)} @@ -188,18 +192,18 @@ build_html(SFd,DFd,Encoding,FuncsAndCs) -> build_html(SFd,DFd,Encoding,file:read_line(SFd),1,FuncsAndCs, false,undefined). -%% function start line found -build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs], - _IsFuncDef,_FAndLastL) -> - FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8), - file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]), - build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL}); %% line of last expression in function found build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,_IsFuncDef,{F,LastL}) -> LastLineLink = test_server_ctrl:uri_encode(F++"-last_expr",utf8), file:write(DFd,["<a name=\"", to_raw_list(LastLineLink,Enc),"\"/>"]), build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,true,undefined); +%% function start line found +build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs], + _IsFuncDef,_FAndLastL) -> + FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8), + file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]), + build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL}); build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs], _IsFuncDef,FAndLastL) -> build_html(SFd,DFd,Enc,{ok,Str},L,FuncsAndCs,true,FAndLastL); diff --git a/lib/test_server/test/erl2html2_SUITE.erl b/lib/test_server/test/erl2html2_SUITE.erl index 908985c879..796b84dedd 100644 --- a/lib/test_server/test/erl2html2_SUITE.erl +++ b/lib/test_server/test/erl2html2_SUITE.erl @@ -130,15 +130,7 @@ groups() -> %% @end %%-------------------------------------------------------------------- all() -> - [m1]. - -%%-------------------------------------------------------------------- -%% @spec TestCase() -> Info -%% Info = [tuple()] -%% @end -%%-------------------------------------------------------------------- -m1() -> - []. + [macros_defined, macros_undefined]. %%-------------------------------------------------------------------- %% @spec TestCase(Config0) -> @@ -149,19 +141,29 @@ m1() -> %% Comment = term() %% @end %%-------------------------------------------------------------------- -m1(Config) -> - {Src,Dst} = convert_module("m1",Config), +macros_defined(Config) -> + %% let erl2html2 use epp as parser + DataDir = ?config(data_dir,Config), + InclDir = filename:join(DataDir, "include"), + {Src,Dst} = convert_module("m1",[InclDir],Config), {true,L} = check_line_numbers(Src,Dst), - ok = check_link_targets(Src,Dst,L,[{baz,0}]), + ok = check_link_targets(Src,Dst,L,[{baz,0}],[]), ok. -convert_module(Mod,Config) -> +macros_undefined(Config) -> + %% let erl2html2 use epp_dodger as parser + {Src,Dst} = convert_module("m1",[],Config), + {true,L} = check_line_numbers(Src,Dst), + ok = check_link_targets(Src,Dst,L,[{baz,0}],[{quux,0}]), + ok. + +convert_module(Mod,InclDirs,Config) -> DataDir = ?config(data_dir,Config), PrivDir = ?config(priv_dir,Config), Src = filename:join(DataDir,Mod++".erl"), Dst = filename:join(PrivDir,Mod++".erl.html"), io:format("<a href=\"~s\">~s</a>\n",[Src,filename:basename(Src)]), - ok = erl2html2:convert(Src, Dst, [], "<html><body>"), + ok = erl2html2:convert(Src, Dst, InclDirs, "<html><body>"), io:format("<a href=\"~s\">~s</a>\n",[Dst,filename:basename(Dst)]), {Src,Dst}. @@ -229,36 +231,46 @@ check_line_number(Last,Line,OrigLine) -> %% function. %% The test module has -compile(export_all), so all functions are %% found by listing the exported ones. -check_link_targets(Src,Dst,L,RmFncs) -> +check_link_targets(Src,Dst,L,RmFncs,ShouldRemain) -> Mod = list_to_atom(filename:basename(filename:rootname(Src))), Exports = Mod:module_info(exports)--[{module_info,0},{module_info,1}|RmFncs], - {ok,{[],L},_} = xmerl_sax_parser:file(Dst, - [{event_fun,fun sax_event/3}, - {event_state,{Exports,0}}]), + LastExprFuncs = [Func || {Func,_A} <- Exports], + {ok,{FAs,Fs,L},_} = + xmerl_sax_parser:file(Dst, + [{event_fun,fun sax_event/3}, + {event_state,{Exports,LastExprFuncs,0}}]), + true = (length(FAs) == length(ShouldRemain)), + [] = [FA || FA <- FAs, not lists:member(FA,ShouldRemain)], + [] = [F || F <- Fs, not lists:keymember(F,1,ShouldRemain)], ok. sax_event(Event,_Loc,State) -> sax_event(Event,State). -sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,PrevLine}) -> +sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,LastExprFuncs,PrevLine}) -> {_,_,"name",Name} = lists:keyfind("name",3,Attrs), case catch list_to_integer(Name) of Line when is_integer(Line) -> case PrevLine + 1 of Line -> -% erlang:display({found_line,Line}), - {Exports,Line}; + {Exports,LastExprFuncs,Line}; Other -> ct:fail({unexpected_line_number_target,Other}) end; {'EXIT',_} -> - {match,[FStr,AStr]} = - re:run(Name,"^(.*)-([0-9]+)$",[{capture,all_but_first,list}]), + {match,[FStr,EndStr]} = + re:run(Name,"^(.*)-(last_expr|[0-9]+)$", + [{capture,all_but_first,list}]), F = list_to_atom(http_uri:decode(FStr)), - A = list_to_integer(AStr), -% erlang:display({found_fnc,F,A}), - A = proplists:get_value(F,Exports), - {lists:delete({F,A},Exports),PrevLine} + case EndStr of + "last_expr" -> + true = lists:member(F,LastExprFuncs), + {Exports,lists:delete(F,LastExprFuncs),PrevLine}; + _ -> + A = list_to_integer(EndStr), + A = proplists:get_value(F,Exports), + {lists:delete({F,A},Exports),LastExprFuncs,PrevLine} + end end; sax_event(_,State) -> State. diff --git a/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl b/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl new file mode 100644 index 0000000000..2a20850a3a --- /dev/null +++ b/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl @@ -0,0 +1 @@ +-define(EPP_SWITCH, on). diff --git a/lib/test_server/test/erl2html2_SUITE_data/m1.erl b/lib/test_server/test/erl2html2_SUITE_data/m1.erl index 156f1d0a51..1d405963a5 100644 --- a/lib/test_server/test/erl2html2_SUITE_data/m1.erl +++ b/lib/test_server/test/erl2html2_SUITE_data/m1.erl @@ -7,9 +7,15 @@ -include("header1.hrl"). -include("header2.hrl"). +-include("header3.hrl"). -define(MACRO1,value). +%% This macro is used to select parser in erl2html2. +%% If EPP_SWITCH is defined epp is used, else epp_dodger. +epp_switch() -> + ?EPP_SWITCH. + %%% Comment foo(x) -> %% Comment diff --git a/lib/tools/doc/src/cprof.xml b/lib/tools/doc/src/cprof.xml index 553597837e..bfddb9f5a8 100644 --- a/lib/tools/doc/src/cprof.xml +++ b/lib/tools/doc/src/cprof.xml @@ -66,7 +66,7 @@ <func> <name>analyse() -> {AllCallCount, ModAnalysisList}</name> <name>analyse(Limit) -> {AllCallCount, ModAnalysisList}</name> - <name>analyse(Mod) -> ModAnlysis</name> + <name>analyse(Mod) -> ModAnalysis</name> <name>analyse(Mod, Limit) -> ModAnalysis</name> <fsummary>Collect and analyse call counters.</fsummary> <type> diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index 85610d623a..38b57b73a9 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -30,65 +30,6 @@ </header> <p>This document describes the changes made to the Tools application.</p> -<section><title>Tools 2.8</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - In order to improve performance of the cover tool, new - functions are added for cover compilation and analysis on - multiple files. This allows for more parallelisation.</p> - <p> - Some improvements of the data base access is also done in - order to improve the performance when analysing and - resetting cover data.</p> - <p> - Minor incompatibility: An error reason from - analyse_to_file is changed from no_source_code_found to - {no_source_code_found,Module}.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-12330 Aux Id: seq12757 </p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Allow maps for supervisor flags and child specs</p> - <p> - Earlier, supervisor flags and child specs were given as - tuples. While this is kept for backwards compatibility, - it is now also allowed to give these parameters as maps, - see <seealso - marker="stdlib:supervisor#sup_flags">sup_flags</seealso> - and <seealso - marker="stdlib:supervisor#child_spec">child_spec</seealso>.</p> - <p> - Own Id: OTP-11043</p> - </item> - <item> - <p> - Remove Mnemosyne rules support.</p> - <p> - Own Id: OTP-12511</p> - </item> - <item> - <p> - Add printout of total number of calls and time in eprof</p> - <p> - Own Id: OTP-12681</p> - </item> - </list> - </section> - -</section> - <section><title>Tools 2.7.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 68c3f6e29c..3b3202d38b 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.8 +TOOLS_VSN = 2.7.2 diff --git a/lib/typer/doc/src/notes.xml b/lib/typer/doc/src/notes.xml index 045b55ffe4..23e22759d6 100644 --- a/lib/typer/doc/src/notes.xml +++ b/lib/typer/doc/src/notes.xml @@ -30,20 +30,6 @@ </header> <p>This document describes the changes made to TypEr.</p> -<section><title>TypEr 0.9.9</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> Properly extract annotations from core code. </p> - <p> - Own Id: OTP-12727</p> - </item> - </list> - </section> - -</section> - <section><title>TypEr 0.9.8</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk index 74c0ccfc59..ce658e257b 100644 --- a/lib/typer/vsn.mk +++ b/lib/typer/vsn.mk @@ -1 +1 @@ -TYPER_VSN = 0.9.9 +TYPER_VSN = 0.9.8 diff --git a/lib/webtool/doc/src/notes.xml b/lib/webtool/doc/src/notes.xml index 4800dd6df4..e571668c91 100644 --- a/lib/webtool/doc/src/notes.xml +++ b/lib/webtool/doc/src/notes.xml @@ -31,23 +31,6 @@ <p>This document describes the changes made to the Webtool application.</p> -<section><title>WebTool 0.9</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The Webtool application has been marked as obsolete and - will be removed from OTP in the next major release (OTP - 19.0).</p> - <p> - Own Id: OTP-10922 Aux Id: OTP-12705 </p> - </item> - </list> - </section> - -</section> - <section><title>WebTool 0.8.10</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c index ea52737fa2..ec1ba7f566 100644 --- a/lib/wx/c_src/wxe_driver.c +++ b/lib/wx/c_src/wxe_driver.c @@ -146,7 +146,12 @@ wxe_driver_stop(ErlDrvData handle) if(sd->port_handle != WXE_DRV_PORT_HANDLE) { // fprintf(stderr, "%s:%d: STOP \r\n", __FILE__,__LINE__); meta_command(DELETE_PORT,sd); - free(handle); + } else { + // fprintf(stderr, "%s:%d: STOP \r\n", __FILE__,__LINE__); + stop_native_gui(wxe_master); + unload_native_gui(); + free(wxe_master); + wxe_master = NULL; } } @@ -154,10 +159,6 @@ static void wxe_driver_unload(void) { // fprintf(stderr, "%s:%d: UNLOAD \r\n", __FILE__,__LINE__); - stop_native_gui(wxe_master); - unload_native_gui(); - free(wxe_master); - wxe_master = NULL; } static ErlDrvSSizeT diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp index ef648e008c..2fd5f0c52c 100644 --- a/lib/wx/c_src/wxe_impl.cpp +++ b/lib/wx/c_src/wxe_impl.cpp @@ -89,7 +89,7 @@ void push_command(int op,char * buf,int len, wxe_data *sd) } void meta_command(int what, wxe_data *sd) { - if(what == PING_PORT) { + if(what == PING_PORT && wxe_status == WXE_INITIATED) { erl_drv_mutex_lock(wxe_batch_locker_m); if(wxe_batch_caller > 0) { wxe_queue->Add(WXE_DEBUG_PING, NULL, 0, sd); @@ -98,9 +98,12 @@ void meta_command(int what, wxe_data *sd) { wxWakeUpIdle(); erl_drv_mutex_unlock(wxe_batch_locker_m); } else { - if(sd) { + if(sd && wxe_status == WXE_INITIATED) { wxeMetaCommand Cmd(sd, what); wxTheApp->AddPendingEvent(Cmd); + if(what == DELETE_PORT) { + free(sd); + } } } } @@ -169,6 +172,7 @@ void WxeApp::MacOpenFile(const wxString &filename) { #endif void WxeApp::shutdown(wxeMetaCommand& Ecmd) { + wxe_status = WXE_EXITING; ExitMainLoop(); delete wxe_queue; delete wxe_queue_cb_saved; @@ -200,6 +204,10 @@ void handle_event_callback(ErlDrvPort port, ErlDrvTermData process) { WxeApp * app = (WxeApp *) wxTheApp; ErlDrvMonitor monitor; + + if(wxe_status != WXE_INITIATED) + return; + // Is thread safe if pdl have been incremented if(driver_monitor_process(port, process, &monitor) == 0) { // Should we be able to handle commands when recursing? probably @@ -217,6 +225,8 @@ void handle_event_callback(ErlDrvPort port, ErlDrvTermData process) void WxeApp::dispatch_cmds() { + if(wxe_status != WXE_INITIATED) + return; erl_drv_mutex_lock(wxe_batch_locker_m); recurse_level++; int level = dispatch(wxe_queue_cb_saved, 0, WXE_STORED); diff --git a/lib/wx/c_src/wxe_impl.h b/lib/wx/c_src/wxe_impl.h index a0a1c84718..b251d5f0f9 100644 --- a/lib/wx/c_src/wxe_impl.h +++ b/lib/wx/c_src/wxe_impl.h @@ -46,7 +46,8 @@ typedef wxString wxeLocaleC; #define WXE_NOT_INITIATED 0 #define WXE_INITIATED 1 -#define WXE_EXITED 2 +#define WXE_EXITING 2 +#define WXE_EXITED 3 #define WXE_ERROR -1 void send_msg(const char *, const wxString *); // For debugging and error msgs diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml index d31e927458..682ab48ca0 100644 --- a/lib/wx/doc/src/notes.xml +++ b/lib/wx/doc/src/notes.xml @@ -31,55 +31,6 @@ <p>This document describes the changes made to the wxErlang application.</p> -<section><title>Wx 1.4</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - The undocumented option <c>generic_debug</c> for - <c>gen_server</c> has been removed.</p> - <p> - Own Id: OTP-12183</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Use wxWidgets-3.0, if found, as default backend on - windows.</p> - <p> - Own Id: OTP-12632</p> - </item> - <item> - <p> - Add missing fields in some events records. May require a - recompilation of user applications.</p> - <p> - Own Id: OTP-12660</p> - </item> - </list> - </section> - - - <section><title>Known Bugs and Problems</title> - <list> - <item> - <p> - Remove raise condition where <c>wx</c> could crash during - emulator stoppage.</p> - <p> - Own Id: OTP-12734</p> - </item> - </list> - </section> - -</section> - <section><title>Wx 1.3.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk index 09fb9f384c..942d4c0d6f 100644 --- a/lib/wx/vsn.mk +++ b/lib/wx/vsn.mk @@ -1 +1 @@ -WX_VSN = 1.4 +WX_VSN = 1.3.3 diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index b9cc2bd329..3fa1f01a79 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -31,20 +31,6 @@ <p>This document describes the changes made to the Xmerl application.</p> -<section><title>Xmerl 1.3.8</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> Remove compiler warnings in xmerl. </p> - <p> - Own Id: OTP-12689</p> - </item> - </list> - </section> - -</section> - <section><title>Xmerl 1.3.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/otp_versions.table b/otp_versions.table index 12790c88a8..fbed2ce427 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,3 +1,4 @@ +OTP-17.5.4 : inets-5.10.8 ssh-3.2.3 # asn1-3.0.4 common_test-1.10.1 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9.1 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4.1 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8.1 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : OTP-17.5.3 : common_test-1.10.1 diameter-1.9.1 erts-6.4.1 snmp-5.1.2 test_server-3.8.1 # asn1-3.0.4 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.7 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 ssh-3.2.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : OTP-17.5.2 : inets-5.10.7 ssh-3.2.2 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : OTP-17.5.1 : ssh-3.2.1 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : |