aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/bin/start.bootbin5285 -> 5285 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5285 -> 5285 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11540 -> 11536 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin12508 -> 12740 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin12264 -> 12156 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin5364 -> 5300 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin9384 -> 9444 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin14688 -> 14692 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_sets.beambin0 -> 2868 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin20368 -> 20368 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app3
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin49960 -> 50068 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin56796 -> 56912 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin47316 -> 47352 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin4668 -> 4620 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin32132 -> 32152 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6744 -> 6696 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin7156 -> 7136 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin28576 -> 28580 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin5480 -> 5460 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin13520 -> 13516 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23068 -> 23076 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin26652 -> 26652 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin12904 -> 12940 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_sctp.beambin2312 -> 2324 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app2
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup8
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7932 -> 7916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin11356 -> 11428 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_anno.beambin5008 -> 4900 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin89464 -> 89840 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app2
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup10
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin24080 -> 24072 bytes
-rw-r--r--erts/configure.in2
-rw-r--r--erts/doc/src/erl.xml9
-rw-r--r--erts/doc/src/erl_nif.xml230
-rw-r--r--erts/emulator/beam/erl_init.c9
-rw-r--r--erts/emulator/beam/erl_nif.c79
-rw-r--r--erts/emulator/beam/erl_nif.h8
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h4
-rw-r--r--erts/emulator/drivers/common/inet_drv.c58
-rw-r--r--erts/emulator/hipe/hipe_bif0.c6
-rw-r--r--erts/emulator/hipe/hipe_bif0.tab2
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c3
-rw-r--r--erts/emulator/test/monitor_SUITE.erl172
-rw-r--r--erts/emulator/test/nif_SUITE.erl52
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c75
-rw-r--r--erts/emulator/test/trace_meta_SUITE.erl610
-rw-r--r--erts/etc/common/erlexec.c2
-rw-r--r--erts/preloaded/src/prim_inet.erl21
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/compiler/doc/src/compile.xml506
-rw-r--r--lib/compiler/doc/src/ref_man.xml2
-rw-r--r--lib/compiler/src/Makefile1
-rw-r--r--lib/compiler/src/beam_dead.erl27
-rw-r--r--lib/compiler/src/beam_dict.erl78
-rw-r--r--lib/compiler/src/beam_jump.erl44
-rw-r--r--lib/compiler/src/beam_type.erl8
-rw-r--r--lib/compiler/src/cerl_sets.erl206
-rw-r--r--lib/compiler/src/compiler.app.src1
-rw-r--r--lib/compiler/src/sys_core_fold.erl144
-rw-r--r--lib/compiler/src/v3_codegen.erl57
-rw-r--r--lib/compiler/src/v3_kernel.erl12
-rw-r--r--lib/compiler/src/v3_life.erl17
-rw-r--r--lib/compiler/test/warnings_SUITE.erl24
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl2
-rw-r--r--lib/debugger/src/int.erl2
-rw-r--r--lib/debugger/test/map_SUITE.erl4
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml7
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl64
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl10
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/notes.xml32
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl187
-rw-r--r--lib/diameter/src/diameter.appup.src10
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl2
-rw-r--r--lib/diameter/test/diameter_3xxx_SUITE.erl72
-rw-r--r--lib/diameter/test/diameter_config_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl115
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl18
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl46
-rw-r--r--lib/diameter/test/diameter_transport_SUITE.erl17
-rw-r--r--lib/diameter/test/diameter_util.erl15
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl3
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/ei.xml3
-rw-r--r--lib/erl_interface/doc/src/erl_eterm.xml6
-rw-r--r--lib/erl_interface/src/decode/decode_big.c21
-rw-r--r--lib/erl_interface/src/encode/encode_double.c12
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c12
-rw-r--r--lib/erl_interface/src/misc/eidef.h21
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE.erl2
-rw-r--r--lib/et/src/Makefile2
-rw-r--r--lib/et/src/et_collector.erl2
-rw-r--r--lib/et/src/et_selector.erl2
-rw-r--r--lib/eunit/doc/overview.edoc15
-rw-r--r--lib/eunit/include/eunit.hrl20
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/hipe/cerl/cerl_pmatch.erl10
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl8
-rw-r--r--lib/hipe/llvm/hipe_llvm_main.erl2
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/inets/doc/src/Makefile3
-rw-r--r--lib/inets/doc/src/httpc.xml2
-rw-r--r--lib/inets/doc/src/httpd.xml10
-rw-r--r--lib/inets/doc/src/httpd_custom_api.xml63
-rw-r--r--lib/inets/doc/src/notes.xml18
-rw-r--r--lib/inets/doc/src/ref_man.xml3
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl6
-rw-r--r--lib/inets/src/http_server/Makefile3
-rw-r--r--lib/inets/src/http_server/httpd_custom.erl69
-rw-r--r--lib/inets/src/http_server/httpd_request.erl140
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl11
-rw-r--r--lib/inets/src/http_server/httpd_response.erl52
-rw-r--r--lib/inets/src/inets_app/inets.app.src1
-rw-r--r--lib/inets/test/httpc_SUITE.erl23
-rw-r--r--lib/inets/test/httpd_SUITE.erl49
-rw-r--r--lib/inets/vsn.mk4
-rw-r--r--lib/kernel/doc/src/error_logger.xml31
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml15
-rw-r--r--lib/kernel/src/inet.erl24
-rw-r--r--lib/kernel/src/inet_parse.erl30
-rw-r--r--lib/kernel/src/inet_sctp.erl19
-rw-r--r--lib/kernel/src/kernel.appup.src6
-rw-r--r--lib/kernel/test/code_SUITE.erl4
-rw-r--r--lib/kernel/test/error_logger_warn_SUITE.erl79
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl32
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl1611
-rw-r--r--lib/kernel/test/inet_SUITE.erl3
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/doc/src/megaco.xml4
-rw-r--r--lib/megaco/src/app/megaco.app.src2
-rw-r--r--lib/megaco/src/app/megaco.appup.src7
-rw-r--r--lib/megaco/src/engine/megaco_trans_sender.erl3
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/os_mon/c_src/cpu_sup.c94
-rw-r--r--lib/os_mon/doc/src/cpu_sup.xml2
-rw-r--r--lib/os_mon/src/cpu_sup.erl5
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl2
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/sasl_app.xml7
-rw-r--r--lib/sasl/src/sasl.erl8
-rw-r--r--lib/sasl/src/sasl_report_file_h.erl4
-rw-r--r--lib/sasl/test/sasl_SUITE.erl31
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/snmp_app.xml2
-rw-r--r--lib/snmp/doc/src/snmp_config.xml2
-rw-r--r--lib/snmp/src/app/snmp.appup.src4
-rw-r--r--lib/snmp/src/manager/snmpm.erl8
-rw-r--r--lib/ssh/doc/src/notes.xml27
-rw-r--r--lib/ssh/doc/src/ssh.xml102
-rw-r--r--lib/ssh/src/ssh.erl157
-rw-r--r--lib/ssh/src/ssh_auth.erl146
-rw-r--r--lib/ssh/src/ssh_auth.hrl2
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl121
-rw-r--r--lib/ssh/src/ssh_transport.erl141
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl60
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl10
-rw-r--r--lib/ssh/test/ssh_unicode_SUITE.erl1
-rw-r--r--lib/ssl/doc/src/notes.xml18
-rw-r--r--lib/ssl/src/ssl.appup.src10
-rw-r--r--lib/ssl/src/ssl_handshake.erl32
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl9
-rw-r--r--lib/stdlib/doc/src/orddict.xml7
-rw-r--r--lib/stdlib/doc/src/supervisor.xml12
-rw-r--r--lib/stdlib/src/erl_anno.erl4
-rw-r--r--lib/stdlib/src/erl_lint.erl68
-rw-r--r--lib/stdlib/src/orddict.erl108
-rw-r--r--lib/stdlib/src/stdlib.appup.src8
-rw-r--r--lib/stdlib/src/supervisor.erl2
-rw-r--r--lib/stdlib/src/zip.erl2
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl50
-rw-r--r--lib/stdlib/test/erl_anno_SUITE.erl1
-rw-r--r--lib/stdlib/test/ets_SUITE.erl14
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl2
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl18
-rw-r--r--lib/stdlib/test/rand_SUITE.erl2
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl23
-rw-r--r--lib/stdlib/test/unicode_SUITE.erl3
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/src/Makefile3
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src3
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tools/test/lcnt_SUITE.erl10
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--otp_versions.table2
-rw-r--r--system/doc/efficiency_guide/binaryhandling.xml1
200 files changed, 4254 insertions, 2705 deletions
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index c4244d91e4..25c092961c 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index c4244d91e4..25c092961c 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index 4e31355235..b25c33084f 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 6fc683bf76..a6757dfcfb 100644
--- a/bootstrap/lib/compiler/ebin/beam_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index 0bd9767208..6ae6de1d8b 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index ccab57cc81..17b3c40eda 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 136036ae39..8dd6375403 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam
index 4094cec9e6..cb3bc1a8bd 100644
--- a/bootstrap/lib/compiler/ebin/beam_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_sets.beam b/bootstrap/lib/compiler/ebin/cerl_sets.beam
new file mode 100644
index 0000000000..d16543cdbe
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/cerl_sets.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index 45f76f5c89..fc1a7e04f8 100644
--- a/bootstrap/lib/compiler/ebin/cerl_trees.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
index dfe077d5b9..814c2f0df5 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -18,7 +18,7 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "5.0.4"},
+ {vsn, "6.0"},
{modules, [
beam_a,
beam_asm,
@@ -45,6 +45,7 @@
cerl,
cerl_clauses,
cerl_inline,
+ cerl_sets,
cerl_trees,
compile,
core_scan,
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index 2ed29957b3..1b5467a54b 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index 7b33f9cbff..e53f0fcd12 100644
--- a/bootstrap/lib/compiler/ebin/v3_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 5817c6ae58..f5cdbb6e40 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam
index 6f68b83ad5..d8e98c021b 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index 9a7907cb38..c4fa46e33e 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index d8018f0a40..b81dfa81d3 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam
index 9a644b6b48..55d123c6a2 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index 7b0517c500..aa75ae9bd1 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index f162729d6a..bd73ff6c5c 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 46e3a567b5..fc12b6b194 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 36869dd323..a76806293f 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index bb2a6cb5e0..8c7c6ba218 100644
--- a/bootstrap/lib/kernel/ebin/inet_db.beam
+++ b/bootstrap/lib/kernel/ebin/inet_db.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index 6d28aa3c95..294afcea30 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_sctp.beam b/bootstrap/lib/kernel/ebin/inet_sctp.beam
index 503cbc11e2..19506c14e9 100644
--- a/bootstrap/lib/kernel/ebin/inet_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index bfeea49e91..54a61e6ef6 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -21,7 +21,7 @@
{application, kernel,
[
{description, "ERTS CXC 138 10"},
- {vsn, "3.2"},
+ {vsn, "4.0"},
{modules, [application,
application_controller,
application_master,
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
index 4cdfb47c24..a0c5e763c1 100644
--- a/bootstrap/lib/kernel/ebin/kernel.appup
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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
@@ -15,9 +15,9 @@
%% under the License.
%%
%% %CopyrightEnd%
-{"3.2",
+{"4.0",
%% Up from - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17
+ [{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17
%% Down to - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17
+ [{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17
}.
diff --git a/bootstrap/lib/kernel/ebin/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam
index 63bedc72d7..944f6a27a4 100644
--- a/bootstrap/lib/kernel/ebin/pg2.beam
+++ b/bootstrap/lib/kernel/ebin/pg2.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 54cf78d6ce..726a130b55 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam
index a07f730e99..4807dac5f9 100644
--- a/bootstrap/lib/stdlib/ebin/erl_anno.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 5a8bc2a7f1..16cacefb7c 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index 92ecc16b4c..6e830917ad 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -19,7 +19,7 @@
%%
{application, stdlib,
[{description, "ERTS CXC 138 10"},
- {vsn, "2.4"},
+ {vsn, "2.5"},
{modules, [array,
base64,
beam_lib,
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
index 2457f9b4ed..a1038a43b6 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.appup
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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
@@ -15,11 +15,9 @@
%% under the License.
%%
%% %CopyrightEnd%
-{"2.4",
+{"2.5",
%% Up from - max one major revision back
- [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
- {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0
+ [{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0-17.5
%% Down to - max one major revision back
- [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
- {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0
+ [{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0-17.5
}.
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index ec0c45c134..6dd01bf004 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index 39d3c51e3f..ce0cef871f 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -4710,6 +4710,8 @@ case $host_os in
use_cpu_sup=yes ;;
linux*)
use_cpu_sup=yes ;;
+ freebsd*)
+ use_cpu_sup=yes ;;
esac
if test "$use_cpu_sup" = "yes"; then
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index ea94a4e82b..98d05dc7de 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -1322,13 +1322,14 @@
<item>
<p>Verbose.</p>
</item>
- <tag><c><![CDATA[+W w | i]]></c></tag>
+ <tag><c><![CDATA[+W w | i | e]]></c></tag>
<item>
<p>Sets the mapping of warning messages for <c><![CDATA[error_logger]]></c>.
Messages sent to the error logger using one of the warning
- routines can be mapped either to errors (default), warnings
- (<c><![CDATA[+W w]]></c>), or info reports (<c><![CDATA[+W i]]></c>). The current
- mapping can be retrieved using
+ routines can be mapped either to errors (<c><![CDATA[+W e]]></c>),
+ warnings (<c><![CDATA[+W w]]></c>), or info reports
+ (<c><![CDATA[+W i]]></c>). The default is warnings.
+ The current mapping can be retrieved using
<c><![CDATA[error_logger:warning_map/0]]></c>. See
<seealso marker="kernel:error_logger#warning_map/0">error_logger(3)</seealso>
for further information.</p>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 4bad8b253c..f64381c99d 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -461,8 +461,9 @@ ok
independent environment with all its terms is valid until you explicitly
invalidates it with <seealso marker="#enif_free_env">enif_free_env</seealso>
or <c>enif_send</c>.</p>
- <p>All elements of a list/tuple must belong to the same environment as the
- list/tuple itself. Terms can be copied between environments with
+ <p>All contained terms of a list/tuple/map must belong to the same
+ environment as the list/tuple/map itself. Terms can be copied between
+ environments with
<seealso marker="#enif_make_copy">enif_make_copy</seealso>.</p>
</item>
<tag><marker id="ErlNifFunc"/>ErlNifFunc</tag>
@@ -564,11 +565,11 @@ typedef enum {
<funcs>
<func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
- <fsummary>Allocate dynamic memory.</fsummary>
+ <fsummary>Allocate dynamic memory</fsummary>
<desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext></name>
- <fsummary>Create a new binary.</fsummary>
+ <fsummary>Create a new binary</fsummary>
<desc><p>Allocate a new binary of size <c>size</c>
bytes. Initialize the structure pointed to by <c>bin</c> to
refer to the allocated binary. The binary must either be released by
@@ -595,7 +596,7 @@ typedef enum {
<desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc>
</func>
<func><name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext></name>
- <fsummary>Clear an environment for reuse.</fsummary>
+ <fsummary>Clear an environment for reuse</fsummary>
<desc><p>Free all terms in an environment and clear it for reuse. The environment must
have been allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.
</p></desc>
@@ -683,14 +684,14 @@ typedef enum {
<c>size-1</c>.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get the length of atom <c>term</c>.</fsummary>
+ <fsummary>Get the length of atom <c>term</c></fsummary>
<desc><p>Set <c>*len</c> to the length (number of bytes excluding
terminating null character) of the atom <c>term</c> with encoding
<c>encode</c>. Return true on success or false if <c>term</c> is not an
atom.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)</nametext></name>
- <fsummary>Read a floating-point number term.</fsummary>
+ <fsummary>Read a floating-point number term</fsummary>
<desc><p>Set <c>*dp</c> to the floating point value of
<c>term</c>. Return true on success or false if <c>term</c> is not a float.</p></desc>
</func>
@@ -719,17 +720,28 @@ typedef enum {
non-empty list.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)</nametext></name>
- <fsummary>Get the length of list <c>term</c>.</fsummary>
+ <fsummary>Get the length of list <c>term</c></fsummary>
<desc><p>Set <c>*len</c> to the length of list <c>term</c> and return true,
or return false if <c>term</c> is not a list.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)</nametext></name>
- <fsummary>Read an long integer term.</fsummary>
+ <fsummary>Read an long integer term</fsummary>
<desc><p>Set <c>*ip</c> to the long integer value of <c>term</c> and
return true, or return false if <c>term</c> is not an integer or is
outside the bounds of type <c>long int</c>.</p></desc>
</func>
- <func><name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext></name>
+ <func><name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)</nametext></name>
+ <fsummary>Read the size of a map term</fsummary>
+ <desc><p>Set <c>*size</c> to the number of key-value pairs in the map <c>term</c> and
+ return true, or return false if <c>term</c> is not a map.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext></name>
+ <fsummary>Get the value of a key in a map</fsummary>
+ <desc><p>Set <c>*value</c> to the value associated with <c>key</c> in the
+ map <c>map</c> and return true. Return false if <c>map</c> is not a map
+ or if <c>map</c> does not contain <c>key</c>.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext></name>
<fsummary>Get the pointer to a resource object</fsummary>
<desc><p>Set <c>*objp</c> to point to the resource object referred to by <c>term</c>.</p>
<p>Return true on success or false if <c>term</c> is not a handle to a resource object
@@ -738,7 +750,7 @@ typedef enum {
<func><name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env,
ERL_NIF_TERM list, char* buf, unsigned size,
ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get a C-string from a list.</fsummary>
+ <fsummary>Get a C-string from a list</fsummary>
<desc><p>Write a null-terminated string, in the buffer pointed to by
<c>buf</c> with size <c>size</c>, consisting of the characters
in the string <c>list</c>. The characters are written using encoding
@@ -751,7 +763,7 @@ typedef enum {
<c>size</c> is less than 1.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)</nametext></name>
- <fsummary>Inspect the elements of a tuple.</fsummary>
+ <fsummary>Inspect the elements of a tuple</fsummary>
<desc><p>If <c>term</c> is a tuple, set <c>*array</c> to point
to an array containing the elements of the tuple and set
<c>*arity</c> to the number of elements. Note that the array
@@ -761,28 +773,35 @@ typedef enum {
tuple.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)</nametext></name>
- <fsummary>Read an unsigned integer term.</fsummary>
+ <fsummary>Read an unsigned integer term</fsummary>
<desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and
return true, or return false if <c>term</c> is not an unsigned integer or
is outside the bounds of type <c>unsigned int</c>.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name>
- <fsummary>Read an unsigned 64-bit integer term.</fsummary>
+ <fsummary>Read an unsigned 64-bit integer term</fsummary>
<desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and
return true, or return false if <c>term</c> is not an unsigned integer or
is outside the bounds of an unsigned 64-bit integer.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name>
- <fsummary>Read an unsigned integer term.</fsummary>
+ <fsummary>Read an unsigned integer term</fsummary>
<desc><p>Set <c>*ip</c> to the unsigned long integer value of <c>term</c>
and return true, or return false if <c>term</c> is not an unsigned integer or is
outside the bounds of type <c>unsigned long</c>.</p></desc>
</func>
- <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env)</nametext></name>
- <fsummary>Check if an exception has been raised.</fsummary>
+ <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)</nametext></name>
+ <fsummary>Check if an exception has been raised</fsummary>
<desc><p>Return true if a pending exception is associated
- with the environment <c>env</c>. The only possible exception is currently
- <c>badarg</c> (see <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>).</p></desc>
+ with the environment <c>env</c>. If <c>reason</c> is a null pointer, ignore it.
+ Otherwise, if there's a pending exception associated with <c>env</c>, set the ERL_NIF_TERM
+ to which <c>reason</c> points to the value of the exception's term. For example, if
+ <seealso marker="#enif_make_badarg">enif_make_badarg</seealso> is called to set a
+ pending <c>badarg</c> exception, a subsequent call to <c>enif_has_pending_exception(env, &amp;reason)</c>
+ will set <c>reason</c> to the atom <c>badarg</c>, then return true.</p>
+ <p>See also: <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>
+ and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>.</p>
+ </desc>
</func>
<func><name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
<fsummary>Inspect the content of a binary</fsummary>
@@ -817,6 +836,10 @@ typedef enum {
<fsummary>Determine if a term is an exception</fsummary>
<desc><p>Return true if <c>term</c> is an exception.</p></desc>
</func>
+ <func><name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
+ <fsummary>Determine if a term is a map</fsummary>
+ <desc><p>Return true if <c>term</c> is a map, false otherwise.</p></desc>
+ </func>
<func><name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
<fsummary>Determine if a term is a number (integer or float)</fsummary>
<desc><p>Return true if <c>term</c> is a number.</p></desc>
@@ -890,18 +913,19 @@ typedef enum {
</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name>
- <fsummary>Make a badarg exception.</fsummary>
+ <fsummary>Make a badarg exception</fsummary>
<desc><p>Make a badarg exception to be returned from a NIF, and associate
it with the environment <c>env</c>. Once a NIF or any function
it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a
<c>badarg</c> exception is raised when the NIF returns, even if the NIF
attempts to return a non-exception term instead.
- The return value from <c>enif_make_badarg</c> may only be used as
- return value from the NIF that invoked it (direct or indirectly)
+ The return value from <c>enif_make_badarg</c> may be used only as the
+ return value from the NIF that invoked it (directly or indirectly)
or be passed to
<seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
not to any other NIF API function.</p>
- <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>.
+ <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>
+ and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>
</p>
<note><p>In earlier versions (older than erts-7.0, OTP 18) the return value
from <c>enif_make_badarg</c> had to be returned from the NIF. This
@@ -909,14 +933,14 @@ typedef enum {
if <c>enif_make_badarg</c> has been invoked.</p></note></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext></name>
- <fsummary>Make a binary term.</fsummary>
+ <fsummary>Make a binary term</fsummary>
<desc><p>Make a binary term from <c>bin</c>. Any ownership of
the binary data will be transferred to the created term and
<c>bin</c> should be considered read-only for the rest of the NIF
call and then as released.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)</nametext></name>
- <fsummary>Make a copy of a term.</fsummary>
+ <fsummary>Make a copy of a term</fsummary>
<desc><p>Make a copy of term <c>src_term</c>. The copy will be created in
environment <c>dst_env</c>. The source term may be located in any
environment.</p></desc>
@@ -957,7 +981,7 @@ typedef enum {
<desc><p>Create an integer term from a signed 64-bit integer.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext></name>
- <fsummary>Create a list term.</fsummary>
+ <fsummary>Create a list term</fsummary>
<desc><p>Create an ordinary list term of length <c>cnt</c>. Expects
<c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the
elements of the list. An empty list is returned if <c>cnt</c> is 0.</p></desc>
@@ -971,28 +995,21 @@ typedef enum {
<name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
<name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
<name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
- <fsummary>Create a list term.</fsummary>
+ <fsummary>Create a list term</fsummary>
<desc><p>Create an ordinary list term with length indicated by the
function name. Prefer these functions (macros) over the variadic
<c>enif_make_list</c> to get a compile time error if the number of
arguments does not match.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name>
- <fsummary>Create a list cell.</fsummary>
+ <fsummary>Create a list cell</fsummary>
<desc><p>Create a list cell <c>[head | tail]</c>.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name>
- <fsummary>Create a list term from an array.</fsummary>
+ <fsummary>Create a list term from an array</fsummary>
<desc><p>Create an ordinary list containing the elements of array <c>arr</c>
of length <c>cnt</c>. An empty list is returned if <c>cnt</c> is 0.</p></desc>
</func>
- <func><name><ret>int</ret><nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list)</nametext></name>
- <fsummary>Create the reverse list of the list <c>term</c>.</fsummary>
- <desc><p>Set <c>*list</c> to the reverse list of the list <c>term</c> and return true,
- or return false if <c>term</c> is not a list. This function should only be used on
- short lists as a copy will be created of the list which will not be released until after the
- nif returns.</p></desc>
- </func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name>
<fsummary>Create an integer term from a long int</fsummary>
<desc><p>Create an integer term from a <c>long int</c>.</p></desc>
@@ -1007,12 +1024,42 @@ typedef enum {
reallocated.</p><p>Return a pointer to the raw binary data and set
<c>*termp</c> to the binary term.</p></desc>
</func>
+ <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name>
+ <fsummary>Make an empty map term</fsummary>
+ <desc><p>Make an empty map term.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Insert key-value pair in map</fsummary>
+ <desc><p>Make a copy of map <c>map_in</c> and insert <c>key</c> with
+ <c>value</c>. If <c>key</c> already exists in <c>map_in</c>, the old
+ associated value is replaced by <c>value</c>. If successful set
+ <c>*map_out</c> to the new map and return true. Return false if
+ <c>map_in</c> is not a map.</p>
+ <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Replace value for key in map</fsummary>
+ <desc><p>Make a copy of map <c>map_in</c> and replace the old associated
+ value for <c>key</c> with <c>new_value</c>. If successful set
+ <c>*map_out</c> to the new map and return true. Return false if
+ <c>map_in</c> is not a map or if it does no contain <c>key</c>.</p>
+ <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Remove key from map</fsummary>
+ <desc><p>If map <c>map_in</c> contains <c>key</c>, make a copy of
+ <c>map_in</c> in <c>*map_out</c> and remove <c>key</c> and associated
+ value. If map <c>map_in</c> does not contain <c>key</c>, set
+ <c>*map_out</c> to <c>map_in</c>. Return true for success or false if
+ <c>map_in</c> is not a map.</p>
+ <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
+ </func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext></name>
<fsummary>Make a pid term</fsummary>
<desc><p>Make a pid term from <c>*pid</c>.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ref(ErlNifEnv* env)</nametext></name>
- <fsummary>Create a reference.</fsummary>
+ <fsummary>Create a reference</fsummary>
<desc><p>Create a reference like <seealso marker="erlang#make_ref-0">erlang:make_ref/0</seealso>.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext></name>
@@ -1050,20 +1097,28 @@ typedef enum {
<seealso marker="#enif_release_resource">enif_release_resource</seealso>.</p>
</desc>
</func>
+ <func><name><ret>int</ret><nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)</nametext></name>
+ <fsummary>Create the reverse of a list</fsummary>
+ <desc><p>Set <c>*list_out</c> to the reverse list of the list <c>list_in</c> and return true,
+ or return false if <c>list_in</c> is not a list. This function should only be used on
+ short lists as a copy will be created of the list which will not be released until after the
+ nif returns.</p>
+ <p>The <c>list_in</c> term must belong to the environment <c>env</c>.</p></desc>
+ </func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create a string.</fsummary>
+ <fsummary>Create a string</fsummary>
<desc><p>Create a list containing the characters of the
null-terminated string <c>string</c> with encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create a string.</fsummary>
+ <fsummary>Create a string</fsummary>
<desc><p>Create a list containing the characters of the string <c>string</c> with
length <c>len</c> and encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.
Null-characters are treated as any other characters.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv*
env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name>
- <fsummary>Make a subbinary term.</fsummary>
+ <fsummary>Make a subbinary term</fsummary>
<desc><p>Make a subbinary of binary <c>bin_term</c>, starting at
zero-based position <c>pos</c> with a length of <c>size</c> bytes.
<c>bin_term</c> must be a binary or bitstring and
@@ -1071,7 +1126,7 @@ typedef enum {
bytes in <c>bin_term</c>.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)</nametext></name>
- <fsummary>Create a tuple term.</fsummary>
+ <fsummary>Create a tuple term</fsummary>
<desc><p>Create a tuple term of arity <c>cnt</c>. Expects
<c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the
elements of the tuple.</p></desc>
@@ -1085,14 +1140,14 @@ typedef enum {
<name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
<name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
<name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
- <fsummary>Create a tuple term.</fsummary>
+ <fsummary>Create a tuple term</fsummary>
<desc><p>Create a tuple term with length indicated by the
function name. Prefer these functions (macros) over the variadic
<c>enif_make_tuple</c> to get a compile time error if the number of
arguments does not match.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name>
- <fsummary>Create a tuple term from an array.</fsummary>
+ <fsummary>Create a tuple term from an array</fsummary>
<desc><p>Create a tuple containing the elements of array <c>arr</c>
of length <c>cnt</c>.</p></desc>
</func>
@@ -1108,6 +1163,72 @@ typedef enum {
<fsummary>Create an integer term from an unsigned long int</fsummary>
<desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc>
</func>
+ <func><name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)</nametext></name>
+ <fsummary>Create a map iterator</fsummary>
+ <desc><p>Create an iterator for the map <c>map</c> by initializing the
+ structure pointed to by <c>iter</c>. The <c>entry</c> argument determines
+ the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c> or
+ <c>ERL_NIF_MAP_ITERATOR_LAST</c>. Return true on success or false if
+ <c>map</c> is not a map.</p>
+ <p>A map iterator is only useful during the lifetime of the environment
+ <c>env</c> that the <c>map</c> belongs to. The iterator must be destroyed by
+ calling <seealso marker="#enif_map_iterator_destroy">
+ enif_map_iterator_destroy</seealso>.</p>
+ <code type="none">
+ERL_NIF_TERM key, value;
+ErlNifMapIterator iter;
+enif_map_iterator_create(env, my_map, ERL_NIF_MAP_ITERATOR_FIRST);
+
+while (enif_map_iterator_get_pair(env, &amp;iter, &amp;key, &amp;value)) {
+ do_something(key,value);
+ enif_map_iterator_next(env, &amp;iter);
+}
+enif_map_iterator_destroy(env, &amp;iter);
+ </code>
+ <note><p>The key-value pairs of a map have no defined iteration
+ order. The only guarantee is that the iteration order of a single map
+ instance is preserved during the lifetime of the environment that the map
+ belongs to.</p>
+ </note>
+ </desc>
+ </func>
+ <func><name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Destroy a map iterator</fsummary>
+ <desc><p>Destroy a map iterator created by
+ <seealso marker="#enif_map_iterator_create">enif_map_iterator_create</seealso>.
+ </p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)</nametext></name>
+ <fsummary>Get key and value at current map iterator position</fsummary>
+ <desc><p>Get key and value terms at current map iterator position.
+ On success set <c>*key</c> and <c>*value</c> and return true.
+ Return false if the iterator is positioned at head (before first entry)
+ or tail (beyond last entry).</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Check if map iterator is positioned before first</fsummary>
+ <desc><p>Return true if map iterator <c>iter</c> is positioned
+ before first entry.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Check if map iterator is positioned after last</fsummary>
+ <desc><p>Return true if map iterator <c>iter</c> is positioned
+ after last entry.</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Increment map iterator to point to next entry</fsummary>
+ <desc><p>Increment map iterator to point to next key-value entry.
+ Return true if the iterator is now positioned at a valid key-value entry,
+ or false if the iterator is positioned at the tail (beyond the last
+ entry).</p></desc>
+ </func>
+ <func><name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Decrement map iterator to point to previous entry</fsummary>
+ <desc><p>Decrement map iterator to point to previous key-value entry.
+ Return true if the iterator is now positioned at a valid key-value entry,
+ or false if the iterator is positioned at the head (before the first
+ entry).</p></desc>
+ </func>
<func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>.
@@ -1168,19 +1289,32 @@ typedef enum {
<c>reload</c> or <c>upgrade</c>.</p>
<p>Was previously named <c>enif_get_data</c>.</p></desc>
</func>
+ <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)</nametext></name>
+ <fsummary>Raise a NIF error exception</fsummary>
+ <desc><p>Create an error exception with the term <c>reason</c> to be returned from a NIF,
+ and associate it with the environment <c>env</c>. Once a NIF or any function it calls
+ invokes <c>enif_raise_exception</c>, the runtime ensures that the exception it creates
+ is raised when the NIF returns, even if the NIF attempts to return a non-exception
+ term instead. The return value from <c>enif_raise_exception</c> may be used only as
+ the return value from the NIF that invoked it (directly or indirectly) or be passed
+ to <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
+ not to any other NIF API function.</p>
+ <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>
+ and <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.</p></desc>
+ </func>
<func><name><ret>int</ret><nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext></name>
- <fsummary>Change the size of a binary.</fsummary>
+ <fsummary>Change the size of a binary</fsummary>
<desc><p>Change the size of a binary <c>bin</c>. The source binary
may be read-only, in which case it will be left untouched and
a mutable copy is allocated and assigned to <c>*bin</c>. Return true on success,
false if memory allocation failed.</p></desc>
</func>
<func><name><ret>void</ret><nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name>
- <fsummary>Release a binary.</fsummary>
+ <fsummary>Release a binary</fsummary>
<desc><p>Release a binary obtained from <c>enif_alloc_binary</c>.</p></desc>
</func>
<func><name><ret>void</ret><nametext>enif_release_resource(void* obj)</nametext></name>
- <fsummary>Release a resource object.</fsummary>
+ <fsummary>Release a resource object</fsummary>
<desc><p>Remove a reference to resource object <c>obj</c>obtained from
<seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
The resource object will be destructed when the last reference is removed.
@@ -1256,12 +1390,12 @@ typedef enum {
</desc>
</func>
<func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
- <fsummary>Get the pid of the calling process.</fsummary>
+ <fsummary>Get the pid of the calling process</fsummary>
<desc><p>Initialize the pid variable <c>*pid</c> to represent the
calling process. Return <c>pid</c>.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_send(ErlNifEnv* env, ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name>
- <fsummary>Send a message to a process.</fsummary>
+ <fsummary>Send a message to a process</fsummary>
<desc><p>Send a message to a process.</p>
<taglist>
<tag><c>env</c></tag>
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 988ff0e2b5..33417833a9 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -625,7 +625,7 @@ void erts_usage(void)
erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n");
- erts_fprintf(stderr, "-W<i|w> set error logger warnings mapping,\n");
+ erts_fprintf(stderr, "-W<i|w|e> set error logger warnings mapping,\n");
erts_fprintf(stderr, " see error_logger documentation for details\n");
erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n");
erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024);
@@ -1253,7 +1253,7 @@ erl_start(int argc, char **argv)
verbose = DEBUG_DEFAULT;
#endif
- erts_error_logger_warnings = am_error;
+ erts_error_logger_warnings = am_warning;
while (i < argc) {
if (argv[i][0] != '-') {
@@ -1991,11 +1991,12 @@ erl_start(int argc, char **argv)
case 'i':
erts_error_logger_warnings = am_info;
break;
+ case 'e':
+ erts_error_logger_warnings = am_error;
+ break;
case 'w':
erts_error_logger_warnings = am_warning;
break;
- case 'e': /* The default */
- erts_error_logger_warnings = am_error;
default:
erts_fprintf(stderr, "unrecognized warning_map option %s\n", arg);
erts_usage();
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 426a00304e..45fc949b81 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -445,7 +445,7 @@ int enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)
int enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)
{
- return term == THE_NON_VALUE;
+ return env->exception_thrown && term == THE_NON_VALUE;
}
int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)
@@ -737,12 +737,21 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
Eterm enif_make_badarg(ErlNifEnv* env)
{
+ return enif_raise_exception(env, am_badarg);
+}
+
+Eterm enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)
+{
env->exception_thrown = 1;
- BIF_ERROR(env->proc, BADARG);
+ env->proc->fvalue = reason;
+ BIF_ERROR(env->proc, EXC_ERROR);
}
-int enif_has_pending_exception(ErlNifEnv* env)
+int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)
{
+ if (env->exception_thrown && reason != NULL) {
+ *reason = env->proc->fvalue;
+ }
return env->exception_thrown;
}
@@ -1538,12 +1547,13 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
* NIF exports need a few more items than the Export struct provides,
* including the erl_module_nif* and a NIF function pointer, so the
* NifExport below adds those. The Export member must be first in the
- * struct. The saved_mfa, saved_argc, nif_level, alloced_argv_sz and argv
- * members are used to track the MFA and arguments of the top NIF in case a
- * chain of one or more enif_schedule_nif() calls results in an exception,
- * since in that case the original MFA and registers have to be restored
- * before returning to Erlang to ensure stacktrace information associated
- * with the exception is correct.
+ * struct. The saved_mfa, exception_thrown, saved_argc, rootset_extra, and
+ * rootset members are used to track the MFA, any pending exception, and
+ * arguments of the top NIF in case a chain of one or more
+ * enif_schedule_nif() calls results in an exception, since in that case
+ * the original MFA and registers have to be restored before returning to
+ * Erlang to ensure stacktrace information associated with the exception is
+ * correct.
*/
typedef ERL_NIF_TERM (*NativeFunPtr)(ErlNifEnv*, int, const ERL_NIF_TERM[]);
@@ -1552,25 +1562,28 @@ typedef struct {
struct erl_module_nif* m;
NativeFunPtr fp;
Eterm saved_mfa[3];
+ int exception_thrown;
int saved_argc;
- int alloced_argv_sz;
- Eterm argv[1];
+ int rootset_extra;
+ Eterm rootset[1];
} NifExport;
/*
* If a process has saved arguments, they need to be part of the GC
* rootset. The function below is called from setup_rootset() in
- * erl_gc.c. This function is declared in erl_process.h.
+ * erl_gc.c. This function is declared in erl_process.h. Any exception term
+ * saved in the NifExport is also made part of the GC rootset here; it
+ * always resides in rootset[0].
*/
int
erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj)
{
NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- int gc = (ep && ep->saved_argc > 0);
+ int gc = ep && (ep->saved_argc > 0 || ep->rootset[0] != NIL);
if (gc) {
- *objv = ep->argv;
- *nobj = ep->saved_argc;
+ *objv = ep->rootset;
+ *nobj = 1 + ep->saved_argc;
}
return gc;
}
@@ -1582,14 +1595,14 @@ static NifExport*
allocate_nif_sched_data(Process* proc, int argc)
{
NifExport* ep;
- size_t argv_extra, total;
+ size_t total;
int i;
- argv_extra = argc > 1 ? sizeof(Eterm)*(argc-1) : 0;
- total = sizeof(NifExport) + argv_extra;
+ total = sizeof(NifExport) + argc*sizeof(Eterm);
ep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, total);
sys_memset((void*) ep, 0, total);
- ep->alloced_argv_sz = argc;
+ ep->rootset_extra = argc;
+ ep->rootset[0] = NIL;
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
ep->exp.addressv[i] = &ep->exp.code[3];
}
@@ -1630,15 +1643,22 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
if (!ep)
ep = allocate_nif_sched_data(proc, argc);
- else if (need_save && ep->alloced_argv_sz < argc) {
+ else if (need_save && ep->rootset_extra < argc) {
NifExport* new_ep = allocate_nif_sched_data(proc, argc);
destroy_nif_export(ep);
ep = new_ep;
}
+ if (env->exception_thrown) {
+ ep->exception_thrown = 1;
+ ep->rootset[0] = env->proc->fvalue;
+ } else {
+ ep->exception_thrown = 0;
+ ep->rootset[0] = NIL;
+ }
ERTS_VBUMP_ALL_REDS(proc);
for (i = 0; i < argc; i++) {
if (need_save)
- ep->argv[i] = reg[i];
+ ep->rootset[i+1] = reg[i];
reg[i] = (Eterm) argv[i];
}
if (need_save) {
@@ -1674,7 +1694,7 @@ restore_nif_mfa(Process* proc, NifExport* ep, int exception)
proc->current[2] = ep->saved_mfa[2];
if (exception)
for (i = 0; i < ep->saved_argc; i++)
- reg[i] = ep->argv[i];
+ reg[i] = ep->rootset[i+1];
ep->saved_argc = 0;
ep->saved_mfa[0] = THE_NON_VALUE;
}
@@ -1699,6 +1719,7 @@ dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data));
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
+ ASSERT(!ep->exception_thrown);
if (ep->fp)
restore_nif_mfa(proc, ep, 0);
return argv[0];
@@ -1716,9 +1737,10 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data));
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
+ ASSERT(ep->exception_thrown);
if (ep->fp)
restore_nif_mfa(proc, ep, 1);
- return enif_make_badarg(env);
+ return enif_raise_exception(env, ep->rootset[0]);
}
/*
@@ -1843,6 +1865,7 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
NifExport* ep;
ERL_NIF_TERM result;
+ ASSERT(!env->exception_thrown);
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep);
ep->fp = NULL;
@@ -1855,7 +1878,7 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
* which case we need to restore the original NIF MFA.
*/
if (ep->fp == NULL)
- restore_nif_mfa(proc, ep, is_non_value(result) && proc->freason != TRAP);
+ restore_nif_mfa(proc, ep, env->exception_thrown);
return result;
}
@@ -2024,8 +2047,8 @@ int enif_map_iterator_create(ErlNifEnv *env,
size_t offset;
switch (entry) {
- case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break;
- case ERL_NIF_MAP_ITERATOR_TAIL: offset = flatmap_get_size(mp) - 1; break;
+ case ERL_NIF_MAP_ITERATOR_FIRST: offset = 0; break;
+ case ERL_NIF_MAP_ITERATOR_LAST: offset = flatmap_get_size(mp) - 1; break;
default: goto error;
}
@@ -2048,12 +2071,12 @@ int enif_map_iterator_create(ErlNifEnv *env,
WSTACK_INIT(iter->u.hash.wstack, ERTS_ALC_T_NIF);
switch (entry) {
- case ERL_NIF_MAP_ITERATOR_HEAD:
+ case ERL_NIF_MAP_ITERATOR_FIRST:
iter->idx = 1;
hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 0);
iter->u.hash.kv = hashmap_iterator_next(&iter->u.hash.wstack->ws);
break;
- case ERL_NIF_MAP_ITERATOR_TAIL:
+ case ERL_NIF_MAP_ITERATOR_LAST:
iter->idx = hashmap_size(map);
hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 1);
iter->u.hash.kv = hashmap_iterator_prev(&iter->u.hash.wstack->ws);
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index c4fdfd4187..af806736fd 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -218,8 +218,12 @@ typedef struct /* All fields all internal and may change */
} ErlNifMapIterator;
typedef enum {
- ERL_NIF_MAP_ITERATOR_HEAD = 1,
- ERL_NIF_MAP_ITERATOR_TAIL = 2
+ ERL_NIF_MAP_ITERATOR_FIRST = 1,
+ ERL_NIF_MAP_ITERATOR_LAST = 2,
+
+ /* deprecated synonyms (undocumented in 17 and 18-rc) */
+ ERL_NIF_MAP_ITERATOR_HEAD = ERL_NIF_MAP_ITERATOR_FIRST,
+ ERL_NIF_MAP_ITERATOR_TAIL = ERL_NIF_MAP_ITERATOR_LAST
} ErlNifMapIteratorEntry;
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index bdcbb32c46..e38a016958 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -156,7 +156,8 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIte
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[]));
-ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env));
+ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_raise_exception, (ErlNifEnv *env, ERL_NIF_TERM reason));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
@@ -307,6 +308,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair)
# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif)
# define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception)
+# define enif_raise_exception ERL_NIF_API_FUNC_MACRO(enif_raise_exception)
/*
** ADD NEW ENTRIES HERE (before this comment)
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 5196eb51c6..e001f31932 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -293,6 +293,10 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
static unsigned long zero_value = 0;
static unsigned long one_value = 1;
+#define TCP_SHUT_WR SD_SEND
+#define TCP_SHUT_RD SD_RECEIVE
+#define TCP_SHUT_RDWR SD_BOTH
+
#elif defined (__OSE__)
/*
@@ -421,6 +425,10 @@ typedef unsigned long u_long;
inet_driver_select((d), (flags), (onoff)); \
} while(0)
+#define TCP_SHUT_WR SHUT_WR
+#define TCP_SHUT_RD SHUT_RD
+#define TCP_SHUT_RDWR SHUT_RDWR
+
#else /* !__OSE__ && !__WIN32__ */
#include <sys/time.h>
@@ -691,6 +699,9 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \
} while(0)
+#define TCP_SHUT_WR SHUT_WR
+#define TCP_SHUT_RD SHUT_RD
+#define TCP_SHUT_RDWR SHUT_RDWR
#endif /* !__WIN32__ && !__OSE__ */
@@ -820,6 +831,10 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define TCP_ADDF_CLOSE_SENT 2 /* Close sent (active mode only) */
#define TCP_ADDF_DELAYED_CLOSE_RECV 4 /* If receive fails, report {error,closed} (passive mode) */
#define TCP_ADDF_DELAYED_CLOSE_SEND 8 /* If send fails, report {error,closed} (passive mode) */
+#define TCP_ADDF_PENDING_SHUT_WR 16 /* Call shutdown(sock, SHUT_WR) when queue empties */
+#define TCP_ADDF_PENDING_SHUT_RDWR 32 /* Call shutdown(sock, SHUT_RDWR) when queue empties */
+#define TCP_ADDF_PENDING_SHUTDOWN \
+ (TCP_ADDF_PENDING_SHUT_WR | TCP_ADDF_PENDING_SHUT_RDWR)
/* *_REQ_* replies */
#define INET_REP_ERROR 0
@@ -1407,6 +1422,8 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev);
static int tcp_recv(tcp_descriptor* desc, int request_len);
static int tcp_deliver(tcp_descriptor* desc, int len);
+static int tcp_shutdown_error(tcp_descriptor* desc, int err);
+
static int tcp_inet_output(tcp_descriptor* desc, HANDLE event);
static int tcp_inet_input(tcp_descriptor* desc, HANDLE event);
@@ -9473,10 +9490,18 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_error(EINVAL, rbuf, rsize);
}
how = buf[0];
- if (sock_shutdown(INETP(desc)->s, how) == 0) {
+ if (how != TCP_SHUT_RD && driver_sizeq(desc->inet.port) > 0) {
+ if (how == TCP_SHUT_WR) {
+ desc->tcp_add_flags |= TCP_ADDF_PENDING_SHUT_WR;
+ } else if (how == TCP_SHUT_RDWR) {
+ desc->tcp_add_flags |= TCP_ADDF_PENDING_SHUT_RDWR;
+ }
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
- } else {
+ }
+ if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) {
return ctl_error(sock_errno(), rbuf, rsize);
+ } else {
+ return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
}
default:
@@ -9613,6 +9638,8 @@ static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev)
else
inet_reply_error(INETP(desc), ENOTCONN);
}
+ else if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN)
+ tcp_shutdown_error(desc, EPIPE);
else if (tcp_sendv(desc, ev) == 0)
inet_reply_ok(INETP(desc));
DEBUGF(("tcp_inet_commandv(%ld) }\r\n", (long)desc->inet.port));
@@ -10506,7 +10533,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
return ret;
}
-static int tcp_send_error(tcp_descriptor* desc, int err)
+static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err)
{
/*
* If the port is busy, we must do some clean-up before proceeding.
@@ -10563,6 +10590,16 @@ static int tcp_send_error(tcp_descriptor* desc, int err)
return -1;
}
+static int tcp_send_error(tcp_descriptor* desc, int err)
+{
+ return tcp_send_or_shutdown_error(desc, err);
+}
+
+static int tcp_shutdown_error(tcp_descriptor* desc, int err)
+{
+ return tcp_send_or_shutdown_error(desc, err);
+}
+
/*
** Send non-blocking vector data
*/
@@ -10763,6 +10800,19 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len)
return 0;
}
+/* shutdown on the socket:
+** Assume caller has confirmed TCP_ADDF_PENDING_SHUTDOWN is set.
+*/
+static void tcp_shutdown_async(tcp_descriptor* desc)
+{
+ int how;
+
+ how = (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUT_WR) ?
+ TCP_SHUT_WR : TCP_SHUT_RDWR;
+ if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how)))
+ tcp_shutdown_error(desc, sock_errno());
+}
+
#ifdef __OSE__
static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event)
@@ -10891,6 +10941,8 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
if ((iov = driver_peekq(ix, &vsize)) == NULL) {
sock_select(INETP(desc), FD_WRITE, 0);
send_empty_out_q_msgs(INETP(desc));
+ if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN)
+ tcp_shutdown_async(desc);
goto done;
}
vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize;
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 099f4f90de..a4311d22e2 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1745,13 +1745,11 @@ BIF_RETTYPE hipe_bifs_check_crc_1(BIF_ALIST_1)
BIF_RET(am_false);
}
-BIF_RETTYPE hipe_bifs_system_crc_1(BIF_ALIST_1)
+BIF_RETTYPE hipe_bifs_system_crc_0(BIF_ALIST_0)
{
Uint crc;
- if (!term_to_Uint(BIF_ARG_1, &crc))
- BIF_ERROR(BIF_P, BADARG);
- crc ^= (HIPE_SYSTEM_CRC ^ HIPE_LITERALS_CRC);
+ crc = HIPE_SYSTEM_CRC;
BIF_RET(Uint_to_term(crc, BIF_P));
}
diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab
index a3e04802df..4271a78de3 100644
--- a/erts/emulator/hipe/hipe_bif0.tab
+++ b/erts/emulator/hipe/hipe_bif0.tab
@@ -74,7 +74,7 @@ bif hipe_bifs:set_native_address_in_fe/2
bif hipe_bifs:find_na_or_make_stub/2
bif hipe_bifs:check_crc/1
-bif hipe_bifs:system_crc/1
+bif hipe_bifs:system_crc/0
bif hipe_bifs:get_rts_param/1
#bif hipe_bifs:tuple_to_float/1
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index ed355ce264..49e8d39360 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -648,8 +648,7 @@ static int do_e(FILE *fp, const char* this_exe)
fprintf(fp, "-define(HIPE_SYSTEM_CRC, %u).\n", system_crc);
}
else {
- fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc(%u)).\n",
- literals_crc);
+ fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc()).\n");
}
return 0;
}
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index dc215b1529..7326dfceb1 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -665,98 +665,86 @@ list_cleanup(Config) when is_list(Config) ->
mixer(doc) ->
"Test mixing of internal and external monitors.";
mixer(Config) when is_list(Config) ->
- ?line PA = filename:dirname(code:which(?MODULE)),
- ?line NN = [j0,j1,j2,j3],
-% ?line NN = [j0,j1],
- ?line NL0 = [begin
- {ok, J} = test_server:start_node
- (X, slave, [{args, "-pa " ++ PA}]),
- J
- end || X <- NN],
- ?line NL1 = lists:duplicate(2,node()) ++ NL0,
- ?line Perm = perm(NL1),
- ?line lists:foreach(
- fun(NL) ->
- ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ],
- ?line [ask_jeeves(P,{monitor_process,self()}) || P <- Js],
- ?line {monitored_by,MB} =
- process_info(self(),monitored_by),
- ?line MBL = lists:sort(MB),
- ?line JsL = lists:sort(Js),
- ?line MBL = JsL,
- ?line {monitors,[]} = process_info(self(),monitors),
- ?line [tell_jeeves(P,{exit,flaff}) || P <- Js],
- ?line wait_for_m([],[],200)
- end,
- Perm),
- ?line lists:foreach(
- fun(NL) ->
- ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ],
- ?line Rs = [begin
- {monitor_process,Ref} =
- ask_jeeves(P,{monitor_process,self()}),
- {P,Ref}
- end
- || P <- Js],
- ?line {monitored_by,MB} =
- process_info(self(),monitored_by),
- ?line MBL = lists:sort(MB),
- ?line JsL = lists:sort(Js),
- ?line MBL = JsL,
- ?line {monitors,[]} = process_info(self(),monitors),
- ?line [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs],
- ?line wait_for_m([],[],200),
- ?line [tell_jeeves(P,{exit,flaff}) || P <- Js]
- end,
- Perm),
- ?line lists:foreach(
- fun(NL) ->
- ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ],
- ?line [ask_jeeves(P,{monitor_process,self()}) || P <- Js],
- ?line [erlang:monitor(process,P) || P <- Js],
- ?line {monitored_by,MB} =
- process_info(self(),monitored_by),
- ?line MBL = lists:sort(MB),
- ?line JsL = lists:sort(Js),
- ?line MBL = JsL,
- ?line {monitors,M} =
- process_info(self(),monitors),
- ?line ML = lists:sort([P||{process,P} <- M]),
- ?line ML = JsL,
- ?line [begin
- tell_jeeves(P,{exit,flaff}),
- receive {'DOWN',_,process,P,_} -> ok end
- end || P <- Js],
- ?line wait_for_m([],[],200)
- end,
- Perm),
- ?line lists:foreach(
- fun(NL) ->
- ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ],
- ?line Rs = [begin
- {monitor_process,Ref} =
- ask_jeeves(P,{monitor_process,self()}),
- {P,Ref}
- end
- || P <- Js],
- ?line R2s = [{P,erlang:monitor(process,P)} || P <- Js],
- ?line {monitored_by,MB} =
- process_info(self(),monitored_by),
- ?line MBL = lists:sort(MB),
- ?line JsL = lists:sort(Js),
- ?line MBL = JsL,
- ?line {monitors,M} =
- process_info(self(),monitors),
- ?line ML = lists:sort([P||{process,P} <- M]),
- ?line ML = JsL,
- ?line [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs],
- ?line wait_for_m(lists:sort(M),[],200),
- ?line [erlang:demonitor(Ref) || {_P,Ref} <- R2s],
- ?line wait_for_m([],[],200),
- ?line [tell_jeeves(P,{exit,flaff}) || P <- Js]
- end,
- Perm),
- [test_server:stop_node(K) || K <- NL0 ],
+ PA = filename:dirname(code:which(?MODULE)),
+ NN = [j0,j1,j2],
+ NL0 = [begin
+ {ok, J} = test_server:start_node(X,slave,[{args, "-pa " ++ PA}]),
+ J
+ end || X <- NN],
+ NL1 = lists:duplicate(2,node()) ++ NL0,
+ Perm = perm(NL1),
+ lists:foreach(
+ fun(NL) ->
+ Js = [start_jeeves({[],M}) || M <- (NL ++ NL)],
+ [ask_jeeves(P,{monitor_process,self()}) || P <- Js],
+ {monitored_by,MB} = process_info(self(),monitored_by),
+ MBL = lists:sort(MB),
+ JsL = lists:sort(Js),
+ MBL = JsL,
+ {monitors,[]} = process_info(self(),monitors),
+ [tell_jeeves(P,{exit,flaff}) || P <- Js],
+ wait_for_m([],[],200)
+ end,
+ Perm),
+ lists:foreach(
+ fun(NL) ->
+ Js = [start_jeeves({[],M}) || M <- (NL ++ NL)],
+ Rs = [begin
+ {monitor_process,Ref} = ask_jeeves(P,{monitor_process,self()}),
+ {P,Ref}
+ end || P <- Js],
+ {monitored_by,MB} = process_info(self(),monitored_by),
+ MBL = lists:sort(MB),
+ JsL = lists:sort(Js),
+ MBL = JsL,
+ {monitors,[]} = process_info(self(),monitors),
+ [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs],
+ wait_for_m([],[],200),
+ [tell_jeeves(P,{exit,flaff}) || P <- Js]
+ end,
+ Perm),
+ lists:foreach(
+ fun(NL) ->
+ Js = [start_jeeves({[],M}) || M <- (NL ++ NL)],
+ [ask_jeeves(P,{monitor_process,self()}) || P <- Js],
+ [erlang:monitor(process,P) || P <- Js],
+ {monitored_by,MB} = process_info(self(),monitored_by),
+ MBL = lists:sort(MB),
+ JsL = lists:sort(Js),
+ MBL = JsL,
+ {monitors,M} = process_info(self(),monitors),
+ ML = lists:sort([P||{process,P} <- M]),
+ ML = JsL,
+ [begin
+ tell_jeeves(P,{exit,flaff}),
+ receive {'DOWN',_,process,P,_} -> ok end
+ end || P <- Js],
+ wait_for_m([],[],200)
+ end,
+ Perm),
+ lists:foreach(
+ fun(NL) ->
+ Js = [start_jeeves({[],M}) || M <- (NL ++ NL)],
+ Rs = [begin
+ {monitor_process,Ref} = ask_jeeves(P,{monitor_process,self()}),
+ {P,Ref}
+ end || P <- Js],
+ R2s = [{P,erlang:monitor(process,P)} || P <- Js],
+ {monitored_by,MB} = process_info(self(),monitored_by),
+ MBL = lists:sort(MB),
+ JsL = lists:sort(Js),
+ MBL = JsL,
+ {monitors,M} = process_info(self(),monitors),
+ ML = lists:sort([P||{process,P} <- M]),
+ ML = JsL,
+ [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs],
+ wait_for_m(lists:sort(M),[],200),
+ [erlang:demonitor(Ref) || {_P,Ref} <- R2s],
+ wait_for_m([],[],200),
+ [tell_jeeves(P,{exit,flaff}) || P <- Js]
+ end,
+ Perm),
+ [test_server:stop_node(K) || K <- NL0],
ok.
named_down(doc) -> ["Test that DOWN message for a named monitor isn't"
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index c35c71dd5b..778f6fd087 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -39,8 +39,9 @@
get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
otp_9828/1,
otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1,
- dirty_nif_exception/1, nif_schedule/1,
- nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1
+ dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1,
+ nif_exception/1, call_nif_exception/1,
+ nif_nan_and_inf/1, nif_atom_too_long/1
]).
-export([many_args_100/100]).
@@ -1387,7 +1388,7 @@ is_checks(Config) when is_list(Config) ->
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2),
try
- ?line error = check_is_exception(),
+ ?line check_is_exception(),
?line throw(expected_badarg)
catch
error:badarg ->
@@ -1599,9 +1600,9 @@ dirty_nif_exception(Config) when is_list(Config) ->
N when is_integer(N) ->
ensure_lib_loaded(Config),
try
- %% this checks that the expected exception
- %% occurs when the NIF returns the result
- %% of enif_make_badarg directly
+ %% this checks that the expected exception occurs when the
+ %% dirty NIF returns the result of enif_make_badarg
+ %% directly
call_dirty_nif_exception(1),
?t:fail(expected_badarg)
catch
@@ -1611,10 +1612,9 @@ dirty_nif_exception(Config) when is_list(Config) ->
ok
end,
try
- %% this checks that the expected exception
- %% occurs when the NIF calls enif_make_badarg
- %% at some point but then returns a value that
- %% isn't an exception
+ %% this checks that the expected exception occurs when the
+ %% dirty NIF calls enif_make_badarg at some point but then
+ %% returns a value that isn't an exception
call_dirty_nif_exception(0),
?t:fail(expected_badarg)
catch
@@ -1622,7 +1622,10 @@ dirty_nif_exception(Config) when is_list(Config) ->
[{?MODULE,call_dirty_nif_exception,[0],_}|_] =
erlang:get_stacktrace(),
ok
- end
+ end,
+ %% this checks that a dirty NIF can raise various terms as
+ %% exceptions
+ ok = nif_raise_exceptions(call_dirty_nif_exception)
catch
error:badarg ->
{skipped,"No dirty scheduler support"}
@@ -1631,12 +1634,18 @@ dirty_nif_exception(Config) when is_list(Config) ->
nif_exception(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
try
- call_nif_exception(),
+ %% this checks that the expected exception occurs when the NIF
+ %% calls enif_make_badarg at some point but then tries to return a
+ %% value that isn't an exception
+ call_nif_exception(0),
?t:fail(expected_badarg)
catch
error:badarg ->
ok
- end.
+ end,
+ %% this checks that a NIF can raise various terms as exceptions
+ ok = nif_raise_exceptions(call_nif_exception),
+ ok.
nif_nan_and_inf(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
@@ -1758,7 +1767,20 @@ check(Exp,Got,Line) ->
io:format("CHECK at ~p: Expected ~p but got ~p\n",[Line,Exp,Got]),
Got
end.
-
+
+nif_raise_exceptions(NifFunc) ->
+ ExcTerms = [{error, test}, "a string", <<"a binary">>,
+ 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]],
+ lists:foldl(fun(Term, ok) ->
+ try
+ erlang:apply(?MODULE,NifFunc,[Term]),
+ ?t:fail({expected,Term})
+ catch
+ error:Term ->
+ [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(),
+ ok
+ end
+ end, ok, ExcTerms).
%% The NIFs:
lib_version() -> undefined.
@@ -1814,7 +1836,7 @@ call_dirty_nif(_,_,_) -> ?nif_stub.
send_from_dirty_nif(_) -> ?nif_stub.
call_dirty_nif_exception(_) -> ?nif_stub.
call_dirty_nif_zero_args() -> ?nif_stub.
-call_nif_exception() -> ?nif_stub.
+call_nif_exception(_) -> ?nif_stub.
call_nif_nan_or_inf(_) -> ?nif_stub.
call_nif_atom_too_long(_) -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 3cc9f51ef8..1639e47d61 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -883,16 +883,19 @@ static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
*
* This function is separate from check_is because it calls enif_make_badarg
* and so it must return the badarg exception as its return value. Thus, the
- * badarg exception indicates success. Failure is indicated by returning an
- * error atom.
+ * badarg exception indicates success.
*/
static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
+ ERL_NIF_TERM badarg, exc_term;
ERL_NIF_TERM error_atom = enif_make_atom(env, "error");
- ERL_NIF_TERM badarg = enif_make_badarg(env);
- if (enif_is_exception(env, error_atom)) return error_atom;
- if (!enif_is_exception(env, badarg)) return error_atom;
- if (!enif_has_pending_exception(env)) return error_atom;
+ ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
+ assert(!enif_is_exception(env, error_atom));
+ badarg = enif_make_badarg(env);
+ assert(enif_is_exception(env, badarg));
+ assert(enif_has_pending_exception(env, NULL));
+ assert(enif_has_pending_exception(env, &exc_term));
+ assert(enif_is_identical(exc_term, badarg_atom));
return badarg;
}
@@ -1536,9 +1539,12 @@ static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
+ ERL_NIF_TERM result;
if (argc != 2)
return enif_make_atom(env, "false");
- return enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv);
+ result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv);
+ assert(!enif_is_exception(env, result));
+ return result;
}
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
@@ -1612,13 +1618,18 @@ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL
{
switch (argc) {
case 1: {
- ERL_NIF_TERM args[255];
- int i;
- args[0] = argv[0];
- for (i = 1; i < 255; i++)
- args[i] = enif_make_int(env, i);
- return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
- call_dirty_nif_exception, 255, argv);
+ int arg;
+ if (enif_get_int(env, argv[0], &arg) && arg < 2) {
+ ERL_NIF_TERM args[255];
+ int i;
+ args[0] = argv[0];
+ for (i = 1; i < 255; i++)
+ args[i] = enif_make_int(env, i);
+ return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
+ call_dirty_nif_exception, 255, args);
+ } else {
+ return enif_raise_exception(env, argv[0]);
+ }
}
case 2: {
int return_badarg_directly;
@@ -1651,14 +1662,32 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL
#endif
/*
- * Call enif_make_badarg, but don't return its return value. Instead,
- * return ok. Result should still be a badarg exception for the erlang
- * caller.
+ * If argv[0] is the integer 0, call enif_make_badarg, but don't return its
+ * return value. Instead, return ok. Result should still be a badarg
+ * exception for the erlang caller.
+ *
+ * For any other value of argv[0], use it as an exception term and return
+ * the exception.
*/
static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- /* ignore return value */ enif_make_badarg(env);
- return enif_make_atom(env, "ok");
+ ERL_NIF_TERM exc_term;
+ ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
+ int arg;
+
+ if (enif_get_int(env, argv[0], &arg) && arg == 0) {
+ /* ignore return value */ enif_make_badarg(env);
+ assert(enif_has_pending_exception(env, NULL));
+ assert(enif_has_pending_exception(env, &exc_term));
+ assert(enif_is_identical(badarg_atom, exc_term));
+ return enif_make_atom(env, "ok");
+ } else {
+ ERL_NIF_TERM exc_retval = enif_raise_exception(env, argv[0]);
+ assert(enif_has_pending_exception(env, NULL));
+ assert(enif_has_pending_exception(env, &exc_term));
+ assert(enif_is_identical(argv[0], exc_term));
+ return exc_retval;
+ }
}
#if !defined(NAN) || !defined(INFINITY)
@@ -1693,7 +1722,7 @@ static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_
}
res = enif_make_double(env, val);
assert(enif_is_exception(env, res));
- assert(enif_has_pending_exception(env));
+ assert(enif_has_pending_exception(env, NULL));
if (strcmp(arg, "tuple") == 0) {
return enif_make_tuple2(env, argv[0], res);
} else {
@@ -1802,7 +1831,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER
if (argc != 1 && !enif_is_map(env, map))
return enif_make_int(env, __LINE__);
- if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD))
+ if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST))
return enif_make_int(env, __LINE__);
cnt = 0;
@@ -1817,7 +1846,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER
if (cnt && next_ret)
return enif_make_int(env, __LINE__);
- if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL))
+ if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST))
return enif_make_int(env, __LINE__);
cnt = 0;
@@ -1913,7 +1942,7 @@ static ErlNifFunc nif_funcs[] =
{"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
#endif
- {"call_nif_exception", 0, call_nif_exception},
+ {"call_nif_exception", 1, call_nif_exception},
{"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
{"call_nif_atom_too_long", 1, call_nif_atom_too_long},
{"is_map_nif", 1, is_map_nif},
diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl
index 45987cc319..25a59e0b07 100644
--- a/erts/emulator/test/trace_meta_SUITE.erl
+++ b/erts/emulator/test/trace_meta_SUITE.erl
@@ -72,7 +72,7 @@ config(priv_dir,_) ->
info/1, tracer/1, combo/1, nosilent/1]).
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(5)),
+ Dog=test_server:timetrap(test_server:minutes(5)),
[{watchdog, Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -179,107 +179,112 @@ nosilent(Config) when is_list(Config) ->
%%%
basic_test() ->
- ?line Pid = setup(),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
- ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
- ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
- ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?NM,
- ?line [1,1,1,0] = lambda_slave(fun() ->
- exported_wrap(1)
- end),
- ?line ?NM,
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
- ?line [1,1,1,0] = lambda_slave(fun() ->
- exported_wrap(1)
- end),
- ?line ?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun
- ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
- ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
- ?line shutdown(),
- ?line ?NM,
+ Pid = setup(),
+ erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
+ [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
+ erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
+ [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?NM,
+ [1,1,1,0] = lambda_slave(fun() ->
+ exported_wrap(1)
+ end),
+ ?NM,
+ erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
+ [1,1,1,0] = lambda_slave(fun() ->
+ exported_wrap(1)
+ end),
+ ?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun
+ ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
+ erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
+ shutdown(),
+ ?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
return_test() ->
- ?line Pid = setup(),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
+ Pid = setup(),
+ erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
[meta]),
- ?line erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}],
+ erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}],
[meta]),
- ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
- ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
- ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
- ?line ?RFT(Pid,{erlang,phash2,2},0) = receive_next(),
- ?line ?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(),
- ?line ?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(),
- ?line ?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(),
- ?line ?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(),
- ?line ?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(),
- ?line shutdown(),
- ?line ?NM,
+ [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
+ ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
+ ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
+ ?RFT(Pid,{erlang,phash2,2},0) = receive_next(),
+ ?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(),
+ ?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(),
+ ?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(),
+ ?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(),
+ ?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(),
+ shutdown(),
+ ?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
on_and_off_test() ->
- ?line Pid = setup(),
- ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]),
- ?line LocalTail = fun() ->
- local_tail(1)
- end,
- ?line [1,0] = lambda_slave(LocalTail),
- ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
- ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
- ?line [1,0] = lambda_slave(LocalTail),
- ?line ?NM,
- ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]),
- ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
- ?line 1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]),
- ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
- ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
- ?line shutdown(),
- ?line erlang:trace_pattern({'_','_','_'},false,[meta]),
- ?line N = erlang:trace_pattern({erlang,'_','_'},true,[meta]),
- ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
- N ->
- ok;
- Else ->
- exit({number_mismatch, {expected, N}, {got, Else}})
- end,
- ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
- N ->
- ok;
- Else2 ->
- exit({number_mismatch, {expected, N}, {got, Else2}})
- end,
- ?line ?NM,
+ Pid = setup(),
+ 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]),
+ LocalTail = fun() ->
+ local_tail(1)
+ end,
+ [1,0] = lambda_slave(LocalTail),
+ ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
+ 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
+ [1,0] = lambda_slave(LocalTail),
+ ?NM,
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]),
+ [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
+ 1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]),
+ [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
+ ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
+ shutdown(),
+ erlang:trace_pattern({'_','_','_'},false,[meta]),
+ N = erlang:trace_pattern({erlang,'_','_'},true,[meta]),
+ case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
+ N -> ok;
+ Else ->
+ exit({number_mismatch, {expected, N}, {got, Else}})
+ end,
+ case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
+ N -> ok;
+ Else2 ->
+ exit({number_mismatch, {expected, N}, {got, Else2}})
+ end,
+ %% ?NM,
+ %% Can't check for erlang:*/* stuff since common_test or test_server
+ %% will likely call list_to_binary in the logger. just drain any potential
+ %% message
+ ok = flush(),
ok.
-
+
+flush() ->
+ receive _ -> flush() after 0 -> ok end.
+
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
stack_grow_test() ->
- ?line Pid = setup(),
- ?line 1 = erlang:trace_pattern({?MODULE,loop,4},
+ Pid = setup(),
+ 1 = erlang:trace_pattern({?MODULE,loop,4},
[{'_',[],[{return_trace}]}],[meta]),
- ?line Num = 1 bsl 15,
- ?line Surface =
+ Num = 1 bsl 15,
+ Surface =
fun (This, ?RFT(P,{?MODULE,loop,4},N), N) when P == Pid->
if N == Num ->
?NM,
@@ -288,7 +293,7 @@ stack_grow_test() ->
This(This, receive_next(), N+1)
end
end,
- ?line Dive =
+ Dive =
fun (This, ?CTT(P,{?MODULE,loop,[{hej,hopp},[a,b,c],4.5,N]}), N)
when P == Pid->
if N == 0 ->
@@ -297,272 +302,263 @@ stack_grow_test() ->
This(This, receive_next(), N-1)
end
end,
- ?line apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
-% ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
-% ?line List = collect(test_server:seconds(5)),
- ?line ok = Dive(Dive, receive_next(), Num),
- ?line ?NM,
+ apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
+% apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
+% List = collect(test_server:seconds(5)),
+ ok = Dive(Dive, receive_next(), Num),
+ ?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
info_test() ->
- ?line setup(),
- ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]},
- {'_',[],[]}],
- ?line Self = self(),
- ?line GoOn = make_ref(),
-
- ?line Pid =
+ setup(),
+ Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, {'_',[],[]}],
+ Self = self(),
+ GoOn = make_ref(),
+ Pid =
spawn_link(
fun () ->
erlang:trace_pattern({?MODULE,exported_wrap,1},
- Prog, [{meta, Self}]),
+ Prog, [{meta, Self}]),
Self ! {self(), GoOn}
end),
- ?line receive {Pid, GoOn} -> ok end,
- ?line {traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced),
- ?line {match_spec, false} =
+ receive {Pid, GoOn} -> ok end,
+ {traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced),
+ {match_spec, false} =
erlang:trace_info({?MODULE,exported_wrap,1}, match_spec),
- ?line {meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
- ?line {meta_match_spec, MMS} =
+ {meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
+ {meta_match_spec, MMS} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
- ?line case MMS of
- Prog ->
- ok;
- Wrong ->
- exit({bad_result, {erlang,trace_info,
- [{?MODULE,exported_wrap,1},
- meta_match_spec]},
- {expected, Prog}, {got, Wrong}})
- end,
- ?line erlang:garbage_collect(self()),
- ?line receive
- after 1 ->
- ok
- end,
- ?line io:format("~p~n",[MMS]),
- ?line {meta_match_spec,MMS2} =
+ case MMS of
+ Prog ->
+ ok;
+ Wrong ->
+ exit({bad_result, {erlang,trace_info,
+ [{?MODULE,exported_wrap,1},
+ meta_match_spec]},
+ {expected, Prog}, {got, Wrong}})
+ end,
+ erlang:garbage_collect(self()),
+ receive
+ after 1 ->
+ ok
+ end,
+ io:format("~p~n",[MMS]),
+ {meta_match_spec,MMS2} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
- ?line io:format("~p~n",[MMS2]),
- ?line case MMS2 of
- Prog ->
- ok;
- Wrong2 ->
- exit({bad_result, {erlang,trace_info,
- [{?MODULE,exported_wrap,1},
- meta_match_spec]},
- {expected, Prog}, {got, Wrong2}})
- end,
- ?line {all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
- ?line {value, {meta, Self}} =
- lists:keysearch(meta, 1, L),
- ?line {value, {meta_match_spec, MMS}} =
- lists:keysearch(meta_match_spec, 1, L),
-
- ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]),
- ?line {meta_match_spec, []} =
+ io:format("~p~n",[MMS2]),
+ case MMS2 of
+ Prog ->
+ ok;
+ Wrong2 ->
+ exit({bad_result, {erlang,trace_info,
+ [{?MODULE,exported_wrap,1},
+ meta_match_spec]},
+ {expected, Prog}, {got, Wrong2}})
+ end,
+ {all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
+ {value, {meta, Self}} = lists:keysearch(meta, 1, L),
+ {value, {meta_match_spec, MMS}} = lists:keysearch(meta_match_spec, 1, L),
+
+ erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]),
+ {meta_match_spec, []} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
-
- ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]),
- ?line {meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
- ?line {meta_match_spec, false} =
+
+ erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]),
+ {meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
+ {meta_match_spec, false} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
- ?line {all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
-
- ?line shutdown(),
- ?line ?NM,
+ {all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
+ shutdown(),
+ ?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
tracer_test() ->
- ?line Slave = setup(),
- ?line Self = self(),
-
- ?line MatchSpec = [{'_',[],[{return_trace}]}],
- ?line Tracer1 = spawn_link(fun () -> relay_n(3, Self) end),
- ?line Setter =
- spawn_link(
- fun () ->
- erlang:trace_pattern({?MODULE,receiver,1},
- MatchSpec,
- [{meta,Tracer1}]),
- erlang:trace_pattern({erlang,phash2,2},
- MatchSpec,
- [{meta,Tracer1}]),
- Self ! {self(), done}
- end),
- ?line receive {Setter, done} -> ok end,
- ?line Ref = make_ref(),
- ?line apply_slave_async(?MODULE, receiver, [Ref]),
- ?line {Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100),
- ?line {Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100),
- ?line {Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100),
+ Slave = setup(),
+ Self = self(),
+
+ MatchSpec = [{'_',[],[{return_trace}]}],
+ Tracer1 = spawn_link(fun () -> relay_n(3, Self) end),
+ Setter = spawn_link(
+ fun () ->
+ erlang:trace_pattern({?MODULE,receiver,1},
+ MatchSpec,
+ [{meta,Tracer1}]),
+ erlang:trace_pattern({erlang,phash2,2},
+ MatchSpec,
+ [{meta,Tracer1}]),
+ Self ! {self(), done}
+ end),
+ receive {Setter, done} -> ok end,
+ Ref = make_ref(),
+ apply_slave_async(?MODULE, receiver, [Ref]),
+ {Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100),
+ {Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100),
+ {Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100),
%% Initiate a return_trace that will fail since the tracer just stopped
- ?line Slave ! Ref,
- ?line receive_no_next(100),
+ Slave ! Ref,
+ receive_no_next(100),
%% The breakpoint has not been hit since the tracer stopped
- ?line {meta,Tracer1} =
+ {meta,Tracer1} =
erlang:trace_info({?MODULE,receiver,1}, meta),
- ?line {meta_match_spec, MatchSpec} =
+ {meta_match_spec, MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
- ?line {meta,Tracer1} =
+ {meta,Tracer1} =
erlang:trace_info({erlang,phash2,2}, meta),
- ?line {meta_match_spec, MatchSpec} =
+ {meta_match_spec, MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
%% Initiate trace messages that will fail
- ?line Ref2 = make_ref(),
- ?line apply_slave_async(?MODULE, receiver, [Ref2]),
- ?line Slave ! Ref2,
- ?line receive_no_next(100),
- ?line {meta,[]} =
+ Ref2 = make_ref(),
+ apply_slave_async(?MODULE, receiver, [Ref2]),
+ Slave ! Ref2,
+ receive_no_next(100),
+ {meta,[]} =
erlang:trace_info({?MODULE,receiver,1}, meta),
- ?line {meta_match_spec, MatchSpec} =
+ {meta_match_spec, MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
- ?line {meta,[]} =
+ {meta,[]} =
erlang:trace_info({erlang,phash2,2}, meta),
- ?line {meta_match_spec, MatchSpec} =
+ {meta_match_spec, MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
%% Change tracer
- ?line Tracer2 = spawn_link(fun () -> relay_n(4, Self) end),
- ?line erlang:trace_pattern({?MODULE,receiver,1},
- MatchSpec,
- [{meta,Tracer2}]),
- ?line erlang:trace_pattern({erlang,phash2,2},
- MatchSpec,
- [{meta,Tracer2}]),
- ?line Ref3 = make_ref(),
- ?line apply_slave_async(?MODULE, receiver, [Ref3]),
- ?line {Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(),
- ?line {Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
- ?line {Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
+ Tracer2 = spawn_link(fun () -> relay_n(4, Self) end),
+ erlang:trace_pattern({?MODULE,receiver,1},
+ MatchSpec,
+ [{meta,Tracer2}]),
+ erlang:trace_pattern({erlang,phash2,2},
+ MatchSpec,
+ [{meta,Tracer2}]),
+ Ref3 = make_ref(),
+ apply_slave_async(?MODULE, receiver, [Ref3]),
+ {Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(),
+ {Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
+ {Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
%% Change tracer between call trace and return trace
- ?line Tracer3 = spawn_link(fun () -> relay_n(4, Self) end),
- ?line erlang:trace_pattern({?MODULE,receiver,1},
- MatchSpec,
- [{meta,Tracer3}]),
- ?line erlang:trace_pattern({erlang,phash2,2},
- MatchSpec,
- [{meta,Tracer3}]),
- ?line Slave ! Ref3,
+ Tracer3 = spawn_link(fun () -> relay_n(4, Self) end),
+ erlang:trace_pattern({?MODULE,receiver,1},
+ MatchSpec,
+ [{meta,Tracer3}]),
+ erlang:trace_pattern({erlang,phash2,2},
+ MatchSpec,
+ [{meta,Tracer3}]),
+ Slave ! Ref3,
%% The return trace should still come from Tracer2
- ?line {Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(),
- ?line Ref4 = make_ref(),
+ {Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(),
+ Ref4 = make_ref(),
%% Now should Tracer3 be used
- ?line apply_slave_async(?MODULE, receiver, [Ref4]),
- ?line Slave ! Ref4,
- ?line {Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(),
- ?line {Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
- ?line {Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
- ?line {Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(),
+ apply_slave_async(?MODULE, receiver, [Ref4]),
+ Slave ! Ref4,
+ {Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(),
+ {Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
+ {Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
+ {Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(),
%% The breakpoint has not been hit since the tracer stopped
- ?line {meta,Tracer3} =
- erlang:trace_info({?MODULE,receiver,1}, meta),
- ?line {meta_match_spec, MatchSpec} =
+ {meta,Tracer3} = erlang:trace_info({?MODULE,receiver,1}, meta),
+ {meta_match_spec, MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
- ?line {meta,Tracer3} =
+ {meta,Tracer3} =
erlang:trace_info({erlang,phash2,2}, meta),
- ?line {meta_match_spec, MatchSpec} =
+ {meta_match_spec, MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
-
- ?line shutdown(),
- ?line ?NM,
+ shutdown(),
+ ?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
combo_test() ->
- ?line Slave = setup(),
- ?line Self = self(),
-
- ?line MatchSpec = [{'_',[],[{return_trace}]}],
- ?line Flags = lists:sort([call, return_to]),
- ?line LocalTracer = spawn_link(fun () -> relay_n(6, Self) end),
- ?line MetaTracer = spawn_link(fun () -> relay_n(4, Self) end),
- ?line 1 = erlang:trace_pattern({?MODULE,receiver,1},
- MatchSpec,
- [local,{meta,MetaTracer}]),
- ?line 1 = erlang:trace_pattern({erlang,phash2,2},
- MatchSpec,
- [local,{meta,MetaTracer}]),
- ?line 1 = erlang:trace(Slave, true,
- [{tracer,LocalTracer} | Flags]),
+ Slave = setup(),
+ Self = self(),
+
+ MatchSpec = [{'_',[],[{return_trace}]}],
+ Flags = lists:sort([call, return_to]),
+ LocalTracer = spawn_link(fun () -> relay_n(6, Self) end),
+ MetaTracer = spawn_link(fun () -> relay_n(4, Self) end),
+ 1 = erlang:trace_pattern({?MODULE,receiver,1},
+ MatchSpec,
+ [local,{meta,MetaTracer}]),
+ 1 = erlang:trace_pattern({erlang,phash2,2},
+ MatchSpec,
+ [local,{meta,MetaTracer}]),
+ 1 = erlang:trace(Slave, true,
+ [{tracer,LocalTracer} | Flags]),
%%
- ?line {all, TraceInfo1} =
+ {all, TraceInfo1} =
erlang:trace_info({?MODULE,receiver,1}, all),
- ?line {meta,MetaTracer} =
+ {meta,MetaTracer} =
erlang:trace_info({?MODULE,receiver,1}, meta),
- ?line {value,{meta,MetaTracer}} =
+ {value,{meta,MetaTracer}} =
lists:keysearch(meta, 1, TraceInfo1),
- ?line {meta_match_spec,MatchSpec} =
+ {meta_match_spec,MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
- ?line {value,{meta_match_spec,MatchSpec}} =
+ {value,{meta_match_spec,MatchSpec}} =
lists:keysearch(meta_match_spec, 1, TraceInfo1),
- ?line {traced,local} =
+ {traced,local} =
erlang:trace_info({?MODULE,receiver,1}, traced),
- ?line {value,{traced,local}} =
+ {value,{traced,local}} =
lists:keysearch(traced, 1, TraceInfo1),
- ?line {match_spec,MatchSpec} =
+ {match_spec,MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, match_spec),
- ?line {value,{match_spec,MatchSpec}} =
+ {value,{match_spec,MatchSpec}} =
lists:keysearch(match_spec, 1, TraceInfo1),
%%
- ?line {all, TraceInfo2} =
+ {all, TraceInfo2} =
erlang:trace_info({erlang,phash2,2}, all),
- ?line {meta,MetaTracer} =
+ {meta,MetaTracer} =
erlang:trace_info({erlang,phash2,2}, meta),
- ?line {value,{meta,MetaTracer}} =
+ {value,{meta,MetaTracer}} =
lists:keysearch(meta, 1, TraceInfo2),
- ?line {meta_match_spec,MatchSpec} =
+ {meta_match_spec,MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
- ?line {value,{meta_match_spec,MatchSpec}} =
+ {value,{meta_match_spec,MatchSpec}} =
lists:keysearch(meta_match_spec, 1, TraceInfo2),
- ?line {traced,local} =
+ {traced,local} =
erlang:trace_info({erlang,phash2,2}, traced),
- ?line {value,{traced,local}} =
+ {value,{traced,local}} =
lists:keysearch(traced, 1, TraceInfo2),
- ?line {match_spec,MatchSpec} =
+ {match_spec,MatchSpec} =
erlang:trace_info({erlang,phash2,2}, match_spec),
- ?line {value,{match_spec,MatchSpec}} =
+ {value,{match_spec,MatchSpec}} =
lists:keysearch(match_spec, 1, TraceInfo2),
%%
- ?line {flags,Flags1} = erlang:trace_info(Slave, flags),
- ?line Flags = lists:sort(Flags1),
- ?line {tracer,LocalTracer} = erlang:trace_info(Slave, tracer),
+ {flags,Flags1} = erlang:trace_info(Slave, flags),
+ Flags = lists:sort(Flags1),
+ {tracer,LocalTracer} = erlang:trace_info(Slave, tracer),
%%
- ?line Ref = make_ref(),
- ?line apply_slave_async(?MODULE, receiver, [Ref]),
- ?line Slave ! Ref,
- ?line ?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer),
- ?line ?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer),
- ?line ?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer),
- ?line ?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer),
- ?line ?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer),
- ?line ?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer),
- ?line case {receive_next_bytag(LocalTracer),
+ Ref = make_ref(),
+ apply_slave_async(?MODULE, receiver, [Ref]),
+ Slave ! Ref,
+ ?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer),
+ ?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer),
+ ?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer),
+ ?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer),
+ ?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer),
+ ?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer),
+ case {receive_next_bytag(LocalTracer),
receive_next_bytag(LocalTracer)} of
{?RF(Slave,{erlang,phash2,2},0),
?RT(Slave,{?MODULE,receiver,1})} ->
- ?line ok;
+ ok;
{?RT(Slave,{?MODULE,receiver,1}),
?RF(Slave,{erlang,phash2,2},0)} ->
- ?line ok;
+ ok;
Error1 -> ?t:fail({unexpected_message, Error1})
end,
- ?line case {receive_next_bytag(LocalTracer),
+ case {receive_next_bytag(LocalTracer),
receive_next_bytag(LocalTracer)} of
{?RF(Slave,{?MODULE,receiver,1},Ref),
?RT(Slave,{?MODULE,slave,1})} ->
- ?line ok;
+ ok;
{?RT(Slave,{?MODULE,slave,1}),
?RF(Slave,{?MODULE,receiver,1},Ref)} ->
- ?line ok;
+ ok;
Error2 -> ?t:fail({unexpected_message, Error2})
end,
-
- ?line shutdown(),
- ?line ?NM,
+ shutdown(),
+ ?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -570,38 +566,38 @@ combo_test() ->
%% Setup silent local call tracing, and start it using meta trace.
nosilent_test() ->
- ?line Pid = setup(),
- ?line Trigger = {?MODULE,id,1},
- ?line TriggerMS = [{[start],[],[{silent,false}]},
- {[stop],[],[{silent,true},{return_trace}]}],
- ?line 1 = erlang:trace(Pid, true, [call,silent,return_to]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
- ?line 1 = erlang:trace_pattern({?MODULE,local2,1},
- [{'_',[],[{return_trace}]}],
- [local]),
- ?line 1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]),
- ?line 1 = erlang:trace_pattern(Trigger,false,[local]),
- ?line 1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]),
- ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
- ?line receive_no_next(17),
- ?line start = apply_slave(?MODULE, id, [start]),
- ?line ?CTT(Pid,{?MODULE,id,[start]}) = receive_next(),
- ?line [2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]),
- ?line ?CT(Pid,{?MODULE,exported_wrap,[2]}) = receive_next(),
- ?line ?CT(Pid,{?MODULE,exported,[2]}) = receive_next(),
- ?line ?CT(Pid,{?MODULE,local,[2]}) = receive_next(),
- ?line ?CT(Pid,{?MODULE,local2,[2]}) = receive_next(),
- ?line ?CT(Pid,{?MODULE,local_tail,[2]}) = receive_next(),
- ?line ?RF(Pid,{?MODULE,local2,1}, [2,0]) = receive_next(),
- ?line ?RT(Pid,{?MODULE,local,1}) = receive_next(),
- ?line ?RT(Pid,{?MODULE,exported,1}) = receive_next(),
- ?line ?RT(Pid,{?MODULE,slave,1}) = receive_next(),
- ?line stop = apply_slave(?MODULE, id, [stop]),
- ?line ?CTT(Pid,{?MODULE,id,[stop]}) = receive_next(),
- ?line ?RFT(Pid,{?MODULE,id,1}, stop) = receive_next(),
- ?line [3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]),
- ?line receive_no_next(17),
- ?line shutdown(),
+ Pid = setup(),
+ Trigger = {?MODULE,id,1},
+ TriggerMS = [{[start],[],[{silent,false}]},
+ {[stop],[],[{silent,true},{return_trace}]}],
+ 1 = erlang:trace(Pid, true, [call,silent,return_to]),
+ erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ 1 = erlang:trace_pattern({?MODULE,local2,1},
+ [{'_',[],[{return_trace}]}],
+ [local]),
+ 1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]),
+ 1 = erlang:trace_pattern(Trigger,false,[local]),
+ 1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]),
+ [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
+ receive_no_next(17),
+ start = apply_slave(?MODULE, id, [start]),
+ ?CTT(Pid,{?MODULE,id,[start]}) = receive_next(),
+ [2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]),
+ ?CT(Pid,{?MODULE,exported_wrap,[2]}) = receive_next(),
+ ?CT(Pid,{?MODULE,exported,[2]}) = receive_next(),
+ ?CT(Pid,{?MODULE,local,[2]}) = receive_next(),
+ ?CT(Pid,{?MODULE,local2,[2]}) = receive_next(),
+ ?CT(Pid,{?MODULE,local_tail,[2]}) = receive_next(),
+ ?RF(Pid,{?MODULE,local2,1}, [2,0]) = receive_next(),
+ ?RT(Pid,{?MODULE,local,1}) = receive_next(),
+ ?RT(Pid,{?MODULE,exported,1}) = receive_next(),
+ ?RT(Pid,{?MODULE,slave,1}) = receive_next(),
+ stop = apply_slave(?MODULE, id, [stop]),
+ ?CTT(Pid,{?MODULE,id,[stop]}) = receive_next(),
+ ?RFT(Pid,{?MODULE,id,1}, stop) = receive_next(),
+ [3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]),
+ receive_no_next(17),
+ shutdown(),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -645,9 +641,9 @@ slave(Sync) ->
Sync ! sync,
receive
{From,apply, M, F, A} ->
- ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
- ?line Res = apply(M,F,A),
- ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
+ ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
+ Res = apply(M,F,A),
+ ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
From ! {apply, Res},
erlang:trace_pattern({?MODULE,slave,1},false,[meta]),
slave(From);
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 23226909a7..d6544a2829 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -1172,7 +1172,7 @@ usage_aux(void)
"[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] "
"[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] "
"[+T LEVEL] [+V] [+v] "
- "[+W<i|w>] [+z MISC_OPTION] [args ...]\n");
+ "[+W<i|w|e>] [+z MISC_OPTION] [args ...]\n");
exit(1);
}
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 79ff013c77..622e1be869 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -127,37 +127,18 @@ drv2protocol(_) -> undefined.
%% TODO: shutdown equivalent for SCTP
%%
shutdown(S, read) when is_port(S) ->
- shutdown_2(S, 0);
+ shutdown_1(S, 0);
shutdown(S, write) when is_port(S) ->
shutdown_1(S, 1);
shutdown(S, read_write) when is_port(S) ->
shutdown_1(S, 2).
shutdown_1(S, How) ->
- case subscribe(S, [subs_empty_out_q]) of
- {ok,[{subs_empty_out_q,N}]} when N > 0 ->
- shutdown_pend_loop(S, N); %% wait for pending output to be sent
- _Other -> ok
- end,
- shutdown_2(S, How).
-
-shutdown_2(S, How) ->
case ctl_cmd(S, ?TCP_REQ_SHUTDOWN, [How]) of
{ok, []} -> ok;
{error,_}=Error -> Error
end.
-shutdown_pend_loop(S, N0) ->
- receive
- {empty_out_q,S} -> ok
- after ?INET_CLOSE_TIMEOUT ->
- case getstat(S, [send_pend]) of
- {ok,[{send_pend,N0}]} -> ok;
- {ok,[{send_pend,N}]} -> shutdown_pend_loop(S, N);
- _ -> ok
- end
- end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% CLOSE(insock()) -> ok
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index c909c908d6..d4c46863a3 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 3.0.4
+ASN1_VSN = 4.0
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 5fccdcdcb5..a271729c82 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -32,15 +32,15 @@
<modulesummary>Erlang Compiler</modulesummary>
<description>
<p>This module provides an interface to the standard Erlang
- compiler. It can generate either a new file which contains
- the object code, or return a binary which can be loaded directly.
+ compiler. It can generate either a new file, which contains
+ the object code, or return a binary, which can be loaded directly.
</p>
</description>
<funcs>
<func>
<name>file(File)</name>
- <fsummary>Compile a file</fsummary>
+ <fsummary>Compiles a file.</fsummary>
<desc>
<p>Is the same as
<c>file(File, [verbose,report_errors,report_warnings])</c>.
@@ -50,7 +50,7 @@
<func>
<name>file(File, Options) -> CompRet</name>
- <fsummary>Compile a file</fsummary>
+ <fsummary>Compiles a file.</fsummary>
<type>
<v>CompRet = ModRet | BinRet | ErrRet</v>
<v>ModRet = {ok,ModuleName} | {ok,ModuleName,Warnings}</v>
@@ -64,39 +64,38 @@
<p>Returns <c>{ok,ModuleName}</c> if successful, or <c>error</c>
if there are errors. An object code file is created if
- the compilation succeeds with no errors. It is considered
+ the compilation succeeds without errors. It is considered
to be an error if the module name in the source code is
not the same as the basename of the output file.</p>
- <p><marker id="type-option"/>Here follows first all elements of <c>Options</c> that in
- some way control the behavior of the compiler.</p>
+ <p><marker id="type-option"/>Available options:</p>
<taglist>
<tag><c>basic_validation</c></tag>
<item>
- <p>This option is fast way to test whether a module will
- compile successfully (mainly useful for code generators
- that want to verify the code they emit). No code will
+ <p>This option is a fast way to test whether a module will
+ compile successfully. This is useful for code generators
+ that want to verify the code that they emit. No code is
generated. If warnings are enabled, warnings generated by
the <c>erl_lint</c> module (such as warnings for unused
- variables and functions) will be returned too.</p>
+ variables and functions) are also returned.</p>
- <p>Use the <c>strong_validation</c> option to generate all
+ <p>Use option <c>strong_validation</c> to generate all
warnings that the compiler would generate.</p>
</item>
<tag><c>strong_validation</c></tag>
<item>
- <p>Similar to the <c>basic_validation</c> option, no code
- will be generated, but more compiler passes will be run
- to ensure also warnings generated by the optimization
- passes are generated (such as clauses that will not match
+ <p>Similar to option <c>basic_validation</c>. No code
+ is generated, but more compiler passes are run
+ to ensure that warnings generated by the optimization
+ passes are generated (such as clauses that will not match,
or expressions that are guaranteed to fail with an
- exception at run-time).</p>
+ exception at runtime).</p>
</item>
<tag><c>binary</c></tag>
<item>
- <p>Causes the compiler to return the object code in a
+ <p>The compiler returns the object code in a
binary instead of creating an object file. If successful,
the compiler returns <c>{ok,ModuleName,Binary}</c>.</p>
</item>
@@ -105,7 +104,9 @@
<item>
<p>The compiler will emit informational warnings about binary
matching optimizations (both successful and unsuccessful).
- See the <em>Efficiency Guide</em> for further information.</p>
+ For more information, see the section about
+ <seealso marker="doc/efficiency_guide:binaryhandling#bin_opt_info">bin_opt_info</seealso>
+ in the Efficiency Guide.</p>
</item>
<tag><c>compressed</c></tag>
@@ -117,20 +118,19 @@
<tag><c>debug_info</c></tag>
<item>
<marker id="debug_info"></marker>
- <p>Include debug information in the form of abstract code
+ <p>Includes debug information in the form of abstract code
(see
<seealso marker="erts:absform">The Abstract Format</seealso>
in ERTS User's Guide) in the compiled beam module. Tools
- such as Debugger, Xref and Cover require the debug
- information to be included.</p>
+ such as <c>Debugger</c>, <c>Xref</c>, and <c>Cover</c> require
+ the debug information to be included.</p>
<p><em>Warning</em>: Source code can be reconstructed from
the debug information. Use encrypted debug information
- (see below) to prevent this.</p>
+ (<c>encrypt_debug_info</c>) to prevent this.</p>
- <p>See
- <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>
- for details.</p>
+ <p>For details, see
+ <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p>
</item>
<tag><c>{debug_info_key,KeyString}</c></tag>
@@ -138,65 +138,61 @@
<tag><c>{debug_info_key,{Mode,KeyString}}</c></tag>
<item>
<marker id="debug_info_key"></marker>
- <p>Include debug information, but encrypt it, so that it
+ <p>Includes debug information, but encrypts it so that it
cannot be accessed without supplying the key. (To give
- the <c>debug_info</c> option as well is allowed, but is
+ option <c>debug_info</c> as well is allowed, but
not necessary.) Using this option is a good way to always
have the debug information available during testing, yet
- protect the source code.</p>
+ protecting the source code.</p>
<p><c>Mode</c> is the type of crypto algorithm to be used
- for encrypting the debug information. The default type --
- and currently the only type -- is <c>des3_cbc</c>.</p>
- <p>See
- <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>
- for details.</p>
+ for encrypting the debug information. The default
+ (and currently the only) type is <c>des3_cbc</c>.</p>
+ <p>For details, see
+ <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p>
</item>
<tag><c>encrypt_debug_info</c></tag>
<item>
<marker id="encrypt_debug_info"></marker>
- <p>Like the <c>debug_info_key</c> option above, except that
- the key will be read from an <c>.erlang.crypt</c> file.
+ <p>Similar to the <c>debug_info_key</c> option, but
+ the key is read from an <c>.erlang.crypt</c> file.
</p>
- <p>See
- <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>
- for details.</p>
+ <p>For details, see
+ <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p>
</item>
<tag><c>makedep</c></tag>
<item>
- <p>Produce a Makefile rule to track headers dependencies.
+ <p>Produces a Makefile rule to track headers dependencies.
No object file is produced.
</p>
<p>By default, this rule is written to
- <c><![CDATA[<File>.Pbeam]]></c>. However, if the option
+ <c><![CDATA[<File>.Pbeam]]></c>. However, if option
<c>binary</c> is set, nothing is written and the rule is
returned in <c>Binary</c>.
</p>
- <p>For instance, if one has the following module:
+ <p>For example, if you have the following module:
</p>
<code>
-module(module).
-include_lib("eunit/include/eunit.hrl").
--include("header.hrl").
- </code>
- <p>Here is the Makefile rule generated by this option:
+-include("header.hrl").</code>
+ <p>The Makefile rule generated by this option looks as follows:
</p>
<code>
module.beam: module.erl \
/usr/local/lib/erlang/lib/eunit/include/eunit.hrl \
- header.hrl
- </code>
+ header.hrl</code>
</item>
<tag><c>{makedep_output, Output}</c></tag>
<item>
- <p>Write generated rule(s) to <c>Output</c> instead of the
+ <p>Writes generated rules to <c>Output</c> instead of the
default <c><![CDATA[<File>.Pbeam]]></c>. <c>Output</c>
can be a filename or an <c>io_device()</c>. To write to
- stdout, use <c>standard_io</c>. However if <c>binary</c>
+ stdout, use <c>standard_io</c>. However, if <c>binary</c>
is set, nothing is written to <c>Output</c> and the
result is returned to the caller with
<c>{ok, ModuleName, Binary}</c>.
@@ -205,7 +201,7 @@ module.beam: module.erl \
<tag><c>{makedep_target, Target}</c></tag>
<item>
- <p>Change the name of the rule emitted to <c>Target</c>.
+ <p>Changes the name of the rule emitted to <c>Target</c>.
</p>
</item>
@@ -217,20 +213,20 @@ module.beam: module.erl \
<tag><c>makedep_add_missing</c></tag>
<item>
- <p>Consider missing headers as generated files and add them to the
+ <p>Considers missing headers as generated files and adds them to the
dependencies.
</p>
</item>
<tag><c>makedep_phony</c></tag>
<item>
- <p>Add a phony target for each dependency.
+ <p>Adds a phony target for each dependency.
</p>
</item>
<tag><c>'P'</c></tag>
<item>
- <p>Produces a listing of the parsed code after preprocessing
+ <p>Produces a listing of the parsed code, after preprocessing
and parse transforms, in the file
<c><![CDATA[<File>.P]]></c>. No object file is produced.
</p>
@@ -238,7 +234,7 @@ module.beam: module.erl \
<tag><c>'E'</c></tag>
<item>
- <p>Produces a listing of the code after all source code
+ <p>Produces a listing of the code, after all source code
transformations have been performed, in the file
<c><![CDATA[<File>.E]]></c>. No object file is produced.
</p>
@@ -258,21 +254,21 @@ module.beam: module.erl \
<tag><c>report</c></tag>
<item>
- <p>This is a short form for both <c>report_errors</c> and
+ <p>A short form for both <c>report_errors</c> and
<c>report_warnings</c>.</p>
</item>
<tag><c>return_errors</c></tag>
<item>
- <p>If this flag is set, then
+ <p>If this flag is set,
<c>{error,ErrorList,WarningList}</c> is returned when
there are errors.</p>
</item>
<tag><c>return_warnings</c></tag>
<item>
- <p>If this flag is set, then an extra field containing
- <c>WarningList</c> is added to the tuples returned on
+ <p>If this flag is set, an extra field, containing
+ <c>WarningList</c>, is added to the tuples returned on
success.</p>
</item>
@@ -284,13 +280,13 @@ module.beam: module.erl \
<tag><c>return</c></tag>
<item>
- <p>This is a short form for both <c>return_errors</c> and
+ <p>A short form for both <c>return_errors</c> and
<c>return_warnings</c>.</p>
</item>
<tag><c>verbose</c></tag>
<item>
- <p>Causes more verbose information from the compiler
+ <p>Causes more verbose information from the compiler,
describing what it is doing.</p>
</item>
@@ -314,7 +310,7 @@ module.beam: module.erl \
<tag><c>{i,Dir}</c></tag>
<item>
- <p>Add <c>Dir</c> to the list of directories to be searched
+ <p>Adds <c>Dir</c> to the list of directories to be searched
when including a file. When encountering an
<c>-include</c> or <c>-include_lib</c> directive,
the compiler searches for header files in the following
@@ -322,14 +318,14 @@ module.beam: module.erl \
<list type="ordered">
<item>
<p><c>"."</c>, the current working directory of
- the file server;</p>
+ the file server</p>
</item>
<item>
- <p>the base name of the compiled file;</p>
+ <p>The base name of the compiled file</p>
</item>
<item>
- <p>the directories specified using the <c>i</c> option.
- The directory specified last is searched first.</p>
+ <p>The directories specified using option <c>i</c>;
+ the directory specified last is searched first</p>
</item>
</list>
</item>
@@ -353,15 +349,15 @@ module.beam: module.erl \
<tag><c>from_asm</c></tag>
<item>
<p>The input file is expected to be assembler code (default
- file suffix ".S"). Note that the format of assembler files
- is not documented, and may change between releases.</p>
+ file suffix ".S"). Notice that the format of assembler files
+ is not documented, and can change between releases.</p>
</item>
<tag><c>from_core</c></tag>
<item>
<p>The input file is expected to be core code (default
- file suffix ".core"). Note that the format of core files
- is not documented, and may change between releases.</p>
+ file suffix ".core"). Notice that the format of core files
+ is not documented, and can change between releases.</p>
</item>
<tag><c>no_strict_record_tests</c></tag>
@@ -369,9 +365,9 @@ module.beam: module.erl \
<p>This option is not recommended.</p>
<p>By default, the generated code for
- the <c>Record#record_tag.field</c> operation verifies that
- the tuple <c>Record</c> is of the correct size for
- the record and that the first element is the tag
+ operation <c>Record#record_tag.field</c> verifies that
+ the tuple <c>Record</c> has the correct size for
+ the record, and that the first element is the tag
<c>record_tag</c>. Use this option to omit
the verification code.</p>
</item>
@@ -390,79 +386,87 @@ module.beam: module.erl \
<tag><c>{no_auto_import,[{F,A}, ...]}</c></tag>
<item>
<p>Makes the function <c>F/A</c> no longer being
- auto-imported from the module <c>erlang</c>, which resolves
- BIF name clashes. This option has to be used to resolve name
- clashes with BIFs auto-imported before R14A, if one wants to
+ auto-imported from the <c>erlang</c> module, which resolves
+ BIF name clashes. This option must be used to resolve name
+ clashes with BIFs auto-imported before R14A, if it is needed to
call the local function with the same name as an
auto-imported BIF without module prefix.</p>
<note>
- <p>From R14A and forward, the compiler resolves calls
+ <p>As from R14A and forward, the compiler resolves calls
without module prefix to local or imported functions before
- trying auto-imported BIFs. If the BIF is to be
+ trying with auto-imported BIFs. If the BIF is to be
called, use the <c>erlang</c> module prefix in the call, not
- <c>{ no_auto_import,[{F,A}, ...]}</c></p>
+ <c>{ no_auto_import,[{F,A}, ...]}</c>.</p>
</note>
<p>If this option is written in the source code, as a
<c>-compile</c> directive, the syntax <c>F/A</c> can be used instead
- of <c>{F,A}</c>. Example:</p>
+ of <c>{F,A}</c>, for example:</p>
<code>-compile({no_auto_import,[error/1]}).</code>
</item>
<tag><c>no_auto_import</c></tag>
<item>
- <p>Do not auto import any functions from the module <c>erlang</c>.</p>
+ <p>Do not auto-import any functions from <c>erlang</c> module.</p>
</item>
<tag><c>no_line_info</c></tag>
<item>
- <p>Omit line number information in order to produce a slightly
+ <p>Omits line number information to produce a slightly
smaller output file.
</p>
</item>
</taglist>
- <p>If warnings are turned on (the <c>report_warnings</c> option
- described above), the following options control what type of
- warnings that will be generated.
+ <p>If warnings are turned on (option <c>report_warnings</c>
+ described earlier), the following options control what type of
+ warnings that are generated.
<marker id="erl_lint_options"></marker>
- With the exception of <c>{warn_format,Verbosity}</c> all
- options below have two forms; one <c>warn_xxx</c> form to
- turn on the warning and one <c>nowarn_xxx</c> form to turn off
- the warning. In the description that follows, the form that
- is used to change the default value is listed.</p>
+ Except from <c>{warn_format,Verbosity}</c>, the following options
+ have two forms:</p>
+ <list type="bulleted">
+ <item>A <c>warn_xxx</c> form, to turn on the warning.</item>
+ <item>A <c>nowarn_xxx</c> form, to turn off the warning.</item>
+ </list>
+ <p>In the descriptions that follow, the form that is used to change
+ the default value are listed.</p>
<taglist>
<tag><c>{warn_format, Verbosity}</c></tag>
<item>
<p>Causes warnings to be emitted for malformed format
strings as arguments to <c>io:format</c> and similar
- functions. <c>Verbosity</c> selects the amount of
- warnings: 0 = no warnings; 1 = warnings for invalid
- format strings and incorrect number of arguments; 2 =
- warnings also when the validity could not be checked
- (for example, when the format string argument is a
- variable). The default verbosity is 1. Verbosity 0 can
- also be selected by the option <c>nowarn_format</c>.</p>
+ functions.</p>
+ <p><c>Verbosity</c> selects the number of warnings:</p>
+ <list type="bulleted">
+ <item><c>0</c> = No warnings</item>
+ <item><c>1</c> = Warnings for invalid format strings and incorrect
+ number of arguments</item>
+ <item><c>2</c> = Warnings also when the validity cannot
+ be checked, for example, when the format string argument is a
+ variable.</item>
+ </list>
+ <p>The default verbosity is <c>1</c>. Verbosity <c>0</c> can
+ also be selected by option <c>nowarn_format</c>.</p>
</item>
<tag><c>nowarn_bif_clash</c></tag>
<item>
- <p>This option is removed, it will generate a fatal error if used.</p>
+ <p>This option is removed, it generates a fatal error if used.</p>
<warning>
- <p>Beginning with R14A, the compiler no longer calls the
+ <p>As from beginning with R14A, the compiler no longer calls the
auto-imported BIF if the name clashes with a local or
- explicitly imported function and a call without explicit
- module name is issued. Instead the local or imported
- function is called. Still accepting <c>nowarn_bif_clash</c> would makes a
- module calling functions clashing with autoimported BIFs
+ explicitly imported function, and a call without explicit
+ module name is issued. Instead, the local or imported
+ function is called. Still accepting <c>nowarn_bif_clash</c> would
+ make a module calling functions clashing with auto-imported BIFs
compile with both the old and new compilers, but with
- completely different semantics, why the option was removed.</p>
+ completely different semantics. This is why the option is removed.</p>
- <p>The use of this option has always been strongly discouraged.
- From OTP R14A and forward it's an error to use it.</p>
+ <p>The use of this option has always been discouraged.
+ As from R14A, it is an error to use it.</p>
<p>To resolve BIF clashes, use explicit module names or the
<c>{no_auto_import,[F/A]}</c> compiler directive.</p>
</warning>
@@ -470,11 +474,11 @@ module.beam: module.erl \
<tag><c>{nowarn_bif_clash, FAs}</c></tag>
<item>
- <p>This option is removed, it will generate a fatal error if used.</p>
+ <p>This option is removed, it generates a fatal error if used.</p>
<warning>
- <p>The use of this option has always been strongly discouraged.
- From OTP R14A and forward it's an error to use it.</p>
+ <p>The use of this option has always been discouraged.
+ As from R14A, it is an error to use it.</p>
<p>To resolve BIF clashes, use explicit module names or the
<c>{no_auto_import,[F/A]}</c> compiler directive.</p>
</warning>
@@ -482,35 +486,29 @@ module.beam: module.erl \
<tag><c>warn_export_all</c></tag>
<item>
- <p>Causes a warning to be emitted if the <c>export_all</c>
- option has also been given.</p>
+ <p>Emits a warning if option <c>export_all</c> is also given.</p>
</item>
<tag><c>warn_export_vars</c></tag>
<item>
- <p>Causes warnings to be emitted for all implicitly
- exported variables referred to after the primitives
- where they were first defined. No warnings for exported
- variables unless they are referred to in some pattern,
- which is the default, can be selected by the option
- <c>nowarn_export_vars</c>.</p>
+ <p>Emits warnings for all implicitly exported variables
+ referred to after the primitives where they were first defined.
+ By default, the compiler only emits warnings for exported
+ variables referred to in a pattern.</p>
</item>
- <tag><c>warn_shadow_vars</c></tag>
+ <tag><c>nowarn_shadow_vars</c></tag>
<item>
- <p>Causes warnings to be emitted for "fresh" variables
- in functional objects or list comprehensions with the same
- name as some already defined variable. The default is to
- warn for such variables. No warnings for shadowed
- variables can be selected by the option
- <c>nowarn_shadow_vars</c>.</p>
+ <p>Turns off warnings for "fresh" variables
+ in functional objects or list comprehensions with the same
+ name as some already defined variable. Default is to
+ emit warnings for such variables.</p>
</item>
<tag><c>nowarn_unused_function</c></tag>
<item>
- <p>Turns off warnings for unused local functions.
- By default (<c>warn_unused_function</c>), warnings are
- emitted for all local functions that are not called
+ <p>Turns off warnings for unused local functions. Default
+ is to emit warnings for all local functions that are not called
directly or indirectly by an exported function.
The compiler does not include unused local functions in
the generated beam file, but the warning is still useful
@@ -519,148 +517,142 @@ module.beam: module.erl \
<tag><c>{nowarn_unused_function, FAs}</c></tag>
<item>
- <p>Turns off warnings for unused local functions as
- <c>nowarn_unused_function</c> but only for the mentioned
+ <p>Turns off warnings for unused local functions like
+ <c>nowarn_unused_function</c> does, but only for the mentioned
local functions. <c>FAs</c> is a tuple <c>{Name,Arity}</c>
or a list of such tuples.</p>
</item>
<tag><c>nowarn_deprecated_function</c></tag>
<item>
- <p>Turns off warnings for calls to deprecated functions. By
- default (<c>warn_deprecated_function</c>), warnings are
- emitted for every call to a function known by the compiler
- to be deprecated. Note that the compiler does not know
- about the <c>-deprecated()</c> attribute but uses an
+ <p>Turns off warnings for calls to deprecated functions. Default
+ is to emit warnings for every call to a function known by the
+ compiler to be deprecated. Notice that the compiler does not know
+ about attribute <c>-deprecated()</c>, but uses an
assembled list of deprecated functions in Erlang/OTP. To
- do a more general check the <c>Xref</c> tool can be used.
+ do a more general check, the <c>Xref</c> tool can be used.
See also
<seealso marker="tools:xref#deprecated_function">xref(3)</seealso>
and the function
- <seealso marker="tools:xref#m/1">xref:m/1</seealso> also
- accessible through
- the <seealso marker="stdlib:c#xm/1">c:xm/1</seealso>
- function.</p>
+ <seealso marker="tools:xref#m/1">xref:m/1</seealso>, also
+ accessible through the function
+ <seealso marker="stdlib:c#xm/1">c:xm/1</seealso>.</p>
</item>
<tag><c>{nowarn_deprecated_function, MFAs}</c></tag>
<item>
- <p>Turns off warnings for calls to deprecated functions as
- <c>nowarn_deprecated_function</c> but only for
+ <p>Turns off warnings for calls to deprecated functions like
+ <c>nowarn_deprecated_function</c> does, but only for
the mentioned functions. <c>MFAs</c> is a tuple
<c>{Module,Name,Arity}</c> or a list of such tuples.</p>
</item>
<tag><c>nowarn_deprecated_type</c></tag>
<item>
- <p>Turns off warnings for uses of deprecated types. By
- default (<c>warn_deprecated_type</c>), warnings are
- emitted for every use of a type known by the compiler
- to be deprecated.</p>
+ <p>Turns off warnings for use of deprecated types. Default
+ is to emit warnings for every use of a type known by the compiler
+ to be deprecated.</p>
</item>
<tag><c>warn_obsolete_guard</c></tag>
<item>
- <p>Causes warnings to be emitted for calls to old type
- testing BIFs such as <c>pid/1</c> and <c>list/1</c>. See
- the
- <seealso marker="doc/reference_manual:expressions#guards">Erlang Reference Manual</seealso>
+ <p>Emits warnings for calls to old type testing BIFs,
+ such as <c>pid/1</c> and <c>list/1</c>. See the
+ <seealso marker="doc/reference_manual:expressions#guards">Erlang Reference Manual</seealso>
for a complete list of type testing BIFs and their old
- equivalents. No warnings for calls to old type testing
- BIFs, which is the default, can be selected by the option
- <c>nowarn_obsolete_guard</c>.</p>
+ equivalents. Default is to emit no warnings for calls to
+ old type testing BIFs.</p>
</item>
<tag><c>warn_unused_import</c></tag>
<item>
- <p>Causes warnings to be emitted for unused imported
- functions. No warnings for unused imported functions,
- which is the default, can be selected by the option
- <c>nowarn_unused_import</c>. </p>
+ <p>Emits warnings for unused imported functions.
+ Default is to emit no warnings for unused imported functions.</p>
</item>
<tag><c>nowarn_unused_vars</c></tag>
<item>
- <p>By default, warnings are emitted for variables which
- are not used, with the exception of variables beginning
- with an underscore ("Prolog style warnings").
+ <p>By default, warnings are emitted for unused variables,
+ except for variables beginning with an underscore
+ ("Prolog style warnings").
Use this option to turn off this kind of warnings.</p>
</item>
<tag><c>nowarn_unused_record</c></tag>
<item>
- <p>Turns off warnings for unused record types. By
- default (<c>warn_unused_records</c>), warnings are
- emitted for unused locally defined record types.</p>
+ <p>Turns off warnings for unused record types. Default is to
+ emit warnings for unused locally defined record types.</p>
</item>
</taglist>
<p>Another class of warnings is generated by the compiler
during optimization and code generation. They warn about
patterns that will never match (such as <c>a=b</c>), guards
- that will always evaluate to false, and expressions that will
+ that always evaluate to false, and expressions that
always fail (such as <c>atom+42</c>).</p>
-
- <p>Note that the compiler does not warn for expressions that it
- does not attempt to optimize. For instance, the compiler tries
- to evaluate <c>1/0</c>, notices that it will cause an
- exception and emits a warning. On the other hand,
- the compiler is silent about the similar expression
- <c>X/0</c>; because of the variable in it, the compiler does
- not even try to evaluate and therefore it emits no warnings.
- </p>
-
- <p>Currently, those warnings cannot be disabled (except by
+ <p>Those warnings cannot be disabled (except by
disabling all warnings).</p>
+ <note>
+ <p>The compiler does not warn for expressions that it
+ does not attempt to optimize. For example, the compiler tries
+ to evaluate <c>1/0</c>, detects that it will cause an
+ exception, and emits a warning. However,
+ the compiler is silent about the similar expression,
+ <c>X/0</c>, because of the variable in it. Thus, the compiler does
+ not even try to evaluate and therefore it emits no warnings.</p>
+ </note>
+
<warning>
- <p>Obviously, the absence of warnings does not mean that
+ <p>The absence of warnings does not mean that
there are no remaining errors in the code.</p>
</warning>
-
- <p>Note that all the options except the include path
- (<c>{i,Dir}</c>) can also be given in the file with a
- <c>-compile([Option,...])</c>. attribute.
- The <c>-compile()</c> attribute is allowed after function
+
+ <note>
+ <p>All options, except the include path
+ (<c>{i,Dir}</c>), can also be given in the file with attribute
+ <c>-compile([Option,...])</c>.
+ Attribute <c>-compile()</c> is allowed after the function
definitions.</p>
-
- <p>Note also that the <c>{nowarn_unused_function, FAs}</c>,
+ </note>
+
+ <note>
+ <p>The options <c>{nowarn_unused_function, FAs}</c>,
<c>{nowarn_bif_clash, FAs}</c>, and
- <c>{nowarn_deprecated_function, MFAs}</c> options are only
+ <c>{nowarn_deprecated_function, MFAs}</c> are only
recognized when given in files. They are not affected by
- the <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or
- <c>warn_deprecated_function</c> options.</p>
+ options <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or
+ <c>warn_deprecated_function</c>.</p>
+ </note>
<p>For debugging of the compiler, or for pure curiosity,
the intermediate code generated by each compiler pass can be
inspected.
- A complete list of the options to produce list files can be
- printed by typing <c>compile:options()</c> at the Erlang
- shell prompt.
- The options will be printed in order that the passes are
+ To print a complete list of the options to produce list files,
+ type <c>compile:options()</c> at the Erlang shell prompt.
+ The options are printed in the order that the passes are
executed. If more than one listing option is used, the one
representing the earliest pass takes effect.</p>
- <p><em>Unrecognized options are ignored.</em></p>
+ <p>Unrecognized options are ignored.</p>
<p>Both <c>WarningList</c> and <c>ErrorList</c> have
the following format:</p>
<code>
-[{FileName,[ErrorInfo]}].
- </code>
-
- <p><c>ErrorInfo</c> is described below. The file name has been
- included here as the compiler uses the Erlang pre-processor
- <c>epp</c>, which allows the code to be included in other
- files. For this reason, it is important to know to
- <em>which</em> file an error or warning line number refers.
+[{FileName,[ErrorInfo]}].</code>
+
+ <p><c>ErrorInfo</c> is described later in this section.
+ The filename is included here, as the compiler uses the
+ Erlang pre-processor <c>epp</c>, which allows the code to be
+ included in other files. It is therefore important to know to
+ <em>which</em> file the line number of an error or a warning refers.
</p>
</desc>
</func>
<func>
<name>forms(Forms)</name>
- <fsummary>Compile a list of forms</fsummary>
+ <fsummary>Compiles a list of forms.</fsummary>
<desc>
<p>Is the same as
<c>forms(File, [verbose,report_errors,report_warnings])</c>.
@@ -670,7 +662,7 @@ module.beam: module.erl \
<func>
<name>forms(Forms, Options) -> CompRet</name>
- <fsummary>Compile a list of forms</fsummary>
+ <fsummary>Compiles a list of forms.</fsummary>
<type>
<v>Forms = [Form]</v>
<v>CompRet = BinRet | ErrRet</v>
@@ -681,48 +673,49 @@ module.beam: module.erl \
<desc>
<p>Analogous to <c>file/1</c>, but takes a list of forms (in
the Erlang abstract format representation) as first argument.
- The option <c>binary</c> is implicit; i.e., no object code
- file is produced. Options that would ordinarily produce a
- listing file, such as 'E', will instead cause the internal
- format for that compiler pass (an Erlang term; usually not a
- binary) to be returned instead of a binary.</p>
+ Option <c>binary</c> is implicit, that is, no object code
+ file is produced. For options that normally produce a listing
+ file, such as 'E', the internal format for that compiler pass
+ (an Erlang term, usually not a binary) is returned instead of
+ a binary.</p>
</desc>
</func>
<func>
<name>format_error(ErrorDescriptor) -> chars()</name>
- <fsummary>Format an error descriptor</fsummary>
+ <fsummary>Formats an error descriptor.</fsummary>
<type>
<v>ErrorDescriptor = errordesc()</v>
</type>
<desc>
<p>Uses an <c>ErrorDescriptor</c> and returns a deep list of
- characters which describes the error. This function is
- usually called implicitly when an <c>ErrorInfo</c> structure
- is processed. See below.</p>
+ characters that describes the error. This function is
+ usually called implicitly when an <c>ErrorInfo</c> structure
+ (described in section
+ <seealso marker="#error_information">Error Information</seealso>) is processed.</p>
</desc>
</func>
<func>
<name>output_generated(Options) -> true | false</name>
- <fsummary>Determine whether the compile will generate an output file</fsummary>
+ <fsummary>Determines whether the compiler generates an output file.</fsummary>
<type>
<v>Options = [term()]</v>
</type>
<desc>
- <p>Determines whether the compiler would generate a <c>beam</c>
+ <p>Determines whether the compiler generates a <c>beam</c>
file with the given options. <c>true</c> means that a <c>beam</c>
- file would be generated; <c>false</c> means that the compiler
- would generate some listing file, return a binary, or merely
- check the syntax of the source code.</p>
+ file is generated. <c>false</c> means that the compiler
+ generates some listing file, returns a binary, or merely
+ checks the syntax of the source code.</p>
</desc>
</func>
<func>
<name>noenv_file(File, Options) -> CompRet</name>
- <fsummary>Compile a file (ignoring ERL_COMPILER_OPTIONS)</fsummary>
+ <fsummary>Compiles a file (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary>
<desc>
- <p>Works exactly like <seealso marker="#file/2">file/2</seealso>,
+ <p>Works like <seealso marker="#file/2">file/2</seealso>,
except that the environment variable <c>ERL_COMPILER_OPTIONS</c>
is not consulted.</p>
</desc>
@@ -730,9 +723,9 @@ module.beam: module.erl \
<func>
<name>noenv_forms(Forms, Options) -> CompRet</name>
- <fsummary>Compile a list of forms (ignoring ERL_COMPILER_OPTIONS)</fsummary>
+ <fsummary>Compiles a list of forms (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary>
<desc>
- <p>Works exactly like <seealso marker="#forms/2">forms/2</seealso>,
+ <p>Works like <seealso marker="#forms/2">forms/2</seealso>,
except that the environment variable <c>ERL_COMPILER_OPTIONS</c>
is not consulted.</p>
</desc>
@@ -740,12 +733,13 @@ module.beam: module.erl \
<func>
<name>noenv_output_generated(Options) -> true | false</name>
- <fsummary>Determine whether the compile will generate an output file (ignoring ERL_COMPILER_OPTIONS)</fsummary>
+ <fsummary>Determines whether the compiler generates an output file
+ (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary>
<type>
<v>Options = [term()]</v>
</type>
<desc>
- <p>Works exactly like
+ <p>Works like
<seealso marker="#output_generated/1">output_generated/1</seealso>,
except that the environment variable <c>ERL_COMPILER_OPTIONS</c>
is not consulted.</p>
@@ -755,14 +749,14 @@ module.beam: module.erl \
</funcs>
<section>
- <title>Default compiler options</title>
+ <title>Default Compiler Options</title>
<p>The (host operating system) environment variable
<c>ERL_COMPILER_OPTIONS</c> can be used to give default compiler
options. Its value must be a valid Erlang term. If the value is a
- list, it will be used as is. If it is not a list, it will be put
+ list, it is used as is. If it is not a list, it is put
into a list.</p>
- <p>The list will be appended to any options given to
+ <p>The list is appended to any options given to
<seealso marker="#file/2">file/2</seealso>,
<seealso marker="#forms/2">forms/2</seealso>, and
<seealso marker="#output_generated/1">output_generated/2</seealso>.
@@ -770,9 +764,9 @@ module.beam: module.erl \
<seealso marker="#noenv_file/2">noenv_file/2</seealso>,
<seealso marker="#noenv_forms/2">noenv_forms/2</seealso>, or
<seealso marker="#noenv_output_generated/1">noenv_output_generated/2</seealso>
- if you don't want the environment variable to be consulted
- (for instance, if you are calling the compiler recursively from
- inside a parse transform).</p>
+ if you do not want the environment variable to be consulted,
+ for example, if you are calling the compiler recursively from
+ inside a parse transform.</p>
</section>
<section>
@@ -781,31 +775,31 @@ module.beam: module.erl \
module. Inlining means that a call to a function is replaced with
the function body with the arguments replaced with the actual
values. The semantics are preserved, except if exceptions are
- generated in the inlined code. Exceptions will be reported as
+ generated in the inlined code. Exceptions are reported as
occurring in the function the body was inlined into. Also,
- <c>function_clause</c> exceptions will be converted to similar
+ <c>function_clause</c> exceptions are converted to similar
<c>case_clause</c> exceptions.</p>
- <p>When a function is inlined, the original function will be
+ <p>When a function is inlined, the original function is
kept if it is exported (either by an explicit export or if the
- <c>export_all</c> option was given) or if not all calls to the
- function were inlined.</p>
+ option <c>export_all</c> was given) or if not all calls to the
+ function are inlined.</p>
<p>Inlining does not necessarily improve running time.
- For instance, inlining may increase Beam stack usage which will
- probably be detrimental to performance for recursive functions.
+ For example, inlining can increase Beam stack use, which
+ probably is detrimental to performance for recursive functions.
</p>
- <p>Inlining is never default; it must be explicitly enabled with a
+ <p>Inlining is never default. It must be explicitly enabled with a
compiler option or a <c>-compile()</c> attribute in the source
module.</p>
- <p>To enable inlining, either use the <c>inline</c> option to
- let the compiler decide which functions to inline or
+ <p>To enable inlining, either use the option <c>inline</c> to
+ let the compiler decide which functions to inline, or
<c>{inline,[{Name,Arity},...]}</c> to have the compiler inline
all calls to the given functions. If the option is given inside
a <c>compile</c> directive in an Erlang module, <c>{Name,Arity}</c>
- may be written as <c>Name/Arity</c>.</p>
+ can be written as <c>Name/Arity</c>.</p>
<p>Example of explicit inlining:</p>
@@ -817,33 +811,30 @@ pi() -> 3.1416.
<p>Example of implicit inlining:</p>
<pre>
--compile(inline).
- </pre>
+-compile(inline).</pre>
- <p>The <c>{inline_size,Size}</c> option controls how large functions
- that are allowed to be inlined. Default is <c>24</c>, which will
- keep the size of the inlined code roughly the same as
- the un-inlined version (only relatively small functions will be
+ <p>The option <c>{inline_size,Size}</c> controls how large functions
+ that are allowed to be inlined. Default is <c>24</c>, which
+ keeps the size of the inlined code roughly the same as
+ the un-inlined version (only relatively small functions are
inlined).</p>
<p>Example:</p>
<pre>
%% Aggressive inlining - will increase code size.
-compile(inline).
--compile({inline_size,100}).
- </pre>
+-compile({inline_size,100}).</pre>
</section>
<section>
- <title>Inlining of list functions</title>
- <p>The compiler can also inline a variety of list manipulation functions
- from the stdlib's lists module.</p>
+ <title>Inlining of List Functions</title>
+ <p>The compiler can also inline various list manipulation functions
+ from the module <c>list</c> in <c>STDLIB</c>.</p>
<p>This feature must be explicitly enabled with a compiler option or a
<c>-compile()</c> attribute in the source module.</p>
- <p>To enable inlining of list functions, use the <c>inline_list_funcs</c>
- option.</p>
+ <p>To enable inlining of list functions, use option <c>inline_list_funcs</c>.</p>
<p>The following functions are inlined:</p>
<list type="bulleted">
@@ -869,24 +860,23 @@ pi() -> 3.1416.
</section>
<section>
+ <marker id="error_information"></marker>
<title>Error Information</title>
- <p>The <c>ErrorInfo</c> mentioned above is the standard
- <c>ErrorInfo</c> structure which is returned from all IO modules.
+ <p>The <c>ErrorInfo</c> mentioned earlier is the standard
+ <c>ErrorInfo</c> structure, which is returned from all I/O modules.
It has the following format:</p>
<code>
-{ErrorLine, Module, ErrorDescriptor}
- </code>
+{ErrorLine, Module, ErrorDescriptor}</code>
- <p><c>ErrorLine</c> will be the atom <c>none</c> if the error does
- not correspond to a specific line (e.g. if the source file does
- not exist).</p>
+ <p><c>ErrorLine</c> is the atom <c>none</c> if the error does
+ not correspond to a specific line, for example, if the source file does
+ not exist.</p>
<p>A string describing the error is obtained with the following
call:</p>
<code>
-Module:format_error(ErrorDescriptor)
- </code>
+Module:format_error(ErrorDescriptor)</code>
</section>
<section>
diff --git a/lib/compiler/doc/src/ref_man.xml b/lib/compiler/doc/src/ref_man.xml
index 6478ad4b11..6584e79c4e 100644
--- a/lib/compiler/doc/src/ref_man.xml
+++ b/lib/compiler/doc/src/ref_man.xml
@@ -29,7 +29,7 @@
<file>application.sgml</file>
</header>
<description>
- <p>The <em>Compiler</em> application compiles Erlang
+ <p>The <c>Compiler</c> application compiles Erlang
code to byte-code. The highly compact byte-code is executed by
the Erlang emulator.</p>
</description>
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 7c4cebdc28..78efc8dff0 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -70,6 +70,7 @@ MODULES = \
cerl \
cerl_clauses \
cerl_inline \
+ cerl_sets \
cerl_trees \
compile \
core_lib \
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index 5932d8ce1d..bbe607cf19 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -96,7 +96,7 @@ move_move_into_block([], Acc) -> reverse(Acc).
%%%
forward(Is, Lc) ->
- forward(Is, gb_trees:empty(), Lc, []).
+ forward(Is, #{}, Lc, []).
forward([{move,_,_}=Move|[{label,L}|_]=Is], D, Lc, Acc) ->
%% move/2 followed by jump/1 is optimized by backward/3.
@@ -115,19 +115,20 @@ forward([{label,Lbl}=LblI,{block,[{set,[Dst],[Lit],move}|BlkIs]}=Blk|Is], D, Lc,
%% cannot be reached in any other way than through the select_val/3
%% instruction (i.e. there can be no fallthrough to such label and
%% it cannot be referenced by, for example, a jump/1 instruction).
- Block = case gb_trees:lookup({Lbl,Dst}, D) of
- {value,Lit} -> {block,BlkIs}; %Safe to remove move instruction.
- _ -> Blk %Must keep move instruction.
- end,
+ Key = {Lbl,Dst},
+ Block = case D of
+ #{Key := Lit} -> {block,BlkIs}; %Safe to remove move instruction.
+ _ -> Blk %Must keep move instruction.
+ end,
forward([Block|Is], D, Lc, [LblI|Acc]);
forward([{label,Lbl}=LblI|[{move,Lit,Dst}|Is1]=Is0], D, Lc, Acc) ->
%% Assumption: The target labels in a select_val/3 instruction
%% cannot be reached in any other way than through the select_val/3
%% instruction (i.e. there can be no fallthrough to such label and
%% it cannot be referenced by, for example, a jump/1 instruction).
- Is = case gb_trees:lookup({Lbl,Dst}, D) of
- {value,Lit} -> Is1; %Safe to remove move instruction.
- _ -> Is0 %Keep move instruction.
+ Is = case maps:find({Lbl,Dst}, D) of
+ {ok,Lit} -> Is1; %Safe to remove move instruction.
+ _ -> Is0 %Keep move instruction.
end,
forward(Is, D, Lc, [LblI|Acc]);
forward([{test,is_eq_exact,_,[Same,Same]}|Is], D, Lc, Acc) ->
@@ -156,11 +157,11 @@ forward([], _, Lc, Acc) -> {Acc,Lc}.
update_value_dict([Lit,{f,Lbl}|T], Reg, D0) ->
Key = {Lbl,Reg},
- D = case gb_trees:lookup(Key, D0) of
- none -> gb_trees:insert(Key, Lit, D0); %New.
- {value,inconsistent} -> D0; %Inconsistent.
- {value,_} -> gb_trees:update(Key, inconsistent, D0)
- end,
+ D = case D0 of
+ #{Key := inconsistent} -> D0;
+ #{Key := _} -> D0#{Key := inconsistent};
+ _ -> D0#{Key => Lit}
+ end,
update_value_dict(T, Reg, D);
update_value_dict([], _, D) -> D.
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index 68dc104dd3..b1aa98278e 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -31,22 +31,22 @@
-type index() :: non_neg_integer().
--type atom_tab() :: gb_trees:tree(atom(), index()).
+-type atom_tab() :: #{atom() => index()}.
-type import_tab() :: gb_trees:tree(mfa(), index()).
--type fname_tab() :: gb_trees:tree(Name :: term(), index()).
--type line_tab() :: gb_trees:tree({Fname :: index(), Line :: term()}, index()).
+-type fname_tab() :: #{Name :: term() => index()}.
+-type line_tab() :: #{{Fname :: index(), Line :: term()} => index()}.
-type literal_tab() :: dict:dict(Literal :: term(), index()).
-record(asm,
- {atoms = gb_trees:empty() :: atom_tab(),
+ {atoms = #{} :: atom_tab(),
exports = [] :: [{label(), arity(), label()}],
locals = [] :: [{label(), arity(), label()}],
imports = gb_trees:empty() :: import_tab(),
strings = <<>> :: binary(), %String pool
lambdas = [], %[{...}]
literals = dict:new() :: literal_tab(),
- fnames = gb_trees:empty() :: fname_tab(),
- lines = gb_trees:empty() :: line_tab(),
+ fnames = #{} :: fname_tab(),
+ lines = #{} :: line_tab(),
num_lines = 0 :: non_neg_integer(), %Number of line instructions
next_import = 0 :: non_neg_integer(),
string_offset = 0 :: non_neg_integer(),
@@ -77,14 +77,12 @@ highest_opcode(#asm{highest_opcode=Op}) -> Op.
%% atom(Atom, Dict) -> {Index,Dict'}
-spec atom(atom(), bdict()) -> {pos_integer(), bdict()}.
-atom(Atom, #asm{atoms=Atoms0}=Dict) when is_atom(Atom) ->
- case gb_trees:lookup(Atom, Atoms0) of
- {value,Index} ->
- {Index,Dict};
- none ->
- NextIndex = gb_trees:size(Atoms0) + 1,
- Atoms = gb_trees:insert(Atom, NextIndex, Atoms0),
- {NextIndex,Dict#asm{atoms=Atoms}}
+atom(Atom, #asm{atoms=Atoms}=Dict) when is_atom(Atom) ->
+ case Atoms of
+ #{ Atom := Index} -> {Index,Dict};
+ _ ->
+ NextIndex = maps:size(Atoms) + 1,
+ {NextIndex,Dict#asm{atoms=Atoms#{Atom=>NextIndex}}}
end.
%% Remembers an exported function.
@@ -177,26 +175,22 @@ line([], #asm{num_lines=N}=Dict) ->
%% No location available. Return the special pre-defined
%% index 0.
{0,Dict#asm{num_lines=N+1}};
-line([{location,Name,Line}], #asm{lines=Lines0,num_lines=N}=Dict0) ->
+line([{location,Name,Line}], #asm{lines=Lines,num_lines=N}=Dict0) ->
{FnameIndex,Dict1} = fname(Name, Dict0),
- case gb_trees:lookup({FnameIndex,Line}, Lines0) of
- {value,Index} ->
- {Index,Dict1#asm{num_lines=N+1}};
- none ->
- Index = gb_trees:size(Lines0) + 1,
- Lines = gb_trees:insert({FnameIndex,Line}, Index, Lines0),
- Dict = Dict1#asm{lines=Lines,num_lines=N+1},
- {Index,Dict}
+ Key = {FnameIndex,Line},
+ case Lines of
+ #{Key := Index} -> {Index,Dict1#asm{num_lines=N+1}};
+ _ ->
+ Index = maps:size(Lines) + 1,
+ {Index, Dict1#asm{lines=Lines#{Key=>Index},num_lines=N+1}}
end.
-fname(Name, #asm{fnames=Fnames0}=Dict) ->
- case gb_trees:lookup(Name, Fnames0) of
- {value,Index} ->
- {Index,Dict};
- none ->
- Index = gb_trees:size(Fnames0),
- Fnames = gb_trees:insert(Name, Index, Fnames0),
- {Index,Dict#asm{fnames=Fnames}}
+fname(Name, #asm{fnames=Fnames}=Dict) ->
+ case Fnames of
+ #{Name := Index} -> {Index,Dict};
+ _ ->
+ Index = maps:size(Fnames),
+ {Index,Dict#asm{fnames=Fnames#{Name=>Index}}}
end.
%% Returns the atom table.
@@ -204,14 +198,12 @@ fname(Name, #asm{fnames=Fnames0}=Dict) ->
-spec atom_table(bdict()) -> {non_neg_integer(), [[non_neg_integer(),...]]}.
atom_table(#asm{atoms=Atoms}) ->
- NumAtoms = gb_trees:size(Atoms),
- Sorted = lists:keysort(2, gb_trees:to_list(Atoms)),
- Fun = fun({A,_}) ->
- L = atom_to_list(A),
- [length(L)|L]
- end,
- AtomTab = lists:map(Fun, Sorted),
- {NumAtoms,AtomTab}.
+ NumAtoms = maps:size(Atoms),
+ Sorted = lists:keysort(2, maps:to_list(Atoms)),
+ {NumAtoms,[begin
+ L = atom_to_list(A),
+ [length(L)|L]
+ end || {A,_} <- Sorted]}.
%% Returns the table of local functions.
%% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]}
@@ -273,11 +265,11 @@ my_term_to_binary(Term) ->
non_neg_integer(),[{non_neg_integer(),non_neg_integer()}]}.
line_table(#asm{fnames=Fnames0,lines=Lines0,num_lines=NumLineInstrs}) ->
- NumFnames = gb_trees:size(Fnames0),
- Fnames1 = lists:keysort(2, gb_trees:to_list(Fnames0)),
+ NumFnames = maps:size(Fnames0),
+ Fnames1 = lists:keysort(2, maps:to_list(Fnames0)),
Fnames = [Name || {Name,_} <- Fnames1],
- NumLines = gb_trees:size(Lines0),
- Lines1 = lists:keysort(2, gb_trees:to_list(Lines0)),
+ NumLines = maps:size(Lines0),
+ Lines1 = lists:keysort(2, maps:to_list(Lines0)),
Lines = [L || {L,_} <- Lines1],
{NumLineInstrs,NumFnames,Fnames,NumLines,Lines}.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 52b6464c7f..80b2998ddc 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -152,14 +152,14 @@ function({function,Name,Arity,CLabel,Asm0}) ->
share(Is0) ->
%% We will get more sharing if we never fall through to a label.
Is = eliminate_fallthroughs(Is0, []),
- share_1(Is, dict:new(), [], []).
+ share_1(Is, #{}, [], []).
share_1([{label,_}=Lbl|Is], Dict, [], Acc) ->
share_1(Is, Dict, [], [Lbl|Acc]);
share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
- case dict:find(Seq, Dict0) of
+ case maps:find(Seq, Dict0) of
error ->
- Dict = dict:store(Seq, L, Dict0),
+ Dict = maps:put(Seq, L, Dict0),
share_1(Is, Dict, [], [Lbl|Seq ++ Acc]);
{ok,Label} ->
share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc])
@@ -188,7 +188,7 @@ clean_non_sharable(Dict) ->
%% a sequence inside the 'try' block is a sequence that ends
%% with an instruction that causes an exception. Any sequence
%% that causes an exception must contain a line/1 instruction.
- dict:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
+ maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
sharable_with_try([{line,_}|_]) ->
%% This sequence may cause an exception and may potentially
@@ -268,13 +268,13 @@ extract_seq_1(_, _) -> no.
-record(st, {fc, %Label for function class errors.
entry, %Entry label (must not be moved).
mlbl, %Moved labels.
- labels %Set of referenced labels.
+ labels :: cerl_sets:set() %Set of referenced labels.
}).
opt([{label,Fc}|_]=Is0, CLabel) ->
Lbls = initial_labels(Is0),
find_fixpoint(fun(Is) ->
- St = #st{fc=Fc,entry=CLabel,mlbl=dict:new(),
+ St = #st{fc=Fc,entry=CLabel,mlbl=#{},
labels=Lbls},
opt(Is, [], St)
end, Is0).
@@ -320,11 +320,11 @@ opt([{test,_,{f,_}=Lbl,_,_,_}=I|Is], Acc, St) ->
opt([{select,_,_R,Fail,Vls}=I|Is], Acc, St) ->
skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) ->
- case dict:find(Lbl, Mlbl) of
+ case maps:find(Lbl, Mlbl) of
{ok,Lbls} ->
%% Essential to remove the list of labels from the dictionary,
%% since we will rescan the inserted labels. We MUST rescan.
- St = St0#st{mlbl=dict:erase(Lbl, Mlbl)},
+ St = St0#st{mlbl=maps:remove(Lbl, Mlbl)},
insert_labels([Lbl|Lbls], Is, Acc, St);
error -> opt(Is, [I|Acc], St0)
end;
@@ -339,7 +339,7 @@ opt([{jump,{f,L}=Lbl}=I|Is], Acc0, #st{mlbl=Mlbl0}=St0) ->
St = case Lbls of
[] -> St0;
[_|_] ->
- Mlbl = dict:append_list(L, Lbls, Mlbl0),
+ Mlbl = maps_append_list(L, Lbls, Mlbl0),
St0#st{mlbl=Mlbl}
end,
skip_unreachable(Is, [I|Acc], label_used(Lbl, St));
@@ -363,14 +363,20 @@ opt([I|Is], Acc, #st{labels=Used0}=St0) ->
end;
opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) ->
Code = reverse(Acc),
- case dict:find(Fc, Mlbl) of
+ case maps:find(Fc, Mlbl) of
{ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code);
error -> Code
end.
+maps_append_list(K,Vs,M) ->
+ case M of
+ #{K:=Vs0} -> M#{K:=Vs0++Vs}; % same order as dict
+ _ -> M#{K => Vs}
+ end.
+
insert_fc_labels([L|Ls], Mlbl, Acc0) ->
Acc = [{label,L}|Acc0],
- case dict:find(L, Mlbl) of
+ case maps:find(L, Mlbl) of
error ->
insert_fc_labels(Ls, Mlbl, Acc);
{ok,Lbls} ->
@@ -434,7 +440,7 @@ skip_unreachable([], Acc, St) ->
%% Add one or more label to the set of used labels.
-label_used({f,L}, St) -> St#st{labels=gb_sets:add(L, St#st.labels)};
+label_used({f,L}, St) -> St#st{labels=cerl_sets:add_element(L,St#st.labels)};
label_used([H|T], St0) -> label_used(T, label_used(H, St0));
label_used([], St) -> St;
label_used(_Other, St) -> St.
@@ -442,7 +448,7 @@ label_used(_Other, St) -> St.
%% Test if label is used.
is_label_used(L, St) ->
- gb_sets:is_member(L, St#st.labels).
+ cerl_sets:is_element(L, St#st.labels).
%% is_unreachable_after(Instruction) -> boolean()
%% Test whether the code after Instruction is unreachable.
@@ -472,14 +478,14 @@ is_exit_instruction(_) -> false.
%% (including inside blocks).
is_label_used_in(Lbl, Is) ->
- is_label_used_in_1(Is, Lbl, gb_sets:empty()).
+ is_label_used_in_1(Is, Lbl, cerl_sets:new()).
is_label_used_in_1([{block,Block}|Is], Lbl, Empty) ->
lists:any(fun(I) -> is_label_used_in_block(I, Lbl) end, Block)
orelse is_label_used_in_1(Is, Lbl, Empty);
is_label_used_in_1([I|Is], Lbl, Empty) ->
Used = ulbl(I, Empty),
- gb_sets:is_member(Lbl, Used) orelse is_label_used_in_1(Is, Lbl, Empty);
+ cerl_sets:is_element(Lbl, Used) orelse is_label_used_in_1(Is, Lbl, Empty);
is_label_used_in_1([], _, _) -> false.
is_label_used_in_block({set,_,_,Info}, Lbl) ->
@@ -506,7 +512,7 @@ remove_unused_labels(Is) ->
rem_unused(Is, Used, []).
rem_unused([{label,Lbl}=I|Is0], Used, [Prev|_]=Acc) ->
- case gb_sets:is_member(Lbl, Used) of
+ case cerl_sets:is_element(Lbl, Used) of
false ->
Is = case is_unreachable_after(Prev) of
true -> drop_upto_label(Is0);
@@ -528,7 +534,7 @@ initial_labels([{line,_}|Is], Acc) ->
initial_labels([{label,Lbl}|Is], Acc) ->
initial_labels(Is, [Lbl|Acc]);
initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) ->
- gb_sets:from_list([Lbl|Acc]).
+ cerl_sets:from_list([Lbl|Acc]).
drop_upto_label([{label,_}|_]=Is) -> Is;
drop_upto_label([_|Is]) -> drop_upto_label(Is);
@@ -576,10 +582,10 @@ ulbl({get_map_elements,Lbl,_Src,_List}, Used) ->
ulbl(_, Used) -> Used.
mark_used({f,0}, Used) -> Used;
-mark_used({f,L}, Used) -> gb_sets:add(L, Used).
+mark_used({f,L}, Used) -> cerl_sets:add_element(L, Used).
mark_used_list([{f,L}|T], Used) ->
- mark_used_list(T, gb_sets:add(L, Used));
+ mark_used_list(T, cerl_sets:add_element(L, Used));
mark_used_list([_|T], Used) ->
mark_used_list(T, Used);
mark_used_list([], Used) -> Used.
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 4731b5e78e..7ab548152e 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -554,10 +554,10 @@ flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) ->
Acc = flush_all(Rs, Is0, Acc0),
{[],Acc};
flush(Rs0, [{set,Ds,Ss,_Op}|_], Acc0) ->
- Save = gb_sets:from_list(Ss),
+ Save = cerl_sets:from_list(Ss),
Acc = save_regs(Rs0, Save, Acc0),
Rs1 = foldl(fun(S, A) -> mark(S, A, clean) end, Rs0, Ss),
- Kill = gb_sets:from_list(Ds),
+ Kill = cerl_sets:from_list(Ds),
Rs = kill_regs(Rs1, Kill),
{Rs,Acc};
flush(Rs0, Is, Acc0) ->
@@ -580,7 +580,7 @@ save_regs(Rs, Save, Acc) ->
foldl(fun(R, A) -> save_reg(R, Save, A) end, Acc, Rs).
save_reg({I,V,dirty}, Save, Acc) ->
- case gb_sets:is_member(V, Save) of
+ case cerl_sets:is_element(V, Save) of
true -> [{set,[V],[{fr,I}],fmove}|checkerror(Acc)];
false -> Acc
end;
@@ -590,7 +590,7 @@ kill_regs(Rs, Kill) ->
[kill_reg(R, Kill) || R <- Rs].
kill_reg({_,V,_}=R, Kill) ->
- case gb_sets:is_member(V, Kill) of
+ case cerl_sets:is_element(V, Kill) of
true -> free;
false -> R
end;
diff --git a/lib/compiler/src/cerl_sets.erl b/lib/compiler/src/cerl_sets.erl
new file mode 100644
index 0000000000..4df78dc432
--- /dev/null
+++ b/lib/compiler/src/cerl_sets.erl
@@ -0,0 +1,206 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(cerl_sets).
+
+%% Standard interface.
+-export([new/0,is_set/1,size/1,to_list/1,from_list/1]).
+-export([is_element/2,add_element/2,del_element/2]).
+-export([union/2,union/1,intersection/2,intersection/1]).
+-export([is_disjoint/2]).
+-export([subtract/2,is_subset/2]).
+-export([fold/3,filter/2]).
+
+-export_type([set/0, set/1]).
+
+%%------------------------------------------------------------------------------
+
+-type set() :: set(_).
+-opaque set(Element) :: #{Element => 'ok'}.
+
+%%------------------------------------------------------------------------------
+
+%% new() -> Set
+-spec new() -> set().
+
+new() -> #{}.
+
+%% is_set(Set) -> boolean().
+%% Return 'true' if Set is a set of elements, else 'false'.
+-spec is_set(Set) -> boolean() when
+ Set :: term().
+
+is_set(S) when is_map(S) -> true;
+is_set(_) -> false.
+
+%% size(Set) -> int().
+%% Return the number of elements in Set.
+-spec size(Set) -> non_neg_integer() when
+ Set :: set().
+
+size(S) -> maps:size(S).
+
+%% to_list(Set) -> [Elem].
+%% Return the elements in Set as a list.
+-spec to_list(Set) -> List when
+ Set :: set(Element),
+ List :: [Element].
+
+to_list(S) -> maps:keys(S).
+
+%% from_list([Elem]) -> Set.
+%% Build a set from the elements in List.
+-spec from_list(List) -> Set when
+ List :: [Element],
+ Set :: set(Element).
+from_list(Ls) -> maps:from_list([{K,ok}||K<-Ls]).
+
+%% is_element(Element, Set) -> boolean().
+%% Return 'true' if Element is an element of Set, else 'false'.
+-spec is_element(Element, Set) -> boolean() when
+ Set :: set(Element).
+
+is_element(E,S) ->
+ case S of
+ #{E := _} -> true;
+ _ -> false
+ end.
+
+%% add_element(Element, Set) -> Set.
+%% Return Set with Element inserted in it.
+-spec add_element(Element, Set1) -> Set2 when
+ Set1 :: set(Element),
+ Set2 :: set(Element).
+
+add_element(E,S) -> S#{E=>ok}.
+
+-spec del_element(Element, Set1) -> Set2 when
+ Set1 :: set(Element),
+ Set2 :: set(Element).
+
+%% del_element(Element, Set) -> Set.
+%% Return Set but with Element removed.
+del_element(E,S) -> maps:remove(E,S).
+
+%% union(Set1, Set2) -> Set
+%% Return the union of Set1 and Set2.
+-spec union(Set1, Set2) -> Set3 when
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
+
+union(S1,S2) -> maps:merge(S1,S2).
+
+%% union([Set]) -> Set
+%% Return the union of the list of sets.
+-spec union(SetList) -> Set when
+ SetList :: [set(Element)],
+ Set :: set(Element).
+
+union([S1,S2|Ss]) ->
+ union1(union(S1, S2), Ss);
+union([S]) -> S;
+union([]) -> new().
+
+union1(S1, [S2|Ss]) ->
+ union1(union(S1, S2), Ss);
+union1(S1, []) -> S1.
+
+%% intersection(Set1, Set2) -> Set.
+%% Return the intersection of Set1 and Set2.
+-spec intersection(Set1, Set2) -> Set3 when
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
+
+intersection(S1, S2) ->
+ filter(fun (E) -> is_element(E, S1) end, S2).
+
+%% intersection([Set]) -> Set.
+%% Return the intersection of the list of sets.
+-spec intersection(SetList) -> Set when
+ SetList :: [set(Element),...],
+ Set :: set(Element).
+
+intersection([S1,S2|Ss]) ->
+ intersection1(intersection(S1, S2), Ss);
+intersection([S]) -> S.
+
+intersection1(S1, [S2|Ss]) ->
+ intersection1(intersection(S1, S2), Ss);
+intersection1(S1, []) -> S1.
+
+%% is_disjoint(Set1, Set2) -> boolean().
+%% Check whether Set1 and Set2 are disjoint.
+-spec is_disjoint(Set1, Set2) -> boolean() when
+ Set1 :: set(Element),
+ Set2 :: set(Element).
+
+is_disjoint(S1, S2) when map_size(S1) < map_size(S2) ->
+ fold(fun (_, false) -> false;
+ (E, true) -> not is_element(E, S2)
+ end, true, S1);
+is_disjoint(S1, S2) ->
+ fold(fun (_, false) -> false;
+ (E, true) -> not is_element(E, S1)
+ end, true, S2).
+
+%% subtract(Set1, Set2) -> Set.
+%% Return all and only the elements of Set1 which are not also in
+%% Set2.
+-spec subtract(Set1, Set2) -> Set3 when
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
+
+subtract(S1, S2) ->
+ filter(fun (E) -> not is_element(E, S2) end, S1).
+
+%% is_subset(Set1, Set2) -> boolean().
+%% Return 'true' when every element of Set1 is also a member of
+%% Set2, else 'false'.
+-spec is_subset(Set1, Set2) -> boolean() when
+ Set1 :: set(Element),
+ Set2 :: set(Element).
+
+is_subset(S1, S2) ->
+ fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1).
+
+%% fold(Fun, Accumulator, Set) -> Accumulator.
+%% Fold function Fun over all elements in Set and return Accumulator.
+-spec fold(Function, Acc0, Set) -> Acc1 when
+ Function :: fun((Element, AccIn) -> AccOut),
+ Set :: set(Element),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc.
+
+fold(F, Init, D) ->
+ lists:foldl(fun(E,Acc) -> F(E,Acc) end,Init,maps:keys(D)).
+
+%% filter(Fun, Set) -> Set.
+%% Filter Set with Fun.
+-spec filter(Pred, Set1) -> Set2 when
+ Pred :: fun((Element) -> boolean()),
+ Set1 :: set(Element),
+ Set2 :: set(Element).
+
+filter(F, D) ->
+ maps:from_list(lists:filter(fun({K,_}) -> F(K) end, maps:to_list(D))).
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index 17d1bd91ce..0bfd998301 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -45,6 +45,7 @@
cerl,
cerl_clauses,
cerl_inline,
+ cerl_sets,
cerl_trees,
compile,
core_scan,
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 6f8279f65e..7f4184fd30 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -92,10 +92,10 @@
-endif.
%% Variable value info.
--record(sub, {v=[], %Variable substitutions
- s=[], %Variables in scope
- t=[], %Types
- in_guard=false}). %In guard or not.
+-record(sub, {v=[], %Variable substitutions
+ s=cerl_sets:new() :: cerl_sets:set(), %Variables in scope
+ t=#{} :: map(), %Types
+ in_guard=false}). %In guard or not.
-type type_info() :: cerl:cerl() | 'bool' | 'integer'.
-type yes_no_maybe() :: 'yes' | 'no' | 'maybe'.
@@ -1123,18 +1123,30 @@ let_substs(Vs0, As0, Sub0) ->
{Vs2,As1,Ss} = let_substs_1(Vs1, As0, Sub1),
Sub2 = sub_add_scope([V || #c_var{name=V} <- Vs2], Sub1),
{Vs2,As1,
- foldl(fun ({V,S}, Sub) -> sub_set_name(V, S, Sub) end, Sub2, Ss)}.
+ foldl(fun ({V,S}, Sub) -> sub_set_name(V, S, Sub) end, Sub2, Ss)}.
let_substs_1(Vs, #c_values{es=As}, Sub) ->
let_subst_list(Vs, As, Sub);
let_substs_1([V], A, Sub) -> let_subst_list([V], [A], Sub);
let_substs_1(Vs, A, _) -> {Vs,A,[]}.
-let_subst_list([V|Vs0], [A|As0], Sub) ->
+let_subst_list([V|Vs0], [A0|As0], Sub) ->
{Vs1,As1,Ss} = let_subst_list(Vs0, As0, Sub),
- case is_subst(A) of
- true -> {Vs1,As1,sub_subst_var(V, A, Sub) ++ Ss};
- false -> {[V|Vs1],[A|As1],Ss}
+ case is_subst(A0) of
+ true ->
+ A = case is_compiler_generated(V) andalso
+ not is_compiler_generated(A0) of
+ true ->
+ %% Propagate the 'compiler_generated' annotation
+ %% along with the value.
+ Ann = [compiler_generated|cerl:get_ann(A0)],
+ cerl:set_ann(A0, Ann);
+ false ->
+ A0
+ end,
+ {Vs1,As1,sub_subst_var(V, A, Sub) ++ Ss};
+ false ->
+ {[V|Vs1],[A0|As1],Ss}
end;
let_subst_list([], [], _) -> {[],[],[]}.
@@ -1242,10 +1254,10 @@ is_subst(_) -> false.
%% to force renaming if variables in the scope occurs as pattern
%% variables.
-sub_new() -> #sub{v=orddict:new(),s=gb_trees:empty(),t=[]}.
+sub_new() -> #sub{v=orddict:new(),s=cerl_sets:new(),t=#{}}.
sub_new(#sub{}=Sub) ->
- Sub#sub{v=orddict:new(),t=[]}.
+ Sub#sub{v=orddict:new(),t=#{}}.
sub_new_preserve_types(#sub{}=Sub) ->
Sub#sub{v=orddict:new()}.
@@ -1262,16 +1274,16 @@ sub_set_var(#c_var{name=V}, Val, Sub) ->
sub_set_name(V, Val, #sub{v=S,s=Scope,t=Tdb0}=Sub) ->
Tdb1 = kill_types(V, Tdb0),
Tdb = copy_type(V, Val, Tdb1),
- Sub#sub{v=orddict:store(V, Val, S),s=gb_sets:add(V, Scope),t=Tdb}.
+ Sub#sub{v=orddict:store(V, Val, S),s=cerl_sets:add_element(V, Scope),t=Tdb}.
sub_del_var(#c_var{name=V}, #sub{v=S,s=Scope,t=Tdb}=Sub) ->
%% Profiling shows that for programs with many record operations,
%% sub_del_var/2 is a bottleneck. Since the scope contains all
%% variables that are live, we know that V cannot be present in S
%% if it is not in the scope.
- case gb_sets:is_member(V, Scope) of
+ case cerl_sets:is_element(V, Scope) of
false ->
- Sub#sub{s=gb_sets:insert(V, Scope)};
+ Sub#sub{s=cerl_sets:add_element(V, Scope)};
true ->
Sub#sub{v=orddict:erase(V, S),t=kill_types(V, Tdb)}
end.
@@ -1282,12 +1294,12 @@ sub_subst_var(#c_var{name=V}, Val, #sub{v=S0}) ->
sub_add_scope(Vs, #sub{s=Scope0}=Sub) ->
Scope = foldl(fun(V, S) when is_integer(V); is_atom(V) ->
- gb_sets:add(V, S)
+ cerl_sets:add_element(V, S)
end, Scope0, Vs),
Sub#sub{s=Scope}.
sub_subst_scope(#sub{v=S0,s=Scope}=Sub) ->
- S = [{-1,#c_var{name=Sv}} || Sv <- gb_sets:to_list(Scope)]++S0,
+ S = [{-1,#c_var{name=Sv}} || Sv <- cerl_sets:to_list(Scope)]++S0,
Sub#sub{v=S}.
sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) ->
@@ -1295,7 +1307,7 @@ sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) ->
%% became the new bottleneck. Since the scope contains all
%% live variables, a variable V can only be the target for
%% a substitution if it is in the scope.
- gb_sets:is_member(V, Scope) andalso v_is_value(V, S).
+ cerl_sets:is_element(V, Scope) andalso v_is_value(V, S).
v_is_value(Var, [{_,#c_var{name=Var}}|_]) -> true;
v_is_value(Var, [_|T]) -> v_is_value(Var, T);
@@ -1760,8 +1772,9 @@ case_opt_compiler_generated(Core) ->
%% return Expr0 unchanged.
%%
case_expand_var(E, #sub{t=Tdb}) ->
- case orddict:find(cerl:var_name(E), Tdb) of
- {ok,T0} ->
+ Key = cerl:var_name(E),
+ case Tdb of
+ #{Key:=T0} ->
case cerl:is_c_tuple(T0) of
false ->
E;
@@ -1785,7 +1798,7 @@ case_expand_var(E, #sub{t=Tdb}) ->
E
end
end;
- error ->
+ _ ->
E
end.
@@ -1899,8 +1912,8 @@ case_data_pat_alias(P, BindTo0, TypeSig, Bs0) ->
%% Here we will need to actually build the data and bind
%% it to the variable.
{Type,Arity} = TypeSig,
- Vars = make_vars([], Arity),
Ann = [compiler_generated],
+ Vars = make_vars(Ann, Arity),
Data = cerl:ann_make_data(Ann, Type, Vars),
Bs = [{BindTo0,P},{P,Data}|Bs0],
{Vars,Bs};
@@ -2147,7 +2160,7 @@ is_bool_expr_list([], _) -> true.
%% functions, or is_record/2).
%%
is_safe_bool_expr(Core, Sub) ->
- is_safe_bool_expr_1(Core, Sub, gb_sets:empty()).
+ is_safe_bool_expr_1(Core, Sub, cerl_sets:new()).
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_record},
@@ -2193,7 +2206,7 @@ is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) ->
true ->
case {is_safe_bool_expr_1(Arg, Sub, BoolVars),Vars} of
{true,[#c_var{name=V}]} ->
- is_safe_bool_expr_1(B, Sub, gb_sets:add(V, BoolVars));
+ is_safe_bool_expr_1(B, Sub, cerl_sets:add_element(V, BoolVars));
{false,_} ->
is_safe_bool_expr_1(B, Sub, BoolVars)
end;
@@ -2202,7 +2215,7 @@ is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) ->
is_safe_bool_expr_1(#c_literal{val=Val}, _Sub, _) ->
is_boolean(Val);
is_safe_bool_expr_1(#c_var{name=V}, _Sub, BoolVars) ->
- gb_sets:is_element(V, BoolVars);
+ cerl_sets:is_element(V, BoolVars);
is_safe_bool_expr_1(_, _, _) -> false.
is_safe_bool_expr_list([C|Cs], Sub, BoolVars) ->
@@ -2236,7 +2249,7 @@ move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner,
%% in <InnerBody>
%%
Arg = body(Arg0, Sub0),
- ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}),
+ ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}),
{OuterVs,ScopeSub} = pattern_list(OuterVs0, ScopeSub0),
OuterBody = body(OuterBody0, ScopeSub),
@@ -2275,15 +2288,15 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
CaVars0 = Ca0#c_clause.pats,
G0 = Ca0#c_clause.guard,
B0 = Ca0#c_clause.body,
- ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}),
+ ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}),
{CaVars,ScopeSub} = pattern_list(CaVars0, ScopeSub0),
G = guard(G0, ScopeSub),
B1 = body(B0, ScopeSub),
{Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0),
- Sub2 = Sub1#sub{s=gb_sets:union(ScopeSub#sub.s,
- Sub1#sub.s)},
+ Sub2 = Sub1#sub{s=cerl_sets:union(ScopeSub#sub.s,
+ Sub1#sub.s)},
Lbody = body(Lbody0, Sub2),
B = Let#c_let{vars=Lvs,arg=core_lib:make_values(B2),body=Lbody},
@@ -2392,8 +2405,9 @@ delay_build_1(Core0, TypeSig) ->
try delay_build_expr(Core0, TypeSig) of
Core ->
{Type,Arity} = TypeSig,
- Vars = make_vars([], Arity),
- Data = cerl:ann_make_data([compiler_generated], Type, Vars),
+ Ann = [compiler_generated],
+ Vars = make_vars(Ann, Arity),
+ Data = cerl:ann_make_data(Ann, Type, Vars),
{yes,Vars,Core,Data}
catch
throw:impossible ->
@@ -2480,7 +2494,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
Arg1;
false ->
%% let <Var> = Arg in <OtherVar> ==> seq Arg OtherVar
- Arg = maybe_suppress_warnings(Arg1, Vs0, PrevBody, Ctxt),
+ Arg = maybe_suppress_warnings(Arg1, Vs0, PrevBody),
expr(#c_seq{arg=Arg,body=Body}, Ctxt,
sub_new_preserve_types(Sub))
end;
@@ -2488,7 +2502,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
%% No variables left.
Body;
{Vs,Arg1,#c_literal{}} ->
- Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody, Ctxt),
+ Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody),
E = case Ctxt of
effect ->
%% Throw away the literal body.
@@ -2507,7 +2521,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
%% seq Arg BodyWithoutVar
case is_any_var_used(Vs, Body) of
false ->
- Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody, Ctxt),
+ Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody),
expr(#c_seq{arg=Arg,body=Body}, Ctxt,
sub_new_preserve_types(Sub));
true ->
@@ -2517,7 +2531,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
end
end.
-%% maybe_suppress_warnings(Arg, [#c_var{}], PreviousBody, Context) -> Arg'
+%% maybe_suppress_warnings(Arg, [#c_var{}], PreviousBody) -> Arg'
%% Try to suppress false warnings when a variable is not used.
%% For instance, we don't expect a warning for useless building in:
%%
@@ -2528,10 +2542,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
%% referenced in the original unoptimized code. If they were, we will
%% consider the warning false and suppress it.
-maybe_suppress_warnings(Arg, _, _, effect) ->
- %% Don't suppress any warnings in effect context.
- Arg;
-maybe_suppress_warnings(Arg, Vs, PrevBody, value) ->
+maybe_suppress_warnings(Arg, Vs, PrevBody) ->
case should_suppress_warning(Arg) of
true ->
Arg; %Already suppressed.
@@ -2555,8 +2566,16 @@ suppress_warning([H|T]) ->
true ->
suppress_warning(cerl:data_es(H) ++ T);
false ->
- Arg = cerl:set_ann(H, [compiler_generated]),
- cerl:c_seq(Arg, suppress_warning(T))
+ %% Some other thing, such as a function call.
+ %% This cannot be the compiler's fault, so the
+ %% warning should not be suppressed. We must
+ %% be careful not to destroy tail-recursion.
+ case T of
+ [] ->
+ H;
+ [_|_] ->
+ cerl:c_seq(H, suppress_warning(T))
+ end
end
end;
suppress_warning([]) -> void().
@@ -2574,7 +2593,7 @@ move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg,
%% let <OuterVars> = <OuterArg>
%% in case <InnerArg> of <InnerClauses> end
%%
- ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}),
+ ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}),
{OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0),
InnerArg = body(InnerArg0, ScopeSub),
Outer#c_let{vars=OuterVars,arg=OuterArg,
@@ -2603,7 +2622,7 @@ move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg,
%% <OuterCb>
%% end
%%
- ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}),
+ ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}),
{OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0),
OuterGuard = guard(OuterGuard0, ScopeSub),
InnerArg = body(InnerArg0, ScopeSub),
@@ -2688,9 +2707,9 @@ is_any_var_used([], _) -> false.
-spec get_type(cerl:cerl(), #sub{}) -> type_info() | 'none'.
get_type(#c_var{name=V}, #sub{t=Tdb}) ->
- case orddict:find(V, Tdb) of
- {ok,Type} -> Type;
- error -> none
+ case Tdb of
+ #{V:=Type} -> Type;
+ _ -> none
end;
get_type(C, _) ->
case cerl:type(C) of
@@ -2805,35 +2824,38 @@ update_types_1(#c_var{name=V,anno=Anno}, Pat, Types) ->
update_types_1(_, _, Types) -> Types.
update_types_2(V, [#c_tuple{}=P], Types) ->
- orddict:store(V, P, Types);
+ Types#{V=>P};
update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) ->
- orddict:store(V, bool, Types);
+ Types#{V=>bool};
update_types_2(V, [Type], Types) when is_atom(Type) ->
- orddict:store(V, Type, Types);
+ Types#{V=>Type};
update_types_2(_, _, Types) -> Types.
%% kill_types(V, Tdb) -> Tdb'
%% Kill any entries that references the variable,
%% either in the key or in the value.
-kill_types(V, [{V,_}|Tdb]) ->
- kill_types(V, Tdb);
-kill_types(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) ->
+kill_types(V, Tdb) ->
+ maps:from_list(kill_types2(V,maps:to_list(Tdb))).
+
+kill_types2(V, [{V,_}|Tdb]) ->
+ kill_types2(V, Tdb);
+kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) ->
case core_lib:is_var_used(V, Tuple) of
- false -> [Entry|kill_types(V, Tdb)];
- true -> kill_types(V, Tdb)
+ false -> [Entry|kill_types2(V, Tdb)];
+ true -> kill_types2(V, Tdb)
end;
-kill_types(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) ->
- [Entry|kill_types(V, Tdb)];
-kill_types(_, []) -> [].
+kill_types2(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) ->
+ [Entry|kill_types2(V, Tdb)];
+kill_types2(_, []) -> [].
%% copy_type(DestVar, SrcVar, Tdb) -> Tdb'
%% If the SrcVar has a type, assign it to DestVar.
%%
copy_type(V, #c_var{name=Src}, Tdb) ->
- case orddict:find(Src, Tdb) of
- {ok,Type} -> orddict:store(V, Type, Tdb);
- error -> Tdb
+ case Tdb of
+ #{Src:=Type} -> Tdb#{V=>Type};
+ _ -> Tdb
end;
copy_type(_, _, Tdb) -> Tdb.
@@ -3237,12 +3259,12 @@ format_error(bin_var_used_in_guard) ->
verify_scope(E, #sub{s=Scope}) ->
Free0 = cerl_trees:free_variables(E),
Free = [V || V <- Free0, not is_tuple(V)], %Ignore function names.
- case ordsets:is_subset(Free, gb_sets:to_list(Scope)) of
+ case ordsets:is_subset(Free, cerl_sets:to_list(Scope)) of
true -> true;
false ->
io:format("~p\n", [E]),
io:format("~p\n", [Free]),
- io:format("~p\n", [gb_sets:to_list(Scope)]),
+ io:format("~p\n", [cerl_sets:to_list(Scope)]),
false
end.
-endif.
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index aa2ebc0f85..c9b1a45cfc 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -43,7 +43,7 @@
-export([module/2]).
-import(lists, [member/2,keymember/3,keysort/2,keydelete/3,
- append/1,map/2,flatmap/2,filter/2,foldl/3,foldr/3,mapfoldl/3,
+ append/1,flatmap/2,filter/2,foldl/3,foldr/3,mapfoldl/3,
sort/1,reverse/1,reverse/2]).
-import(v3_life, [vdb_find/2]).
@@ -57,8 +57,7 @@
break, %Break label
recv, %Receive label
is_top_block, %Boolean: top block or not
- functable=gb_trees:empty(), %Gb tree of local functions:
- % {{Name,Arity},Label}
+ functable=#{}, %Map of local functions: {Name,Arity}=>Label
in_catch=false, %Inside a catch or not.
need_frame, %Need a stack frame.
ultimate_failure %Label for ultimate match failure.
@@ -673,9 +672,7 @@ select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
[{test,select_type_test(Type),{f,Tf},[R]},
{test,is_eq_exact,{f,Vf},[R,{Type,Val}]}|Sis];
select_val_cg(Type, R, Vls0, Tf, Vf, Sis) ->
- Vls1 = map(fun ({f,_Lbl} = F) -> F;
- (Value) -> {Type,Value}
- end, Vls0),
+ Vls1 = [case Value of {f,_Lbl} -> Value; _ -> {Type,Value} end || Value <- Vls0],
[{test,select_type_test(Type),{f,Tf},[R]}, {select_val,R,{f,Vf},{list,Vls1}}|Sis].
select_type_test(integer) -> is_integer;
@@ -1080,7 +1077,7 @@ protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) ->
St2#cg{bfail=Pfail}),
%%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Rs,I,Vdb,Aft}]),
%% Set return values to false.
- Mis = map(fun ({var,V}) -> {move,{atom,false},fetch_var(V, Aft)} end, Rs),
+ Mis = [{move,{atom,false},fetch_var(V,Aft)}||{var,V} <- Rs],
{Tis ++ [{jump,{f,Psucc}},
{label,Pfail}] ++ Mis ++ [{label,Psucc}],
Aft,St3#cg{bfail=St0#cg.bfail}}.
@@ -1263,13 +1260,12 @@ enter_line(_, _, _) ->
local_func_label(Name, Arity, St) ->
local_func_label({Name,Arity}, St).
-local_func_label(Key, #cg{functable=Tab}=St0) ->
- case gb_trees:lookup(Key, Tab) of
- {value,Label} ->
- {Label,St0};
- none ->
+local_func_label(Key, #cg{functable=Map}=St0) ->
+ case Map of
+ #{Key := Label} -> {Label,St0};
+ _ ->
{Label,St} = new_label(St0),
- {Label,St#cg{functable=gb_trees:insert(Key, Label, Tab)}}
+ {Label,St#cg{functable=Map#{Key => Label}}}
end.
%% need_stack_frame(State) -> State'
@@ -1992,25 +1988,28 @@ clear_dead(Sr, Until, Vdb) ->
stk=clear_dead_stk(Sr#sr.stk, Until, Vdb)}.
clear_dead_reg(Sr, Until, Vdb) ->
- Reg = map(fun ({_I,V} = IV) ->
- case vdb_find(V, Vdb) of
- {V,_,L} when L > Until -> IV;
- _ -> free %Remove anything else
- end;
- ({reserved,_I,_V} = Reserved) -> Reserved;
- (free) -> free
- end, Sr#sr.reg),
+ Reg = [case R of
+ {_I,V} = IV ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L > Until -> IV;
+ _ -> free %Remove anything else
+ end;
+ {reserved,_I,_V} = Reserved -> Reserved;
+ free -> free
+ end || R <- Sr#sr.reg],
reserve(Sr#sr.res, Reg, Sr#sr.stk).
clear_dead_stk(Stk, Until, Vdb) ->
- map(fun ({V} = T) ->
- case vdb_find(V, Vdb) of
- {V,_,L} when L > Until -> T;
- _ -> dead %Remove anything else
- end;
- (free) -> free;
- (dead) -> dead
- end, Stk).
+ [case S of
+ {V} = T ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L > Until -> T;
+ _ -> dead %Remove anything else
+ end;
+ free -> free;
+ dead -> dead
+ end || S <- Stk].
+
%% sr_merge(Sr1, Sr2) -> Sr.
%% Merge two stack/register states keeping the longest of both stack
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 7dff58582e..c21b2a1505 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -114,7 +114,7 @@ copy_anno(Kdst, Ksrc) ->
ff, %Current function
vcount=0, %Variable counter
fcount=0, %Fun counter
- ds=[], %Defined variables
+ ds=cerl_sets:new() :: cerl_sets:set(), %Defined variables
funs=[], %Fun functions
free=[], %Free variables
ws=[] :: [warning()], %Warnings.
@@ -148,7 +148,7 @@ include_attribute(_) -> true.
function({#c_var{name={F,Arity}=FA},Body}, St0) ->
try
- St1 = St0#kern{func=FA,ff=undefined,vcount=0,fcount=0,ds=sets:new()},
+ St1 = St0#kern{func=FA,ff=undefined,vcount=0,fcount=0,ds=cerl_sets:new()},
{#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1),
{B1,_,St3} = ubody(B0, return, St2),
%%B1 = B0, St3 = St2, %Null second pass
@@ -715,15 +715,15 @@ force_variable(Ke, St0) ->
%% handling.
pattern(#c_var{anno=A,name=V}, _Isub, Osub, St0) ->
- case sets:is_element(V, St0#kern.ds) of
+ case cerl_sets:is_element(V, St0#kern.ds) of
true ->
{New,St1} = new_var_name(St0),
{#k_var{anno=A,name=New},
set_vsub(V, New, Osub),
- St1#kern{ds=sets:add_element(New, St1#kern.ds)}};
+ St1#kern{ds=cerl_sets:add_element(New, St1#kern.ds)}};
false ->
{#k_var{anno=A,name=V},Osub,
- St0#kern{ds=sets:add_element(V, St0#kern.ds)}}
+ St0#kern{ds=cerl_sets:add_element(V, St0#kern.ds)}}
end;
pattern(#c_literal{anno=A,val=Val}, _Isub, Osub, St) ->
{#k_literal{anno=A,val=Val},Osub,St};
@@ -897,7 +897,7 @@ new_vars(0, St, Vs) -> {Vs,St}.
make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ].
add_var_def(V, St) ->
- St#kern{ds=sets:add_element(V#k_var.name, St#kern.ds)}.
+ St#kern{ds=cerl_sets:add_element(V#k_var.name, St#kern.ds)}.
%%add_vars_def(Vs, St) ->
%% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end,
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index 4b1f1c3f71..ee0565efb6 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -411,7 +411,7 @@ is_gc_bif(Bif, Arity) ->
%% must be sorted.
init_vars(Vs) ->
- sort([{V,0,0} || {var,V} <- Vs]).
+ vdb_new(Vs).
new_vars([], _, Vdb) -> Vdb;
new_vars([V], I, Vdb) -> vdb_store_new(V, {V,I,I}, Vdb);
@@ -430,6 +430,16 @@ use_vars(Vs, I, Vdb) -> vdb_update_vars(Vs, Vdb, I).
add_var(V, F, L, Vdb) ->
vdb_store_new(V, {V,F,L}, Vdb).
+%% is_in_guard() -> true|false.
+
+is_in_guard() ->
+ get(guard_refc) > 0.
+
+%% vdb
+
+vdb_new(Vs) ->
+ sort([{V,0,0} || {var,V} <- Vs]).
+
vdb_find(V, Vdb) ->
case lists:keyfind(V, 1, Vdb) of
false -> error;
@@ -471,8 +481,3 @@ vdb_sub(Min, Max, Vdb) ->
[ if L >= Max -> {V,F,locked};
true -> Vd
end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ].
-
-%% is_in_guard() -> true|false.
-
-is_in_guard() ->
- get(guard_refc) > 0.
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 5742b7e6cf..4e266875ee 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -283,6 +283,7 @@ bad_arith(Config) when is_list(Config) ->
{3,sys_core_fold,{eval_failure,badarith}},
{9,sys_core_fold,nomatch_guard},
{9,sys_core_fold,{eval_failure,badarith}},
+ {9,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
{10,sys_core_fold,nomatch_guard},
{10,sys_core_fold,{eval_failure,badarith}},
{15,sys_core_fold,{eval_failure,badarith}}
@@ -371,7 +372,7 @@ files(Config) when is_list(Config) ->
%% Test warnings for term construction and BIF calls in effect context.
effect(Config) when is_list(Config) ->
- Ts = [{lc,
+ Ts = [{effect,
<<"
t(X) ->
case X of
@@ -477,6 +478,19 @@ effect(Config) when is_list(Config) ->
m9(Bs) ->
[{B,ok} = {B,foo:bar(B)} || B <- Bs],
ok.
+
+ m10(ConfigTableSize) ->
+ case ConfigTableSize of
+ apa ->
+ CurrentConfig = {id(camel_phase3),id(sms)},
+ case CurrentConfig of
+ {apa, bepa} -> ok;
+ _ -> ok
+ end
+ end,
+ ok.
+
+ id(I) -> I.
">>,
[],
{warnings,[{5,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
@@ -754,6 +768,14 @@ no_warnings(Config) when is_list(Config) ->
case R0 of
{r,V1,_V2,V3} -> {r,V1,\"def\",V3}
end.
+
+ d(In0, Bool) ->
+ {In1,Int} = case id(Bool) of
+ false -> {In0,0}
+ end,
+ [In1,Int].
+
+ id(I) -> I.
">>,
[],
[]}],
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 05e682c893..69f71ba5dd 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 5.0.4
+COMPILER_VSN = 6.0
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 8489b59562..55b1b3e8c4 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.5
+CRYPTO_VSN = 3.6
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 4438466bb0..ffdfc46496 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -140,7 +140,7 @@ init(Pid, Parent, Meta, TraceWin, BackTrace, Strings) ->
int:meta(Meta, trace, State3#state.trace),
- gui_enable_updown(stack_trace, {1,1}),
+ gui_enable_updown(State3#state.stack_trace, {1,1}),
gui_enable_btrace(false, false),
dbg_wx_trace_win:display(Win,idle),
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index 33954ca82c..6f84ca3bca 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -365,7 +365,7 @@ stop() ->
%% function will receive the following messages:
%% {int, {interpret, Mod}}
%% {int, {no_interpret, Mod}}
-%% {int, {new_process, Pid, Function, Status, Info}}
+%% {int, {new_process, {Pid, Function, Status, Info}}}
%% {int, {new_status, Pid, Status, Info}}
%% {int, {new_break, {Point, Options}}}
%% {int, {delete_break, Point}}
diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl
index 12fdd184b8..74847e161f 100644
--- a/lib/debugger/test/map_SUITE.erl
+++ b/lib/debugger/test/map_SUITE.erl
@@ -1308,7 +1308,7 @@ t_guard_receive(Config) when is_list(Config) ->
done = call(Pid, done),
ok.
--define(t_guard_receive_large_procs, 150).
+-define(t_guard_receive_large_procs, 50).
t_guard_receive_large(Config) when is_list(Config) ->
M = lists:foldl(fun(_,#{procs := Ps } = M) ->
@@ -1326,7 +1326,7 @@ guard_receive_large_loop(M) ->
receive
#{pid := Pid, msg := hello} ->
case M of
- #{done := Count, procs := #{Pid := 150}} ->
+ #{done := Count, procs := #{Pid := 15}} ->
Pid ! {self(), done},
guard_receive_large_loop(M#{done := Count + 1});
#{procs := #{Pid := Count} = Ps} ->
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index b82f0f4e37..b6fd4e8e44 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 4.0.3
+DEBUGGER_VSN = 4.1
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 5f52906625..fc076c24a6 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -70,7 +70,7 @@
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
[--dump_callgraph file] [--no_native] [--fullpath]
- [--statistics]</code>
+ [--statistics] [--no_native_cache]</code>
<p>Options:</p>
<taglist>
<tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also
@@ -198,6 +198,11 @@
heuristically performs when dialyzing many files; this avoids the
compilation time but it may result in (much) longer analysis
time.</item>
+ <tag><c><![CDATA[--no_native_cache]]></c></tag>
+ <item>By default, Dialyzer caches the results of native compilation in the
+ <c>$XDG_CACHE_HOME/erlang/dialyzer_hipe_cache</c> directory.
+ <c>XDG_CACHE_HOME</c> defaults to <c>$HOME/.cache</c>.
+ Use this option to disable caching.</item>
<tag><c><![CDATA[--fullpath]]></c></tag>
<item>Display the full path names of files for which warnings are emitted.</item>
<tag><c><![CDATA[--gui]]></c></tag>
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 4386a8d52a..55fcd15641 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -512,32 +512,82 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
dialyzer_worker],
report_native_comp(Options),
{T1, _} = statistics(wall_clock),
- native_compile(Mods),
+ Cache = (get(dialyzer_options_native_cache) =/= false),
+ native_compile(Mods, Cache),
{T2, _} = statistics(wall_clock),
report_elapsed_time(T1, T2, Options)
end
end.
-native_compile(Mods) ->
+native_compile(Mods, Cache) ->
case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
true ->
Parent = self(),
- Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods],
+ Pids = [spawn(fun () -> Parent ! {self(), hc(M, Cache)} end) || M <- Mods],
lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
false ->
- lists:foreach(fun (Mod) -> hc(Mod) end, Mods)
+ lists:foreach(fun (Mod) -> hc(Mod, Cache) end, Mods)
end.
-hc(Mod) ->
+hc(Mod, Cache) ->
{module, Mod} = code:ensure_loaded(Mod),
case code:is_module_native(Mod) of
true -> ok;
false ->
%% io:format(" ~w", [Mod]),
- {ok, Mod} = hipe:c(Mod),
- ok
+ case Cache of
+ false ->
+ {ok, Mod} = hipe:c(Mod),
+ ok;
+ true ->
+ hc_cache(Mod)
+ end
end.
+hc_cache(Mod) ->
+ CacheBase = cache_base_dir(),
+ %% Use HiPE architecture and version in directory name, to avoid
+ %% clashes between incompatible binaries.
+ HipeArchVersion =
+ lists:concat(
+ [erlang:system_info(hipe_architecture), "-",
+ hipe:version(), "-",
+ hipe_bifs:system_crc()]),
+ CacheDir = filename:join(CacheBase, HipeArchVersion),
+ OrigBeamFile = code:which(Mod),
+ {ok, {Mod, <<Checksum:128>>}} = beam_lib:md5(OrigBeamFile),
+ CachedBeamFile = filename:join(CacheDir, lists:concat([Mod, "-", Checksum, ".beam"])),
+ ok = filelib:ensure_dir(CachedBeamFile),
+ ModBin =
+ case filelib:is_file(CachedBeamFile) of
+ true ->
+ {ok, BinFromFile} = file:read_file(CachedBeamFile),
+ BinFromFile;
+ false ->
+ {ok, Mod, CompiledBin} = compile:file(OrigBeamFile, [from_beam, native, binary]),
+ ok = file:write_file(CachedBeamFile, CompiledBin),
+ CompiledBin
+ end,
+ code:unstick_dir(filename:dirname(OrigBeamFile)),
+ {module, Mod} = code:load_binary(Mod, CachedBeamFile, ModBin),
+ true = code:is_module_native(Mod),
+ ok.
+
+cache_base_dir() ->
+ %% http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
+ %% If XDG_CACHE_HOME is set to an absolute path, use it as base.
+ XdgCacheHome = os:getenv("XDG_CACHE_HOME"),
+ CacheHome =
+ case is_list(XdgCacheHome) andalso filename:pathtype(XdgCacheHome) =:= absolute of
+ true ->
+ XdgCacheHome;
+ false ->
+ %% Otherwise, the default is $HOME/.cache.
+ {ok, [[Home]]} = init:get_argument(home),
+ filename:join(Home, ".cache")
+ end,
+ filename:join([CacheHome, "dialyzer_hipe_cache"]).
+
new_state() ->
#cl_state{}.
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 21fc424a1b..fae88ed6e8 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -75,6 +75,9 @@ cl(["-nn"|T]) ->
cl(["--no_native"|T]) ->
put(dialyzer_options_native, false),
cl(T);
+cl(["--no_native_cache"|T]) ->
+ put(dialyzer_options_native_cache, false),
+ cl(T);
cl(["--plt_info"|T]) ->
put(dialyzer_options_analysis_type, plt_info),
cl(T);
@@ -363,7 +366,7 @@ help_message() ->
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
[--dump_callgraph file] [--no_native] [--fullpath]
- [--statistics]
+ [--statistics] [--no_native_cache]
Options:
files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
Use Dialyzer from the command line to detect defects in the
@@ -468,6 +471,11 @@ Options:
Bypass the native code compilation of some key files that Dialyzer
heuristically performs when dialyzing many files; this avoids the
compilation time but it may result in (much) longer analysis time.
+ --no_native_cache
+ By default, Dialyzer caches the results of native compilation in the
+ $XDG_CACHE_HOME/erlang/dialyzer_hipe_cache directory.
+ XDG_CACHE_HOME defaults to $HOME/.cache. Use this option to disable
+ caching.
--fullpath
Display the full path names of files for which warnings are emitted.
--gui
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 527afaf4ef..48e0830109 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.7.4
+DIALYZER_VSN = 2.8
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 6931788c83..c5df63a7f0 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -42,6 +42,36 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 1.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken relay counters.</p>
+ <p>
+ OTP-12654 in OTP 17.5.3 broke counters in the case of
+ answer messages received in the relay application.
+ Counters were accumulated as unknown messages or
+ no_result_code instead of as relayed messages on the
+ intended Result-Code and 'Experimental-Result' tuples.</p>
+ <p>
+ Own Id: OTP-12741</p>
+ </item>
+ <item>
+ <p>
+ Fix diameter_sctp listener race.</p>
+ <p>
+ An oversight in OTP-12428 made it possible to start a
+ transport process that could not establish associations.</p>
+ <p>
+ Own Id: OTP-12744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.9.1</title>
<section><title>Known Bugs and Problems</title>
@@ -65,7 +95,7 @@ first.</p>
received in an answer not setting the E-bit. The correct
AVP is now extracted from the incoming message.</p>
<p>
- Own Id: OTP-12654 Aux Id: seq12851 </p>
+ Own Id: OTP-12654</p>
</item>
<item>
<p>
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index ffd2c0afa2..eb4bbae931 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -131,11 +131,11 @@ peer_down(TPid) ->
%% incr/4
%% ---------------------------------------------------------------------------
-incr(Dir, #diameter_packet{header = H}, TPid, Dict) ->
- incr(Dir, H, TPid, Dict);
+incr(Dir, #diameter_packet{header = H}, TPid, AppDict) ->
+ incr(Dir, H, TPid, AppDict);
-incr(Dir, #diameter_header{} = H, TPid, Dict) ->
- incr(TPid, {msg_id(H, Dict), Dir}).
+incr(Dir, #diameter_header{} = H, TPid, AppDict) ->
+ incr(TPid, {msg_id(H, AppDict), Dir}).
%% ---------------------------------------------------------------------------
%% incr_error/4
@@ -143,26 +143,26 @@ incr(Dir, #diameter_header{} = H, TPid, Dict) ->
%% Identify messages using the application dictionary, not the encode
%% dictionary, which may differ in the case of answer-message.
-incr_error(Dir, T, Pid, {_Dict, AppDict}) ->
+incr_error(Dir, T, Pid, {_MsgDict, AppDict}) ->
incr_error(Dir, T, Pid, AppDict);
%% Decoded message without errors.
incr_error(recv, #diameter_packet{errors = []}, _, _) ->
ok;
-incr_error(recv = D, #diameter_packet{header = H}, TPid, Dict) ->
- incr_error(D, H, TPid, Dict);
+incr_error(recv = D, #diameter_packet{header = H}, TPid, AppDict) ->
+ incr_error(D, H, TPid, AppDict);
%% Encoded message with errors and an identifiable header ...
-incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, Dict) ->
- incr_error(D, H, TPid, Dict);
+incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, AppDict) ->
+ incr_error(D, H, TPid, AppDict);
%% ... or not.
incr_error(send = D, {_,_}, TPid, _) ->
incr_error(D, unknown, TPid);
-incr_error(Dir, #diameter_header{} = H, TPid, Dict) ->
- incr_error(Dir, msg_id(H, Dict), TPid);
+incr_error(Dir, #diameter_header{} = H, TPid, AppDict) ->
+ incr_error(Dir, msg_id(H, AppDict), TPid);
incr_error(Dir, Id, TPid, _) ->
incr_error(Dir, Id, TPid).
@@ -179,18 +179,20 @@ incr_error(Dir, Id, TPid) ->
| Reason
when Pkt :: #diameter_packet{},
TPid :: pid(),
- DictT :: module() | {module(), module(), module()},
+ DictT :: module() | {MsgDict :: module(),
+ AppDict :: module(),
+ CommonDict:: module()},
Counter :: {'Result-Code', integer()}
| {'Experimental-Result', integer(), integer()},
Reason :: atom().
-incr_rc(Dir, Pkt, TPid, {Dict, _, _} = DictT) ->
+incr_rc(Dir, Pkt, TPid, {_, AppDict, _} = DictT) ->
try
incr_result(Dir, Pkt, TPid, DictT)
catch
exit: {E,_} when E == no_result_code;
E == invalid_error_bit ->
- incr(TPid, {msg_id(Pkt#diameter_packet.header, Dict), Dir, E}),
+ incr(TPid, {msg_id(Pkt#diameter_packet.header, AppDict), Dir, E}),
E
end;
@@ -259,7 +261,8 @@ recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) ->
%% any others are discarded.
%% ... or not.
-recv(false, false, _, _, _, _) ->
+recv(false, false, TPid, _, _, _) ->
+ incr(TPid, {{unknown, 0}, recv, discarded}),
ok.
%% spawn_request/4
@@ -307,14 +310,14 @@ recv_request(TPid, Pkt, Dict0, RecvData) -> %% from old code
%% recv_R/5
-recv_R({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
+recv_R({#diameter_app{id = Id, dictionary = AppDict} = App, Caps},
TPid,
Pkt0,
Dict0,
RecvData) ->
- incr(recv, Pkt0, TPid, Dict),
- Pkt = errors(Id, diameter_codec:decode(Id, Dict, Pkt0)),
- incr_error(recv, Pkt, TPid, Dict),
+ incr(recv, Pkt0, TPid, AppDict),
+ Pkt = errors(Id, diameter_codec:decode(Id, AppDict, Pkt0)),
+ incr_error(recv, Pkt, TPid, AppDict),
{Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, Pkt)};
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
@@ -522,14 +525,17 @@ send_A(_, _, _, _) ->
%% send_A/6
-send_A(T, TPid, DictT, ReqPkt, EvalPktFs, EvalFs) ->
- reply(T, TPid, DictT, EvalPktFs, ReqPkt),
+send_A(T, TPid, {AppDict, Dict0} = DictT0, ReqPkt, EvalPktFs, EvalFs) ->
+ {MsgDict, Pkt} = reply(T, TPid, DictT0, EvalPktFs, ReqPkt),
+ incr(send, Pkt, TPid, AppDict),
+ incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing
+ send(TPid, Pkt),
lists:foreach(fun diameter_lib:eval/1, EvalFs).
%% answer/6
answer({reply, Ans}, _Caps, _Pkt, App, Dict0, _RecvData) ->
- {dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
+ {msg_dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
answer({call, Opts}, Caps, Pkt, App, Dict0, RecvData) ->
#diameter_caps{origin_host = {OH,_}}
@@ -552,27 +558,37 @@ answer({answer_message, RC} = T, Caps, Pkt, App, Dict0, _RecvData) ->
orelse ?ERROR({invalid_return, T, handle_request, App}),
answer_message(RC, Caps, Dict0, Pkt).
-%% dict/3
+%% msg_dict/3
+%%
+%% Return the dictionary defining the message grammar in question: the
+%% application dictionary or the common dictionary.
+
+msg_dict(AppDict, Dict0, [Msg])
+ when is_list(Msg);
+ is_tuple(Msg) ->
+ msg_dict(AppDict, Dict0, Msg);
-%% An incoming answer, not yet decoded.
-dict(Dict, Dict0, #diameter_packet{header
- = #diameter_header{is_request = false,
- is_error = E},
- msg = undefined}) ->
- if E -> Dict0; true -> Dict end;
+msg_dict(AppDict, Dict0, Msg) ->
+ choose(is_answer_message(Msg, Dict0), Dict0, AppDict).
-dict(Dict, Dict0, [Msg]) ->
- dict(Dict, Dict0, Msg);
+%% Incoming, not yet decoded.
+is_answer_message(#diameter_packet{header = #diameter_header{} = H,
+ msg = undefined},
+ Dict0) ->
+ is_answer_message([H], Dict0);
-dict(Dict, Dict0, #diameter_packet{msg = Msg}) ->
- dict(Dict, Dict0, Msg);
+is_answer_message(#diameter_packet{msg = Msg}, Dict0) ->
+ is_answer_message(Msg, Dict0);
-dict(Dict, Dict0, Msg) ->
- choose(is_answer_message(Msg, Dict0), Dict0, Dict).
+%% Message sent as a header/avps list.
+is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) ->
+ E andalso not R;
+%% Message sent as a tagged avp/value list.
is_answer_message([Name | _], _) ->
Name == 'answer-message';
+%% Message sent as a record.
is_answer_message(Rec, Dict) ->
try
'answer-message' == Dict:rec2msg(element(1,Rec))
@@ -642,7 +658,7 @@ resend(false,
%%
%% Relay a reply to a relayed request.
-%% Answer from the peer: reset the hop by hop identifier and send.
+%% Answer from the peer: reset the hop by hop identifier.
resend(#diameter_packet{bin = B}
= Pkt,
_Caps,
@@ -681,13 +697,13 @@ is_loop(Code, Vid, OH, Dict0, Avps) ->
%% reply/5
%% Local answer ...
-reply({Dict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
- local(Ans, TPid, {Dict, AppDict, Dict0}, Fs, ReqPkt);
+reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
+ local(Ans, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt);
%% ... or relayed.
-reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) ->
+reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, Fs, _ReqPkt) ->
eval_packet(Pkt, Fs),
- send(TPid, Pkt).
+ {msg_dict(AppDict, Dict0, Pkt), Pkt}.
%% local/5
%%
@@ -700,14 +716,12 @@ local([Msg], TPid, DictT, Fs, ReqPkt)
is_tuple(Msg) ->
local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []});
-local(Msg, TPid, {Dict, AppDict, Dict0} = DictT, Fs, ReqPkt) ->
- Pkt = encode({Dict, AppDict},
+local(Msg, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt) ->
+ Pkt = encode({MsgDict, AppDict},
TPid,
- reset(make_answer_packet(Msg, ReqPkt), Dict, Dict0),
+ reset(make_answer_packet(Msg, ReqPkt), MsgDict, Dict0),
Fs),
- incr(send, Pkt, TPid, AppDict),
- incr_rc(send, Pkt, TPid, DictT), %% count outgoing
- send(TPid, Pkt).
+ {MsgDict, Pkt}.
%% reset/3
@@ -1067,54 +1081,75 @@ find_avp(Code, VId, [_ | Avps]) ->
%% Increment a stats counter for result codes in incoming and outgoing
%% answers.
+%% Message sent as a header/avps list.
+incr_result(send = Dir,
+ #diameter_packet{msg = [#diameter_header{} = H | _]}
+ = Pkt,
+ TPid,
+ DictT) ->
+ incr_res(Dir, Pkt#diameter_packet{header = H}, TPid, DictT);
+
%% Outgoing message as binary: don't count. (Sending binaries is only
%% partially supported.)
-incr_result(_, #diameter_packet{msg = undefined = No}, _, _) ->
+incr_result(send, #diameter_packet{header = undefined = No}, _, _) ->
No;
%% Incoming or outgoing. Outgoing with encode errors never gets here
%% since encode fails.
-incr_result(Dir, Pkt, TPid, {Dict, AppDict, Dict0}) ->
- #diameter_packet{header = #diameter_header{is_error = E}
- = Hdr,
- errors = Es}
- = Pkt,
+incr_result(Dir, Pkt, TPid, DictT) ->
+ incr_res(Dir, Pkt, TPid, DictT).
+
+incr_res(Dir,
+ #diameter_packet{header = #diameter_header{is_error = E}
+ = Hdr,
+ errors = Es}
+ = Pkt,
+ TPid,
+ DictT) ->
+ {MsgDict, AppDict, Dict0} = DictT,
Id = msg_id(Hdr, AppDict),
+ %% Could be {relay, 0}, in which case the R-bit is redundant since
+ %% only answers are being counted. Let it be however, so that the
+ %% same tuple is in both send/recv and result code counters.
%% Count incoming decode errors.
recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict),
%% Exit on a missing result code.
- T = rc_counter(Dict, Dir, Pkt),
- T == false andalso ?LOGX(no_result_code, {Dict, Dir, Hdr}),
+ T = rc_counter(MsgDict, Dir, Pkt),
+ T == false andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}),
{Ctr, RC, Avp} = T,
%% Or on an inappropriate value.
is_result(RC, E, Dict0)
- orelse ?LOGX(invalid_error_bit, {Dict, Dir, Hdr, Avp}),
+ orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}),
incr(TPid, {Id, Dir, Ctr}),
Ctr.
%% msg_id/2
-msg_id(#diameter_packet{header = H}, Dict) ->
- msg_id(H, Dict);
+msg_id(#diameter_packet{header = H}, AppDict) ->
+ msg_id(H, AppDict);
%% Only count on known keys so as not to be vulnerable to attack:
%% there are 2^32 (application ids) * 2^24 (command codes) = 2^56
%% pairs for an attacker to choose from.
-msg_id(Hdr, Dict) ->
+msg_id(Hdr, AppDict) ->
{Aid, Code, R} = Id = diameter_codec:msg_id(Hdr),
- if Aid == ?APP_ID_RELAY ->
+ case AppDict:id() of
+ ?APP_ID_RELAY ->
{relay, R};
- true ->
- choose(Aid /= Dict:id() orelse '' == Dict:msg_name(Code, 0 == R),
- unknown,
- Id)
+ A ->
+ unknown(A /= Aid orelse '' == AppDict:msg_name(Code, 0 == R), Id)
end.
+unknown(true, {_, _, R}) ->
+ {unknown, R};
+unknown(false, Id) ->
+ Id.
+
%% No E-bit: can't be 3xxx.
is_result(RC, false, _Dict0) ->
RC < 3000 orelse 4000 =< RC;
@@ -1142,7 +1177,11 @@ incr(TPid, Counter) ->
%% applications MUST include either one Result-Code AVP or one
%% Experimental-Result AVP.
-rc_counter(Dict, recv, #diameter_packet{header = H, avps = As}) ->
+rc_counter(Dict, Dir, #diameter_packet{header = H,
+ avps = As,
+ msg = Msg})
+ when Dir == recv; %% decoded incoming
+ Msg == undefined -> %% relayed outgoing
rc_counter(Dict, [H|As]);
rc_counter(Dict, _, #diameter_packet{msg = Msg}) ->
@@ -1434,12 +1473,12 @@ fold_record(Rec, R) ->
%% send_R/6
send_R(Pkt0,
- {TPid, Caps, #diameter_app{dictionary = Dict} = App},
+ {TPid, Caps, #diameter_app{dictionary = AppDict} = App},
Opts,
{Pid, Ref},
SvcName,
Fs) ->
- Pkt = encode(Dict, TPid, Pkt0, Fs),
+ Pkt = encode(AppDict, TPid, Pkt0, Fs),
#options{timeout = Timeout}
= Opts,
@@ -1452,7 +1491,7 @@ send_R(Pkt0,
packet = Pkt0},
try
- incr(send, Pkt, TPid, Dict),
+ incr(send, Pkt, TPid, AppDict),
TRef = send_request(TPid, Pkt, Req, SvcName, Timeout),
Pid ! Ref, %% tell caller a send has been attempted
handle_answer(SvcName,
@@ -1492,10 +1531,10 @@ handle_answer(SvcName,
id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
- Dict = dict(AppDict, Dict0, Pkt),
- handle_A(errors(Id, diameter_codec:decode({Dict, AppDict}, Pkt)),
+ MsgDict = msg_dict(AppDict, Dict0, Pkt),
+ handle_A(errors(Id, diameter_codec:decode({MsgDict, AppDict}, Pkt)),
SvcName,
- Dict,
+ MsgDict,
Dict0,
App,
Req).
@@ -1765,19 +1804,19 @@ retransmit(T, {_, _, App}, _, _, _, _) ->
?ERROR({invalid_return, T, prepare_retransmit, App}).
resend_request(Pkt0,
- {TPid, Caps, #diameter_app{dictionary = Dict}},
+ {TPid, Caps, #diameter_app{dictionary = AppDict}},
Req0,
SvcName,
Tmo,
Fs) ->
- Pkt = encode(Dict, TPid, Pkt0, Fs),
+ Pkt = encode(AppDict, TPid, Pkt0, Fs),
Req = Req0#request{transport = TPid,
packet = Pkt0,
caps = Caps},
?LOG(retransmission, Pkt#diameter_packet.header),
- incr(TPid, {msg_id(Pkt, Dict), send, retransmission}),
+ incr(TPid, {msg_id(Pkt, AppDict), send, retransmission}),
TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
{TRef, Req}.
@@ -1887,7 +1926,7 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
find_avp(Code, VId, Avps)
of
A ->
- avp_decode(Dict, Name, ungroup(A))
+ (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name}
catch
error: _ ->
undefined
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 0ef0fd35a9..b89859ed24 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -65,11 +65,14 @@
{update, diameter_sup, supervisor}]},
{"1.9", [{load_module, diameter_codec}, %% 17.5
{load_module, diameter_traffic},
+ {load_module, diameter_sctp},
{load_module, diameter_gen_base_rfc6733},
{load_module, diameter_gen_acct_rfc6733},
{load_module, diameter_gen_base_rfc3588},
{load_module, diameter_gen_base_accounting},
- {load_module, diameter_gen_relay}]}
+ {load_module, diameter_gen_relay}]},
+ {"1.9.1", [{load_module, diameter_traffic}, %% 17.5.3
+ {load_module, diameter_sctp}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -120,7 +123,10 @@
{load_module, diameter_gen_base_rfc3588},
{load_module, diameter_gen_acct_rfc6733},
{load_module, diameter_gen_base_rfc6733},
+ {load_module, diameter_sctp},
{load_module, diameter_traffic},
- {load_module, diameter_codec}]}
+ {load_module, diameter_codec}]},
+ {"1.9.1", [{load_module, diameter_sctp},
+ {load_module, diameter_traffic}]}
]
}.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 2c8d6f0a14..f80de0a816 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -223,9 +223,9 @@ init(T) ->
i({listen, Ref, {Opts, Addrs}}) ->
{[Matches], Rest} = proplists:split(Opts, [accept]),
{LAs, Sock} = AS = open(Addrs, Rest, ?DEFAULT_PORT),
- proc_lib:init_ack({ok, self(), LAs}),
ok = gen_sctp:listen(Sock, true),
true = diameter_reg:add_new({?MODULE, listener, {Ref, AS}}),
+ proc_lib:init_ack({ok, self(), LAs}),
start_timer(#listener{ref = Ref,
socket = Sock,
accept = accept(Matches)});
diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl
index 44fc3a60aa..44cb0cc484 100644
--- a/lib/diameter/test/diameter_3xxx_SUITE.erl
+++ b/lib/diameter/test/diameter_3xxx_SUITE.erl
@@ -195,13 +195,13 @@ counters(_, _, _, _) ->
stats(?CLIENT, E, rfc3588, L)
when E == answer;
E == answer_3xxx ->
- [{{unknown,recv},2},
+ [{{{unknown,0},recv},2},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},6},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3001}},1},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3001}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',3008}},2},
@@ -213,15 +213,15 @@ stats(?CLIENT, E, rfc3588, L)
stats(?SERVER, E, rfc3588, L)
when E == answer;
E == answer_3xxx ->
- [{{unknown,recv},1},
- {{unknown,send},2},
+ [{{{unknown,0},send},2},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},6},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3001}},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3001}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',3008}},2},
@@ -232,13 +232,13 @@ stats(?SERVER, E, rfc3588, L)
= L;
stats(?CLIENT, answer, rfc6733, L) ->
- [{{unknown,recv},2},
+ [{{{unknown,0},recv},2},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},8},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3001}},1},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3001}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',3008}},2},
{{{0,275,0},recv,{'Result-Code',3999}},1},
@@ -248,15 +248,15 @@ stats(?CLIENT, answer, rfc6733, L) ->
= L;
stats(?SERVER, answer, rfc6733, L) ->
- [{{unknown,recv},1},
- {{unknown,send},2},
+ [{{{unknown,0},send},2},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},8},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3001}},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3001}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',3008}},2},
{{{0,275,0},send,{'Result-Code',3999}},1},
@@ -267,13 +267,13 @@ stats(?SERVER, answer, rfc6733, L) ->
= L;
stats(?CLIENT, answer_3xxx, rfc6733, L) ->
- [{{unknown,recv},2},
+ [{{{unknown,0},recv},2},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},8},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3001}},1},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3001}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',3008}},2},
@@ -284,15 +284,15 @@ stats(?CLIENT, answer_3xxx, rfc6733, L) ->
= L;
stats(?SERVER, answer_3xxx, rfc6733, L) ->
- [{{unknown,recv},1},
- {{unknown,send},2},
+ [{{{unknown,0},send},2},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},8},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3001}},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3001}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',3008}},2},
@@ -304,12 +304,12 @@ stats(?SERVER, answer_3xxx, rfc6733, L) ->
= L;
stats(?CLIENT, callback, rfc3588, L) ->
- [{{unknown,recv},1},
+ [{{{unknown,0},recv},1},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},6},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},2},
{{{0,275,0},recv,{'Result-Code',3999}},1},
@@ -318,14 +318,14 @@ stats(?CLIENT, callback, rfc3588, L) ->
= L;
stats(?SERVER, callback, rfc3588, L) ->
- [{{unknown,recv},1},
- {{unknown,send},1},
+ [{{{unknown,0},send},1},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},6},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},2},
{{{0,275,0},send,{'Result-Code',3999}},1},
@@ -335,12 +335,12 @@ stats(?SERVER, callback, rfc3588, L) ->
= L;
stats(?CLIENT, callback, rfc6733, L) ->
- [{{unknown,recv},1},
+ [{{{unknown,0},recv},1},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},8},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},2},
{{{0,275,0},recv,{'Result-Code',3999}},1},
@@ -350,14 +350,14 @@ stats(?CLIENT, callback, rfc6733, L) ->
= L;
stats(?SERVER, callback, rfc6733, L) ->
- [{{unknown,recv},1},
- {{unknown,send},1},
+ [{{{unknown,0},send},1},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},8},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},2},
{{{0,275,0},send,{'Result-Code',3999}},1},
diff --git a/lib/diameter/test/diameter_config_SUITE.erl b/lib/diameter/test/diameter_config_SUITE.erl
index bbdf672291..4bcaa8119f 100644
--- a/lib/diameter/test/diameter_config_SUITE.erl
+++ b/lib/diameter/test/diameter_config_SUITE.erl
@@ -50,7 +50,7 @@
{request_errors, RE},
{call_mutates_state, C}]]
|| D <- [diameter_gen_base_rfc3588, diameter_gen_base_rfc6733],
- M <- [?MODULE, [?MODULE, now()]],
+ M <- [?MODULE, [?MODULE, diameter_lib:now()]],
A <- [0, common, make_ref()],
S <- [[], make_ref()],
AE <- [report, callback, discard],
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 735a908d97..7142239bbb 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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
@@ -49,6 +49,7 @@
send_timeout_1/1,
send_timeout_2/1,
info/1,
+ counters/1,
disconnect/1,
stop_services/1,
stop/1]).
@@ -120,6 +121,7 @@ all() ->
start_services,
connect,
{group, all},
+ counters,
{group, all, [parallel]},
disconnect,
stop_services,
@@ -201,8 +203,8 @@ send3(_Config) ->
send4(_Config) ->
call(?SERVER4).
-%% Send an ASR that loops between the relays and expect the loop to
-%% be detected.
+%% Send an ASR that loops between the relays (RELAY1 -> RELAY2 ->
+%% RELAY1) and expect the loop to be detected.
send_loop(_Config) ->
Req = ['ASR', {'Destination-Realm', realm(?SERVER1)},
{'Destination-Host', ?SERVER1},
@@ -227,8 +229,103 @@ send_timeout(Tmo) ->
call(Req, [{filter, realm}, {timeout, Tmo}]).
info(_Config) ->
+ %% Wait for RELAY1 to have answered all requests, so that the
+ %% suite doesn't end before all answers are sent and counted.
+ receive after 6000 -> ok end,
[] = ?util:info().
+counters(_Config) ->
+ [] = ?util:run([[fun counters/2, K, S]
+ || K <- [statistics, transport, connections],
+ S <- ?SERVICES]).
+
+counters(Key, Svc) ->
+ counters(Key, Svc, [_|_] = diameter:service_info(Svc, Key)).
+
+counters(statistics, Svc, Stats) ->
+ stats(Svc, lists:foldl(fun({K,N},D) -> orddict:update_counter(K, N, D) end,
+ orddict:new(),
+ lists:append([L || {P,L} <- Stats, is_pid(P)])));
+
+counters(_, _, _) ->
+ todo.
+
+stats(?CLIENT, L) ->
+ [{{{0,257,0},recv},2}, %% CEA
+ {{{0,257,1},send},2}, %% CER
+ {{{0,258,0},recv},1}, %% RAA (send_timeout_1)
+ {{{0,258,1},send},2}, %% RAR (send_timeout_[12])
+ {{{0,274,0},recv},1}, %% ASA (send_loop)
+ {{{0,274,1},send},1}, %% ASR (send_loop)
+ {{{0,275,0},recv},4}, %% STA (send[1-4])
+ {{{0,275,1},send},4}, %% STR (send[1-4])
+ {{{unknown,0},recv,discarded},1}, %% RAR (send_timeout_2)
+ {{{0,257,0},recv,{'Result-Code',2001}},2}, %% CEA
+ {{{0,258,0},recv,{'Result-Code',3002}},1}, %% RAA (send_timeout_1)
+ {{{0,274,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{0,275,0},recv,{'Result-Code',2001}},4}] %% STA (send[1-4])
+ = L;
+
+stats(S, L)
+ when S == ?SERVER1;
+ S == ?SERVER2;
+ S == ?SERVER3;
+ S == ?SERVER4 ->
+ [{{{0,257,0},send},1}, %% CEA
+ {{{0,257,1},recv},1}, %% CER
+ {{{0,275,0},send},1}, %% STA (send[1-4])
+ {{{0,275,1},recv},1}, %% STR (send[1-4])
+ {{{0,257,0},send,{'Result-Code',2001}},1}, %% CEA
+ {{{0,275,0},send,{'Result-Code',2001}},1}] %% STA (send[1-4])
+ = L;
+
+stats(?RELAY1, L) ->
+ [{{{relay,0},recv},3}, %% STA x 2 (send[12])
+ %% ASA (send_loop)
+ {{{relay,0},send},6}, %% STA x 2 (send[12])
+ %% ASA x 2 (send_loop)
+ %% RAA x 2 (send_timeout_[12])
+ {{{relay,1},recv},6}, %% STR x 2 (send[12])
+ %% ASR x 2 (send_loop)
+ %% RAR x 2 (send_timeout_[12])
+ {{{relay,1},send},5}, %% STR x 2 (send[12])
+ %% ASR (send_loop)
+ %% RAR x 2 (send_timeout_[12])
+ {{{0,257,0},recv},3}, %% CEA
+ {{{0,257,0},send},1}, %% "
+ {{{0,257,1},recv},1}, %% CER
+ {{{0,257,1},send},3}, %% "
+ {{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{relay,0},send,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},send,{'Result-Code',3002}},2}, %% RAA (send_timeout_[12])
+ {{{relay,0},send,{'Result-Code',3005}},2}, %% ASA (send_loop)
+ {{{0,257,0},recv,{'Result-Code',2001}},3}, %% CEA
+ {{{0,257,0},send,{'Result-Code',2001}},1}] %% "
+ = L;
+
+stats(?RELAY2, L) ->
+ [{{{relay,0},recv},3}, %% STA x 2 (send[34])
+ %% ASA (send_loop)
+ {{{relay,0},send},3}, %% STA x 2 (send[34])
+ %% ASA (send_loop)
+ {{{relay,1},recv},5}, %% STR x 2 (send[34])
+ %% RAR x 2 (send_timeout_[12])
+ %% ASR (send_loop)
+ {{{relay,1},send},3}, %% STR x 2 (send[34])
+ %% ASR (send_loop)
+ {{{0,257,0},recv},2}, %% CEA
+ {{{0,257,0},send},2}, %% "
+ {{{0,257,1},recv},2}, %% CER
+ {{{0,257,1},send},2}, %% "
+ {{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{relay,0},send,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},send,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{0,257,0},recv,{'Result-Code',2001}},2}, %% CEA
+ {{{0,257,0},send,{'Result-Code',2001}},2}] %% "
+ = L.
+
%% ===========================================================================
realm(Host) ->
@@ -303,18 +400,24 @@ handle_request(Pkt, OH, {_Ref, #diameter_caps{origin_host = {OH,_}} = Caps})
when OH /= ?CLIENT ->
request(Pkt, Caps).
-%% RELAY1 routes any ASR or RAR to RELAY2 ...
+%% RELAY1 answers ACR after it's timed out at the client.
+request(#diameter_packet{header = #diameter_header{cmd_code = 271}},
+ #diameter_caps{origin_host = {?RELAY1, _}}) ->
+ receive after 1000 -> {answer_message, 3004} end; %% TOO_BUSY
+
+%% RELAY1 routes any ASR or RAR to RELAY2.
request(#diameter_packet{header = #diameter_header{cmd_code = C}},
#diameter_caps{origin_host = {?RELAY1, _}})
when C == 274; %% ASR
C == 258 -> %% RAR
{relay, [{filter, {realm, realm(?RELAY2)}}]};
-%% ... which in turn routes it back. Expect diameter to either answer
-%% either with DIAMETER_LOOP_DETECTED/DIAMETER_UNABLE_TO_COMPLY.
+%% RELAY2 routes ASR back to RELAY1 to induce DIAMETER_LOOP_DETECTED.
request(#diameter_packet{header = #diameter_header{cmd_code = 274}},
#diameter_caps{origin_host = {?RELAY2, _}}) ->
{relay, [{filter, {host, ?RELAY1}}]};
+
+%% RELAY2 discards RAR to induce DIAMETER_UNABLE_TO_DELIVER.
request(#diameter_packet{header = #diameter_header{cmd_code = 258}},
#diameter_caps{origin_host = {?RELAY2, _}}) ->
discard;
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 55565692ec..e5bbda9c91 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -319,19 +319,19 @@ make_cert(Dir, Base) ->
make_cert(Dir, Base ++ "_key.pem", Base ++ "_ca.pem").
make_cert(Dir, Keyfile, Certfile) ->
- [K,C] = Paths = [filename:join([Dir, F]) || F <- [Keyfile, Certfile]],
+ [KP,CP] = [filename:join([Dir, F]) || F <- [Keyfile, Certfile]],
- KCmd = join(["openssl genrsa -out", K, "2048"]),
- CCmd = join(["openssl req -new -x509 -key", K, "-out", C, "-days 7",
- "-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]),
+ KC = join(["openssl genrsa -out", KP, "2048"]),
+ CC = join(["openssl req -new -x509 -key", KP, "-out", CP, "-days 7",
+ "-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]),
%% Hope for the best and only check that files are written.
- os:cmd(KCmd),
- os:cmd(CCmd),
+ [{_, _, {ok,_}},{_, _, {ok,_}}]
+ = [{P,O,T} || {P,C} <- [{KP,KC}, {CP,CC}],
+ O <- [os:cmd(C)],
+ T <- [file:read_file_info(P)]],
- [_,_] = [T || P <- Paths, {ok, T} <- [file:read_file_info(P)]],
-
- {K,C}.
+ {KP,CP}.
join(Strs) ->
string:join(Strs, " ").
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 7ff6ba7ab9..17faf30a9b 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -145,8 +145,12 @@
%% them as binary.
-define(STRING_DECODES, [true, false]).
+%% Which transport protocol to use.
+-define(TRANSPORTS, [tcp, sctp]).
+
-record(group,
- {client_service,
+ {transport,
+ client_service,
client_encoding,
client_dict0,
client_strings,
@@ -234,19 +238,20 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 60}}].
+ [{timetrap, {seconds, 10}}].
all() ->
[start, result_codes, {group, traffic}, outstanding, empty, stop].
groups() ->
Ts = tc(),
+ Sctp = ?util:have_sctp(),
[{?util:name([R,D,A,C]), [parallel], Ts} || R <- ?ENCODINGS,
D <- ?RFCS,
A <- ?ENCODINGS,
C <- ?CONTAINERS]
++
- [{?util:name([R,D,A,C,SD,CD]),
+ [{?util:name([T,R,D,A,C,SD,CD]),
[],
[start_services,
add_transports,
@@ -254,15 +259,19 @@ groups() ->
{group, ?util:name([R,D,A,C])},
remove_transports,
stop_services]}
- || R <- ?ENCODINGS,
+ || T <- ?TRANSPORTS,
+ T /= sctp orelse Sctp,
+ R <- ?ENCODINGS,
D <- ?RFCS,
A <- ?ENCODINGS,
C <- ?CONTAINERS,
SD <- ?STRING_DECODES,
CD <- ?STRING_DECODES]
++
- [{traffic, [parallel], [{group, ?util:name([R,D,A,C,SD,CD])}
- || R <- ?ENCODINGS,
+ [{traffic, [parallel], [{group, ?util:name([T,R,D,A,C,SD,CD])}
+ || T <- ?TRANSPORTS,
+ T /= sctp orelse Sctp,
+ R <- ?ENCODINGS,
D <- ?RFCS,
A <- ?ENCODINGS,
C <- ?CONTAINERS,
@@ -271,8 +280,9 @@ groups() ->
init_per_group(Name, Config) ->
case ?util:name(Name) of
- [R,D,A,C,SD,CD] ->
- G = #group{client_service = [$C|?util:unique_string()],
+ [T,R,D,A,C,SD,CD] ->
+ G = #group{transport = T,
+ client_service = [$C|?util:unique_string()],
client_encoding = R,
client_dict0 = dict0(D),
client_strings = CD,
@@ -288,8 +298,18 @@ init_per_group(Name, Config) ->
end_per_group(_, _) ->
ok.
+%% Skip testcases that can reasonably fail under SCTP.
init_per_testcase(Name, Config) ->
- [{testcase, Name} | Config].
+ case [skip || #group{transport = sctp}
+ <- [proplists:get_value(group, Config)],
+ send_maxlen == Name
+ orelse send_long == Name]
+ of
+ [skip] ->
+ {skip, sctp};
+ [] ->
+ [{testcase, Name} | Config]
+ end.
end_per_testcase(_, _) ->
ok.
@@ -367,16 +387,18 @@ start_services(Config) ->
| ?SERVICE(CN, CD)]).
add_transports(Config) ->
- #group{client_service = CN,
+ #group{transport = T,
+ client_service = CN,
server_service = SN}
= group(Config),
LRef = ?util:listen(SN,
- tcp,
+ T,
[{capabilities_cb, fun capx/2},
+ {pool_size, 8},
{spawn_opt, [{min_heap_size, 8096}]},
{applications, apps(rfc3588)}]),
Cs = [?util:connect(CN,
- tcp,
+ T,
LRef,
[{id, Id},
{capabilities, [{'Origin-State-Id', origin(Id)}]},
diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl
index f098851bea..78bddbd1cf 100644
--- a/lib/diameter/test/diameter_transport_SUITE.erl
+++ b/lib/diameter/test/diameter_transport_SUITE.erl
@@ -64,7 +64,7 @@
= #diameter_caps{host_ip_address
= Addrs}}).
-%% The term we register after open a listening port with gen_tcp.
+%% The term we register after open a listening port with gen_{tcp,sctp}.
-define(TEST_LISTENER(Ref, PortNr),
{?MODULE, listen, Ref, PortNr}).
@@ -85,7 +85,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {minutes, 2}}].
+ [{timetrap, {seconds, 15}}].
all() ->
[start,
@@ -401,12 +401,13 @@ gen_listen(tcp) ->
%% gen_accept/2
gen_accept(sctp, Sock) ->
- Assoc = ?RECV(?SCTP(Sock, {_, #sctp_assoc_change{state = comm_up,
- outbound_streams = O,
- inbound_streams = I,
- assoc_id = A}}),
- {O, I, A}),
- putr(assoc, Assoc),
+ #sctp_assoc_change{state = comm_up,
+ outbound_streams = OS,
+ inbound_streams = IS,
+ assoc_id = Id}
+ = ?RECV(?SCTP(Sock, {_, #sctp_assoc_change{} = S}), S),
+
+ putr(assoc, {OS, IS, Id}),
{ok, Sock};
gen_accept(tcp, LSock) ->
gen_tcp:accept(LSock).
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index c496876ee1..df7d268429 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -204,13 +204,14 @@ seed() ->
%% unique_string/0
unique_string() ->
- us(diameter_lib:now()).
-
-us({M,S,U}) ->
- tl(lists:append(["-" ++ integer_to_list(N) || N <- [M,S,U]]));
-
-us(MonoT) ->
- integer_to_list(MonoT).
+ try erlang:unique_integer() of
+ N ->
+ integer_to_list(N)
+ catch
+ error: undef -> %% OTP < 18
+ {M,S,U} = timestamp(),
+ tl(lists:append(["-" ++ integer_to_list(N) || N <- [M,S,U]]))
+ end.
%% ---------------------------------------------------------------------------
%% have_sctp/0
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 5a3ff2c92f..f39e12686e 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -673,8 +673,7 @@ jitter(T,D) ->
%% Generate a unique hostname for the faked peer.
hostname() ->
- {M,S,U} = diameter_util:timestamp(),
- lists:flatten(io_lib:format("~p-~p-~p", [M,S,U])).
+ ?util:unique_string().
putr(Key, Val) ->
put({?MODULE, Key}, Val).
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index db7f72c44e..c278e74dca 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -16,5 +16,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.9.1
+DIAMETER_VSN = 1.9.2
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 24cfbf16d5..49a73331c6 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.16
+EDOC_VSN = 0.7.17
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 90495eebd6..32e0e0e2d8 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -202,6 +202,9 @@ typedef enum {
<desc>
<p>Encodes a double-precision (64 bit) floating point number in
the binary format.</p>
+ <p>
+ The function returns <c><![CDATA[-1]]></c> if the floating point number is not finite.
+ </p>
</desc>
</func>
<func>
diff --git a/lib/erl_interface/doc/src/erl_eterm.xml b/lib/erl_interface/doc/src/erl_eterm.xml
index 429f77501c..2152192696 100644
--- a/lib/erl_interface/doc/src/erl_eterm.xml
+++ b/lib/erl_interface/doc/src/erl_eterm.xml
@@ -371,9 +371,11 @@ iohead ::= Binary
<p><c><![CDATA[f]]></c> is a value to be converted to an Erlang float.</p>
<p></p>
<p>The function returns an Erlang float object with the value
- specified in <c><![CDATA[f]]></c>.</p>
+ specified in <c><![CDATA[f]]></c> or <c><![CDATA[NULL]]></c> if
+ <c><![CDATA[f]]></c> is not finite.
+ </p>
<p><c><![CDATA[ERL_FLOAT_VALUE(t)]]></c> can be used to retrieve the
- value from an Erlang float.</p>
+ value from an Erlang float.</p>
</desc>
</func>
<func>
diff --git a/lib/erl_interface/src/decode/decode_big.c b/lib/erl_interface/src/decode/decode_big.c
index 477880b331..016ed2eac2 100644
--- a/lib/erl_interface/src/decode/decode_big.c
+++ b/lib/erl_interface/src/decode/decode_big.c
@@ -150,27 +150,6 @@ int ei_big_comp(erlang_big *x, erlang_big *y)
#define INLINED_FP_CONVERSION 1
#endif
-#ifdef USE_ISINF_ISNAN /* simulate finite() */
-# define isfinite(f) (!isinf(f) && !isnan(f))
-# define HAVE_ISFINITE
-#elif defined(__GNUC__) && defined(HAVE_FINITE)
-/* We use finite in gcc as it emits assembler instead of
- the function call that isfinite emits. The assembler is
- significantly faster. */
-# ifdef isfinite
-# undef isfinite
-# endif
-# define isfinite finite
-# ifndef HAVE_ISFINITE
-# define HAVE_ISFINITE
-# endif
-#elif defined(isfinite) && !defined(HAVE_ISFINITE)
-# define HAVE_ISFINITE
-#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE)
-# define isfinite finite
-# define HAVE_ISFINITE
-#endif
-
#ifdef NO_FPE_SIGNALS
# define ERTS_FP_CHECK_INIT() do {} while (0)
# define ERTS_FP_ERROR(f, Action) if (!isfinite(f)) { Action; } else {}
diff --git a/lib/erl_interface/src/encode/encode_double.c b/lib/erl_interface/src/encode/encode_double.c
index 148a49f73a..72a1c60808 100644
--- a/lib/erl_interface/src/encode/encode_double.c
+++ b/lib/erl_interface/src/encode/encode_double.c
@@ -21,12 +21,24 @@
#include "eidef.h"
#include "eiext.h"
#include "putget.h"
+#if defined(HAVE_ISFINITE)
+#include <math.h>
+#endif
int ei_encode_double(char *buf, int *index, double p)
{
char *s = buf + *index;
char *s0 = s;
+ /* Erlang does not handle Inf and NaN, so we return an error rather
+ * than letting the Erlang VM complain about a bad external
+ * term. */
+#if defined(HAVE_ISFINITE)
+ if(!isfinite(p)) {
+ return -1;
+ }
+#endif
+
if (!buf)
s += 9;
else {
diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c
index 636d26b24b..66cca7decf 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.c
+++ b/lib/erl_interface/src/legacy/erl_eterm.c
@@ -26,6 +26,9 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#if defined(HAVE_ISFINITE)
+#include <math.h>
+#endif
#include "ei_locking.h"
#include "ei_resolve.h"
@@ -125,6 +128,15 @@ ETERM *erl_mk_float (double d)
{
ETERM *ep;
+#if defined(HAVE_ISFINITE)
+ /* Erlang does not handle Inf and NaN, so we return an error
+ * rather than letting the Erlang VM complain about a bad external
+ * term. */
+ if(!isfinite(d)) {
+ return NULL;
+ }
+#endif
+
ep = erl_alloc_eterm(ERL_FLOAT);
ERL_COUNT(ep) = 1;
ERL_FLOAT_VALUE(ep) = d;
diff --git a/lib/erl_interface/src/misc/eidef.h b/lib/erl_interface/src/misc/eidef.h
index bd3d0bf631..e0dc325b48 100644
--- a/lib/erl_interface/src/misc/eidef.h
+++ b/lib/erl_interface/src/misc/eidef.h
@@ -41,6 +41,27 @@
typedef int socklen_t;
#endif
+#ifdef USE_ISINF_ISNAN /* simulate finite() */
+# define isfinite(f) (!isinf(f) && !isnan(f))
+# define HAVE_ISFINITE
+#elif defined(__GNUC__) && defined(HAVE_FINITE)
+/* We use finite in gcc as it emits assembler instead of
+ the function call that isfinite emits. The assembler is
+ significantly faster. */
+# ifdef isfinite
+# undef isfinite
+# endif
+# define isfinite finite
+# ifndef HAVE_ISFINITE
+# define HAVE_ISFINITE
+# endif
+#elif defined(isfinite) && !defined(HAVE_ISFINITE)
+# define HAVE_ISFINITE
+#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE)
+# define isfinite finite
+# define HAVE_ISFINITE
+#endif
+
typedef unsigned char uint8; /* FIXME use configure */
typedef unsigned short uint16;
typedef unsigned int uint32;
diff --git a/lib/erl_interface/test/ei_encode_SUITE.erl b/lib/erl_interface/test/ei_encode_SUITE.erl
index 50dc8b6a3c..86e0d8cd08 100644
--- a/lib/erl_interface/test/ei_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_encode_SUITE.erl
@@ -174,7 +174,7 @@ test_ei_encode_ulonglong(Config) when is_list(Config) ->
%% ######################################################################## %%
-%% A "character" for us is an 8 bit integer, alwasy positive, i.e.
+%% A "character" for us is an 8 bit integer, always positive, i.e.
%% it is unsigned.
%% FIXME maybe the API should change to use "unsigned char" to be clear?!
diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile
index 377e593712..b6873371ed 100644
--- a/lib/et/src/Makefile
+++ b/lib/et/src/Makefile
@@ -65,7 +65,7 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/et/ebin -I../include
+ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/et/ebin -I../include -Werror
# ----------------------------------------------------
# Special Build Targets
diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl
index e05c67be60..1f60dee8ca 100644
--- a/lib/et/src/et_collector.erl
+++ b/lib/et/src/et_collector.erl
@@ -64,6 +64,8 @@
-export([init/1,terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2]).
+-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]).
+
-include("et_internal.hrl").
-include("../include/et.hrl").
diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl
index c8e9c907b2..5497096377 100644
--- a/lib/et/src/et_selector.erl
+++ b/lib/et/src/et_selector.erl
@@ -28,6 +28,8 @@
parse_event/2
]).
+-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]).
+
-include("../include/et.hrl").
%%----------------------------------------------------------------------
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index eb60f673ef..df716cdeea 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -572,21 +572,6 @@ 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/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index 8a829396ec..53d291430d 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -166,26 +166,6 @@
%% 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 dca8b3ece0..b551ee6eb6 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.9
+EUNIT_VSN = 2.3
diff --git a/lib/hipe/cerl/cerl_pmatch.erl b/lib/hipe/cerl/cerl_pmatch.erl
index 3bc93e80dd..4f04b0a7ed 100644
--- a/lib/hipe/cerl/cerl_pmatch.erl
+++ b/lib/hipe/cerl/cerl_pmatch.erl
@@ -31,7 +31,7 @@
-module(cerl_pmatch).
--define(NO_UNUSED, true).
+%%-define(NO_UNUSED, true).
-export([clauses/2]).
-ifndef(NO_UNUSED).
@@ -59,6 +59,8 @@
%% @see transform/2
-ifndef(NO_UNUSED).
+-spec core_transform(cerl:c_module(), [_]) -> cerl:c_module().
+
core_transform(M, Opts) ->
cerl:to_records(transform(cerl:from_records(M), Opts)).
-endif. % NO_UNUSED
@@ -76,6 +78,8 @@ core_transform(M, Opts) ->
%% @see core_transform/2
-ifndef(NO_UNUSED).
+-spec transform(cerl:cerl(), [_]) -> cerl:cerl().
+
transform(M, _Opts) ->
expr(M, env__empty()).
-endif. % NO_UNUSED
@@ -109,7 +113,7 @@ transform(M, _Opts) ->
%% @see expr/2
%% @see transform/2
--spec clauses([cerl:cerl()], rec_env:environment()) ->
+-spec clauses([cerl:cerl(),...], rec_env:environment()) ->
{cerl:cerl(), [cerl:cerl()]}.
clauses(Cs, Env) ->
@@ -406,6 +410,8 @@ make_let(Vs, A, B) ->
%% @see rec_env
-ifndef(NO_UNUSED).
+-spec expr(cerl:cerl(), rec_env:environment()) -> cerl:cerl().
+
expr(E, Env) ->
case cerl:type(E) of
literal ->
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 5b1401b34a..ee77d65932 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1113,8 +1113,8 @@ type(hipe_bifs, set_native_address, 3, Xs, Opaques) ->
type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) ->
strict(hipe_bifs, set_native_address_in_fe, 2, Xs,
fun (_) -> t_atom('true') end, Opaques);
-type(hipe_bifs, system_crc, 1, Xs, Opaques) ->
- strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques);
+type(hipe_bifs, system_crc, 0, _, _Opaques) ->
+ t_crc32();
type(hipe_bifs, term_to_word, 1, Xs, Opaques) ->
strict(hipe_bifs, term_to_word, 1, Xs,
fun (_) -> t_integer() end, Opaques);
@@ -2490,8 +2490,8 @@ arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
arg_types(hipe_bifs, set_native_address_in_fe, 2) ->
[t_integer(), t_integer()];
-arg_types(hipe_bifs, system_crc, 1) ->
- [t_crc32()];
+arg_types(hipe_bifs, system_crc, 0) ->
+ [];
arg_types(hipe_bifs, term_to_word, 1) ->
[t_any()];
arg_types(hipe_bifs, update_code_size, 3) ->
diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
index 0e50c9539b..3c24425828 100644
--- a/lib/hipe/llvm/hipe_llvm_main.erl
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -465,7 +465,7 @@ remove_temp_folder(Dir, Options) ->
end.
unique_id(FunName, Arity) ->
- integer_to_list(erlang:phash2({FunName, Arity, now()})).
+ integer_to_list(erlang:phash2({FunName, Arity, erlang:unique_integer()})).
unique_folder(FunName, Arity, Options) ->
DirName = "llvm_" ++ unique_id(FunName, Arity) ++ "/",
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index 60b4e0559b..e507ae933f 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.11.3
+HIPE_VSN = 3.12
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index 1a8e1c7ca8..961bfa838d 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-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
@@ -52,6 +52,7 @@ XML_REF3_FILES = \
httpc.xml\
httpd.xml \
httpd_conf.xml \
+ httpd_custom_api.xml \
httpd_socket.xml \
httpd_util.xml \
mod_alias.xml \
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 4178cb7d4c..6984408932 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -366,7 +366,7 @@ filename() = string()
<tag><c><![CDATA[receiver]]></c></tag>
<item>
<p>Defines how the client will deliver the result of an
- asynchroneous request (<c>sync</c> has the value
+ asynchronous request (<c>sync</c> has the value
<c>false</c>). </p>
<taglist>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index e40660ab39..435f99ee23 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -204,7 +204,15 @@
<marker id="props_limit"></marker>
<p><em>Limit properties</em> </p>
- <taglist>
+ <taglist>
+
+ <marker id="prop_customize"></marker>
+ <tag>{customize, atom()}</tag>
+ <item>
+ <p>A callback module to customize the inets HTTP servers behaviour
+ see <seealso marker="http_custom_api"> httpd_custom_api</seealso> </p>
+ </item>
+
<marker id="prop_disable_chunked_encoding"></marker>
<tag>{disable_chunked_transfer_encoding_send, boolean()}</tag>
<item>
diff --git a/lib/inets/doc/src/httpd_custom_api.xml b/lib/inets/doc/src/httpd_custom_api.xml
new file mode 100644
index 0000000000..faf1d277df
--- /dev/null
+++ b/lib/inets/doc/src/httpd_custom_api.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2015</year><year>2015</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>httpd_custom_api</title>
+ <file>httpd_custom_api.xml</file>
+ </header>
+ <module>httpd_custom_api</module>
+ <modulesummary>Behaviour with optional callbacks to customize the inets HTTP server.</modulesummary>
+ <description>
+ <p> The module implementing this behaviour shall be supplied to to the servers
+ configuration with the option <seealso marker="httpd:prop_customize"> customize</seealso></p>
+
+ </description>
+ <funcs>
+ <func>
+ <name>response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP response headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP response headers before they are sent to the client.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP request headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP request headers before they are processed by the server.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
+
+
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index bae8e327a3..f563a8c4b0 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,7 +32,23 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.10.8</title>
+ <section><title>Inets 5.10.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add behaviour with optional callbacks to customize the
+ inets HTTP server.</p>
+ <p>
+ Own Id: OTP-12776</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml
index aaedf330b4..3afb020431 100644
--- a/lib/inets/doc/src/ref_man.xml
+++ b/lib/inets/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -39,6 +39,7 @@
<xi:include href="httpc.xml"/>
<xi:include href="httpd.xml"/>
<xi:include href="httpd_conf.xml"/>
+ <xi:include href="httpd_custom_api.xml"/>
<xi:include href="httpd_socket.xml"/>
<xi:include href="httpd_util.xml"/>
<xi:include href="mod_alias.xml"/>
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 8f2f11ce8e..f4f0c37570 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -87,7 +87,7 @@
%% block the httpc manager process in odd cases such as trying to call
%% a server that does not exist. (See OTP-6735) The only API function
%% sending messages to the handler process that can be called before
-%% init has compleated is cancel and that is not a problem! (Send and
+%% init has completed is cancel and that is not a problem! (Send and
%% stream will not be called before the first request has been sent and
%% the reply or part of it has arrived.)
%%--------------------------------------------------------------------
@@ -392,7 +392,7 @@ handle_call(info, _, State) ->
%% When the request in process has been canceled the handler process is
%% stopped and the pipelined requests will be reissued or remaining
%% requests will be sent on a new connection. This is is
-%% based on the assumption that it is proably cheaper to reissue the
+%% based on the assumption that it is probably cheaper to reissue the
%% requests than to wait for a potentiall large response that we then
%% only throw away. This of course is not always true maybe we could
%% do something smarter here?! If the request canceled is not
@@ -1345,7 +1345,7 @@ handle_empty_queue(Session, ProfileName, TimeOut, State) ->
%% closed by the server, the client may want to close it.
NewState = activate_queue_timeout(TimeOut, State),
update_session(ProfileName, Session, #session.queue_length, 0),
- %% Note mfa will be initilized when a new request
+ %% Note mfa will be initialized when a new request
%% arrives.
{noreply,
NewState#state{request = undefined,
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 51e3dd9212..00bad51ff9 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2013. All Rights Reserved.
+# Copyright Ericsson AB 2005-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
@@ -46,6 +46,7 @@ MODULES = \
httpd_connection_sup\
httpd_cgi \
httpd_conf \
+ httpd_custom \
httpd_example \
httpd_esi \
httpd_file\
diff --git a/lib/inets/src/http_server/httpd_custom.erl b/lib/inets/src/http_server/httpd_custom.erl
new file mode 100644
index 0000000000..342469a579
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_custom.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(httpd_custom).
+
+-export([response_header/1, request_header/1]).
+-export([customize_headers/3]).
+
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
+response_header(Header) ->
+ {true, httpify(Header)}.
+request_header(Header) ->
+ {true, Header}.
+
+customize_headers(?MODULE, Function, Arg) ->
+ ?MODULE:Function(Arg);
+customize_headers(Module, Function, Arg) ->
+ try Module:Function(Arg) of
+ {true, Value} ->
+ ?MODULE:Function(Value);
+ false ->
+ false
+ catch
+ _:_ ->
+ ?MODULE:Function(Arg)
+ end.
+
+httpify({Key0, Value}) ->
+ %% make sure first letter is capital (defacto standard)
+ Words1 = string:tokens(Key0, "-"),
+ Words2 = upify(Words1, []),
+ Key = new_key(Words2),
+ Key ++ ": " ++ Value ++ ?CRLF .
+
+new_key([]) ->
+ "";
+new_key([W]) ->
+ W;
+new_key([W1,W2]) ->
+ W1 ++ "-" ++ W2;
+new_key([W|R]) ->
+ W ++ "-" ++ new_key(R).
+
+upify([], Acc) ->
+ lists:reverse(Acc);
+upify([Key|Rest], Acc) ->
+ upify(Rest, [upify2(Key)|Acc]).
+
+upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
+ [C-($a-$A)|Rest];
+upify2(Str) ->
+ Str.
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 3ff07616f9..782120c284 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -42,28 +42,28 @@
%%%=========================================================================
%%% Internal application API
%%%=========================================================================
-parse([Bin, MaxSizes]) ->
- ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]),
- parse_method(Bin, [], 0, proplists:get_value(max_method, MaxSizes), MaxSizes, []);
+parse([Bin, Options]) ->
+ ?hdrt("parse", [{bin, Bin}, {max_sizes, Options}]),
+ parse_method(Bin, [], 0, proplists:get_value(max_method, Options), Options, []);
parse(Unknown) ->
?hdrt("parse", [{unknown, Unknown}]),
exit({bad_args, Unknown}).
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-parse_method([Bin, Method, Current, Max, MaxSizes, Result]) ->
- parse_method(Bin, Method, Current, Max, MaxSizes, Result).
+parse_method([Bin, Method, Current, Max, Options, Result]) ->
+ parse_method(Bin, Method, Current, Max, Options, Result).
-parse_uri([Bin, URI, Current, Max, MaxSizes, Result]) ->
- parse_uri(Bin, URI, Current, Max, MaxSizes, Result).
+parse_uri([Bin, URI, Current, Max, Options, Result]) ->
+ parse_uri(Bin, URI, Current, Max, Options, Result).
-parse_version([Bin, Rest, Version, Current, Max, MaxSizes, Result]) ->
- parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, MaxSizes,
+parse_version([Bin, Rest, Version, Current, Max, Options, Result]) ->
+ parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, Options,
Result).
-parse_headers([Bin, Rest, Header, Headers, Current, Max, MaxSizes, Result]) ->
+parse_headers([Bin, Rest, Header, Headers, Current, Max, Options, Result]) ->
parse_headers(<<Rest/binary, Bin/binary>>,
- Header, Headers, Current, Max, MaxSizes, Result).
+ Header, Headers, Current, Max, Options, Result).
whole_body([Bin, Body, Length]) ->
whole_body(<<Body/binary, Bin/binary>>, Length).
@@ -134,13 +134,13 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-parse_method(<<>>, Method, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_method, [Method, Current, Max, MaxSizes, Result]};
-parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, MaxSizes, Result) ->
- parse_uri(Rest, [], 0, proplists:get_value(max_uri, MaxSizes), MaxSizes,
+parse_method(<<>>, Method, Current, Max, Options, Result) ->
+ {?MODULE, parse_method, [Method, Current, Max, Options, Result]};
+parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, Options, Result) ->
+ parse_uri(Rest, [], 0, proplists:get_value(max_uri, Options), Options,
[string:strip(lists:reverse(Method)) | Result]);
-parse_method(<<Octet, Rest/binary>>, Method, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_method(Rest, [Octet | Method], Current + 1, Max, MaxSizes, Result);
+parse_method(<<Octet, Rest/binary>>, Method, Current, Max, Options, Result) when Current =< Max ->
+ parse_method(Rest, [Octet | Method], Current + 1, Max, Options, Result);
parse_method(_, _, _, Max, _, _) ->
%% We do not know the version of the client as it comes after the
%% method send the lowest version in the response so that the client
@@ -153,30 +153,30 @@ parse_uri(_, _, Current, MaxURI, _, _)
%% uri send the lowest version in the response so that the client
%% will be able to handle it.
{error, {size_error, MaxURI, 414, "URI unreasonably long"},lowest_version()};
-parse_uri(<<>>, URI, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_uri, [URI, Current, Max, MaxSizes, Result]};
-parse_uri(<<?SP, Rest/binary>>, URI, _, _, MaxSizes, Result) ->
- parse_version(Rest, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
+parse_uri(<<>>, URI, Current, Max, Options, Result) ->
+ {?MODULE, parse_uri, [URI, Current, Max, Options, Result]};
+parse_uri(<<?SP, Rest/binary>>, URI, _, _, Options, Result) ->
+ parse_version(Rest, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
%% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n"
-parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, MaxSizes, Result) ->
- parse_version(Data, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
+parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, Options, Result) ->
+ parse_version(Data, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
-parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, MaxSizes, Result) ->
- parse_uri(Rest, [Octet | URI], Current + 1, Max, MaxSizes, Result).
+parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, Options, Result) ->
+ parse_uri(Rest, [Octet | URI], Current + 1, Max, Options, Result).
-parse_version(<<>>, Version, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_version, [<<>>, Version, Current, Max, MaxSizes, Result]};
-parse_version(<<?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result) ->
+parse_version(<<>>, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [<<>>, Version, Current, Max, Options, Result]};
+parse_version(<<?LF, Rest/binary>>, Version, Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result);
-parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, MaxSizes, Result) ->
- parse_headers(Rest, [], [], 0, proplists:get_value(max_header, MaxSizes), MaxSizes,
+ parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, Options, Result);
+parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, Options, Result) ->
+ parse_headers(Rest, [], [], 0, proplists:get_value(max_header, Options), Options,
[string:strip(lists:reverse(Version)) | Result]);
-parse_version(<<?CR>> = Data, Version, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_version, [Data, Version, Current, Max, MaxSizes, Result]};
-parse_version(<<Octet, Rest/binary>>, Version, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_version(Rest, [Octet | Version], Current + 1, Max, MaxSizes, Result);
+parse_version(<<?CR>> = Data, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [Data, Version, Current, Max, Options, Result]};
+parse_version(<<Octet, Rest/binary>>, Version, Current, Max, Options, Result) when Current =< Max ->
+ parse_version(Rest, [Octet | Version], Current + 1, Max, Options, Result);
parse_version(_, _, _, Max,_,_) ->
{error, {size_error, Max, 413, "Version string unreasonably long"}, lowest_version()}.
@@ -185,34 +185,42 @@ parse_headers(_, _, _, Current, Max, _, Result)
HttpVersion = lists:nth(3, lists:reverse(Result)),
{error, {size_error, Max, 413, "Headers unreasonably long"}, HttpVersion};
-parse_headers(<<>>, Header, Headers, Current, Max, MaxSizes, Result) ->
+parse_headers(<<>>, Header, Headers, Current, Max, Options, Result) ->
{?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max,
- MaxSizes, Result]};
-parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
+ Options, Result]};
+parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
- MaxSizes, Result);
+ Options, Result);
-parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
+parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([Body, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
- MaxSizes, Result) ->
+ Options, Result) ->
+ Customize = proplists:get_value(customize, Options),
case http_request:key_value(lists:reverse(Header)) of
undefined -> %% Skip headers with missing :
- {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(Headers, #http_request_h{}), Headers} | Result]))};
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end,
+ Headers),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders, #http_request_h{}), FinalHeaders} | Result]))};
NewHeader ->
- case check_header(NewHeader, MaxSizes) of
+ case check_header(NewHeader, Options) of
ok ->
- {ok, list_to_tuple(lists:reverse([Body, {http_request:headers([NewHeader | Headers],
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end, [NewHeader | Headers]),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders,
#http_request_h{}),
- [NewHeader | Headers]} | Result]))};
+ FinalHeaders} | Result]))};
{error, Reason} ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
@@ -221,12 +229,12 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
end;
parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
-parse_headers(<<?LF>>, [], [], Current, Max, MaxSizes, Result) ->
+ Options, Result]};
+parse_headers(<<?LF>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, [], [], Current, Max, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, [], [], Current, Max, Options, Result);
%% There where no headers, which is unlikely to happen.
parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
@@ -235,30 +243,30 @@ parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
{ok, NewResult};
parse_headers(<<?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, Options, Result);
parse_headers(<<?CR,?LF>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
+ Options, Result]};
parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
case http_request:key_value(lists:reverse(Header)) of
undefined -> %% Skip headers with missing :
parse_headers(Rest, [Octet], Headers,
- 0, Max, MaxSizes, Result);
+ 0, Max, Options, Result);
NewHeader ->
- case check_header(NewHeader, MaxSizes) of
+ case check_header(NewHeader, Options) of
ok ->
parse_headers(Rest, [Octet], [NewHeader | Headers],
- 0, Max, MaxSizes, Result);
+ 0, Max, Options, Result);
{error, Reason} ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
{error, Reason, HttpVersion}
@@ -266,19 +274,19 @@ parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
end;
parse_headers(<<?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
+ Options, Result]};
parse_headers(<<?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR, ?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<Octet, Rest/binary>>, Header, Headers, Current,
- Max, MaxSizes, Result) ->
+ Max, Options, Result) ->
parse_headers(Rest, [Octet | Header], Headers, Current + 1, Max,
- MaxSizes, Result).
+ Options, Result).
whole_body(Body, Length) ->
case size(Body) of
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index f7a9fe5d49..9947e17b47 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -121,13 +121,15 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
MaxURISize = max_uri_size(ConfigDB),
NrOfRequest = max_keep_alive_request(ConfigDB),
MaxContentLen = max_content_length(ConfigDB),
+ Customize = customize(ConfigDB),
{_, Status} = httpd_manager:new_connection(Manager),
MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
{max_version, ?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, MaxContentLen}
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
]]},
State = #state{mod = Mod,
@@ -550,11 +552,13 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
MaxHeaderSize = max_header_size(ModData#mod.config_db),
MaxURISize = max_uri_size(ModData#mod.config_db),
MaxContentLen = max_content_length(ModData#mod.config_db),
+ Customize = customize(ModData#mod.config_db),
MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
{max_version, ?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, MaxContentLen}
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
]]},
TmpState = State#state{mod = NewModData,
mfa = MFA,
@@ -640,3 +644,6 @@ max_keep_alive_request(ConfigDB) ->
max_content_length(ConfigDB) ->
httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
+
+customize(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, customize, httpd_custom).
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 2fa91d47a0..71dc05e46d 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-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
@@ -176,7 +176,7 @@ send_header(#mod{socket_type = Type,
StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ",
httpd_util:reason_phrase(NewStatusCode), ?CRLF],
ConnectionHeader = get_connection(Conn, NewVer),
- Head = list_to_binary([StatusLine, Headers, ConnectionHeader , ?CRLF]),
+ Head = [StatusLine, Headers, ConnectionHeader , ?CRLF],
httpd_socket:deliver(Type, Sock, Head).
map_status_code("HTTP/1.0", Code)
@@ -286,45 +286,21 @@ create_header(ConfigDb, KeyValueTupleHeaders) ->
Date = httpd_util:rfc1123_date(),
ContentType = "text/html",
Server = server(ConfigDb),
- NewHeaders = add_default_headers([{"date", Date},
- {"content-type", ContentType}
- | if Server=="" -> [];
- true -> [{"server", Server}]
- end
- ],
- KeyValueTupleHeaders),
- lists:map(fun fix_header/1, NewHeaders).
-
-
+ Headers0 = add_default_headers([{"date", Date},
+ {"content-type", ContentType}
+ | if Server=="" -> [];
+ true -> [{"server", Server}]
+ end
+ ],
+ KeyValueTupleHeaders),
+ CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom),
+ lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(CustomizeCB, response_header, H)
+ end,
+ [Header || Header <- Headers0]).
server(ConfigDb) ->
httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
-fix_header({Key0, Value}) ->
- %% make sure first letter is capital
- Words1 = string:tokens(Key0, "-"),
- Words2 = upify(Words1, []),
- Key = new_key(Words2),
- Key ++ ": " ++ Value ++ ?CRLF .
-
-new_key([]) ->
- "";
-new_key([W]) ->
- W;
-new_key([W1,W2]) ->
- W1 ++ "-" ++ W2;
-new_key([W|R]) ->
- W ++ "-" ++ new_key(R).
-
-upify([], Acc) ->
- lists:reverse(Acc);
-upify([Key|Rest], Acc) ->
- upify(Rest, [upify2(Key)|Acc]).
-
-upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
- [C-($a-$A)|Rest];
-upify2(Str) ->
- Str.
-
add_default_headers([], Headers) ->
Headers;
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index b7c3e341e8..6ba9795d9e 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -63,6 +63,7 @@
httpd_cgi,
httpd_connection_sup,
httpd_conf,
+ httpd_custom,
httpd_esi,
httpd_example,
httpd_file,
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 0dfc65e8f7..ab7ffadf75 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1289,7 +1289,9 @@ dummy_server_init(Caller, ip_comm, Inet, _) ->
{max_header, ?HTTP_MAX_HEADER_SIZE},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}]]},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
[], ListenSocket);
dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
@@ -1305,7 +1307,8 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
{max_method, ?HTTP_MAX_METHOD_STRING},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
]]},
[], ListenSocket).
@@ -1384,18 +1387,20 @@ handle_request(Module, Function, Args, Socket) ->
stop;
<<>> ->
{httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version,?HTTP_MAX_VERSION_STRING},
- {max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
- ]]};
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]};
Data ->
handle_request(httpd_request, parse,
[Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
]], Socket)
end;
NewMFA ->
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 7670c2cc60..c90887bcf3 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -53,6 +53,8 @@ all() ->
{group, https_basic},
{group, http_limit},
{group, https_limit},
+ {group, http_custom},
+ {group, https_custom},
{group, http_basic_auth},
{group, https_basic_auth},
{group, http_auth_api},
@@ -76,6 +78,8 @@ groups() ->
{https_basic, [], basic_groups()},
{http_limit, [], [{group, limit}]},
{https_limit, [], [{group, limit}]},
+ {http_custom, [], [{group, custom}]},
+ {https_custom, [], [{group, custom}]},
{http_basic_auth, [], [{group, basic_auth}]},
{https_basic_auth, [], [{group, basic_auth}]},
{http_auth_api, [], [{group, auth_api}]},
@@ -92,6 +96,7 @@ groups() ->
{https_reload, [], [{group, reload}]},
{http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]},
{limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {custom, [], [customize]},
{reload, [], [non_disturbing_reconfiger_dies,
disturbing_reconfiger_dies,
non_disturbing_1_1,
@@ -178,6 +183,7 @@ end_per_suite(_Config) ->
%%--------------------------------------------------------------------
init_per_group(Group, Config0) when Group == https_basic;
Group == https_limit;
+ Group == https_custom;
Group == https_basic_auth;
Group == https_auth_api;
Group == https_auth_api_dets;
@@ -188,6 +194,7 @@ init_per_group(Group, Config0) when Group == https_basic;
init_ssl(Group, Config0);
init_per_group(Group, Config0) when Group == http_basic;
Group == http_limit;
+ Group == http_custom;
Group == http_basic_auth;
Group == http_auth_api;
Group == http_auth_api_dets;
@@ -977,6 +984,30 @@ missing_CR(Config) ->
{version, Version}]).
%%-------------------------------------------------------------------------
+customize() ->
+ [{doc, "Test filtering of headers with custom callback"}].
+
+customize(Config) when is_list(Config) ->
+ Version = "HTTP/1.1",
+ Host = ?config(host, Config),
+ Type = ?config(type, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {no_header, "Server"},
+ {version, Version}]).
+
+response_header({"server", _}) ->
+ false;
+response_header(Header) ->
+ {true, Header}.
+
+%%-------------------------------------------------------------------------
max_header() ->
["Denial Of Service (DOS) attack, prevented by max_header"].
max_header(Config) when is_list(Config) ->
@@ -1320,24 +1351,26 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
start_apps(Group) when Group == https_basic;
Group == https_limit;
+ Group == https_custom;
Group == https_basic_auth;
Group == https_auth_api;
Group == https_auth_api_dets;
Group == https_auth_api_mnesia;
- Group == http_htaccess;
- Group == http_security;
- Group == http_reload
+ Group == https_htaccess;
+ Group == https_security;
+ Group == https_reload
->
inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
start_apps(Group) when Group == http_basic;
Group == http_limit;
+ Group == http_custom;
Group == http_basic_auth;
Group == http_auth_api;
Group == http_auth_api_dets;
Group == http_auth_api_mnesia;
- Group == https_htaccess;
- Group == https_security;
- Group == https_reload;
+ Group == http_htaccess;
+ Group == http_security;
+ Group == http_reload;
Group == http_mime_types->
inets_test_lib:start_apps([inets]).
@@ -1390,6 +1423,10 @@ server_config(http_limit, Config) ->
[{max_clients, 1},
%% Make sure option checking code is run
{max_content_length, 100000002}] ++ server_config(http, Config);
+server_config(http_custom, Config) ->
+ [{custom, ?MODULE}] ++ server_config(http, Config);
+server_config(https_custom, Config) ->
+ [{custom, ?MODULE}] ++ server_config(https, Config);
server_config(https_limit, Config) ->
[{max_clients, 1}] ++ server_config(https, Config);
server_config(http_basic_auth, Config) ->
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index ecb84e447c..f52347e39e 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -2,7 +2,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
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.10.8
+INETS_VSN = 6.0
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index df2f0b01ee..f49d63b5a6 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -58,12 +58,11 @@
specific events. (<c>add_report_handler/1,2</c>). Also, there is
a useful event handler in STDLIB for multi-file logging of events,
see <c>log_mf_h(3)</c>.</p>
- <p>Warning events were introduced in Erlang/OTP R9C. To retain
- backwards compatibility, these are by default tagged as errors,
- thus showing up as error reports in the logs. By using
- the command line flag <c><![CDATA[+W <w | i>]]></c>, they can instead
- be tagged as warnings or info. Tagging them as warnings may
- require rewriting existing user defined event handlers.</p>
+ <p>Warning events were introduced in Erlang/OTP R9C and are enabled
+ by default as of 18.0. To retain backwards compatibility with existing
+ user defined event handlers, these may be tagged as errors or info
+ using the command line flag <c><![CDATA[+W <e | i | w>]]></c>, thus
+ showing up as error or info reports in the logs.</p>
</description>
<datatypes>
<datatype>
@@ -132,7 +131,7 @@ ok</pre>
<desc>
<p>Returns the current mapping for warning events. Events sent
using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c>
- are tagged as errors (default), warnings or info, depending
+ are tagged as errors, warnings (default) or info, depending
on the value of the command line flag <c>+W</c>.</p>
<pre>
os$ <input>erl</input>
@@ -140,25 +139,25 @@ Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll]
Eshell V5.4.8 (abort with ^G)
1> <input>error_logger:warning_map().</input>
-error
-2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input>
+warning
+2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input>
-=ERROR REPORT==== 11-Aug-2005::15:31:23 ===
-Warnings tagged as: error
+=WARNING REPORT==== 11-Aug-2005::15:31:55 ===
+Warnings tagged as: warning
ok
3>
User switch command
--> q
-os$ <input>erl +W w</input>
+os$ <input>erl +W e</input>
Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll]
Eshell V5.4.8 (abort with ^G)
1> <input>error_logger:warning_map().</input>
-warning
-2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input>
+error
+2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input>
-=WARNING REPORT==== 11-Aug-2005::15:31:55 ===
-Warnings tagged as: warning
+=ERROR REPORT==== 11-Aug-2005::15:31:23 ===
+Warnings tagged as: error
ok</pre>
</desc>
</func>
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 820ecd1e30..71ef5cd48f 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -347,11 +347,22 @@ do_recv(Sock, Bs) ->
</func>
<func>
<name name="shutdown" arity="2"/>
- <fsummary>Immediately close a socket</fsummary>
+ <fsummary>Asynchronously close a socket</fsummary>
<desc>
- <p>Immediately close a socket in one or two directions.</p>
+ <p>Close a socket in one or two directions.</p>
<p><c><anno>How</anno> == write</c> means closing the socket for writing,
reading from it is still possible.</p>
+ <p>If <c><anno>How</anno> == read</c>, or there is no outgoing
+ data buffered in the <c><anno>Socket</anno></c> port,
+ then the socket is shutdown immediately and any error encountered
+ is returned in <c><anno>Reason</anno></c>.</p>
+ <p>If there is data buffered in the socket port, then the attempt
+ to shutdown the socket is postponed until that data is written to the
+ kernel socket send buffer. Any errors encountered will result
+ in the socket being closed and <c>{error, closed}</c> being returned
+ on the next
+ <seealso marker="gen_tcp#recv/2">recv/2</seealso> or
+ <seealso marker="gen_tcp#send/2">send/2</seealso>.</p>
<p>To be able to handle that the peer has done a shutdown on
the write side, the <c>{exit_on_close, false}</c> option
is useful.</p>
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index ec2c350931..d668738109 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1527,26 +1527,28 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
_ ->
case prim_inet:getopt(S, active) of
{ok, A0} ->
- case A0 of
- false -> ok;
- _ -> ok = prim_inet:setopt(S, active, false)
- end,
- case tcp_sync_input(S, NewOwner, false) of
- true -> %% socket already closed,
+ SetOptRes =
+ case A0 of
+ false -> ok;
+ _ -> prim_inet:setopt(S, active, false)
+ end,
+ case {tcp_sync_input(S, NewOwner, false), SetOptRes} of
+ {true, _} -> %% socket already closed
ok;
- false ->
+ {false, ok} ->
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S), %% unlink from port
case A0 of
false -> ok;
- _ -> ok = prim_inet:setopt(S, active, A0)
- end,
- ok
+ _ -> prim_inet:setopt(S, active, A0)
+ end
catch
error:Reason ->
{error, Reason}
- end
+ end;
+ {false, Error} ->
+ Error
end;
Error ->
Error
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index a88c94a453..a694642b19 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -675,28 +675,22 @@ ipv6_addr_done(Ar, Br, N) ->
ipv6_addr_done(Ar) ->
list_to_tuple(lists:reverse(Ar)).
-%% Collect Hex digits
-hex(Cs) -> hex(Cs, []).
-%%
-hex([C|Cs], R) when C >= $0, C =< $9 ->
- hex(Cs, [C|R]);
-hex([C|Cs], R) when C >= $a, C =< $f ->
- hex(Cs, [C|R]);
-hex([C|Cs], R) when C >= $A, C =< $F ->
- hex(Cs, [C|R]);
-hex(Cs, [_|_]=R) when is_list(Cs) ->
+%% Collect 1-4 Hex digits
+hex(Cs) -> hex(Cs, [], 4).
+%%
+hex([C|Cs], R, N) when C >= $0, C =< $9, N > 0 ->
+ hex(Cs, [C|R], N-1);
+hex([C|Cs], R, N) when C >= $a, C =< $f, N > 0 ->
+ hex(Cs, [C|R], N-1);
+hex([C|Cs], R, N) when C >= $A, C =< $F, N > 0 ->
+ hex(Cs, [C|R], N-1);
+hex(Cs, [_|_]=R, _) when is_list(Cs) ->
{lists:reverse(R),Cs};
-hex(_, _) ->
+hex(_, _, _) ->
erlang:error(badarg).
%% Hex string to integer
-hex_to_int(Cs0) ->
- case strip0(Cs0) of
- Cs when length(Cs) =< 4 ->
- erlang:list_to_integer("0"++Cs, 16);
- _ ->
- erlang:error(badarg)
- end.
+hex_to_int(Cs) -> erlang:list_to_integer(Cs, 16).
%% Dup onto head of existing list
dup(0, _, L) ->
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index 93528d305d..f0f13c8d4a 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -133,15 +133,18 @@ connect_get_assoc(S, Addr, Port, Active, Timer) ->
Timeout = inet:timeout(Timer),
receive
{sctp,S,Addr,Port,{_,#sctp_assoc_change{state=St}=Ev}} ->
- case Active of
- once ->
- ok = prim_inet:setopt(S, active, once);
- _ -> ok
- end,
- if St =:= comm_up ->
+ SetOptRes =
+ case Active of
+ once -> prim_inet:setopt(S, active, once);
+ _ -> ok
+ end,
+ case {St, SetOptRes} of
+ {comm_up, ok} ->
{ok,Ev};
- true ->
- {error,Ev}
+ {_, ok} ->
+ {error,Ev};
+ {_, Error} ->
+ Error
end
after Timeout ->
{error,timeout}
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 1bae762bed..5d3836bad7 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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
@@ -17,7 +17,7 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17
+ [{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17
%% Down to - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17
+ [{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17
}.
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 549c65d034..c82aaf0582 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1654,9 +1654,7 @@ get_mode(Config) when is_list(Config) ->
init(Tester) ->
{ok, Tester}.
-handle_event({error, _GL, {emulator, _, _}}, Tester) ->
- {ok, Tester};
-handle_event({error, _GL, Msg}, Tester) ->
+handle_event({warning_msg, _GL, Msg}, Tester) ->
Tester ! Msg,
{ok, Tester};
handle_event(_Event, State) ->
diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl
index 2bf467610e..fb576d77a3 100644
--- a/lib/kernel/test/error_logger_warn_SUITE.erl
+++ b/lib/kernel/test/error_logger_warn_SUITE.erl
@@ -21,8 +21,8 @@
-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,
- basic/1,warnings_info/1,warnings_warnings/1,
- rb_basic/1,rb_warnings_info/1,rb_warnings_warnings/1,
+ basic/1,warnings_info/1,warnings_errors/1,
+ rb_basic/1,rb_warnings_info/1,rb_warnings_errors/1,
rb_trunc/1,rb_utc/1,file_utc/1]).
%% Internal exports.
@@ -48,8 +48,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [basic, warnings_info, warnings_warnings, rb_basic,
- rb_warnings_info, rb_warnings_warnings, rb_trunc,
+ [basic, warnings_info, warnings_errors, rb_basic,
+ rb_warnings_info, rb_warnings_errors, rb_trunc,
rb_utc, file_utc].
groups() ->
@@ -88,11 +88,11 @@ warnings_info(Config) when is_list(Config) ->
put(elw_config,Config),
warnings_info().
-warnings_warnings(doc) ->
- ["Tests mapping warnings to warnings functionality"];
-warnings_warnings(Config) when is_list(Config) ->
+warnings_errors(doc) ->
+ ["Tests mapping warnings to errors functionality"];
+warnings_errors(Config) when is_list(Config) ->
put(elw_config,Config),
- warnings_warnings().
+ warnings_errors().
rb_basic(doc) ->
["Tests basic rb functionality"];
@@ -106,11 +106,11 @@ rb_warnings_info(Config) when is_list(Config) ->
put(elw_config,Config),
rb_warnings_info().
-rb_warnings_warnings(doc) ->
- ["Tests warnings as warnings rb functionality"];
-rb_warnings_warnings(Config) when is_list(Config) ->
+rb_warnings_errors(doc) ->
+ ["Tests warnings as errors rb functionality"];
+rb_warnings_errors(Config) when is_list(Config) ->
put(elw_config,Config),
- rb_warnings_warnings().
+ rb_warnings_errors().
rb_trunc(doc) ->
["Tests rb functionality on truncated data"];
@@ -159,6 +159,9 @@ install_relay(Node) ->
rpc:call(Node,error_logger,add_report_handler,[?MODULE,[self()]]).
+warning_map(Node) ->
+ rpc:call(Node,error_logger,warning_map,[]).
+
format(Node,A,B) ->
rpc:call(Node,error_logger,format,[A,B]).
error_msg(Node,A,B) ->
@@ -185,19 +188,20 @@ basic() ->
?line ok = install_relay(Node),
?line Self = self(),
?line GL = group_leader(),
+ ?line warning = warning_map(Node),
?line format(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
?line error_msg(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
?line warning_msg(Node,"~p~n",[Self]),
- ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
+ ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}),
?line info_msg(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line error_report(Node,Report),
?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}),
?line warning_report(Node,Report),
- ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}),
+ ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}),
?line info_report(Node,Report),
?line ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}),
@@ -209,6 +213,7 @@ warnings_info() ->
?line ok = install_relay(Node),
?line Self = self(),
?line GL = group_leader(),
+ ?line info = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line warning_msg(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}),
@@ -217,16 +222,17 @@ warnings_info() ->
?line stop_node(Node),
ok.
-warnings_warnings() ->
- ?line Node = start_node(nn(),"+Ww"),
+warnings_errors() ->
+ ?line Node = start_node(nn(),"+We"),
?line ok = install_relay(Node),
?line Self = self(),
?line GL = group_leader(),
+ ?line error = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line warning_msg(Node,"~p~n",[Self]),
- ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}),
+ ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
?line warning_report(Node,Report),
- ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}),
+ ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}),
?line stop_node(Node),
ok.
@@ -356,6 +362,7 @@ rb_basic() ->
"error_logger_mf_maxfiles 5"),
?line Self = self(),
?line GL = group_leader(),
+ ?line warning = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line fake_gl(Node,warning_msg,"~p~n",[Self]),
?line fake_gl(Node,warning_report,Report),
@@ -363,10 +370,14 @@ rb_basic() ->
?line application:start(sasl),
?line rb:start([{report_dir, rd()}]),
?line rb:list(),
- ?line true = (one_rb_lines([error]) > 1),
- ?line true = (one_rb_lines([error_report]) > 1),
- ?line 1 = one_rb_findstr([error],pid_to_list(Self)),
- ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)),
+ ?line true = (one_rb_lines([error]) =:= 0),
+ ?line true = (one_rb_lines([error_report]) =:= 0),
+ ?line 0 = one_rb_findstr([error],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)),
+ ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)),
+ ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([info_report],pid_to_list(Self)),
?line 2 = one_rb_findstr([],pid_to_list(Self)),
?line true = (one_rb_findstr([progress],"===") > 4),
?line rb:stop(),
@@ -381,6 +392,7 @@ rb_warnings_info() ->
"error_logger_mf_maxfiles 5"),
?line Self = self(),
?line GL = group_leader(),
+ ?line info = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line fake_gl(Node,warning_msg,"~p~n",[Self]),
?line fake_gl(Node,warning_report,Report),
@@ -403,13 +415,14 @@ rb_warnings_info() ->
?line stop_node(Node),
ok.
-rb_warnings_warnings() ->
+rb_warnings_errors() ->
?line clean_rd(),
- ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++
+ ?line Node = start_node(nn(),"+W e -boot start_sasl -sasl error_logger_mf_dir "++
quote(rd())++" error_logger_mf_maxbytes 5000 "
"error_logger_mf_maxfiles 5"),
?line Self = self(),
?line GL = group_leader(),
+ ?line error = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line fake_gl(Node,warning_msg,"~p~n",[Self]),
?line fake_gl(Node,warning_report,Report),
@@ -417,12 +430,12 @@ rb_warnings_warnings() ->
?line application:start(sasl),
?line rb:start([{report_dir, rd()}]),
?line rb:list(),
- ?line true = (one_rb_lines([error]) =:= 0),
- ?line true = (one_rb_lines([error_report]) =:= 0),
- ?line 0 = one_rb_findstr([error],pid_to_list(Self)),
- ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)),
- ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)),
- ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)),
+ ?line true = (one_rb_lines([error]) > 1),
+ ?line true = (one_rb_lines([error_report]) > 1),
+ ?line 1 = one_rb_findstr([error],pid_to_list(Self)),
+ ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([warning_msg],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([warning_report],pid_to_list(Self)),
?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)),
?line 0 = one_rb_findstr([info_report],pid_to_list(Self)),
?line 2 = one_rb_findstr([],pid_to_list(Self)),
@@ -434,7 +447,7 @@ rb_warnings_warnings() ->
rb_trunc() ->
?line clean_rd(),
- ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++
+ ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++
quote(rd())++" error_logger_mf_maxbytes 5000 "
"error_logger_mf_maxfiles 5"),
?line Self = self(),
@@ -467,7 +480,7 @@ rb_trunc() ->
rb_utc() ->
?line clean_rd(),
- ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++
+ ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++
quote(rd())++" error_logger_mf_maxbytes 5000 "
"error_logger_mf_maxfiles 5 -sasl utc_log true"),
?line Self = self(),
@@ -500,7 +513,7 @@ rb_utc() ->
file_utc() ->
?line file:delete(lf()),
- ?line SS="+W w -stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"),
+ ?line SS="-stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"),
%erlang:display(SS),
?line Node = start_node(nn(),SS),
%erlang:display(rpc:call(Node,application,get_env,[kernel,error_logger])),
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index c27d265550..4a527e2f51 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -32,6 +32,7 @@
t_connect_bad/1,
t_recv_timeout/1, t_recv_eof/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
+ t_shutdown_async/1,
t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]).
-export([getsockfd/0,closesockfd/1]).
@@ -41,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
- t_fdopen, t_fdconnect, t_implicit_inet6].
+ t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6].
groups() ->
[{t_accept, [], [t_accept_timeout]},
@@ -155,7 +156,34 @@ t_shutdown_error(Config) when is_list(Config) ->
?line ok = gen_tcp:close(L),
?line {error, closed} = gen_tcp:shutdown(L, read_write),
ok.
-
+
+t_shutdown_async(Config) when is_list(Config) ->
+ ?line {OS, _} = os:type(),
+ ?line {ok, L} = gen_tcp:listen(0, [{sndbuf, 4096}]),
+ ?line {ok, Port} = inet:port(L),
+ ?line {ok, Client} = gen_tcp:connect(localhost, Port,
+ [{recbuf, 4096},
+ {active, false}]),
+ ?line {ok, S} = gen_tcp:accept(L),
+ ?line PayloadSize = 1024 * 1024,
+ ?line Payload = lists:duplicate(PayloadSize, $.),
+ ?line ok = gen_tcp:send(S, Payload),
+ ?line case erlang:port_info(S, queue_size) of
+ {queue_size, N} when N > 0 -> ok;
+ {queue_size, 0} when OS =:= win32 -> ok;
+ {queue_size, 0} = T -> ?t:fail({unexpected, T})
+ end,
+
+ ?line ok = gen_tcp:shutdown(S, write),
+ ?line {ok, Buf} = gen_tcp:recv(Client, PayloadSize),
+ ?line {error, closed} = gen_tcp:recv(Client, 0),
+ ?line case length(Buf) of
+ PayloadSize -> ok;
+ Sz -> ?t:fail({payload_size,
+ {expected, PayloadSize},
+ {received, Sz}})
+ end.
+
%%% gen_tcp:fdopen/2
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 30c46f3048..4f0d7a7d50 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -60,19 +60,19 @@ init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
[{watchdog, Dog}|Config];
init_per_testcase(iter_max_socks, Config) when is_list(Config) ->
Dog = case os:type() of
- {win32,_} ->
- test_server:timetrap(test_server:minutes(30));
- _Else ->
- test_server:timetrap(test_server:seconds(240))
- end,
+ {win32,_} ->
+ test_server:timetrap(test_server:minutes(30));
+ _Else ->
+ test_server:timetrap(test_server:seconds(240))
+ end,
[{watchdog, Dog}|Config];
init_per_testcase(accept_system_limit, Config) when is_list(Config) ->
case os:type() of
- {ose,_} ->
- {skip,"Skip in OSE"};
- _ ->
- Dog = test_server:timetrap(test_server:seconds(240)),
- [{watchdog,Dog}|Config]
+ {ose,_} ->
+ {skip,"Skip in OSE"};
+ _ ->
+ Dog = test_server:timetrap(test_server:seconds(240)),
+ [{watchdog,Dog}|Config]
end;
init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
Dog = test_server:timetrap(test_server:seconds(600)),
@@ -121,8 +121,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
-
default_options(doc) ->
["Tests kernel application variables inet_default_listen_options and "
"inet_default_connect_options"];
@@ -130,69 +128,68 @@ default_options(suite) ->
[];
default_options(Config) when is_list(Config) ->
%% First check the delay_send option
- ?line {true,true,true}=do_delay_send_1(),
- ?line {false,false,false}=do_delay_send_2(),
- ?line {true,false,false}=do_delay_send_3(),
- ?line {false,false,false}=do_delay_send_4(),
- ?line {false,false,false}=do_delay_send_5(),
- ?line {false,true,true}=do_delay_send_6(),
+ {true,true,true}=do_delay_send_1(),
+ {false,false,false}=do_delay_send_2(),
+ {true,false,false}=do_delay_send_3(),
+ {false,false,false}=do_delay_send_4(),
+ {false,false,false}=do_delay_send_5(),
+ {false,true,true}=do_delay_send_6(),
%% Now lets start some nodes with different combinations of options:
- ?line {true,true,true} = do_delay_on_other_node("",
- fun do_delay_send_1/0),
- ?line {true,false,false} =
+ {true,true,true} = do_delay_on_other_node("", fun do_delay_send_1/0),
+ {true,false,false} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_2/0),
- ?line {false,true,true} =
+ {false,true,true} =
do_delay_on_other_node("-kernel inet_default_listen_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_2/0),
- ?line {true,true,true} =
+ {true,true,true} =
do_delay_on_other_node("-kernel inet_default_listen_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_3/0),
- ?line {true,true,true} =
+ {true,true,true} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_6/0),
- ?line {false,false,false} =
+ {false,false,false} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_5/0),
- ?line {false,true,true} =
+ {false,true,true} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{delay_send,true}]\" "
"-kernel inet_default_listen_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_5/0),
- ?line {true,false,false} =
+ {true,false,false} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{delay_send,true}]\" "
"-kernel inet_default_listen_options "
"\"[{delay_send,true}]\"",
fun do_delay_send_4/0),
- ?line {true,true,true} =
+ {true,true,true} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"{delay_send,true}\" "
"-kernel inet_default_listen_options "
"\"{delay_send,true}\"",
fun do_delay_send_2/0),
%% Active is to dangerous and is supressed
- ?line {true,true,true} =
+ {true,true,true} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"{active,false}\" "
"-kernel inet_default_listen_options "
"\"{active,false}\"",
fun do_delay_send_7/0),
- ?line {true,true,true} =
+ {true,true,true} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{active,false},{delay_send,true}]\" "
"-kernel inet_default_listen_options "
"\"[{active,false},{delay_send,true}]\"",
fun do_delay_send_7/0),
- ?line {true,true,true} =
+ {true,true,true} =
do_delay_on_other_node("-kernel inet_default_connect_options "
"\"[{active,false},{delay_send,true}]\" "
"-kernel inet_default_listen_options "
@@ -204,12 +201,10 @@ default_options(Config) when is_list(Config) ->
do_delay_on_other_node(XArgs, Function) ->
Dir = filename:dirname(code:which(?MODULE)),
{ok,Node} = test_server:start_node(test_default_options_slave,slave,
- [{args,"-pa " ++ Dir ++ " " ++
- XArgs}]),
+ [{args,"-pa " ++ Dir ++ " " ++ XArgs}]),
Res = rpc:call(Node,erlang,apply,[Function,[]]),
test_server:stop_node(Node),
Res.
-
do_delay_send_1() ->
{ok,LS}=gen_tcp:listen(0,[{delay_send,true}]),
@@ -301,8 +296,6 @@ do_delay_send_7() ->
gen_tcp:close(S),
gen_tcp:close(LS),
{B1,B2,B3}.
-
-
controlling_process(doc) ->
["Open a listen port and change controlling_process for it",
@@ -313,18 +306,18 @@ controlling_process(Config) when is_list(Config) ->
{ok,S} = gen_tcp:listen(0,[]),
Pid2 = spawn(?MODULE,not_owner,[S]),
Pid2 ! {self(),2,control},
- ?line {error, E} = receive {2,_E} ->
+ {error, E} = receive {2,_E} ->
_E
after 10000 -> timeout
end,
io:format("received ~p~n",[E]),
Pid = spawn(?MODULE,not_owner,[S]),
- ?line ok = gen_tcp:controlling_process(S,Pid),
+ ok = gen_tcp:controlling_process(S,Pid),
Pid ! {self(),1,control},
- ?line ok = receive {1,ok} ->
- ok
- after 1000 -> timeout
- end,
+ ok = receive {1,ok} ->
+ ok
+ after 1000 -> timeout
+ end,
Pid ! close.
not_owner(S) ->
@@ -377,7 +370,7 @@ no_accept(Config) when is_list(Config) ->
{tcp_closed, Client} ->
ok
after 5000 ->
- ?line test_server:fail(never_closed)
+ test_server:fail(never_closed)
end.
@@ -386,30 +379,30 @@ close_with_pending_output(doc) ->
"to the other end."];
close_with_pending_output(suite) -> [];
close_with_pending_output(Config) when is_list(Config) ->
- ?line {ok, L} = gen_tcp:listen(0, [binary, {active, false}]),
- ?line {ok, {_, Port}} = inet:sockname(L),
- ?line Packets = 16,
- ?line Total = 2048*Packets,
+ {ok, L} = gen_tcp:listen(0, [binary, {active, false}]),
+ {ok, {_, Port}} = inet:sockname(L),
+ Packets = 16,
+ Total = 2048*Packets,
case start_remote(close_pending) of
{ok, Node} ->
- ?line {ok, Host} = inet:gethostname(),
- ?line spawn_link(Node, ?MODULE, sender, [Port, Packets, Host]),
- ?line {ok, A} = gen_tcp:accept(L),
- ?line case gen_tcp:recv(A, Total) of
+ {ok, Host} = inet:gethostname(),
+ spawn_link(Node, ?MODULE, sender, [Port, Packets, Host]),
+ {ok, A} = gen_tcp:accept(L),
+ case gen_tcp:recv(A, Total) of
{ok, Bin} when byte_size(Bin) == Total ->
gen_tcp:close(A),
gen_tcp:close(L);
{ok, Bin} ->
- ?line test_server:fail({small_packet,
+ test_server:fail({small_packet,
byte_size(Bin)});
Error ->
- ?line test_server:fail({unexpected, Error})
+ test_server:fail({unexpected, Error})
end,
ok;
{error, no_remote_hosts} ->
{skipped,"No remote hosts"};
{error, Other} ->
- ?line ?t:fail({failed_to_start_slave_node, Other})
+ ?t:fail({failed_to_start_slave_node, Other})
end.
sender(Port, Packets, Host) ->
@@ -556,63 +549,62 @@ otp_3924(Config) when is_list(Config) ->
otp_3924_1(MaxDelay).
otp_3924_1(MaxDelay) ->
- ?line {ok, Node} = start_node(otp_3924),
- ?line DataLen = 100*1024,
- ?line Data = otp_3924_data(DataLen),
+ {ok, Node} = start_node(otp_3924),
+ DataLen = 100*1024,
+ Data = otp_3924_data(DataLen),
% Repeat the test a couple of times to prevent the test from passing
% by chance.
- repeat(10,
- fun (N) ->
- ?line ok = otp_3924(MaxDelay, Node, Data, DataLen, N)
- end),
- ?line test_server:stop_node(Node),
+ repeat(10, fun(N) ->
+ ok = otp_3924(MaxDelay, Node, Data, DataLen, N)
+ end),
+ test_server:stop_node(Node),
ok.
otp_3924(MaxDelay, Node, Data, DataLen, N) ->
- ?line {ok, L} = gen_tcp:listen(0, [list, {active, false}]),
- ?line {ok, {_, Port}} = inet:sockname(L),
- ?line {ok, Host} = inet:gethostname(),
- ?line Sender = spawn_link(Node,
- ?MODULE,
- otp_3924_sender,
- [self(), Host, Port, Data]),
- ?line Data = otp_3924_receive_data(L, Sender, MaxDelay, DataLen, N),
- ?line ok = gen_tcp:close(L).
+ {ok, L} = gen_tcp:listen(0, [list, {active, false}]),
+ {ok, {_, Port}} = inet:sockname(L),
+ {ok, Host} = inet:gethostname(),
+ Sender = spawn_link(Node,
+ ?MODULE,
+ otp_3924_sender,
+ [self(), Host, Port, Data]),
+ Data = otp_3924_receive_data(L, Sender, MaxDelay, DataLen, N),
+ ok = gen_tcp:close(L).
otp_3924_receive_data(LSock, Sender, MaxDelay, Len, N) ->
- ?line OP = process_flag(priority, max),
- ?line OTE = process_flag(trap_exit, true),
- ?line TimeoutRef = make_ref(),
- ?line Data = (catch begin
- ?line Sender ! start,
- ?line {ok, Sock} = gen_tcp:accept(LSock),
- ?line D = otp_3924_receive_data(Sock,
- TimeoutRef,
- MaxDelay,
- Len,
- [],
- 0),
- ?line ok = gen_tcp:close(Sock),
- D
- end),
- ?line unlink(Sender),
- ?line process_flag(trap_exit, OTE),
- ?line process_flag(priority, OP),
+ OP = process_flag(priority, max),
+ OTE = process_flag(trap_exit, true),
+ TimeoutRef = make_ref(),
+ Data = (catch begin
+ Sender ! start,
+ {ok, Sock} = gen_tcp:accept(LSock),
+ D = otp_3924_receive_data(Sock,
+ TimeoutRef,
+ MaxDelay,
+ Len,
+ [],
+ 0),
+ ok = gen_tcp:close(Sock),
+ D
+ end),
+ unlink(Sender),
+ process_flag(trap_exit, OTE),
+ process_flag(priority, OP),
receive
{'EXIT', _, TimeoutRef} ->
- ?line test_server:fail({close_not_fast_enough,MaxDelay,N});
+ test_server:fail({close_not_fast_enough,MaxDelay,N});
{'EXIT', Sender, Reason} ->
- ?line test_server:fail({sender_exited, Reason});
+ test_server:fail({sender_exited, Reason});
{'EXIT', _Other, Reason} ->
- ?line test_server:fail({linked_process_exited, Reason})
+ test_server:fail({linked_process_exited, Reason})
after 0 ->
case Data of
{'EXIT', {A,B}} ->
- ?line test_server:fail({A,B,N});
+ test_server:fail({A,B,N});
{'EXIT', Failure} ->
- ?line test_server:fail(Failure);
+ test_server:fail(Failure);
_ ->
- ?line Data
+ Data
end
end.
@@ -623,12 +615,12 @@ otp_3924_receive_data(Sock, TimeoutRef, MaxDelay, Len, Acc, AccLen) ->
NewAccLen = AccLen + length(Data),
if
NewAccLen == Len ->
- ?line {ok, TRef} = timer:exit_after(MaxDelay,
+ {ok, TRef} = timer:exit_after(MaxDelay,
self(),
TimeoutRef),
- ?line {error, closed} = gen_tcp:recv(Sock, 0),
- ?line timer:cancel(TRef),
- ?line lists:flatten([Acc, Data]);
+ {error, closed} = gen_tcp:recv(Sock, 0),
+ timer:cancel(TRef),
+ lists:flatten([Acc, Data]);
NewAccLen > Len ->
exit({received_too_much, NewAccLen});
true ->
@@ -713,8 +705,8 @@ get_status(doc) ->
"is called."];
get_status(suite) -> [];
get_status(Config) when is_list(Config) ->
- ?line {ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]),
- ?line {status,Pid,_,_} = sys:get_status(Pid).
+ {ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]),
+ {status,Pid,_,_} = sys:get_status(Pid).
-define(RECOVER_SLEEP, 60000).
-define(RETRY_SLEEP, 15000).
@@ -744,19 +736,19 @@ do_iter_max_socks(N, failed) ->
MS = max_socks(),
[MS|do_iter_max_socks(N-1, failed)];
do_iter_max_socks(N, First) when is_integer(First) ->
- ?line MS = max_socks(),
+ MS = max_socks(),
if MS == First ->
- ?line [MS|do_iter_max_socks(N-1, First)];
+ [MS|do_iter_max_socks(N-1, First)];
true ->
- ?line io:format("Sleeping for ~p seconds...~n",
+ io:format("Sleeping for ~p seconds...~n",
[?RETRY_SLEEP/1000]),
- ?line ?t:sleep(?RETRY_SLEEP),
- ?line io:format("Trying again...~n", []),
- ?line RetryMS = max_socks(),
- ?line if RetryMS == First ->
- ?line [RetryMS|do_iter_max_socks(N-1, First)];
+ ?t:sleep(?RETRY_SLEEP),
+ io:format("Trying again...~n", []),
+ RetryMS = max_socks(),
+ if RetryMS == First ->
+ [RetryMS|do_iter_max_socks(N-1, First)];
true ->
- ?line [RetryMS|do_iter_max_socks(N-1, failed)]
+ [RetryMS|do_iter_max_socks(N-1, failed)]
end
end.
@@ -768,7 +760,7 @@ all_equal([Rule | T]) ->
all_equal(Rule, [Rule | T]) ->
all_equal(Rule, T);
all_equal(_, [_ | _]) ->
- ?line ?t:sleep(?RECOVER_SLEEP), % Wait a while and *hope* that we'll
+ ?t:sleep(?RECOVER_SLEEP), % Wait a while and *hope* that we'll
% recover so other tests won't be
% affected.
?t:fail(max_socket_mismatch);
@@ -776,9 +768,9 @@ all_equal(_Rule, []) ->
ok.
max_socks() ->
- ?line Socks = open_socks(),
- ?line N = length(Socks),
- ?line lists:foreach(fun(S) -> ok = gen_tcp:close(S) end, Socks),
+ Socks = open_socks(),
+ N = length(Socks),
+ lists:foreach(fun(S) -> ok = gen_tcp:close(S) end, Socks),
io:format("Got ~p sockets", [N]),
N.
@@ -817,18 +809,18 @@ passive_sockets(doc) ->
["Tests that when 'the other side' on a passive socket closes, the connecting",
"side still can read until the end of data."];
passive_sockets(Config) when is_list(Config) ->
- ?line spawn_link(?MODULE, passive_sockets_server,
- [[{active,false}],self()]),
- ?line receive
- {socket,Port} -> ok
- end,
+ spawn_link(?MODULE, passive_sockets_server,
+ [[{active,false}],self()]),
+ receive
+ {socket,Port} -> ok
+ end,
?t:sleep(500),
- ?line case gen_tcp:connect("localhost", Port, [{active, false}]) of
- {ok, Sock} ->
- passive_sockets_read(Sock);
- Error ->
- ?t:fail({"Could not connect to server", Error})
- end.
+ case gen_tcp:connect("localhost", Port, [{active, false}]) of
+ {ok, Sock} ->
+ passive_sockets_read(Sock);
+ Error ->
+ ?t:fail({"Could not connect to server", Error})
+ end.
%%
%% Read until we get an {error, closed}. If we get another error, this test case
@@ -847,58 +839,58 @@ passive_sockets_read(Sock) ->
end.
passive_sockets_server(Opts, Parent) ->
- ?line case gen_tcp:listen(0, Opts) of
- {ok, LSock} ->
- {ok,{_,Port}} = inet:sockname(LSock),
- Parent ! {socket,Port},
- passive_sockets_server_accept(LSock);
- Error ->
- ?t:fail({"Could not create listen socket", Error})
- end.
+ case gen_tcp:listen(0, Opts) of
+ {ok, LSock} ->
+ {ok,{_,Port}} = inet:sockname(LSock),
+ Parent ! {socket,Port},
+ passive_sockets_server_accept(LSock);
+ Error ->
+ ?t:fail({"Could not create listen socket", Error})
+ end.
passive_sockets_server_accept(Sock) ->
- ?line case gen_tcp:accept(Sock) of
- {ok, Socket} ->
- ?t:sleep(500), % Simulate latency
- passive_sockets_server_send(Socket, 5),
- passive_sockets_server_accept(Sock);
- Error ->
- ?t:fail({"Could not accept connection", Error})
- end.
+ case gen_tcp:accept(Sock) of
+ {ok, Socket} ->
+ ?t:sleep(500), % Simulate latency
+ passive_sockets_server_send(Socket, 5),
+ passive_sockets_server_accept(Sock);
+ Error ->
+ ?t:fail({"Could not accept connection", Error})
+ end.
passive_sockets_server_send(Socket, 0) ->
io:format("Closing other end..~n", []),
gen_tcp:close(Socket);
passive_sockets_server_send(Socket, X) ->
- ?line Data = lists:duplicate(1024*X, $a),
- ?line case gen_tcp:send(Socket, Data) of
- ok ->
- ?t:sleep(50), % Simulate some processing.
- passive_sockets_server_send(Socket, X-1);
- {error, _Reason} ->
- ?t:fail("Failed to send data")
- end.
+ Data = lists:duplicate(1024*X, $a),
+ case gen_tcp:send(Socket, Data) of
+ ok ->
+ ?t:sleep(50), % Simulate some processing.
+ passive_sockets_server_send(Socket, X-1);
+ {error, _Reason} ->
+ ?t:fail("Failed to send data")
+ end.
accept_closed_by_other_process(doc) ->
["Tests the return value from gen_tcp:accept when ",
"the socket is closed from another process. (OTP-3817)"];
accept_closed_by_other_process(Config) when is_list(Config) ->
- ?line Parent = self(),
- ?line {ok, ListenSocket} = gen_tcp:listen(0, []),
- ?line Child =
+ Parent = self(),
+ {ok, ListenSocket} = gen_tcp:listen(0, []),
+ Child =
spawn_link(
fun() ->
Parent ! {self(), gen_tcp:accept(ListenSocket)}
end),
- ?line receive after 1000 -> ok end,
- ?line ok = gen_tcp:close(ListenSocket),
- ?line receive
- {Child, {error, closed}} ->
- ok;
- {Child, Other} ->
- ?t:fail({"Wrong result of gen_tcp:accept", Other})
- end.
+ receive after 1000 -> ok end,
+ ok = gen_tcp:close(ListenSocket),
+ receive
+ {Child, {error, closed}} ->
+ ok;
+ {Child, Other} ->
+ ?t:fail({"Wrong result of gen_tcp:accept", Other})
+ end.
repeat(N, Fun) ->
repeat(N, N, Fun).
@@ -915,9 +907,9 @@ closed_socket(suite) ->
closed_socket(doc) ->
["Tests the response when using a closed socket as argument"];
closed_socket(Config) when is_list(Config) ->
- ?line {ok, LS1} = gen_tcp:listen(0, []),
- ?line erlang:yield(),
- ?line ok = gen_tcp:close(LS1),
+ {ok, LS1} = gen_tcp:listen(0, []),
+ erlang:yield(),
+ ok = gen_tcp:close(LS1),
%% If the following delay is uncommented, the result error values
%% below will change from {error, einval} to {error, closed} since
%% inet_db then will have noticed that the socket is closed.
@@ -925,19 +917,18 @@ closed_socket(Config) when is_list(Config) ->
%% in inet_db processes the 'EXIT' message from the port,
%% the socket is unregistered.
%%
- %% ?line test_server:sleep(test_server:seconds(2)),
+ %% test_server:sleep(test_server:seconds(2)),
%%
- ?line {error, R_send} = gen_tcp:send(LS1, "data"),
- ?line {error, R_recv} = gen_tcp:recv(LS1, 17),
- ?line {error, R_accept} = gen_tcp:accept(LS1),
- ?line {error, R_controlling_process} =
+ {error, R_send} = gen_tcp:send(LS1, "data"),
+ {error, R_recv} = gen_tcp:recv(LS1, 17),
+ {error, R_accept} = gen_tcp:accept(LS1),
+ {error, R_controlling_process} =
gen_tcp:controlling_process(LS1, self()),
%%
- ?line ok = io:format("R_send = ~p~n", [R_send]),
- ?line ok = io:format("R_recv = ~p~n", [R_recv]),
- ?line ok = io:format("R_accept = ~p~n", [R_accept]),
- ?line ok = io:format("R_controlling_process = ~p~n",
- [R_controlling_process]),
+ ok = io:format("R_send = ~p~n", [R_send]),
+ ok = io:format("R_recv = ~p~n", [R_recv]),
+ ok = io:format("R_accept = ~p~n", [R_accept]),
+ ok = io:format("R_controlling_process = ~p~n", [R_controlling_process]),
ok.
%%%
@@ -945,28 +936,27 @@ closed_socket(Config) when is_list(Config) ->
%%%
shutdown_active(Config) when is_list(Config) ->
- ?line shutdown_common(true).
+ shutdown_common(true).
shutdown_passive(Config) when is_list(Config) ->
- ?line shutdown_common(false).
+ shutdown_common(false).
shutdown_common(Active) ->
- ?line P = sort_server(Active),
+ P = sort_server(Active),
io:format("Sort server port: ~p\n", [P]),
- ?line do_sort(P, []),
- ?line do_sort(P, ["glurf"]),
- ?line do_sort(P, ["abc","nisse","dum"]),
-
- ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 255)]),
- ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(77, 999)]),
- ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 55)]),
- ?line do_sort(P, []),
- ?line do_sort(P, ["apa"]),
- ?line do_sort(P, ["kluns","gorilla"]),
- ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 1233)]),
- ?line do_sort(P, []),
-
+ do_sort(P, []),
+ do_sort(P, ["glurf"]),
+ do_sort(P, ["abc","nisse","dum"]),
+
+ do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 255)]),
+ do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(77, 999)]),
+ do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 55)]),
+ do_sort(P, []),
+ do_sort(P, ["apa"]),
+ do_sort(P, ["kluns","gorilla"]),
+ do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 1233)]),
+ do_sort(P, []),
receive
Any ->
?t:fail({unexpected_message,Any})
@@ -985,14 +975,14 @@ do_sort(P, List0) ->
sort_server(Active) ->
Opts = [{exit_on_close,false},{packet,line},{active,Active}],
- ?line {ok,L} = gen_tcp:listen(0, Opts),
+ {ok,L} = gen_tcp:listen(0, Opts),
Go = make_ref(),
- ?line Pid = spawn_link(fun() ->
- receive Go -> sort_server_1(L, Active) end
- end),
- ?line ok = gen_tcp:controlling_process(L, Pid),
- ?line Pid ! Go,
- ?line {ok,Port} = inet:port(L),
+ Pid = spawn_link(fun() ->
+ receive Go -> sort_server_1(L, Active) end
+ end),
+ ok = gen_tcp:controlling_process(L, Pid),
+ Pid ! Go,
+ {ok,Port} = inet:port(L),
Port.
sort_server_1(L, Active) ->
@@ -1042,17 +1032,17 @@ shutdown_pending(Config) when is_list(Config) ->
Data = [<<N:32>>,ones(N),42],
P = a_server(),
io:format("Server port: ~p\n", [P]),
- ?line {ok,S} = gen_tcp:connect(localhost, P, []),
- ?line gen_tcp:send(S, Data),
- ?line gen_tcp:shutdown(S, write),
- ?line receive
- {tcp,S,Msg} ->
- io:format("~p\n", [Msg]),
- ?line N = list_to_integer(Msg) - 5;
- Other ->
- ?t:fail({unexpected,Other})
- end,
- ok.
+ {ok,S} = gen_tcp:connect(localhost, P, []),
+ gen_tcp:send(S, Data),
+ gen_tcp:shutdown(S, write),
+ receive
+ {tcp,S,Msg} ->
+ io:format("~p\n", [Msg]),
+ N = list_to_integer(Msg) - 5;
+ Other ->
+ ?t:fail({unexpected,Other})
+ end,
+ ok.
ones(0) -> [];
ones(1) -> [1];
@@ -1065,10 +1055,10 @@ shutdown_pending(Config) when is_list(Config) ->
end.
a_server() ->
- ?line {ok,L} = gen_tcp:listen(0, [{exit_on_close,false},{active,false}]),
- ?line Pid = spawn_link(fun() -> a_server(L) end),
- ?line ok = gen_tcp:controlling_process(L, Pid),
- ?line {ok,Port} = inet:port(L),
+ {ok,L} = gen_tcp:listen(0, [{exit_on_close,false},{active,false}]),
+ Pid = spawn_link(fun() -> a_server(L) end),
+ ok = gen_tcp:controlling_process(L, Pid),
+ {ok,Port} = inet:port(L),
Port.
a_server(L) ->
@@ -1090,19 +1080,18 @@ shutdown_pending(Config) when is_list(Config) ->
%% corrupt data. The testcase will be killed by the timetrap timeout
%% if the bug is present.
http_bad_packet(Config) when is_list(Config) ->
- ?line {ok,L} = gen_tcp:listen(0,
- [{active, false},
- binary,
- {reuseaddr, true},
- {packet, http}]),
- ?line {ok,Port} = inet:port(L),
- ?line spawn_link(fun() -> erlang:yield(), http_bad_client(Port) end),
- ?line case gen_tcp:accept(L) of
- {ok,S} ->
- http_worker(S);
- Err ->
- exit({accept,Err})
- end.
+ {ok,L} = gen_tcp:listen(0, [{active, false},
+ binary,
+ {reuseaddr, true},
+ {packet, http}]),
+ {ok,Port} = inet:port(L),
+ spawn_link(fun() -> erlang:yield(), http_bad_client(Port) end),
+ case gen_tcp:accept(L) of
+ {ok,S} ->
+ http_worker(S);
+ Err ->
+ exit({accept,Err})
+ end.
http_worker(S) ->
case gen_tcp:recv(S, 0, 30000) of
@@ -1122,9 +1111,9 @@ http_bad_client(Port) ->
%% Fill send queue and then start receiving.
%%
busy_send(Config) when is_list(Config) ->
- ?line Master = self(),
- ?line Msg = <<"the quick brown fox jumps over a lazy dog~n">>,
- ?line Server =
+ Master = self(),
+ Msg = <<"the quick brown fox jumps over a lazy dog~n">>,
+ Server =
spawn_link(fun () ->
{ok,L} = gen_tcp:listen
(0, [{active,false},binary,
@@ -1134,45 +1123,42 @@ busy_send(Config) when is_list(Config) ->
busy_send_client(Port, Master, Msg)},
busy_send_srv(L, Master, Msg)
end),
- ?line io:format("~p Server~n", [Server]),
- ?line receive
- {Server,client,Client} ->
- ?line io:format("~p Client~n", [Client]),
- ?line busy_send_loop(Server, Client, 0)
- end.
+ io:format("~p Server~n", [Server]),
+ receive
+ {Server,client,Client} ->
+ io:format("~p Client~n", [Client]),
+ busy_send_loop(Server, Client, 0)
+ end.
busy_send_loop(Server, Client, N) ->
%% Master
%%
- ?line receive {Server,send} ->
+ receive {Server,send} ->
busy_send_loop(Server, Client, N+1)
after 2000 ->
%% Send queue full, sender blocked
%% -> stop sender and release client
- ?line io:format("Send timeout, time to receive...~n", []),
- ?line Server ! {self(),close},
- ?line Client ! {self(),recv,N+1},
- ?line receive
- {Server,send} ->
- ?line busy_send_2(Server, Client, N+1)
- after 10000 ->
- %% If this happens, see busy_send_srv
- ?t:fail({timeout,{server,not_send,flush([])}})
- end
- end.
+ io:format("Send timeout, time to receive...~n", []),
+ Server ! {self(),close},
+ Client ! {self(),recv,N+1},
+ receive
+ {Server,send} ->
+ busy_send_2(Server, Client, N+1)
+ after 10000 ->
+ %% If this happens, see busy_send_srv
+ ?t:fail({timeout,{server,not_send,flush([])}})
+ end
+ end.
busy_send_2(Server, Client, _N) ->
%% Master
%%
- ?line receive
- {Server,[closed]} ->
- ?line receive
- {Client,[0,{error,closed}]} ->
- ok
- end
- after 10000 ->
- ?t:fail({timeout,{server,not_closed,flush([])}})
- end.
+ receive
+ {Server,[closed]} ->
+ receive {Client,[0,{error,closed}]} -> ok end
+ after 10000 ->
+ ?t:fail({timeout,{server,not_closed,flush([])}})
+ end.
busy_send_srv(L, Master, Msg) ->
%% Server
@@ -1228,7 +1214,7 @@ busy_send_client_loop(Socket, Master, Msg, N) ->
busy_disconnect_passive(Config) when is_list(Config) ->
MuchoData = list_to_binary(ones(64*1024)),
- ?line [do_busy_disconnect_passive(MuchoData) || _ <- lists:seq(1, 10)],
+ [do_busy_disconnect_passive(MuchoData) || _ <- lists:seq(1, 10)],
ok.
do_busy_disconnect_passive(MuchoData) ->
@@ -1236,8 +1222,8 @@ do_busy_disconnect_passive(MuchoData) ->
busy_disconnect_passive_send(S, MuchoData).
busy_disconnect_passive_send(S, Data) ->
- ?line case gen_tcp:send(S, Data) of
- ok -> ?line busy_disconnect_passive_send(S, Data);
+ case gen_tcp:send(S, Data) of
+ ok -> busy_disconnect_passive_send(S, Data);
{error,closed} -> ok
end.
@@ -1248,7 +1234,7 @@ busy_disconnect_passive_send(S, Data) ->
%%%
busy_disconnect_active(Config) when is_list(Config) ->
MuchoData = list_to_binary(ones(64*1024)),
- ?line [do_busy_disconnect_active(MuchoData) || _ <- lists:seq(1, 10)],
+ [do_busy_disconnect_active(MuchoData) || _ <- lists:seq(1, 10)],
ok.
do_busy_disconnect_active(MuchoData) ->
@@ -1256,21 +1242,21 @@ do_busy_disconnect_active(MuchoData) ->
busy_disconnect_active_send(S, MuchoData).
busy_disconnect_active_send(S, Data) ->
- ?line case gen_tcp:send(S, Data) of
- ok -> ?line busy_disconnect_active_send(S, Data);
+ case gen_tcp:send(S, Data) of
+ ok -> busy_disconnect_active_send(S, Data);
{error,closed} ->
receive
{tcp_closed,S} -> ok;
- _Other -> ?line ?t:fail()
+ _Other -> ?t:fail()
end
end.
busy_disconnect_prepare_server(ConnectOpts) ->
- ?line Sender = self(),
- ?line Server = spawn_link(fun() -> busy_disconnect_server(Sender) end),
+ Sender = self(),
+ Server = spawn_link(fun() -> busy_disconnect_server(Sender) end),
receive {port,Server,Port} -> ok end,
- ?line {ok,S} = gen_tcp:connect(localhost, Port, ConnectOpts),
+ {ok,S} = gen_tcp:connect(localhost, Port, ConnectOpts),
Server ! {Sender,sending},
S.
@@ -1304,8 +1290,8 @@ busy_disconnect_server_wait_for_busy(Sender, S) ->
%%% Fill send queue
%%%
fill_sendq(Config) when is_list(Config) ->
- ?line Master = self(),
- ?line Server =
+ Master = self(),
+ Server =
spawn_link(fun () ->
{ok,L} = gen_tcp:listen
(0, [{active,false},binary,
@@ -1315,12 +1301,12 @@ fill_sendq(Config) when is_list(Config) ->
fill_sendq_client(Port, Master)},
fill_sendq_srv(L, Master)
end),
- ?line io:format("~p Server~n", [Server]),
- ?line receive {Server,client,Client} ->
- ?line io:format("~p Client~n", [Client]),
- ?line receive {Server,reader,Reader} ->
- ?line io:format("~p Reader~n", [Reader]),
- ?line fill_sendq_loop(Server, Client, Reader)
+ io:format("~p Server~n", [Server]),
+ receive {Server,client,Client} ->
+ io:format("~p Client~n", [Client]),
+ receive {Server,reader,Reader} ->
+ io:format("~p Reader~n", [Reader]),
+ fill_sendq_loop(Server, Client, Reader)
end
end.
@@ -1331,21 +1317,21 @@ fill_sendq_loop(Server, Client, Reader) ->
fill_sendq_loop(Server, Client, Reader)
after 2000 ->
%% Send queue full, sender blocked -> close client.
- ?line io:format("Send timeout, closing Client...~n", []),
- ?line Client ! {self(),close},
- ?line receive {Server,[{error,closed}]} ->
- ?line io:format("Got server closed.~n"),
- ?line receive {Reader,[{error,closed}]} ->
- ?line io:format
+ io:format("Send timeout, closing Client...~n", []),
+ Client ! {self(),close},
+ receive {Server,[{error,closed}]} ->
+ io:format("Got server closed.~n"),
+ receive {Reader,[{error,closed}]} ->
+ io:format
("Got reader closed.~n"),
ok
after 3000 ->
?t:fail({timeout,{closed,reader}})
end;
{Reader,[{error,closed}]} ->
- ?line io:format("Got reader closed.~n"),
- ?line receive {Server,[{error,closed}]} ->
- ?line io:format("Got server closed~n"),
+ io:format("Got reader closed.~n"),
+ receive {Server,[{error,closed}]} ->
+ io:format("Got server closed~n"),
ok
after 3000 ->
?t:fail({timeout,{closed,server}})
@@ -1416,39 +1402,39 @@ fill_sendq_client(Port, Master) ->
%%% a closed socket.
%%%
partial_recv_and_close(Config) when is_list(Config) ->
- ?line Msg = "the quick brown fox jumps over a lazy dog 0123456789\n",
- ?line Len = length(Msg),
- ?line {ok,L} = gen_tcp:listen(0, [{active,false}]),
- ?line {ok,P} = inet:port(L),
- ?line {ok,S} = gen_tcp:connect("localhost", P, [{active,false}]),
- ?line {ok,A} = gen_tcp:accept(L),
- ?line ok = gen_tcp:send(S, Msg),
- ?line ok = gen_tcp:close(S),
- ?line {error,closed} = gen_tcp:recv(A, Len+1),
+ Msg = "the quick brown fox jumps over a lazy dog 0123456789\n",
+ Len = length(Msg),
+ {ok,L} = gen_tcp:listen(0, [{active,false}]),
+ {ok,P} = inet:port(L),
+ {ok,S} = gen_tcp:connect("localhost", P, [{active,false}]),
+ {ok,A} = gen_tcp:accept(L),
+ ok = gen_tcp:send(S, Msg),
+ ok = gen_tcp:close(S),
+ {error,closed} = gen_tcp:recv(A, Len+1),
ok.
%%% Try to receive more than available number of bytes from
%%% a closed socket, this time waiting in the recv before closing.
%%%
partial_recv_and_close_2(Config) when is_list(Config) ->
- ?line Msg = "the quick brown fox jumps over a lazy dog 0123456789\n",
- ?line Len = length(Msg),
- ?line {ok,L} = gen_tcp:listen(0, [{active,false}]),
- ?line {ok,P} = inet:port(L),
- ?line Server = self(),
- ?line Client =
+ Msg = "the quick brown fox jumps over a lazy dog 0123456789\n",
+ Len = length(Msg),
+ {ok,L} = gen_tcp:listen(0, [{active,false}]),
+ {ok,P} = inet:port(L),
+ Server = self(),
+ Client =
spawn_link(
fun () ->
receive after 2000 -> ok end,
{ok,S} = gen_tcp:connect("localhost", P, [{active,false}]),
- ?line ok = gen_tcp:send(S, Msg),
+ ok = gen_tcp:send(S, Msg),
receive {Server,close} -> ok end,
receive after 2000 -> ok end,
- ?line ok = gen_tcp:close(S)
+ ok = gen_tcp:close(S)
end),
- ?line {ok,A} = gen_tcp:accept(L),
- ?line Client ! {Server,close},
- ?line {error,closed} = gen_tcp:recv(A, Len+1),
+ {ok,A} = gen_tcp:accept(L),
+ Client ! {Server,close},
+ {error,closed} = gen_tcp:recv(A, Len+1),
ok.
%%% Here we tests that gen_tcp:recv/2 will return {error,closed} following
@@ -1471,151 +1457,151 @@ do_partial_recv_and_close_3() ->
receive
{port,Port} -> ok
end,
- ?line Much = ones(8*64*1024),
- ?line {ok,S} = gen_tcp:connect(localhost, Port, [{active,false}]),
+ Much = ones(8*64*1024),
+ {ok,S} = gen_tcp:connect(localhost, Port, [{active,false}]),
%% Send a lot of data (most of it will be queued). The receiver will read one byte
%% and close the connection. The write operation will fail.
- ?line gen_tcp:send(S, Much),
+ gen_tcp:send(S, Much),
%% We should always get {error,closed} here.
- ?line {error,closed} = gen_tcp:recv(S, 0).
+ {error,closed} = gen_tcp:recv(S, 0).
test_prio_put_get() ->
Tos = 3 bsl 5,
- ?line {ok,L1} = gen_tcp:listen(0, [{active,false}]),
- ?line ok = inet:setopts(L1,[{priority,3}]),
- ?line ok = inet:setopts(L1,[{tos,Tos}]),
- ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]),
- ?line ok = inet:setopts(L1,[{priority,3}]), % Dont destroy each other
- ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]),
- ?line ok = inet:setopts(L1,[{reuseaddr,true}]), % Dont let others destroy
- ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]),
- ?line gen_tcp:close(L1),
+ {ok,L1} = gen_tcp:listen(0, [{active,false}]),
+ ok = inet:setopts(L1,[{priority,3}]),
+ ok = inet:setopts(L1,[{tos,Tos}]),
+ {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]),
+ ok = inet:setopts(L1,[{priority,3}]), % Dont destroy each other
+ {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]),
+ ok = inet:setopts(L1,[{reuseaddr,true}]), % Dont let others destroy
+ {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]),
+ gen_tcp:close(L1),
ok.
test_prio_accept() ->
- ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
- {reuseaddr,true},{priority,4}]),
- ?line {ok,Port} = inet:port(Sock),
- ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
- {active,false},
- {reuseaddr,true},
- {priority,4}]),
- ?line {ok,Sock3}=gen_tcp:accept(Sock),
- ?line {ok,[{priority,4}]} = inet:getopts(Sock,[priority]),
- ?line {ok,[{priority,4}]} = inet:getopts(Sock2,[priority]),
- ?line {ok,[{priority,4}]} = inet:getopts(Sock3,[priority]),
- ?line gen_tcp:close(Sock),
- ?line gen_tcp:close(Sock2),
- ?line gen_tcp:close(Sock3),
+ {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
+ {reuseaddr,true},{priority,4}]),
+ {ok,Port} = inet:port(Sock),
+ {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
+ {active,false},
+ {reuseaddr,true},
+ {priority,4}]),
+ {ok,Sock3}=gen_tcp:accept(Sock),
+ {ok,[{priority,4}]} = inet:getopts(Sock,[priority]),
+ {ok,[{priority,4}]} = inet:getopts(Sock2,[priority]),
+ {ok,[{priority,4}]} = inet:getopts(Sock3,[priority]),
+ gen_tcp:close(Sock),
+ gen_tcp:close(Sock2),
+ gen_tcp:close(Sock3),
ok.
test_prio_accept2() ->
Tos1 = 4 bsl 5,
Tos2 = 3 bsl 5,
- ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
- {reuseaddr,true},{priority,4},
- {tos,Tos1}]),
- ?line {ok,Port} = inet:port(Sock),
- ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
- {active,false},
- {reuseaddr,true},
- {priority,4},
- {tos,Tos2}]),
- ?line {ok,Sock3}=gen_tcp:accept(Sock),
- ?line {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]),
- ?line {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
- ?line {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]),
- ?line gen_tcp:close(Sock),
- ?line gen_tcp:close(Sock2),
- ?line gen_tcp:close(Sock3),
+ {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
+ {reuseaddr,true},{priority,4},
+ {tos,Tos1}]),
+ {ok,Port} = inet:port(Sock),
+ {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
+ {active,false},
+ {reuseaddr,true},
+ {priority,4},
+ {tos,Tos2}]),
+ {ok,Sock3}=gen_tcp:accept(Sock),
+ {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]),
+ {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
+ {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]),
+ gen_tcp:close(Sock),
+ gen_tcp:close(Sock2),
+ gen_tcp:close(Sock3),
ok.
test_prio_accept3() ->
Tos1 = 4 bsl 5,
Tos2 = 3 bsl 5,
- ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
- {reuseaddr,true},
- {tos,Tos1}]),
- ?line {ok,Port} = inet:port(Sock),
- ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
- {active,false},
- {reuseaddr,true},
- {tos,Tos2}]),
- ?line {ok,Sock3}=gen_tcp:accept(Sock),
- ?line {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]),
- ?line {ok,[{priority,0},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
- ?line {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]),
- ?line gen_tcp:close(Sock),
- ?line gen_tcp:close(Sock2),
- ?line gen_tcp:close(Sock3),
+ {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
+ {reuseaddr,true},
+ {tos,Tos1}]),
+ {ok,Port} = inet:port(Sock),
+ {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
+ {active,false},
+ {reuseaddr,true},
+ {tos,Tos2}]),
+ {ok,Sock3}=gen_tcp:accept(Sock),
+ {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]),
+ {ok,[{priority,0},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
+ {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]),
+ gen_tcp:close(Sock),
+ gen_tcp:close(Sock2),
+ gen_tcp:close(Sock3),
ok.
test_prio_accept_async() ->
Tos1 = 4 bsl 5,
Tos2 = 3 bsl 5,
Ref = make_ref(),
- ?line spawn(?MODULE,priority_server,[{self(),Ref}]),
- ?line Port = receive
- {Ref,P} -> P
- after 5000 -> ?t:fail({error,"helper process timeout"})
- end,
- ?line receive
- after 3000 -> ok
- end,
- ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
- {active,false},
- {reuseaddr,true},
- {priority,4},
- {tos,Tos2}]),
- ?line receive
- {Ref,{ok,[{priority,4},{tos,Tos1}]}} ->
- ok ;
- {Ref,Error} ->
- ?t:fail({missmatch,Error})
- after 5000 -> ?t:fail({error,"helper process timeout"})
- end,
- ?line receive
- {Ref,{ok,[{priority,4},{tos,Tos1}]}} ->
- ok ;
- {Ref,Error2} ->
- ?t:fail({missmatch,Error2})
- after 5000 -> ?t:fail({error,"helper process timeout"})
- end,
-
- ?line {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
- ?line catch gen_tcp:close(Sock2),
+ spawn(?MODULE,priority_server,[{self(),Ref}]),
+ Port = receive
+ {Ref,P} -> P
+ after 5000 -> ?t:fail({error,"helper process timeout"})
+ end,
+ receive
+ after 3000 -> ok
+ end,
+ {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0},
+ {active,false},
+ {reuseaddr,true},
+ {priority,4},
+ {tos,Tos2}]),
+ receive
+ {Ref,{ok,[{priority,4},{tos,Tos1}]}} ->
+ ok;
+ {Ref,Error} ->
+ ?t:fail({missmatch,Error})
+ after 5000 -> ?t:fail({error,"helper process timeout"})
+ end,
+ receive
+ {Ref,{ok,[{priority,4},{tos,Tos1}]}} ->
+ ok;
+ {Ref,Error2} ->
+ ?t:fail({missmatch,Error2})
+ after 5000 -> ?t:fail({error,"helper process timeout"})
+ end,
+
+ {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
+ catch gen_tcp:close(Sock2),
ok.
priority_server({Parent,Ref}) ->
Tos1 = 4 bsl 5,
- ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
- {reuseaddr,true},{priority,4},
- {tos,Tos1}]),
- ?line {ok,Port} = inet:port(Sock),
+ {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false},
+ {reuseaddr,true},{priority,4},
+ {tos,Tos1}]),
+ {ok,Port} = inet:port(Sock),
Parent ! {Ref,Port},
- ?line {ok,Sock3}=gen_tcp:accept(Sock),
+ {ok,Sock3}=gen_tcp:accept(Sock),
Parent ! {Ref, inet:getopts(Sock,[priority,tos])},
Parent ! {Ref, inet:getopts(Sock3,[priority,tos])},
ok.
test_prio_fail() ->
- ?line {ok,L} = gen_tcp:listen(0, [{active,false}]),
- ?line {error,_} = inet:setopts(L,[{priority,1000}]),
+ {ok,L} = gen_tcp:listen(0, [{active,false}]),
+ {error,_} = inet:setopts(L,[{priority,1000}]),
% This error could only happen in linux kernels earlier than 2.6.24.4
% Privilege check is now disabled and IP_TOS can never fail (only silently
% be masked).
-% ?line {error,_} = inet:setopts(L,[{tos,6 bsl 5}]),
- ?line gen_tcp:close(L),
+% {error,_} = inet:setopts(L,[{tos,6 bsl 5}]),
+ gen_tcp:close(L),
ok.
test_prio_udp() ->
Tos = 3 bsl 5,
- ?line {ok,S} = gen_udp:open(0,[{active,false},binary,{tos, Tos},
- {priority,3}]),
- ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(S,[priority,tos]),
- ?line gen_udp:close(S),
+ {ok,S} = gen_udp:open(0,[{active,false},binary,{tos, Tos},
+ {priority,3}]),
+ {ok,[{priority,3},{tos,Tos}]} = inet:getopts(S,[priority,tos]),
+ gen_udp:close(S),
ok.
so_priority(doc) ->
@@ -1623,9 +1609,9 @@ so_priority(doc) ->
so_priority(suite) ->
[];
so_priority(Config) when is_list(Config) ->
- ?line {ok,L} = gen_tcp:listen(0, [{active,false}]),
- ?line ok = inet:setopts(L,[{priority,1}]),
- ?line case inet:getopts(L,[priority]) of
+ {ok,L} = gen_tcp:listen(0, [{active,false}]),
+ ok = inet:setopts(L,[{priority,1}]),
+ case inet:getopts(L,[priority]) of
{ok,[{priority,1}]} ->
gen_tcp:close(L),
test_prio_put_get(),
@@ -1641,7 +1627,7 @@ so_priority(Config) when is_list(Config) ->
{unix,linux} ->
case os:version() of
{X,Y,_} when (X > 2) or ((X =:= 2) and (Y >= 4)) ->
- ?line ?t:fail({error,
+ ?t:fail({error,
"so_priority should work on this "
"OS, but does not"});
_ ->
@@ -1657,18 +1643,19 @@ so_priority(Config) when is_list(Config) ->
millis() ->
erlang:monotonic_time(milli_seconds).
-collect_accepts(Tmo) ->
+collect_accepts(0,_) -> [];
+collect_accepts(N,Tmo) ->
A = millis(),
receive
{accepted,P,Msg} ->
- [{P,Msg}] ++ collect_accepts(Tmo-(millis() - A))
+ [{P,Msg}] ++ collect_accepts(N-1,Tmo-(millis() - A))
after Tmo ->
[]
end.
--define(EXPECT_ACCEPTS(Pattern,Timeout),
+-define(EXPECT_ACCEPTS(Pattern,N,Timeout),
(fun() ->
- case collect_accepts(Timeout) of
+ case collect_accepts(if N =:= infinity -> -1; true -> N end,Timeout) of
Pattern ->
ok;
Other ->
@@ -1704,20 +1691,20 @@ primitive_accept(suite) ->
primitive_accept(doc) ->
["Test singular accept"];
primitive_accept(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line {ok,PortNo}=inet:port(LS),
- ?line Parent = self(),
- ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
- ?line P = spawn(F),
- ?line gen_tcp:connect("localhost",PortNo,[]),
- ?line receive
- {accepted,P,{ok,P0}} when is_port(P0) ->
- ok;
- {accepted,P,Other0} ->
- {error,Other0}
- after 500 ->
- {error,timeout}
- end.
+ {ok,LS}=gen_tcp:listen(0,[]),
+ {ok,PortNo}=inet:port(LS),
+ Parent = self(),
+ F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
+ P = spawn(F),
+ gen_tcp:connect("localhost",PortNo,[]),
+ receive
+ {accepted,P,{ok,P0}} when is_port(P0) ->
+ ok;
+ {accepted,P,Other0} ->
+ {error,Other0}
+ after 500 ->
+ {error,timeout}
+ end.
multi_accept_close_listen(suite) ->
@@ -1725,111 +1712,109 @@ multi_accept_close_listen(suite) ->
multi_accept_close_listen(doc) ->
["Closing listen socket when multi-accepting"];
multi_accept_close_listen(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
- ?line spawn(F),
- ?line spawn(F),
- ?line spawn(F),
- ?line spawn(F),
- ?line gen_tcp:close(LS),
- ?line ?EXPECT_ACCEPTS([{_,{error,closed}},{_,{error,closed}},
- {_,{error,closed}},{_,{error,closed}}], 500).
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
+ spawn(F),
+ spawn(F),
+ spawn(F),
+ spawn(F),
+ gen_tcp:close(LS),
+ ?EXPECT_ACCEPTS([{_,{error,closed}},{_,{error,closed}},
+ {_,{error,closed}},{_,{error,closed}}],4,500).
accept_timeout(suite) ->
[];
accept_timeout(doc) ->
["Single accept with timeout"];
accept_timeout(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS,1000)} end,
- ?line P = spawn(F),
- ?line ?EXPECT_ACCEPTS([{P,{error,timeout}}],2000).
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS,1000)} end,
+ P = spawn(F),
+ ?EXPECT_ACCEPTS([{P,{error,timeout}}],1,2000).
accept_timeouts_in_order(suite) ->
[];
accept_timeouts_in_order(doc) ->
["Check that multi-accept timeouts happen in the correct order"];
accept_timeouts_in_order(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line P1 = spawn(mktmofun(1000,Parent,LS)),
- ?line P2 = spawn(mktmofun(1200,Parent,LS)),
- ?line P3 = spawn(mktmofun(1300,Parent,LS)),
- ?line P4 = spawn(mktmofun(1400,Parent,LS)),
- ?line ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}},
- {P3,{error,timeout}},{P4,{error,timeout}}], 2000).
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ P1 = spawn(mktmofun(1000,Parent,LS)),
+ P2 = spawn(mktmofun(1200,Parent,LS)),
+ P3 = spawn(mktmofun(1300,Parent,LS)),
+ P4 = spawn(mktmofun(1400,Parent,LS)),
+ ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}},
+ {P3,{error,timeout}},{P4,{error,timeout}}],infinity,2000).
accept_timeouts_in_order2(suite) ->
[];
accept_timeouts_in_order2(doc) ->
["Check that multi-accept timeouts happen in the correct order (more)"];
accept_timeouts_in_order2(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line P1 = spawn(mktmofun(1400,Parent,LS)),
- ?line P2 = spawn(mktmofun(1300,Parent,LS)),
- ?line P3 = spawn(mktmofun(1200,Parent,LS)),
- ?line P4 = spawn(mktmofun(1000,Parent,LS)),
- ?line ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P3,{error,timeout}},
- {P2,{error,timeout}},{P1,{error,timeout}}], 2000).
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ P1 = spawn(mktmofun(1400,Parent,LS)),
+ P2 = spawn(mktmofun(1300,Parent,LS)),
+ P3 = spawn(mktmofun(1200,Parent,LS)),
+ P4 = spawn(mktmofun(1000,Parent,LS)),
+ ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P3,{error,timeout}},
+ {P2,{error,timeout}},{P1,{error,timeout}}],infinity,2000).
accept_timeouts_in_order3(suite) ->
[];
accept_timeouts_in_order3(doc) ->
["Check that multi-accept timeouts happen in the correct order (even more)"];
accept_timeouts_in_order3(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line P1 = spawn(mktmofun(1200,Parent,LS)),
- ?line P2 = spawn(mktmofun(1400,Parent,LS)),
- ?line P3 = spawn(mktmofun(1300,Parent,LS)),
- ?line P4 = spawn(mktmofun(1000,Parent,LS)),
- ?line ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}},
- {P3,{error,timeout}},{P2,{error,timeout}}], 2000).
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ P1 = spawn(mktmofun(1200,Parent,LS)),
+ P2 = spawn(mktmofun(1400,Parent,LS)),
+ P3 = spawn(mktmofun(1300,Parent,LS)),
+ P4 = spawn(mktmofun(1000,Parent,LS)),
+ ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}},
+ {P3,{error,timeout}},{P2,{error,timeout}}],infinity,2000).
accept_timeouts_mixed(suite) ->
[];
accept_timeouts_mixed(doc) ->
["Check that multi-accept timeouts behave correctly when mixed with successful timeouts"];
accept_timeouts_mixed(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line {ok,PortNo}=inet:port(LS),
- ?line P1 = spawn(mktmofun(1000,Parent,LS)),
- ?line wait_until_accepting(P1,500),
- ?line P2 = spawn(mktmofun(2000,Parent,LS)),
- ?line wait_until_accepting(P2,500),
- ?line P3 = spawn(mktmofun(3000,Parent,LS)),
- ?line wait_until_accepting(P3,500),
- ?line P4 = spawn(mktmofun(4000,Parent,LS)),
- ?line wait_until_accepting(P4,500),
- ?line ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}}],1500),
- ?line {ok,_}=gen_tcp:connect("localhost",PortNo,[]),
- ?line ok = ?EXPECT_ACCEPTS([{P2,{ok,Port0}}] when is_port(Port0),100),
- ?line ok = ?EXPECT_ACCEPTS([{P3,{error,timeout}}],2000),
- ?line gen_tcp:connect("localhost",PortNo,[]),
- ?line ?EXPECT_ACCEPTS([{P4,{ok,Port1}}] when is_port(Port1),100).
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ {ok,PortNo}=inet:port(LS),
+ P1 = spawn(mktmofun(1000,Parent,LS)),
+ wait_until_accepting(P1,500),
+ P2 = spawn(mktmofun(2000,Parent,LS)),
+ wait_until_accepting(P2,500),
+ P3 = spawn(mktmofun(3000,Parent,LS)),
+ wait_until_accepting(P3,500),
+ P4 = spawn(mktmofun(4000,Parent,LS)),
+ wait_until_accepting(P4,500),
+ ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}}],infinity,1500),
+ {ok,_}=gen_tcp:connect("localhost",PortNo,[]),
+ ok = ?EXPECT_ACCEPTS([{P2,{ok,Port0}}] when is_port(Port0),infinity,100),
+ ok = ?EXPECT_ACCEPTS([{P3,{error,timeout}}],infinity,2000),
+ gen_tcp:connect("localhost",PortNo,[]),
+ ?EXPECT_ACCEPTS([{P4,{ok,Port1}}] when is_port(Port1),infinity,100).
killing_acceptor(suite) ->
[];
killing_acceptor(doc) ->
["Check that single acceptor behaves as expected when killed"];
killing_acceptor(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Pid = spawn(fun() -> erlang:display({accepted,self(),gen_tcp:accept(LS)}) end),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L1} = prim_inet:getstatus(LS),
- ?line true = lists:member(accepting, L1),
- ?line exit(Pid,kill),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L2} = prim_inet:getstatus(LS),
- ?line false = lists:member(accepting, L2),
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Pid = spawn(fun() -> erlang:display({accepted,self(),gen_tcp:accept(LS)}) end),
+ receive after 100 -> ok
+ end,
+ {ok,L1} = prim_inet:getstatus(LS),
+ true = lists:member(accepting, L1),
+ exit(Pid,kill),
+ receive after 100 -> ok
+ end,
+ {ok,L2} = prim_inet:getstatus(LS),
+ false = lists:member(accepting, L2),
ok.
killing_multi_acceptors(suite) ->
@@ -1837,26 +1822,24 @@ killing_multi_acceptors(suite) ->
killing_multi_acceptors(doc) ->
["Check that multi acceptors behaves as expected when killed"];
killing_multi_acceptors(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
- ?line F2 = mktmofun(1000,Parent,LS),
- ?line Pid = spawn(F),
- ?line Pid2 = spawn(F2),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L1} = prim_inet:getstatus(LS),
- ?line true = lists:member(accepting, L1),
- ?line exit(Pid,kill),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L2} = prim_inet:getstatus(LS),
- ?line true = lists:member(accepting, L2),
- ?line ok = ?EXPECT_ACCEPTS([{Pid2,{error,timeout}}],1000),
- ?line {ok,L3} = prim_inet:getstatus(LS),
- ?line false = lists:member(accepting, L3),
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
+ F2 = mktmofun(1000,Parent,LS),
+ Pid = spawn(F),
+ Pid2 = spawn(F2),
+ receive after 100 -> ok
+ end,
+ {ok,L1} = prim_inet:getstatus(LS),
+ true = lists:member(accepting, L1),
+ exit(Pid,kill),
+ receive after 100 -> ok
+ end,
+ {ok,L2} = prim_inet:getstatus(LS),
+ true = lists:member(accepting, L2),
+ ok = ?EXPECT_ACCEPTS([{Pid2,{error,timeout}}],1,1000),
+ {ok,L3} = prim_inet:getstatus(LS),
+ false = lists:member(accepting, L3),
ok.
killing_multi_acceptors2(suite) ->
@@ -1864,40 +1847,36 @@ killing_multi_acceptors2(suite) ->
killing_multi_acceptors2(doc) ->
["Check that multi acceptors behaves as expected when killed (more)"];
killing_multi_acceptors2(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line {ok,PortNo}=inet:port(LS),
- ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
- ?line F2 = mktmofun(1000,Parent,LS),
- ?line Pid = spawn(F),
- ?line Pid2 = spawn(F),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L1} = prim_inet:getstatus(LS),
- ?line true = lists:member(accepting, L1),
- ?line exit(Pid,kill),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L2} = prim_inet:getstatus(LS),
- ?line true = lists:member(accepting, L2),
- ?line exit(Pid2,kill),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L3} = prim_inet:getstatus(LS),
- ?line false = lists:member(accepting, L3),
- ?line Pid3 = spawn(F2),
- ?line receive after 100 ->
- ok
- end,
- ?line {ok,L4} = prim_inet:getstatus(LS),
- ?line true = lists:member(accepting, L4),
- ?line gen_tcp:connect("localhost",PortNo,[]),
- ?line ok = ?EXPECT_ACCEPTS([{Pid3,{ok,Port}}] when is_port(Port),100),
- ?line {ok,L5} = prim_inet:getstatus(LS),
- ?line false = lists:member(accepting, L5),
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ {ok,PortNo}=inet:port(LS),
+ F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
+ F2 = mktmofun(1000,Parent,LS),
+ Pid = spawn(F),
+ Pid2 = spawn(F),
+ receive after 100 -> ok
+ end,
+ {ok,L1} = prim_inet:getstatus(LS),
+ true = lists:member(accepting, L1),
+ exit(Pid,kill),
+ receive after 100 -> ok
+ end,
+ {ok,L2} = prim_inet:getstatus(LS),
+ true = lists:member(accepting, L2),
+ exit(Pid2,kill),
+ receive after 100 -> ok
+ end,
+ {ok,L3} = prim_inet:getstatus(LS),
+ false = lists:member(accepting, L3),
+ Pid3 = spawn(F2),
+ receive after 100 -> ok
+ end,
+ {ok,L4} = prim_inet:getstatus(LS),
+ true = lists:member(accepting, L4),
+ gen_tcp:connect("localhost",PortNo,[]),
+ ok = ?EXPECT_ACCEPTS([{Pid3,{ok,Port}}] when is_port(Port),1,100),
+ {ok,L5} = prim_inet:getstatus(LS),
+ false = lists:member(accepting, L5),
ok.
several_accepts_in_one_go(suite) ->
@@ -1906,33 +1885,19 @@ several_accepts_in_one_go(doc) ->
["checks that multi-accept works when more than one accept can be "
"done at once (wb test of inet_driver)"];
several_accepts_in_one_go(Config) when is_list(Config) ->
- ?line {ok,LS}=gen_tcp:listen(0,[]),
- ?line Parent = self(),
- ?line {ok,PortNo}=inet:port(LS),
- ?line F1 = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
- ?line F2 = fun() -> Parent ! {connected,self(),gen_tcp:connect("localhost",PortNo,[])} end,
- ?line spawn(F1),
- ?line spawn(F1),
- ?line spawn(F1),
- ?line spawn(F1),
- ?line spawn(F1),
- ?line spawn(F1),
- ?line spawn(F1),
- ?line spawn(F1),
- ?line ok = ?EXPECT_ACCEPTS([],500),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line spawn(F2),
- ?line ok = ?EXPECT_ACCEPTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],15000),
- ?line ok = ?EXPECT_CONNECTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],1000),
+ {ok,LS}=gen_tcp:listen(0,[]),
+ Parent = self(),
+ {ok,PortNo}=inet:port(LS),
+ F1 = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end,
+ F2 = fun() -> Parent ! {connected,self(),gen_tcp:connect("localhost",PortNo,[])} end,
+ Ns = lists:seq(1,8),
+ _ = [spawn(F1) || _ <- Ns],
+ ok = ?EXPECT_ACCEPTS([],1,500), % wait for tmo
+ _ = [spawn(F2) || _ <- Ns],
+ ok = ?EXPECT_ACCEPTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],8,15000),
+ ok = ?EXPECT_CONNECTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],1000),
ok.
-
flush(Msgs) ->
erlang:yield(),
receive Msg -> flush([Msg|Msgs])
@@ -1967,13 +1932,13 @@ accept_system_limit(doc) ->
["Check that accept returns {error, system_limit} "
"(and not {error, enfile}) when running out of ports"];
accept_system_limit(Config) when is_list(Config) ->
- ?line {ok, LS} = gen_tcp:listen(0, []),
- ?line {ok, TcpPort} = inet:port(LS),
+ {ok, LS} = gen_tcp:listen(0, []),
+ {ok, TcpPort} = inet:port(LS),
Me = self(),
- ?line Connector = spawn_link(fun () -> connector(TcpPort, Me) end),
+ Connector = spawn_link(fun () -> connector(TcpPort, Me) end),
receive {Connector, sync} -> Connector ! {self(), continue} end,
- ?line ok = acceptor(LS, false, []),
- ?line Connector ! stop,
+ ok = acceptor(LS, false, []),
+ Connector ! stop,
ok.
acceptor(LS, GotSL, A) ->
@@ -2020,49 +1985,49 @@ active_once_closed(doc) ->
["Check that active once and tcp_close messages behave as expected"];
active_once_closed(Config) when is_list(Config) ->
(fun() ->
- ?line {Loop,A} = setup_closed_ao(),
- ?line Loop({{error,closed},{error,econnaborted}},
+ {Loop,A} = setup_closed_ao(),
+ Loop({{error,closed},{error,econnaborted}},
fun() -> gen_tcp:send(A,"Hello") end),
- ?line ok = inet:setopts(A,[{active,once}]),
- ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
- ?line {error,einval} = inet:setopts(A,[{active,once}]),
- ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end
+ ok = inet:setopts(A,[{active,once}]),
+ ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
+ {error,einval} = inet:setopts(A,[{active,once}]),
+ ok = receive {tcp_closed, A} -> error after 1000 -> ok end
end)(),
(fun() ->
- ?line {Loop,A} = setup_closed_ao(),
- ?line Loop({{error,closed},{error,econnaborted}},
+ {Loop,A} = setup_closed_ao(),
+ Loop({{error,closed},{error,econnaborted}},
fun() -> gen_tcp:send(A,"Hello") end),
- ?line ok = inet:setopts(A,[{active,true}]),
- ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
- ?line {error,einval} = inet:setopts(A,[{active,true}]),
- ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end
+ ok = inet:setopts(A,[{active,true}]),
+ ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
+ {error,einval} = inet:setopts(A,[{active,true}]),
+ ok = receive {tcp_closed, A} -> error after 1000 -> ok end
end)(),
(fun() ->
- ?line {Loop,A} = setup_closed_ao(),
- ?line Loop({{error,closed},{error,econnaborted}},
+ {Loop,A} = setup_closed_ao(),
+ Loop({{error,closed},{error,econnaborted}},
fun() -> gen_tcp:send(A,"Hello") end),
- ?line ok = inet:setopts(A,[{active,true}]),
- ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
- ?line {error,einval} = inet:setopts(A,[{active,once}]),
- ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end
+ ok = inet:setopts(A,[{active,true}]),
+ ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
+ {error,einval} = inet:setopts(A,[{active,once}]),
+ ok = receive {tcp_closed, A} -> error after 1000 -> ok end
end)(),
(fun() ->
- ?line {Loop,A} = setup_closed_ao(),
- ?line Loop({{error,closed},{error,econnaborted}},
+ {Loop,A} = setup_closed_ao(),
+ Loop({{error,closed},{error,econnaborted}},
fun() -> gen_tcp:send(A,"Hello") end),
- ?line ok = inet:setopts(A,[{active,once}]),
- ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
- ?line {error,einval} = inet:setopts(A,[{active,true}]),
- ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end
+ ok = inet:setopts(A,[{active,once}]),
+ ok = receive {tcp_closed, A} -> ok after 1000 -> error end,
+ {error,einval} = inet:setopts(A,[{active,true}]),
+ ok = receive {tcp_closed, A} -> error after 1000 -> ok end
end)(),
(fun() ->
- ?line {Loop,A} = setup_closed_ao(),
- ?line Loop({{error,closed},{error,econnaborted}},
+ {Loop,A} = setup_closed_ao(),
+ Loop({{error,closed},{error,econnaborted}},
fun() -> gen_tcp:send(A,"Hello") end),
- ?line ok = inet:setopts(A,[{active,false}]),
- ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end,
- ?line ok = inet:setopts(A,[{active,once}]),
- ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end
+ ok = inet:setopts(A,[{active,false}]),
+ ok = receive {tcp_closed, A} -> error after 1000 -> ok end,
+ ok = inet:setopts(A,[{active,once}]),
+ ok = receive {tcp_closed, A} -> ok after 1000 -> error end
end)().
send_timeout(suite) ->
@@ -2071,10 +2036,10 @@ send_timeout(doc) ->
["Test the send_timeout socket option"];
send_timeout(Config) when is_list(Config) ->
%% Basic
- BasicFun =
+ BasicFun =
fun(AutoClose) ->
- ?line {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose),
- ?line {error,timeout} =
+ {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose),
+ {error,timeout} =
Loop(fun() ->
Res = gen_tcp:send(A,<<1:10000>>),
%%erlang:display(Res),
@@ -2082,57 +2047,55 @@ send_timeout(Config) when is_list(Config) ->
end),
%% Check that the socket is not busy/closed...
Error = after_send_timeout(AutoClose),
- ?line {error,Error} = gen_tcp:send(A,<<"Hej">>),
- ?line test_server:stop_node(RNode)
+ {error,Error} = gen_tcp:send(A,<<"Hej">>),
+ test_server:stop_node(RNode)
end,
BasicFun(false),
BasicFun(true),
%% Check timeout length
- ?line Self = self(),
- ?line Pid =
- spawn(fun() ->
- {Loop,A,RNode} = setup_timeout_sink(1000, true),
- {error,timeout} =
- Loop(fun() ->
- Res = gen_tcp:send(A,<<1:10000>>),
- %%erlang:display(Res),
- Self ! Res,
- Res
- end),
- test_server:stop_node(RNode)
- end),
- ?line Diff = get_max_diff(),
- ?line io:format("Max time for send: ~p~n",[Diff]),
- ?line true = (Diff > 500) and (Diff < 1500),
+ Self = self(),
+ Pid = spawn(fun() ->
+ {Loop,A,RNode} = setup_timeout_sink(1000, true),
+ {error,timeout} = Loop(fun() ->
+ Res = gen_tcp:send(A,<<1:10000>>),
+ %%erlang:display(Res),
+ Self ! Res,
+ Res
+ end),
+ test_server:stop_node(RNode)
+ end),
+ Diff = get_max_diff(),
+ io:format("Max time for send: ~p~n",[Diff]),
+ true = (Diff > 500) and (Diff < 1500),
%% Let test_server slave die...
- ?line Mon = erlang:monitor(process, Pid),
- ?line receive {'DOWN',Mon,process,Pid,_} -> ok end,
+ Mon = erlang:monitor(process, Pid),
+ receive {'DOWN',Mon,process,Pid,_} -> ok end,
%% Check that parallell writers do not hang forever
- ParaFun =
+ ParaFun =
fun(AutoClose) ->
- ?line {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose),
+ {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose),
SenderFun = fun() ->
- {error,Error} =
+ {error,Error} =
Loop(fun() ->
gen_tcp:send(A, <<1:10000>>)
end),
Self ! {error,Error}
end,
- ?line spawn_link(SenderFun),
- ?line spawn_link(SenderFun),
- ?line receive
- {error,timeout} -> ok
- after 10000 ->
- ?line exit(timeout)
- end,
+ spawn_link(SenderFun),
+ spawn_link(SenderFun),
+ receive
+ {error,timeout} -> ok
+ after 10000 ->
+ exit(timeout)
+ end,
NextErr = after_send_timeout(AutoClose),
- ?line receive
- {error,NextErr} -> ok
- after 10000 ->
- ?line exit(timeout)
- end,
- ?line {error,NextErr} = gen_tcp:send(A,<<"Hej">>),
- ?line test_server:stop_node(RNode)
+ receive
+ {error,NextErr} -> ok
+ after 10000 ->
+ exit(timeout)
+ end,
+ {error,NextErr} = gen_tcp:send(A,<<"Hej">>),
+ test_server:stop_node(RNode)
end,
ParaFun(false),
ParaFun(true),
@@ -2166,25 +2129,25 @@ send_timeout_active(Config) when is_list(Config) ->
%% Basic
BasicFun =
fun(AutoClose) ->
- ?line {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose),
+ {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose),
inet:setopts(A, [{active, once}]),
- ?line Mad = spawn_link(RNode,fun() -> mad_sender(C) end),
- ?line {error,timeout} =
- Loop(fun() ->
- receive
- {tcp, _Sock, _Data} ->
- inet:setopts(A, [{active, once}]),
- Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
- %erlang:display(Res),
- Res;
- Err ->
- io:format("sock closed: ~p~n", [Err]),
- Err
- end
- end),
- unlink(Mad),
+ Mad = spawn_link(RNode,fun() -> mad_sender(C) end),
+ {error,timeout} =
+ Loop(fun() ->
+ receive
+ {tcp, _Sock, _Data} ->
+ inet:setopts(A, [{active, once}]),
+ Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
+ %erlang:display(Res),
+ Res;
+ Err ->
+ io:format("sock closed: ~p~n", [Err]),
+ Err
+ end
+ end),
+ unlink(Mad),
exit(Mad,kill),
- ?line test_server:stop_node(RNode)
+ test_server:stop_node(RNode)
end,
BasicFun(false),
flush(),
@@ -2227,29 +2190,29 @@ get_max_diff(Max) ->
Max
end
after 10000 ->
- exit(timeout)
+ exit(timeout)
end.
setup_closed_ao() ->
Dir = filename:dirname(code:which(?MODULE)),
{ok,R} = test_server:start_node(test_default_options_slave,slave,
- [{args,"-pa " ++ Dir}]),
+ [{args,"-pa " ++ Dir}]),
Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
{ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}]),
- Fun = fun(F) ->
- receive
- {From,X} when is_function(X) ->
- From ! {self(),X()}, F(F);
- die -> ok
- end
- end,
+ Fun = fun(F) ->
+ receive
+ {From,X} when is_function(X) ->
+ From ! {self(),X()}, F(F);
+ die -> ok
+ end
+ end,
Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
{ok, Port} = inet:port(L),
- Remote = fun(Fu) ->
- Pid ! {self(), Fu},
- receive {Pid,X} -> X
- end
- end,
+ Remote = fun(Fu) ->
+ Pid ! {self(), Fu},
+ receive {Pid,X} -> X
+ end
+ end,
{ok, C} = Remote(fun() ->
gen_tcp:connect(Host,Port,
[{active,false},{packet,2}])
@@ -2257,106 +2220,106 @@ setup_closed_ao() ->
{ok,A} = gen_tcp:accept(L),
gen_tcp:send(A,"Hello"),
{ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
- ok = Remote(fun() -> gen_tcp:close(C) end),
- Loop2 = fun(_,_,_,0) ->
+ ok = Remote(fun() -> gen_tcp:close(C) end),
+ Loop2 = fun(_,_,_,0) ->
{failure, timeout};
- (L2,{MA,MB},F2,N) ->
- case F2() of
- MA -> MA;
- MB -> MB;
- Other -> io:format("~p~n",[Other]),
- receive after 1000 -> ok end,
- L2(L2,{MA,MB},F2,N-1)
- end
+ (L2,{MA,MB},F2,N) ->
+ case F2() of
+ MA -> MA;
+ MB -> MB;
+ Other -> io:format("~p~n",[Other]),
+ receive after 1000 -> ok end,
+ L2(L2,{MA,MB},F2,N-1)
+ end
end,
Loop = fun(Match2,F3) -> Loop2(Loop2,Match2,F3,10) end,
test_server:stop_node(R),
{Loop,A}.
setup_timeout_sink(Timeout, AutoClose) ->
- ?line Dir = filename:dirname(code:which(?MODULE)),
- ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
- [{args,"-pa " ++ Dir}]),
- ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
- ?line {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ [{args,"-pa " ++ Dir}]),
+ Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
{send_timeout,Timeout},
{send_timeout_close,AutoClose}]),
- ?line Fun = fun(F) ->
- receive
- {From,X} when is_function(X) ->
- From ! {self(),X()}, F(F);
- die -> ok
- end
- end,
- ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
- ?line {ok, Port} = inet:port(L),
- ?line Remote = fun(Fu) ->
- Pid ! {self(), Fu},
- receive {Pid,X} -> X
- end
+ Fun = fun(F) ->
+ receive
+ {From,X} when is_function(X) ->
+ From ! {self(),X()}, F(F);
+ die -> ok
+ end
+ end,
+ Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ {ok, Port} = inet:port(L),
+ Remote = fun(Fu) ->
+ Pid ! {self(), Fu},
+ receive {Pid,X} -> X
+ end
end,
- ?line {ok, C} = Remote(fun() ->
+ {ok, C} = Remote(fun() ->
gen_tcp:connect(Host,Port,
- [{active,false},{packet,2}])
+ [{active,false},{packet,2}])
end),
- ?line {ok,A} = gen_tcp:accept(L),
- ?line gen_tcp:send(A,"Hello"),
- ?line {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
- ?line Loop2 = fun(_,_,0) ->
- {failure, timeout};
- (L2,F2,N) ->
+ {ok,A} = gen_tcp:accept(L),
+ gen_tcp:send(A,"Hello"),
+ {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ Loop2 = fun(_,_,0) ->
+ {failure, timeout};
+ (L2,F2,N) ->
Ret = F2(),
io:format("~p~n",[Ret]),
case Ret of
- ok -> receive after 1 -> ok end,
+ ok -> receive after 1 -> ok end,
L2(L2,F2,N-1);
Other -> Other
- end
+ end
end,
- ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
{Loop,A,R}.
setup_active_timeout_sink(Timeout, AutoClose) ->
- ?line Dir = filename:dirname(code:which(?MODULE)),
- ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
- [{args,"-pa " ++ Dir}]),
- ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
- ?line {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true},
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ [{args,"-pa " ++ Dir}]),
+ Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true},
{send_timeout,Timeout},
{send_timeout_close,AutoClose}]),
- ?line Fun = fun(F) ->
- receive
- {From,X} when is_function(X) ->
- From ! {self(),X()}, F(F);
- die -> ok
- end
- end,
- ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
- ?line {ok, Port} = inet:port(L),
- ?line Remote = fun(Fu) ->
- Pid ! {self(), Fu},
- receive {Pid,X} -> X
- end
+ Fun = fun(F) ->
+ receive
+ {From,X} when is_function(X) ->
+ From ! {self(),X()}, F(F);
+ die -> ok
+ end
+ end,
+ Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ {ok, Port} = inet:port(L),
+ Remote = fun(Fu) ->
+ Pid ! {self(), Fu},
+ receive {Pid,X} -> X
+ end
end,
- ?line {ok, C} = Remote(fun() ->
+ {ok, C} = Remote(fun() ->
gen_tcp:connect(Host,Port,
- [{active,false}])
+ [{active,false}])
end),
- ?line {ok,A} = gen_tcp:accept(L),
- ?line gen_tcp:send(A,"Hello"),
- ?line {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end),
- ?line Loop2 = fun(_,_,0) ->
- {failure, timeout};
- (L2,F2,N) ->
+ {ok,A} = gen_tcp:accept(L),
+ gen_tcp:send(A,"Hello"),
+ {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ Loop2 = fun(_,_,0) ->
+ {failure, timeout};
+ (L2,F2,N) ->
Ret = F2(),
io:format("~p~n",[Ret]),
case Ret of
- ok -> receive after 1 -> ok end,
+ ok -> receive after 1 -> ok end,
L2(L2,F2,N-1);
Other -> Other
- end
+ end
end,
- ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
{Loop,A,R,C}.
@@ -2374,22 +2337,22 @@ otp_7731(doc) ->
"Leaking message from inet_drv {inet_reply,P,ok} "
"when a socket sending resumes working after a send_timeout";
otp_7731(Config) when is_list(Config) ->
- ?line ServerPid = spawn_link(?MODULE, otp_7731_server, [self()]),
- ?line receive {ServerPid, ready, PortNum} -> ok end,
+ ServerPid = spawn_link(?MODULE, otp_7731_server, [self()]),
+ receive {ServerPid, ready, PortNum} -> ok end,
- ?line {ok, Socket} = gen_tcp:connect("localhost", PortNum,
- [binary, {active, false}, {packet, raw},
- {send_timeout, 1000}]),
+ {ok, Socket} = gen_tcp:connect("localhost", PortNum,
+ [binary, {active, false}, {packet, raw},
+ {send_timeout, 1000}]),
otp_7731_send(Socket),
io:format("Sending complete...\n",[]),
ServerPid ! {self(), recv},
- receive {ServerPid, ok} -> ok end,
-
+ receive {ServerPid, ok} -> ok end,
+
io:format("Client waiting for leaking messages...\n",[]),
%% Now make sure inet_drv does not leak any internal messages.
receive Msg ->
- ?line test_server:fail({unexpected, Msg})
+ test_server:fail({unexpected, Msg})
after 1000 ->
ok
end,
@@ -2399,15 +2362,15 @@ otp_7731(Config) when is_list(Config) ->
otp_7731_send(Socket) ->
Bin = <<1:10000>>,
io:format("Client sending ~p bytes...\n",[size(Bin)]),
- ?line case gen_tcp:send(Socket, Bin) of
- ok -> otp_7731_send(Socket);
- {error,timeout} -> ok
- end.
+ case gen_tcp:send(Socket, Bin) of
+ ok -> otp_7731_send(Socket);
+ {error,timeout} -> ok
+ end.
otp_7731_server(ClientPid) ->
- ?line {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw},
- {active, false}]),
- ?line {ok, {_, PortNum}} = inet:sockname(LSocket),
+ {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw},
+ {active, false}]),
+ {ok, {_, PortNum}} = inet:sockname(LSocket),
io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]),
ClientPid ! {self(), ready, PortNum},
@@ -2429,7 +2392,7 @@ otp_7731_server(ClientPid) ->
otp_7731_recv(Socket) ->
- ?line case gen_tcp:recv(Socket, 0, 1000) of
+ case gen_tcp:recv(Socket, 0, 1000) of
{ok, Bin} ->
io:format("Server received ~p bytes\n",[size(Bin)]),
otp_7731_recv(Socket);
@@ -2448,21 +2411,21 @@ zombie_sockets(Config) when is_list(Config) ->
register(zombie_collector,self()),
Calls = 10,
Server = spawn_link(?MODULE, zombie_server,[self(), Calls]),
- ?line {Server, ready, PortNum} = receive Msg -> Msg end,
+ {Server, ready, PortNum} = receive Msg -> Msg end,
io:format("Ports before = ~p\n",[lists:sort(erlang:ports())]),
zombie_client_loop(Calls, PortNum),
Ports = lists:sort(zombie_collector(Calls,[])),
Server ! terminate,
io:format("Collected ports = ~p\n",[Ports]),
- ?line [] = zombies_alive(Ports, 10),
+ [] = zombies_alive(Ports, 10),
timer:sleep(1000),
ok.
zombie_client_loop(0, _) -> ok;
zombie_client_loop(N, PortNum) when is_integer(PortNum) ->
- ?line {ok, Socket} = gen_tcp:connect("localhost", PortNum,
- [binary, {active, false}, {packet, raw}]),
- ?line gen_tcp:close(Socket), % to make server recv fail
+ {ok, Socket} = gen_tcp:connect("localhost", PortNum,
+ [binary, {active, false}, {packet, raw}]),
+ gen_tcp:close(Socket), % to make server recv fail
zombie_client_loop(N-1, PortNum).
@@ -2491,19 +2454,19 @@ zombies_alive(Ports, WaitSec) ->
end.
zombie_server(Pid, Calls) ->
- ?line {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw},
- {active, false}, {backlog, Calls}]),
- ?line {ok, {_, PortNum}} = inet:sockname(LSocket),
+ {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw},
+ {active, false}, {backlog, Calls}]),
+ {ok, {_, PortNum}} = inet:sockname(LSocket),
io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]),
BigBin = list_to_binary(lists:duplicate(100*1024, 77)),
Pid ! {self(), ready, PortNum},
zombie_accept_loop(LSocket, BigBin, Calls),
- ?line terminate = receive Msg -> Msg end.
+ terminate = receive Msg -> Msg end.
zombie_accept_loop(_, _, 0) ->
ok;
zombie_accept_loop(Socket, BigBin, Calls) ->
- ?line case gen_tcp:accept(Socket) of
+ case gen_tcp:accept(Socket) of
{ok, NewSocket} ->
spawn_link(fun() -> zombie_serve_client(NewSocket, BigBin) end),
zombie_accept_loop(Socket, BigBin, Calls-1);
@@ -2513,29 +2476,27 @@ zombie_accept_loop(Socket, BigBin, Calls) ->
zombie_serve_client(Socket, Bin) ->
%%io:format("Got connection on ~p\n",[Socket]),
- ?line gen_tcp:send(Socket, Bin),
+ gen_tcp:send(Socket, Bin),
%%io:format("Sent data, waiting for reply on ~p\n",[Socket]),
- ?line case gen_tcp:recv(Socket, 4) of
+ case gen_tcp:recv(Socket, 4) of
{error,closed} -> ok;
{error,econnaborted} -> ok % may be returned on Windows
end,
%%io:format("Closing ~p\n",[Socket]),
- ?line gen_tcp:close(Socket),
+ gen_tcp:close(Socket),
zombie_collector ! {closed, Socket}.
-
-
otp_7816(suite) -> [];
otp_7816(doc) ->
"Hanging send on windows when sending iolist with more than 16 binaries.";
otp_7816(Config) when is_list(Config) ->
Client = self(),
- ?line Server = spawn_link(fun()-> otp_7816_server(Client) end),
- ?line receive {Server, ready, PortNum} -> ok end,
+ Server = spawn_link(fun()-> otp_7816_server(Client) end),
+ receive {Server, ready, PortNum} -> ok end,
- ?line {ok, Socket} = gen_tcp:connect("localhost", PortNum,
- [binary, {active, false}, {packet, 4},
- {send_timeout, 10}]),
+ {ok, Socket} = gen_tcp:connect("localhost", PortNum,
+ [binary, {active, false}, {packet, 4},
+ {send_timeout, 10}]),
%% We use the undocumented feature that sending can be resumed after
%% a send_timeout without any data loss if the peer starts to receive data.
%% Unless of course the 7816-bug is in affect, in which case the write event
@@ -2545,9 +2506,9 @@ otp_7816(Config) when is_list(Config) ->
io:format("Sending complete...\n",[]),
- ?line ok = gen_tcp:close(Socket),
+ ok = gen_tcp:close(Socket),
Server ! {self(), closed},
- ?line {Server, closed} = receive M -> M end.
+ {Server, closed} = receive M -> M end.
otp_7816_send(Socket, BinNr, BinSize, Server) ->
@@ -2555,7 +2516,7 @@ otp_7816_send(Socket, BinNr, BinSize, Server) ->
SentBytes = otp_7816_send_data(Socket, Data, 0) * BinNr * BinSize,
io:format("Client sent ~p bytes...\n",[SentBytes]),
Server ! {self(),recv,SentBytes},
- ?line {Server, ok} = receive M -> M end.
+ {Server, ok} = receive M -> M end.
@@ -2570,15 +2531,15 @@ otp_7816_send_data(Socket, Data, Loops) ->
otp_7816_server(Client) ->
- ?line {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, 4},
+ {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, 4},
{active, false}]),
- ?line {ok, {_, PortNum}} = inet:sockname(LSocket),
+ {ok, {_, PortNum}} = inet:sockname(LSocket),
io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]),
Client ! {self(), ready, PortNum},
- ?line {ok, CSocket} = gen_tcp:accept(LSocket),
+ {ok, CSocket} = gen_tcp:accept(LSocket),
io:format("Server got connection...\n",[]),
- ?line gen_tcp:close(LSocket),
+ gen_tcp:close(LSocket),
otp_7816_server_loop(CSocket),
@@ -2592,13 +2553,13 @@ otp_7816_server_loop(CSocket) ->
{Client, recv, RecvBytes} ->
io:format("Server start receiving...\n",[]),
- ?line ok = otp_7816_recv(CSocket, RecvBytes),
+ ok = otp_7816_recv(CSocket, RecvBytes),
Client ! {self(), ok},
otp_7816_server_loop(CSocket);
{Client, closed} ->
- ?line {error, closed} = gen_tcp:recv(CSocket, 0, 1000),
+ {error, closed} = gen_tcp:recv(CSocket, 0, 1000),
Client ! {self(), closed}
end.
@@ -2607,7 +2568,7 @@ otp_7816_recv(_, 0) ->
io:format("Server got all.\n",[]),
ok;
otp_7816_recv(CSocket, BytesLeft) ->
- ?line case gen_tcp:recv(CSocket, 0, 1000) of
+ case gen_tcp:recv(CSocket, 0, 1000) of
{ok, Bin} when byte_size(Bin) =< BytesLeft ->
io:format("Server received ~p of ~p bytes.\n",[size(Bin), BytesLeft]),
otp_7816_recv(CSocket, BytesLeft - byte_size(Bin));
@@ -2619,8 +2580,8 @@ otp_7816_recv(CSocket, BytesLeft) ->
otp_8102(doc) -> ["Receive a packet with a faulty packet header"];
otp_8102(suite) -> [];
otp_8102(Config) when is_list(Config) ->
- ?line {ok, LSocket} = gen_tcp:listen(0, []),
- ?line {ok, {_, PortNum}} = inet:sockname(LSocket),
+ {ok, LSocket} = gen_tcp:listen(0, []),
+ {ok, {_, PortNum}} = inet:sockname(LSocket),
io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]),
[otp_8102_do(LSocket, PortNum, otp_8102_packet(Type,Size))
@@ -2640,18 +2601,18 @@ otp_8102_packet({cdr,little}, Size) ->
otp_8102_do(LSocket, PortNum, {Bin,PType}) ->
io:format("Connect with packet option ~p ...\n",[PType]),
- ?line {ok, RSocket} = gen_tcp:connect("localhost", PortNum, [binary,
+ {ok, RSocket} = gen_tcp:connect("localhost", PortNum, [binary,
{packet,PType},
{active,true}]),
- ?line {ok, SSocket} = gen_tcp:accept(LSocket),
+ {ok, SSocket} = gen_tcp:accept(LSocket),
io:format("Got connection, sending ~p...\n",[Bin]),
- ?line ok = gen_tcp:send(SSocket, Bin),
+ ok = gen_tcp:send(SSocket, Bin),
io:format("Sending complete...\n",[]),
- ?line {tcp_error,RSocket,emsgsize} = receive M -> M end,
+ {tcp_error,RSocket,emsgsize} = receive M -> M end,
io:format("Got error msg, ok.\n",[]),
gen_tcp:close(SSocket),
@@ -2660,61 +2621,61 @@ otp_8102_do(LSocket, PortNum, {Bin,PType}) ->
otp_9389(doc) -> ["Verify packet_size handles long HTTP header lines"];
otp_9389(suite) -> [];
otp_9389(Config) when is_list(Config) ->
- ?line {ok, LS} = gen_tcp:listen(0, [{active,false}]),
- ?line {ok, {_, PortNum}} = inet:sockname(LS),
+ {ok, LS} = gen_tcp:listen(0, [{active,false}]),
+ {ok, {_, PortNum}} = inet:sockname(LS),
io:format("Listening on ~w with port number ~p\n", [LS, PortNum]),
OrigLinkHdr = "/" ++ string:chars($S, 8192),
_Server = spawn_link(
fun() ->
- ?line {ok, S} = gen_tcp:accept(LS),
- ?line ok = inet:setopts(S, [{packet_size, 16384}]),
- ?line ok = otp_9389_loop(S, OrigLinkHdr),
- ?line ok = gen_tcp:close(S)
+ {ok, S} = gen_tcp:accept(LS),
+ ok = inet:setopts(S, [{packet_size, 16384}]),
+ ok = otp_9389_loop(S, OrigLinkHdr),
+ ok = gen_tcp:close(S)
end),
- ?line {ok, S} = gen_tcp:connect("localhost", PortNum,
+ {ok, S} = gen_tcp:connect("localhost", PortNum,
[binary, {active, false}]),
Req = "GET / HTTP/1.1\r\n"
++ "Host: localhost\r\n"
++ "Link: " ++ OrigLinkHdr ++ "\r\n\r\n",
- ?line ok = gen_tcp:send(S, Req),
- ?line ok = inet:setopts(S, [{packet, http}]),
- ?line {ok, {http_response, {1,1}, 200, "OK"}} = gen_tcp:recv(S, 0),
- ?line ok = inet:setopts(S, [{packet, httph}, {packet_size, 16384}]),
- ?line {ok, {http_header, _, 'Content-Length', _, "0"}} = gen_tcp:recv(S, 0),
- ?line {ok, {http_header, _, "Link", _, LinkHdr}} = gen_tcp:recv(S, 0),
- ?line true = (LinkHdr == OrigLinkHdr),
+ ok = gen_tcp:send(S, Req),
+ ok = inet:setopts(S, [{packet, http}]),
+ {ok, {http_response, {1,1}, 200, "OK"}} = gen_tcp:recv(S, 0),
+ ok = inet:setopts(S, [{packet, httph}, {packet_size, 16384}]),
+ {ok, {http_header, _, 'Content-Length', _, "0"}} = gen_tcp:recv(S, 0),
+ {ok, {http_header, _, "Link", _, LinkHdr}} = gen_tcp:recv(S, 0),
+ true = (LinkHdr == OrigLinkHdr),
ok = gen_tcp:close(S),
ok = gen_tcp:close(LS),
ok.
otp_9389_loop(S, OrigLinkHdr) ->
- ?line ok = inet:setopts(S, [{active,once},{packet,http}]),
+ ok = inet:setopts(S, [{active,once},{packet,http}]),
receive
{http, S, {http_request, 'GET', _, _}} ->
- ?line ok = otp_9389_loop(S, OrigLinkHdr, undefined)
+ ok = otp_9389_loop(S, OrigLinkHdr, undefined)
after
3000 ->
- ?line error({timeout,request_line})
+ error({timeout,request_line})
end.
otp_9389_loop(S, OrigLinkHdr, ok) ->
- ?line Resp = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n" ++
+ Resp = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n" ++
"Link: " ++ OrigLinkHdr ++ "\r\n\r\n",
- ?line ok = gen_tcp:send(S, Resp);
+ ok = gen_tcp:send(S, Resp);
otp_9389_loop(S, OrigLinkHdr, State) ->
- ?line ok = inet:setopts(S, [{active,once}, {packet,httph}]),
+ ok = inet:setopts(S, [{active,once}, {packet,httph}]),
receive
{http, S, http_eoh} ->
- ?line otp_9389_loop(S, OrigLinkHdr, ok);
+ otp_9389_loop(S, OrigLinkHdr, ok);
{http, S, {http_header, _, "Link", _, LinkHdr}} ->
- ?line LinkHdr = OrigLinkHdr,
- ?line otp_9389_loop(S, OrigLinkHdr, State);
+ LinkHdr = OrigLinkHdr,
+ otp_9389_loop(S, OrigLinkHdr, State);
{http, S, {http_header, _, _Hdr, _, _Val}} ->
- ?line otp_9389_loop(S, OrigLinkHdr, State);
+ otp_9389_loop(S, OrigLinkHdr, State);
{http, S, {http_error, Err}} ->
- ?line error({error, Err})
+ error({error, Err})
after
3000 ->
- ?line error({timeout,header})
+ error({timeout,header})
end.
wrapping_oct(doc) ->
@@ -2725,7 +2686,7 @@ wrapping_oct(Config) when is_list(Config) ->
{ok,Sock} = gen_tcp:listen(0,[{active,false},{mode,binary}]),
{ok,Port} = inet:port(Sock),
spawn_link(?MODULE,oct_acceptor,[Sock]),
- Res = oct_datapump(Port,16#1FFFFFFFF),
+ Res = oct_datapump(Port,16#10000FFFF),
gen_tcp:close(Sock),
ok = Res,
ok.
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 44a32fc1ec..c77de9316f 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -569,8 +569,11 @@ parse_address(Config) when is_list(Config) ->
"::-1",
"::g",
"f:f11::10100:2",
+ "f:f11::01100:2",
"::17000",
+ "::01700",
"10000::",
+ "01000::",
"::8:7:6:5:4:3:2:1",
"8:7:6:5:4:3:2:1::",
"8:7:6:5:4::3:2:1",
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e1d447a465..c912da0091 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 3.2
+KERNEL_VSN = 4.0
diff --git a/lib/megaco/doc/src/megaco.xml b/lib/megaco/doc/src/megaco.xml
index dff1c3afc6..0a8dfe8a13 100644
--- a/lib/megaco/doc/src/megaco.xml
+++ b/lib/megaco/doc/src/megaco.xml
@@ -336,7 +336,7 @@ megaco_incr_timer() = #megaco_incr_timer{}
<tag><c><![CDATA[request_keep_alive_timeout]]></c></tag>
<item>
<p>Specifies the timeout time for the request-keep-alive timer. </p>
- <p>This timer is started when the <em>first</em> reply to an asynchroneous
+ <p>This timer is started when the <em>first</em> reply to an asynchronous
request (issued using the
<seealso marker="megaco#cast">megaco:cast/3</seealso> function)
arrives. As long as this timer is running, replies will
@@ -837,7 +837,7 @@ megaco_incr_timer() = #megaco_incr_timer{}
<tag><c><![CDATA[request_keep_alive_timeout]]></c></tag>
<item>
<p>Specifies the timeout time for the request-keep-alive timer. </p>
- <p>This timer is started when the <em>first</em> reply to an asynchroneous
+ <p>This timer is started when the <em>first</em> reply to an asynchronous
request (issued using the
<seealso marker="megaco#cast">megaco:cast/3</seealso> function)
arrives. As long as this timer is running, replies will
diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src
index 3720b1109e..573b1857f6 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -114,7 +114,7 @@
{env, []},
{mod, {megaco_sup, []}},
{runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14","kernel-3.0",
- "et-1.5","erts-6.0","debugger-4.0",
+ "et-1.5","erts-7.0","debugger-4.0",
"asn1-3.0"]}
]}.
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 92504e8e87..1c55a92b55 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -2,7 +2,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
@@ -183,11 +183,15 @@
%% |
%% v
%% 3.17.3
+%% |
+%% v
+%% 3.18
%%
%%
{"%VSN%",
[
+ {"3.17.3", [{restart_application,megaco}]},
{"3.17.2", []},
{"3.17.1", [{restart_application,megaco}]},
{"3.17.0.3", [{restart_application,megaco}]},
@@ -202,6 +206,7 @@
}
],
[
+ {"3.17.3", [{restart_application,megaco}]},
{"3.17.2", []},
{"3.17.1", [{restart_application,megaco}]},
{"3.17.0.3", [{restart_application,megaco}]},
diff --git a/lib/megaco/src/engine/megaco_trans_sender.erl b/lib/megaco/src/engine/megaco_trans_sender.erl
index 710fef405a..e07f404289 100644
--- a/lib/megaco/src/engine/megaco_trans_sender.erl
+++ b/lib/megaco/src/engine/megaco_trans_sender.erl
@@ -672,8 +672,7 @@ to(To, Start) ->
%% Time in milli seconds
t() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
+ erlang:monotonic_time(milli_seconds).
warning_msg(F, A) ->
?megaco_warning("Transaction sender: " ++ F, A).
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index b23339e408..79dd495c4b 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.12.5
+MNESIA_VSN = 4.13
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index 10ed3bdfe5..7e7e32099b 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.0.4
+OBSERVER_VSN = 2.1
diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c
index 20bb9ce391..9e217db105 100644
--- a/lib/os_mon/c_src/cpu_sup.c
+++ b/lib/os_mon/c_src/cpu_sup.c
@@ -53,7 +53,6 @@
#endif
#if defined(__linux__)
-#include <string.h> /* strlen */
#define PROCSTAT "/proc/stat"
#define BUFFERSIZE (256)
@@ -73,6 +72,13 @@ typedef struct {
#endif
+#if defined(__FreeBSD__)
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#define CU_BSD_VALUES (6)
+#endif
+
+
#define FD_IN (0)
#define FD_OUT (1)
#define FD_ERR (2)
@@ -157,12 +163,16 @@ static int processors_online() {
}
#endif
+#if defined(__FreeBSD__)
+void getsysctl(const char *, void *, size_t);
+#endif
+
int main(int argc, char** argv) {
char cmd;
int rc;
int sz;
unsigned int *rv;
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
unsigned int no_of_cpus = 0;
#endif
@@ -175,7 +185,14 @@ int main(int argc, char** argv) {
#if defined(__linux__)
no_of_cpus = processors_online();
if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_VALUES))) == NULL) {
- error("cpu_cup: malloc error");
+ error("cpu_sup: malloc error");
+ }
+#endif
+
+#if defined(__FreeBSD__)
+ getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
+ if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_BSD_VALUES))) == NULL) {
+ error("cpu_sup: malloc error");
}
#endif
@@ -204,14 +221,14 @@ int main(int argc, char** argv) {
case AVG5: bsd_loadavg(1); break;
case AVG15: bsd_loadavg(2); break;
#endif
-#if defined(__sun__) || defined(__linux__)
+#if defined(__sun__) || defined(__linux__) || defined(__FreeBSD__)
case UTIL: util_measure(&rv,&sz); sendv(rv, sz); break;
#endif
case QUIT: free((void*)rv); return 0;
default: error("Bad command"); break;
}
}
- return 0; /* supress warnings */
+ return 0; /* suppress warnings */
}
/* ---------------------------- *
@@ -520,6 +537,71 @@ static void util_measure(unsigned int **result_vec, int *result_sz) {
#endif
/* ---------------------------- *
+ * FreeBSD stat functions *
+ * ---------------------------- */
+
+#if defined(__FreeBSD__)
+
+#define EXIT_WITH(msg) (rich_error(msg, __FILE__, __LINE__))
+#define RICH_BUFLEN (213) /* left in error(char*) */
+
+void rich_error(const char *reason, const char *file, const int line) {
+ char buf[RICH_BUFLEN];
+ snprintf(buf, RICH_BUFLEN, "%s (%s:%i)", reason, file, line);
+ error(buf);
+}
+#undef RICH_BUFLEN
+
+static void util_measure(unsigned int **result_vec, int *result_sz) {
+ int no_of_cpus;
+ size_t size_cpu_times;
+ unsigned long *cpu_times;
+ unsigned int *rv = NULL;
+ int i;
+
+ getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
+ /* Header constant CPUSTATES = #long values per cpu. */
+ size_cpu_times = sizeof(long) * CPUSTATES * no_of_cpus;
+ cpu_times = malloc(size_cpu_times);
+ if (!cpu_times) {
+ EXIT_WITH("badalloc");
+ }
+ getsysctl("kern.cp_times", cpu_times, size_cpu_times);
+
+ rv = *result_vec;
+ rv[0] = no_of_cpus;
+ rv[1] = CU_BSD_VALUES;
+ ++rv; /* first value is number of cpus */
+ ++rv; /* second value is number of entries */
+
+ for (i = 0; i < no_of_cpus; ++i) {
+ int offset = i * CPUSTATES;
+ rv[ 0] = CU_CPU_ID; rv[ 1] = i;
+ rv[ 2] = CU_USER; rv[ 3] = cpu_times[CP_USER + offset];
+ rv[ 4] = CU_NICE_USER; rv[ 5] = cpu_times[CP_NICE + offset];
+ rv[ 6] = CU_KERNEL; rv[ 7] = cpu_times[CP_SYS + offset];
+ rv[ 8] = CU_IDLE; rv[ 9] = cpu_times[CP_IDLE + offset];
+ rv[10] = CU_HARD_IRQ; rv[11] = cpu_times[CP_INTR + offset];
+ rv += CU_BSD_VALUES*2;
+ }
+
+ *result_sz = 2 + 2*CU_BSD_VALUES * no_of_cpus;
+}
+
+void getsysctl(const char *name, void *ptr, size_t len)
+{
+ size_t gotlen = len;
+ if (sysctlbyname(name, ptr, &gotlen, NULL, 0) != 0) {
+ EXIT_WITH("sysctlbyname failed");
+ }
+ if (gotlen != len) {
+ EXIT_WITH("sysctlbyname: unexpected length");
+ }
+}
+#endif
+
+
+/* ---------------------------- *
* Generic functions *
* ---------------------------- */
@@ -581,5 +663,3 @@ static void error(char* err_msg) {
;
exit(-1);
}
-
-
diff --git a/lib/os_mon/doc/src/cpu_sup.xml b/lib/os_mon/doc/src/cpu_sup.xml
index 59da876208..4a8f5bffa0 100644
--- a/lib/os_mon/doc/src/cpu_sup.xml
+++ b/lib/os_mon/doc/src/cpu_sup.xml
@@ -34,7 +34,7 @@
and CPU utilization. It is part of the OS_Mon application, see
<seealso marker="os_mon_app">os_mon(6)</seealso>. Available for Unix,
although CPU utilization values (<c>util/0,1</c>) are only
- available for Solaris and Linux.</p>
+ available for Solaris, Linux and FreeBSD.</p>
<p>The load values are proportional to how long time a runnable
Unix process has to spend in the run queue before it is scheduled.
Accordingly, higher values mean more system load. The returned
diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl
index 0c26956c57..d8cfd845bc 100644
--- a/lib/os_mon/src/cpu_sup.erl
+++ b/lib/os_mon/src/cpu_sup.erl
@@ -121,7 +121,7 @@ util(Args) when is_list (Args) ->
util(_) ->
erlang:error(badarg).
--spec util() -> float().
+-spec util() -> float() | {'error', any()}.
util() ->
case util([]) of
@@ -160,7 +160,8 @@ handle_call(?quit, _From, State) ->
handle_call({?util, D, PC}, {Client, _Tag},
#state{os_type = {unix, Flavor}} = State)
when Flavor == sunos;
- Flavor == linux ->
+ Flavor == linux;
+ Flavor == freebsd ->
case measurement_server_call(State#state.server, {?util, D, PC, Client}) of
{error, Reason} ->
{ reply,
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
index 9f58e043db..7da819379c 100644
--- a/lib/os_mon/test/cpu_sup_SUITE.erl
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -64,6 +64,8 @@ all() ->
[load_api, util_api, util_values, port, unavailable];
{unix, linux} ->
[load_api, util_api, util_values, port, unavailable];
+ {unix, freebsd} ->
+ [load_api, util_api, util_values, port, unavailable];
{unix, _OSname} -> [load_api];
_OS -> [unavailable]
end.
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index 833e855e0e..7f2667e40a 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.3.1
+OS_MON_VSN = 2.4
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index dd9cc2991c..b99b3bb713 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.0.12
+PARSETOOLS_VSN = 2.1
diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml
index 9c3c80bd13..572e550061 100644
--- a/lib/sasl/doc/src/sasl_app.xml
+++ b/lib/sasl/doc/src/sasl_app.xml
@@ -92,6 +92,13 @@
<item>Installs <c>sasl_report_file_h</c> in the error logger.
This makes all reports go to the file <c>FileName</c>.
<c>FileName</c> is a string.</item>
+ <tag><c>{file,FileName,Modes}</c></tag>
+ <item>Same as <c>{file,FileName}</c> except that the <c>Modes</c>
+ allows to specify the modes used for opening the <c>FileName</c>
+ given to the <seealso marker="kernel:file#open/2">file:open/2</seealso>
+ call. When not specified, the <c>Modes</c> defaults to <c>[write]</c>.
+ Use <c>[append]</c> for having the <c>FileName</c> open in append mode.
+ <c>FileName</c> is a string.</item>
<tag><c>false</c></tag>
<item>
<p>No SASL error logger handler is installed.</p>
diff --git a/lib/sasl/src/sasl.erl b/lib/sasl/src/sasl.erl
index fdea6da13e..4a220f0511 100644
--- a/lib/sasl/src/sasl.erl
+++ b/lib/sasl/src/sasl.erl
@@ -55,7 +55,9 @@ get_sasl_error_logger() ->
case application:get_env(sasl, sasl_error_logger) of
{ok, false} -> undefined;
{ok, tty} -> tty;
- {ok, {file, File}} when is_list(File) -> {file, File};
+ {ok, {file, File}} when is_list(File) -> {file, File, [write]};
+ {ok, {file, File, Modes}} when is_list(File), is_list(Modes) ->
+ {file, File, Modes};
{ok, Bad} -> exit({bad_config, {sasl, {sasl_error_logger, Bad}}});
_ -> undefined
end.
@@ -125,9 +127,9 @@ delete_sasl_error_logger(Type) ->
error_logger:delete_report_handler(mod(Type)).
mod(tty) -> sasl_report_tty_h;
-mod({file, _File}) -> sasl_report_file_h.
+mod({file, _File, _Modes}) -> sasl_report_file_h.
-args({file, File}, Type) -> {File, type(Type)};
+args({file, File, Modes}, Type) -> {File, Modes, type(Type)};
args(_, Type) -> type(Type).
type(error) -> error;
diff --git a/lib/sasl/src/sasl_report_file_h.erl b/lib/sasl/src/sasl_report_file_h.erl
index f42b4b5ff2..a5bd0ac055 100644
--- a/lib/sasl/src/sasl_report_file_h.erl
+++ b/lib/sasl/src/sasl_report_file_h.erl
@@ -28,9 +28,9 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
-init({File, Type}) ->
+init({File, Modes, Type}) when is_list(Modes) ->
process_flag(trap_exit, true),
- case file:open(File, [write]) of
+ case file:open(File, Modes) of
{ok,Fd} ->
{ok, {Fd, File, Type}};
What ->
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index d7b99d506e..d9ab9e551c 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -26,10 +26,11 @@
%% Test cases must be exported.
-export([app_test/1,
appup_test/1,
- log_mf_h_env/1]).
+ log_mf_h_env/1,
+ log_file/1]).
all() ->
- [log_mf_h_env, app_test, appup_test].
+ [log_mf_h_env, log_file, app_test, appup_test].
groups() ->
[].
@@ -151,10 +152,9 @@ check_appup([],_,_) ->
log_mf_h_env(Config) ->
PrivDir = ?config(priv_dir,Config),
LogDir = filename:join(PrivDir,sasl_SUITE_log_dir),
- ok = file:make_dir(LogDir),
+ ok = filelib:ensure_dir(LogDir),
application:stop(sasl),
- SaslEnv = application:get_all_env(sasl),
- lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv),
+ clear_env(sasl),
ok = application:set_env(sasl,error_logger_mf_dir,LogDir),
match_error(missing_config,application:start(sasl)),
@@ -178,6 +178,23 @@ log_mf_h_env(Config) ->
ok = application:set_env(sasl,error_logger_mf_dir,LogDir),
ok = application:start(sasl).
+log_file(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ LogDir = filename:join(PrivDir,sasl_SUITE_log_dir),
+ ok = filelib:ensure_dir(LogDir),
+ File = filename:join(LogDir, "file.log"),
+ application:stop(sasl),
+ clear_env(sasl),
+
+ ok = application:set_env(sasl,sasl_error_logger,{file, File}, [{persistent, true}]),
+ ok = application:start(sasl),
+ application:stop(sasl),
+ ok = application:set_env(sasl,sasl_error_logger,{file, File, [append]}, [{persistent, true}]),
+ ok = application:start(sasl),
+ application:stop(sasl),
+ ok = application:set_env(sasl,sasl_error_logger, tty, [{persistent, false}]),
+ ok = application:start(sasl).
+
%%-----------------------------------------------------------------
%% Internal
@@ -185,3 +202,7 @@ match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) ->
ok;
match_error(Expected,Actual) ->
?t:fail({unexpected_return,Expected,Actual}).
+
+clear_env(App) ->
+ [application:unset_env(App,Opt) || {Opt,_} <- application:get_all_env(App)],
+ ok.
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index 4259a2d76c..8d1a043410 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 2.4.1
+SASL_VSN = 2.4.2
diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml
index 86f0981988..e36908a5b9 100644
--- a/lib/snmp/doc/src/snmp_app.xml
+++ b/lib/snmp/doc/src/snmp_app.xml
@@ -587,7 +587,7 @@
<marker id="manager_server_timeout"></marker>
<tag><c><![CDATA[server_timeout() = integer() <optional>]]></c></tag>
<item>
- <p>Asynchroneous request cleanup time. For every requests,
+ <p>Asynchronous request cleanup time. For every requests,
some info is stored internally, in order to be able to
deliver the reply (when it arrives) to the proper destination.
If the reply arrives, this info will be deleted. But if
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml
index 0ec8bb91cf..d1ee6545dd 100644
--- a/lib/snmp/doc/src/snmp_config.xml
+++ b/lib/snmp/doc/src/snmp_config.xml
@@ -616,7 +616,7 @@ in so far as it will be converted to the new format if found.
<marker id="manager_server_timeout"></marker>
<tag><c><![CDATA[server_timeout() = integer() <optional>]]></c></tag>
<item>
- <p>Asynchroneous request cleanup time. For every requests,
+ <p>Asynchronous request cleanup time. For every requests,
some info is stored internally, in order to be able to
deliver the reply (when it arrives) to the proper destination.
If the reply arrives, this info will be deleted. But if
diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index 081163b368..a21ff863be 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -28,6 +28,8 @@
%% {update, snmpa_local_db, soft, soft_purge, soft_purge, []}
%% {add_module, snmpm_net_if_mt}
[
+ {"5.1.2", [ % Only runtime dependencies change
+ ]},
{"5.1.1", [{restart_application, snmp}]},
{"5.1", [ % Only compiler changes
]},
@@ -47,6 +49,8 @@
%% {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
[
+ {"5.1.2", [ % Only runtime dependencies change
+ ]},
{"5.1.1", [{restart_application, snmp}]},
{"5.1", [ % Only compiler changes
]},
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 8976322c4e..96e3d55b46 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -520,7 +520,7 @@ sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
-%% --- asynchroneous get-request ---
+%% --- asynchronous get-request ---
%%
%% The reply will be delivered to the user
%% through a call to handle_pdu/5
@@ -588,7 +588,7 @@ sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% --- asynchroneous get_next-request ---
+%% --- asynchronous get_next-request ---
%%
async_get_next2(UserId, TargetName, Oids) ->
@@ -654,7 +654,7 @@ sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% --- asynchroneous set-request ---
+%% --- asynchronous set-request ---
%%
async_set2(UserId, TargetName, VarsAndVals) ->
@@ -746,7 +746,7 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout,
%% </BACKWARD-COMPAT>
-%% --- asynchroneous get-bulk ---
+%% --- asynchronous get-bulk ---
%%
async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) ->
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 579a3ae4a8..c77ee1e77a 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,33 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Gracefully terminate if sockets is unexpectedly closed.</p>
+ <p>
+ Own Id: OTP-12782</p>
+ </item>
+ <item>
+ <p>
+ Made Codenomicon Defensics test suite pass: <list>
+ <item>limit number of algorithms in kexinit
+ message</item> <item>check 'e' and 'f' parameters in
+ kexdh</item> <item>implement 'keyboard-interactive' user
+ authentication on server side</item> <item> return plain
+ text message to bad version exchange message</item>
+ </list></p>
+ <p>
+ Own Id: OTP-12784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index df13442fc6..cf58806aa8 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -35,13 +35,15 @@
<section>
<title>SSH</title>
-
+ <marker id="supported"/>
<list type="bulleted">
<item>For application dependencies see <seealso marker="SSH_app"> ssh(6)</seealso> </item>
<item>Supported SSH version is 2.0.</item>
+ <item>Supported public key algorithms: ssh-rsa and ssh-dss.</item>
<item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1.</item>
<item>Supported encryption algorithms: aes128-ctr, aes128-cb and 3des-cbc.</item>
<item>Supported key exchange algorithms: diffie-hellman-group1-sha1.</item>
+ <item>Supported compression algorithms: none, zlib, [email protected],</item>
<item>Supports unicode filenames if the emulator and the underlaying OS support it.
See section DESCRIPTION in the
<seealso marker="kernel:file">file</seealso> manual page in <c>kernel</c>
@@ -79,6 +81,18 @@
<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>algs_list() =</c></tag>
+ <item><p><c>list( alg_entry() )</c></p></item>
+
+ <tag><c>alg_entry() =</c></tag>
+ <item><p><c>{kex, simple_algs()} | {public_key, simple_algs()} | {cipher, double_algs()} | {mac, double_algs()} | {compression, double_algs()}</c></p></item>
+
+ <tag><c>simple_algs() =</c></tag>
+ <item><p><c>list( atom() )</c></p></item>
+
+ <tag><c>double_algs() =</c></tag>
+ <item><p><c>[{client2serverlist,simple_algs()},{server2client,simple_algs()}] | simple_algs()</c></p></item>
</taglist>
</section>
@@ -160,19 +174,58 @@
and <c>password</c>. However, those optins are not always desirable
to use from a security point of view.</p>
</item>
+
<tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag>
<item>
+ <note>
+ <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c>
+ option is used. The equivalence of <c>{public_key_alg,'ssh-dss'}</c> is
+ <c>{preferred_algorithms, [{public_key,['ssh-dss','ssh-rsa']}]}</c>.</p>
+ </note>
<p>Sets the preferred public key algorithm to use for user
authentication. If the preferred algorithm fails,
the other algorithm is tried. The default is
to try <c><![CDATA['ssh-rsa']]></c> first.</p>
</item>
+
<tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag>
<item>
+ <note>
+ <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c>
+ option is used. The equivalence of <c>{pref_public_key_algs,['ssh-dss']}</c> is
+ <c>{preferred_algorithms, [{public_key,['ssh-dss']}]}</c>.</p>
+ </note>
<p>List of public key algorithms to try to use.
<c>'ssh-rsa'</c> and <c>'ssh-dss'</c> are available.
Overrides <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p>
</item>
+
+ <tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag>
+ <item>
+ <p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can
+ be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>.
+ </p>
+ <p>Here is an example of this option:</p>
+ <code>
+{preferred_algorithms,
+ [{public_key,['ssh-rsa','ssh-dss']},
+ {cipher,[{client2server,['aes128-ctr']},
+ {server2client,['aes128-cbc','3des-cbc']}]},
+ {mac,['hmac-sha2-256','hmac-sha1']},
+ {compression,[none,zlib]}
+}
+</code>
+ <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same
+algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values,
+kex is implicit but public_key is set explicitly.</p>
+
+ <warning>
+ <p>Changing the values can make a connection less secure. Do not change unless you
+ know exactly what you are doing. If you do not understand the values then you
+ are not supposed to change them.</p>
+ </warning>
+ </item>
+
<tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag>
<item>
<p>Sets a time-out on the transport layer
@@ -341,6 +394,33 @@
user. From a security perspective this option makes
the server very vulnerable.</p>
</item>
+
+ <tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag>
+ <item>
+ <p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can
+ be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>.
+ </p>
+ <p>Here is an example of this option:</p>
+ <code>
+{preferred_algorithms,
+ [{public_key,['ssh-rsa','ssh-dss']},
+ {cipher,[{client2server,['aes128-ctr']},
+ {server2client,['aes128-cbc','3des-cbc']}]},
+ {mac,['hmac-sha2-256','hmac-sha1']},
+ {compression,[none,zlib]}
+}
+</code>
+ <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same
+algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values,
+kex is implicit but public_key is set explicitly.</p>
+
+ <warning>
+ <p>Changing the values can make a connection less secure. Do not change unless you
+ know exactly what you are doing. If you do not understand the values then you
+ are not supposed to change them.</p>
+ </warning>
+ </item>
+
<tag><c><![CDATA[{pwdfun, fun(User::string(), password::string()) -> boolean()}]]></c></tag>
<item>
<p>Provides a function for password validation. This function is called
@@ -445,6 +525,26 @@
</desc>
</func>
+ <func>
+ <name>default_algorithms() -> algs_list()</name>
+ <fsummary>Get a list declaring the supported algorithms</fsummary>
+ <desc>
+ <p>Returns a key-value list, where the keys are the different types of algorithms and the values are the
+ algorithms themselves. An example:</p>
+ <code>
+20> ssh:default_algorithms().
+[{kex,['diffie-hellman-group1-sha1']},
+ {public_key,['ssh-rsa','ssh-dss']},
+ {cipher,[{client2server,['aes128-ctr','aes128-cbc','3des-cbc']},
+ {server2client,['aes128-ctr','aes128-cbc','3des-cbc']}]},
+ {mac,[{client2server,['hmac-sha2-256','hmac-sha1']},
+ {server2client,['hmac-sha2-256','hmac-sha1']}]},
+ {compression,[{client2server,[none,zlib]},
+ {server2client,[none,zlib]}]}]
+21>
+</code>
+ </desc>
+ </func>
<func>
<name>shell(Host) -> </name>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 71e7d77475..4a07473f74 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -28,6 +28,7 @@
-export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2,
channel_info/3,
daemon/1, daemon/2, daemon/3,
+ default_algorithms/0,
stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
shell/1, shell/2, shell/3]).
@@ -208,6 +209,11 @@ shell(Host, Port, Options) ->
end.
%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+default_algorithms() ->
+ ssh_transport:default_algorithms().
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
fix_idle_time(SshOptions) ->
@@ -259,7 +265,7 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
end.
handle_options(Opts) ->
- try handle_option(proplists:unfold(Opts), [], []) of
+ try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of
{Inet, Ssh} ->
{handle_ip(Inet), Ssh}
catch
@@ -267,6 +273,35 @@ handle_options(Opts) ->
Error
end.
+
+algs_compatibility(Os) ->
+ %% Take care of old options 'public_key_alg' and 'pref_public_key_algs'
+ comp_pk(proplists:get_value(preferred_algorithms,Os),
+ proplists:get_value(pref_public_key_algs,Os),
+ proplists:get_value(public_key_alg, Os),
+ [{K,V} || {K,V} <- Os,
+ K =/= public_key_alg,
+ K =/= pref_public_key_algs]
+ ).
+
+comp_pk(undefined, undefined, undefined, Os) -> Os;
+comp_pk( PrefAlgs, _, _, Os) when PrefAlgs =/= undefined -> Os;
+
+comp_pk(undefined, undefined, ssh_dsa, Os) -> comp_pk(undefined, undefined, 'ssh-dss', Os);
+comp_pk(undefined, undefined, ssh_rsa, Os) -> comp_pk(undefined, undefined, 'ssh-rsa', Os);
+comp_pk(undefined, undefined, PK, Os) ->
+ PKs = [PK | ssh_transport:supported_algorithms(public_key)--[PK]],
+ [{preferred_algorithms, [{public_key,PKs}] } | Os];
+
+comp_pk(undefined, PrefPKs, _, Os) when PrefPKs =/= undefined ->
+ PKs = [case PK of
+ ssh_dsa -> 'ssh-dss';
+ ssh_rsa -> 'ssh-rsa';
+ _ -> PK
+ end || PK <- PrefPKs],
+ [{preferred_algorithms, [{public_key,PKs}]} | Os].
+
+
handle_option([], SocketOptions, SshOptions) ->
{SocketOptions, SshOptions};
handle_option([{system_dir, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -279,8 +314,6 @@ handle_option([{silently_accept_hosts, _} = Opt | Rest], SocketOptions, SshOptio
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{public_key_alg, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{connect_timeout, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{user, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -297,10 +330,6 @@ handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
%%Backwards compatibility
handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option({user_interaction, Value}) | SshOptions]);
@@ -331,7 +360,9 @@ handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) ->
+handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
@@ -367,19 +398,8 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) -
Opt;
handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({public_key_alg, ssh_dsa}) ->
- {public_key_alg, 'ssh-dss'};
-handle_ssh_option({public_key_alg, ssh_rsa}) ->
- {public_key_alg, 'ssh-rsa'};
-handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' ->
- Opt;
-handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 ->
- case handle_pref_algs(Value, []) of
- {true, NewOpts} ->
- NewOpts;
- _ ->
- throw({error, {eoptions, Opt}})
- end;
+handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
+ handle_pref_algs(Opt);
handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
Opt;
handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 ->
@@ -411,6 +431,13 @@ handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
Opt;
handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
+handle_ssh_option({auth_method_kb_interactive_data, {Name,Instruction,Prompt,Echo}} = Opt) when is_list(Name),
+ is_list(Instruction),
+ is_list(Prompt),
+ is_boolean(Echo) ->
+ Opt;
+handle_ssh_option({auth_method_kb_interactive_data, F} = Opt) when is_function(F,3) ->
+ Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
@@ -465,23 +492,83 @@ handle_inet_option({reuseaddr, _} = Opt) ->
%% Option verified by inet
handle_inet_option(Opt) ->
Opt.
+
+
%% Check preferred algs
-handle_pref_algs([], Acc) ->
- {true, lists:reverse(Acc)};
-handle_pref_algs([H|T], Acc) ->
- case H of
- ssh_dsa ->
- handle_pref_algs(T, ['ssh-dss'| Acc]);
- ssh_rsa ->
- handle_pref_algs(T, ['ssh-rsa'| Acc]);
- 'ssh-dss' ->
- handle_pref_algs(T, ['ssh-dss'| Acc]);
- 'ssh-rsa' ->
- handle_pref_algs(T, ['ssh-rsa'| Acc]);
- _ ->
- false
+
+handle_pref_algs({preferred_algorithms,Algs}) ->
+ try alg_duplicates(Algs, [], []) of
+ [] ->
+ {preferred_algorithms,
+ [try ssh_transport:supported_algorithms(Key)
+ of
+ DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs)
+ catch
+ _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}},
+ "Bad preferred_algorithms key"}})
+ end || {Key,Vals} <- Algs]
+ };
+
+ Dups ->
+ throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}})
+ catch
+ _:_ ->
+ throw({error, {{eoptions, preferred_algorithms}, "Malformed"}})
end.
+alg_duplicates([{K,V}|KVs], Ks, Dups0) ->
+ Dups =
+ case lists:member(K,Ks) of
+ true ->
+ [K|Dups0];
+ false ->
+ Dups0
+ end,
+ case V--lists:usort(V) of
+ [] ->
+ alg_duplicates(KVs, [K|Ks], Dups);
+ Ds ->
+ alg_duplicates(KVs, [K|Ks], Dups++Ds)
+ end;
+alg_duplicates([], _Ks, Dups) ->
+ Dups.
+
+handle_pref_alg(Key,
+ Vs=[{client2server,C2Ss=[_|_]},{server2client,S2Cs=[_|_]}],
+ [{client2server,Sup_C2Ss},{server2client,Sup_S2Cs}]
+ ) ->
+ chk_alg_vs(Key, C2Ss, Sup_C2Ss),
+ chk_alg_vs(Key, S2Cs, Sup_S2Cs),
+ {Key, Vs};
+
+handle_pref_alg(Key,
+ Vs=[{server2client,[_|_]},{client2server,[_|_]}],
+ Sup=[{client2server,_},{server2client,_}]
+ ) ->
+ handle_pref_alg(Key, lists:reverse(Vs), Sup);
+
+handle_pref_alg(Key,
+ Vs=[V|_],
+ Sup=[{client2server,_},{server2client,_}]
+ ) when is_atom(V) ->
+ handle_pref_alg(Key, [{client2server,Vs},{server2client,Vs}], Sup);
+
+handle_pref_alg(Key,
+ Vs=[V|_],
+ Sup=[S|_]
+ ) when is_atom(V), is_atom(S) ->
+ chk_alg_vs(Key, Vs, Sup),
+ {Key, Vs};
+
+handle_pref_alg(Key, Vs, _) ->
+ throw({error, {{eoptions, {preferred_algorithms,[{Key,Vs}]}}, "Badly formed list"}}).
+
+chk_alg_vs(OptKey, Values, SupportedValues) ->
+ case (Values -- SupportedValues) of
+ [] -> Values;
+ Bad -> throw({error, {{eoptions, {OptKey,Bad}}, "Unsupported value(s) found"}})
+ end.
+
handle_ip(Inet) -> %% Default to ipv4
case lists:member(inet, Inet) of
true ->
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 45c4d52d7e..df9a97c8f8 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -30,7 +30,8 @@
-export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1,
service_request_msg/1, init_userauth_request_msg/1,
userauth_request_msg/1, handle_userauth_request/3,
- handle_userauth_info_request/3, handle_userauth_info_response/2
+ handle_userauth_info_request/3, handle_userauth_info_response/2,
+ default_public_key_algorithms/0
]).
%%--------------------------------------------------------------------
@@ -115,33 +116,16 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
service = "ssh-connection",
method = "none",
data = <<>>},
- case proplists:get_value(pref_public_key_algs, Opts, false) of
- false ->
- FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG),
- SecondAlg = other_alg(FirstAlg),
- Prefs = method_preference(FirstAlg, SecondAlg),
- ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
- userauth_preference = Prefs,
- userauth_methods = none,
- service = "ssh-connection"});
- Algs ->
- FirstAlg = lists:nth(1, Algs),
- case length(Algs) =:= 2 of
- true ->
- SecondAlg = other_alg(FirstAlg),
- Prefs = method_preference(FirstAlg, SecondAlg),
- ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
- userauth_preference = Prefs,
- userauth_methods = none,
- service = "ssh-connection"});
- _ ->
- Prefs = method_preference(FirstAlg),
- ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
- userauth_preference = Prefs,
- userauth_methods = none,
- service = "ssh-connection"})
- end
- end;
+
+
+ Algs = proplists:get_value(public_key,
+ proplists:get_value(preferred_algorithms, Opts, []),
+ default_public_key_algorithms()),
+ Prefs = method_preference(Algs),
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ userauth_preference = Prefs,
+ userauth_methods = none,
+ service = "ssh-connection"});
{error, no_user} ->
ErrStr = "Could not determine the users name",
throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_ILLEGAL_USER_NAME,
@@ -259,6 +243,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
+ method = "keyboard-interactive",
+ data = _},
+ _, #ssh{opts = Opts} = Ssh) ->
+ %% RFC4256
+ %% The data field contains:
+ %% - language tag (deprecated). If =/=[] SHOULD use it however. We skip
+ %% it for simplicity.
+ %% - submethods. "... the user can give a hint of which actual methods
+ %% he wants to use. ...". It's a "MAY use" so we skip
+ %% it. It also needs an understanding between the client
+ %% and the server.
+ %%
+ %% "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS,
+ %% SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message."
+ Default = {"SSH server",
+ "Enter password for \""++User++"\"",
+ "pwd: ",
+ false},
+
+ {Name, Instruction, Prompt, Echo} =
+ case proplists:get_value(auth_method_kb_interactive_data, Opts) of
+ undefined ->
+ Default;
+ {_,_,_,_}=V ->
+ V;
+ F when is_function(F) ->
+ {_,PeerName} = Ssh#ssh.peer,
+ F(PeerName, User, "ssh-connection")
+ end,
+ EchoEnc = case Echo of
+ true -> <<?TRUE>>;
+ false -> <<?FALSE>>
+ end,
+ Msg = #ssh_msg_userauth_info_request{name = unicode:characters_to_list(Name),
+ instruction = unicode:characters_to_list(Instruction),
+ language_tag = "",
+ num_prompts = 1,
+ data = <<?STRING(unicode:characters_to_binary(Prompt)),
+ EchoEnc/binary
+ >>
+ },
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ opts = [{max_kb_tries,3},{kb_userauth_info_msg,Msg}|Opts]
+ })};
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
method = Other}, _,
#ssh{userauth_supported_methods = Methods} = Ssh) ->
{not_authorized, {User, {authmethod, Other}},
@@ -280,6 +312,38 @@ handle_userauth_info_request(
#ssh_msg_userauth_info_response{num_responses = NumPrompts,
data = Responses}, Ssh)}.
+handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
+ data = <<?UINT32(Sz), Password:Sz/binary>>},
+ #ssh{opts = Opts0,
+ user = User} = Ssh) ->
+ NumTriesLeft = proplists:get_value(max_kb_tries, Opts0, 0) - 1,
+ Opts = lists:keydelete(max_kb_tries,1,Opts0),
+ case check_password(User, unicode:characters_to_list(Password), Opts) of
+ true ->
+ {authorized, User,
+ ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
+ false when NumTriesLeft > 0 ->
+ UserAuthInfoMsg =
+ (proplists:get_value(kb_userauth_info_msg,Opts))
+ #ssh_msg_userauth_info_request{name = "",
+ instruction =
+ lists:concat(
+ ["Bad user or password, try again. ",
+ integer_to_list(NumTriesLeft),
+ " tries left."])},
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(UserAuthInfoMsg,
+ Ssh#ssh{opts = [{max_kb_tries,NumTriesLeft}|Opts]})};
+
+ false ->
+ {not_authorized, {User, {error,"Bad user or password"}},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = "",
+ partial_success = false},
+ Ssh#ssh{opts = lists:keydelete(kb_userauth_info_msg,1,Opts)}
+ )}
+ end;
+
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
@@ -287,20 +351,20 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{},
"keyboard-interactive",
language = "en"}).
+
+default_public_key_algorithms() -> ?PREFERRED_PK_ALGS.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-method_preference(Alg1, Alg2) ->
- [{"publickey", ?MODULE, publickey_msg, [Alg1]},
- {"publickey", ?MODULE, publickey_msg,[Alg2]},
- {"password", ?MODULE, password_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
- ].
-method_preference(Alg1) ->
- [{"publickey", ?MODULE, publickey_msg, [Alg1]},
- {"password", ?MODULE, password_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
- ].
+method_preference(Algs) ->
+ lists:foldr(fun(A, Acc) ->
+ [{"publickey", ?MODULE, publickey_msg, [A]} | Acc]
+ end,
+ [{"password", ?MODULE, password_msg, []},
+ {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
+ ],
+ Algs).
user_name(Opts) ->
Env = case os:type() of
@@ -418,10 +482,6 @@ keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
language = "en"}})
end.
-other_alg('ssh-rsa') ->
- 'ssh-dss';
-other_alg('ssh-dss') ->
- 'ssh-rsa'.
decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
?UINT32(Len1), BinE:Len1/binary,
?UINT32(Len2), BinN:Len2/binary>>
diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl
index 6cd8e6bf14..764c9f4246 100644
--- a/lib/ssh/src/ssh_auth.hrl
+++ b/lib/ssh/src/ssh_auth.hrl
@@ -23,7 +23,7 @@
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
--define(PREFERRED_PK_ALG, 'ssh-rsa').
+-define(PREFERRED_PK_ALGS, ['ssh-rsa','ssh-dss']).
-define(SSH_MSG_USERAUTH_REQUEST, 50).
-define(SSH_MSG_USERAUTH_FAILURE, 51).
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 65208ae158..3bdca4ba94 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -33,7 +33,7 @@
-include("ssh_transport.hrl").
-include("ssh_auth.hrl").
-include("ssh_connect.hrl").
-
+-compile(export_all).
-export([start_link/3]).
%% Internal application API
@@ -333,22 +333,25 @@ info(ConnectionHandler, ChannelProcess) ->
hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
send_msg(VsnMsg, State),
- {ok, [{recbuf, Size}]} = inet:getopts(Socket, [recbuf]),
- inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
- {next_state, hello, State#state{recbuf = Size}};
+ case getopt(recbuf, Socket) of
+ {ok, Size} ->
+ inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
+ {next_state, hello, State#state{recbuf = Size}};
+ {error, Reason} ->
+ {stop, {shutdown, Reason}, State}
+ end;
hello({info_line, _Line},#state{role = client, socket = Socket} = State) ->
%% The server may send info lines before the version_exchange
inet:setopts(Socket, [{active, once}]),
{next_state, hello, State};
-hello({info_line, _Line},#state{role = server} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Did not receive expected protocol version exchange",
- language = "en"},
- handle_disconnect(DisconnectMsg, State);
+hello({info_line, _Line},#state{role = server,
+ socket = Socket,
+ transport_cb = Transport } = State) ->
+ %% as openssh
+ Transport:send(Socket, "Protocol mismatch."),
+ {stop, {shutdown,"Protocol mismatch in version exchange."}, State};
hello({version_exchange, Version}, #state{ssh_params = Ssh0,
socket = Socket,
@@ -501,10 +504,21 @@ userauth(#ssh_msg_userauth_info_request{} = Msg,
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_userauth_info_response{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response(Msg, Ssh0),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
+ #state{ssh_params = #ssh{role = server,
+ peer = {_, Address}} = Ssh0,
+ opts = Opts, starter = Pid} = State) ->
+ case ssh_auth:handle_userauth_info_response(Msg, Ssh0) of
+ {authorized, User, {Reply, Ssh}} ->
+ send_msg(Reply, State),
+ Pid ! ssh_connected,
+ connected_fun(User, Address, "keyboard-interactive", Opts),
+ {next_state, connected,
+ next_packet(State#state{auth_user = User, ssh_params = Ssh})};
+ {not_authorized, {User, Reason}, {Reply, Ssh}} ->
+ retry_fun(User, Address, Reason, Opts),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+ end;
userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh,
starter = Pid} = State) ->
@@ -1156,54 +1170,38 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) ->
supported_host_keys(client, _, Options) ->
try
- case extract_algs(proplists:get_value(pref_public_key_algs, Options, false), []) of
- false ->
- ["ssh-rsa", "ssh-dss"];
- Algs ->
- Algs
+ case proplists:get_value(public_key,
+ proplists:get_value(preferred_algorithms,Options,[])
+ ) of
+ undefined ->
+ ssh_auth:default_public_key_algorithms();
+ L ->
+ L -- (L--ssh_auth:default_public_key_algorithms())
end
+ of
+ [] ->
+ {stop, {shutdown, "No public key algs"}};
+ Algs ->
+ [atom_to_list(A) || A<-Algs]
catch
exit:Reason ->
{stop, {shutdown, Reason}}
end;
supported_host_keys(server, KeyCb, Options) ->
- lists:foldl(fun(Type, Acc) ->
- case available_host_key(KeyCb, Type, Options) of
- {error, _} ->
- Acc;
- Alg ->
- [Alg | Acc]
- end
- end, [],
- %% Prefered alg last so no need to reverse
- ["ssh-dss", "ssh-rsa"]).
-extract_algs(false, _) ->
- false;
-extract_algs([],[]) ->
- false;
-extract_algs([], NewList) ->
- lists:reverse(NewList);
-extract_algs([H|T], NewList) ->
- case H of
- 'ssh-dss' ->
- extract_algs(T, ["ssh-dss"|NewList]);
- 'ssh-rsa' ->
- extract_algs(T, ["ssh-rsa"|NewList])
- end.
-available_host_key(KeyCb, "ssh-dss"= Alg, Opts) ->
- case KeyCb:host_key('ssh-dss', Opts) of
- {ok, _} ->
- Alg;
- Other ->
- Other
- end;
-available_host_key(KeyCb, "ssh-rsa" = Alg, Opts) ->
- case KeyCb:host_key('ssh-rsa', Opts) of
- {ok, _} ->
- Alg;
- Other ->
- Other
- end.
+ Algs=
+ [atom_to_list(A) || A <- proplists:get_value(public_key,
+ proplists:get_value(preferred_algorithms,Options,[]),
+ ssh_auth:default_public_key_algorithms()
+ ),
+ available_host_key(KeyCb, A, Options)
+ ],
+ Algs.
+
+
+%% Alg :: atom()
+available_host_key(KeyCb, Alg, Opts) ->
+ element(1, catch KeyCb:host_key(Alg, Opts)) == ok.
+
send_msg(Msg, #state{socket = Socket, transport_cb = Transport}) ->
Transport:send(Socket, Msg).
@@ -1779,3 +1777,12 @@ start_timeout(_,_, infinity) ->
ok;
start_timeout(Channel, From, Time) ->
erlang:send_after(Time, self(), {timeout, {Channel, From}}).
+
+getopt(Opt, Socket) ->
+ case inet:getopts(Socket, [Opt]) of
+ {ok, [{Opt, Value}]} ->
+ {ok, Value};
+ Other ->
+ {error, {unexpected_getopts_return, Other}}
+ end.
+
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index d6414bab6c..ea9bca2390 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -31,6 +31,8 @@
-export([versions/2, hello_version_msg/1]).
-export([next_seqnum/1, decrypt_first_block/2, decrypt_blocks/3,
+ supported_algorithms/0, supported_algorithms/1,
+ default_algorithms/0, default_algorithms/1,
is_valid_mac/3,
handle_hello_version/1,
key_exchange_init_msg/1,
@@ -42,6 +44,68 @@
unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1,
sign/3, verify/4]).
+%%%----------------------------------------------------------------------------
+%%%
+%%% There is a difference between supported and default algorithms. The
+%%% SUPPORTED algorithms can be handled (maybe untested...). The DEFAULT ones
+%%% are announced in ssh_msg_kexinit and in ssh:default_algorithms/0 to the
+%%% user.
+%%%
+%%% A supported algorithm can be requested in the option 'preferred_algorithms',
+%%% but may give unexpected results because of being promoted to default.
+%%%
+%%% This makes it possible to add experimental algorithms (in supported_algorithms)
+%%% and test them without letting the default users know about them.
+%%%
+
+default_algorithms() -> [{K,default_algorithms(K)} || K <- algo_classes()].
+
+algo_classes() -> [kex, public_key, cipher, mac, compression].
+
+default_algorithms(compression) ->
+ %% Do not announce '[email protected]' because there seem to be problems
+ supported_algorithms(compression, same(['[email protected]']));
+default_algorithms(Alg) ->
+ supported_algorithms(Alg).
+
+
+supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()].
+
+supported_algorithms(kex) ->
+ ['diffie-hellman-group1-sha1'];
+supported_algorithms(public_key) ->
+ ssh_auth:default_public_key_algorithms();
+supported_algorithms(cipher) ->
+ Supports = crypto:supports(),
+ CipherAlgos = [{aes_ctr, 'aes128-ctr'}, {aes_cbc128, 'aes128-cbc'}, {des3_cbc, '3des-cbc'}],
+ Algs = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- CipherAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))],
+ same(Algs);
+supported_algorithms(mac) ->
+ Supports = crypto:supports(),
+ HashAlgos = [{sha256, 'hmac-sha2-256'}, {sha, 'hmac-sha1'}],
+ Algs = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- HashAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))],
+ same(Algs);
+supported_algorithms(compression) ->
+ same(['none','zlib','[email protected]']).
+
+
+supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) ->
+ [{client2server,As1},{server2client,As2}] = supported_algorithms(Key),
+ [{client2server,As1--BL1},{server2client,As2--BL2}];
+supported_algorithms(Key, BlackList) ->
+ supported_algorithms(Key) -- BlackList.
+
+
+
+
+same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
+
+
+%%%----------------------------------------------------------------------------
versions(client, Options)->
Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION),
{Vsn, format_version(Vsn, software_version(Options))};
@@ -128,62 +192,45 @@ key_exchange_init_msg(Ssh0) ->
kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) ->
Random = ssh_bits:random(16),
- Compression = case proplists:get_value(compression, Opts, none) of
- openssh_zlib -> ["[email protected]", "none"];
- zlib -> ["zlib", "none"];
- none -> ["none", "zlib"]
- end,
- kexinit_messsage(Role, Random, Compression, HostKeyAlgs).
+ PrefAlgs =
+ case proplists:get_value(preferred_algorithms,Opts) of
+ undefined ->
+ default_algorithms();
+ Algs0 ->
+ Algs0
+ end,
+ kexinit_message(Role, Random, PrefAlgs, HostKeyAlgs).
key_init(client, Ssh, Value) ->
Ssh#ssh{c_keyinit = Value};
key_init(server, Ssh, Value) ->
Ssh#ssh{s_keyinit = Value}.
-available_ssh_algos() ->
- Supports = crypto:supports(),
- CipherAlgos = [{aes_ctr, "aes128-ctr"}, {aes_cbc128, "aes128-cbc"}, {des3_cbc, "3des-cbc"}],
- Ciphers = [SshAlgo ||
- {CryptoAlgo, SshAlgo} <- CipherAlgos,
- lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))],
- HashAlgos = [{sha256, "hmac-sha2-256"}, {sha, "hmac-sha1"}],
- Hashs = [SshAlgo ||
- {CryptoAlgo, SshAlgo} <- HashAlgos,
- lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))],
- {Ciphers, Hashs}.
-
-kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
- {CipherAlgs, HashAlgs} = available_ssh_algos(),
- #ssh_msg_kexinit{
- cookie = Random,
- kex_algorithms = ["diffie-hellman-group1-sha1"],
- server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = CipherAlgs,
- encryption_algorithms_server_to_client = CipherAlgs,
- mac_algorithms_client_to_server = HashAlgs,
- mac_algorithms_server_to_client = HashAlgs,
- compression_algorithms_client_to_server = Compression,
- compression_algorithms_server_to_client = Compression,
- languages_client_to_server = [],
- languages_server_to_client = []
- };
-kexinit_messsage(server, Random, Compression, HostKeyAlgs) ->
- {CipherAlgs, HashAlgs} = available_ssh_algos(),
+kexinit_message(_Role, Random, Algs, HostKeyAlgs) ->
#ssh_msg_kexinit{
cookie = Random,
- kex_algorithms = ["diffie-hellman-group1-sha1"],
+ kex_algorithms = to_strings( get_algs(kex,Algs) ),
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = CipherAlgs,
- encryption_algorithms_server_to_client = CipherAlgs,
- mac_algorithms_client_to_server = HashAlgs,
- mac_algorithms_server_to_client = HashAlgs,
- compression_algorithms_client_to_server = Compression,
- compression_algorithms_server_to_client = Compression,
+ encryption_algorithms_client_to_server = c2s(cipher,Algs),
+ encryption_algorithms_server_to_client = s2c(cipher,Algs),
+ mac_algorithms_client_to_server = c2s(mac,Algs),
+ mac_algorithms_server_to_client = s2c(mac,Algs),
+ compression_algorithms_client_to_server = c2s(compression,Algs),
+ compression_algorithms_server_to_client = s2c(compression,Algs),
languages_client_to_server = [],
languages_server_to_client = []
}.
+c2s(Key, Algs) -> x2y(client2server, Key, Algs).
+s2c(Key, Algs) -> x2y(server2client, Key, Algs).
+
+x2y(DirectionKey, Key, Algs) -> to_strings(proplists:get_value(DirectionKey, get_algs(Key,Algs))).
+
+get_algs(Key, Algs) -> proplists:get_value(Key, Algs, default_algorithms(Key)).
+
+to_strings(L) -> lists:map(fun erlang:atom_to_list/1, L).
+
new_keys_message(Ssh0) ->
{SshPacket, Ssh} =
ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -448,6 +495,7 @@ select_algorithm(Role, Client, Server) ->
decompress = Decompression,
c_lng = C_Lng,
s_lng = S_Lng},
+%%ct:pal("~p~n Client=~p~n Server=~p~n Alg=~p~n",[Role,Client,Server,Alg]),
{ok, Alg}.
select_encrypt_decrypt(client, Client, Server) ->
@@ -537,10 +585,15 @@ alg_final(SSH0) ->
{ok,SSH6} = decompress_final(SSH5),
SSH6.
-select_all(CL, SL) ->
+select_all(CL, SL) when length(CL) + length(SL) < 50 ->
A = CL -- SL, %% algortihms only used by client
%% algorithms used by client and server (client pref)
- lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)).
+ lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A));
+select_all(_CL, _SL) ->
+ throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Too many algorithms",
+ language = "en"}).
+
select([], []) ->
none;
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index aaf0fa9905..cff695681e 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -56,6 +56,7 @@ all() ->
ssh_daemon_minimal_remote_max_packet_size_option,
ssh_msg_debug_fun_option_client,
ssh_msg_debug_fun_option_server,
+ preferred_algorithms,
id_string_no_opt_client,
id_string_own_string_client,
id_string_random_client,
@@ -92,6 +93,7 @@ basic_tests() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
+ catch crypto:stop(),
case catch crypto:start() of
ok ->
Config;
@@ -289,7 +291,7 @@ exec_compressed(Config) when is_list(Config) ->
UserDir = ?config(priv_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir},
- {compression, zlib},
+ {preferred_algorithms,[{compression, [zlib]}]},
{failfun, fun ssh_test_lib:failfun/2}]),
ConnectionRef =
@@ -1064,6 +1066,57 @@ ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
ssh:stop_daemon(Server).
%%--------------------------------------------------------------------
+%% This test try every algorithm by connecting to an Erlang server
+preferred_algorithms(Config) ->
+ SystemDir = ?config(data_dir, 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),
+
+ {Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ Available = ssh:default_algorithms(),
+ Tests = [[{Tag,[Alg]}] || {Tag, SubAlgs} <- Available,
+ is_atom(hd(SubAlgs)),
+ Alg <- SubAlgs]
+ ++ [[{Tag,[{T1,[A1]},{T2,[A2]}]}] || {Tag, [{T1,As1},{T2,As2}]} <- Available,
+ A1 <- As1,
+ A2 <- As2],
+ ct:log("TESTS: ~p",[Tests]),
+ [connect_exec_channel(Host,Port,PrefAlgs) || PrefAlgs <- Tests],
+ ssh:stop_daemon(Server).
+
+
+connect_exec_channel(_Host, Port, Algs) ->
+ ct:log("Try ~p",[Algs]),
+ ConnectionRef = ssh_test_lib:connect(Port, [{silently_accept_hosts, true},
+ {user_interaction, false},
+ {user, "vego"},
+ {password, "morot"},
+ {preferred_algorithms,Algs}
+ ]),
+ chan_exec(ConnectionRef, "2*21.", <<"42\n">>),
+ ssh:close(ConnectionRef).
+
+chan_exec(ConnectionRef, Cmnd, Expected) ->
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,Cmnd, infinity),
+ Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, Expected}},
+ case ssh_test_lib:receive_exec_result(Data0) of
+ expected ->
+ ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
+ {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
+ = ExitStatus0} ->
+ ct:pal("0: Collected data ~p", [ExitStatus0]),
+ ssh_test_lib:receive_exec_result(Data0,
+ ConnectionRef, ChannelId0);
+ Other0 ->
+ ct:fail(Other0)
+ end.
+
+%%--------------------------------------------------------------------
id_string_no_opt_client(Config) ->
{Server, _Host, Port} = fake_daemon(Config),
{error,_} = ssh:connect("localhost", Port, [], 1000),
@@ -1233,12 +1286,15 @@ openssh_zlib_basic_test(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
+ {preferred_algorithms,[{compression, ['[email protected]']}]},
{failfun, fun ssh_test_lib:failfun/2}]),
ConnectionRef =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
{user_interaction, false},
- {compression, openssh_zlib}]),
+ {preferred_algorithms,[{compression, ['[email protected]',
+ none]}]}
+ ]),
ok = ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index db51f65509..f0c337cf2f 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -65,6 +65,7 @@ ptty() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
+ catch crypto:stop(),
case catch crypto:start() of
ok ->
Config;
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index cb74a27638..850b1cbf6b 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -49,6 +49,7 @@ all() ->
init_per_suite(Config) ->
+ catch crypto:stop(),
case (catch crypto:start()) of
ok ->
ssh:start(),
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 0ce8eec906..925b02a437 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -68,6 +68,7 @@ groups() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
+ catch crypto:stop(),
case (catch crypto:start()) of
ok ->
DataDir = ?config(data_dir, Config),
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
index cc34cc0793..eac7575486 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -52,6 +52,7 @@ groups() ->
init_per_suite(Config) ->
catch ssh:stop(),
+ catch crypto:stop(),
case catch crypto:start() of
ok ->
DataDir = ?config(data_dir, Config),
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index a61fd2dd41..277e3a1b08 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -60,6 +60,7 @@ groups() ->
].
init_per_suite(Config) ->
+ catch crypto:stop(),
case catch crypto:start() of
ok ->
case gen_tcp:connect("localhost", 22, []) of
@@ -166,9 +167,11 @@ erlang_client_openssh_server_exec_compressed() ->
[{doc, "Test that compression option works"}].
erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
+ CompressAlgs = [zlib, '[email protected]',none],
ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
{user_interaction, false},
- {compression, zlib}]),
+ {preferred_algorithms,
+ [{compression,CompressAlgs}]}]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId,
"echo testing", infinity),
@@ -326,8 +329,11 @@ erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
KnownHosts = filename:join(PrivDir, "known_hosts"),
+%% CompressAlgs = [zlib, '[email protected]'], % Does not work
+ CompressAlgs = [zlib],
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {compression, zlib},
+ {preferred_algorithms,
+ [{compression, CompressAlgs}]},
{failfun, fun ssh_test_lib:failfun/2}]),
ct:sleep(500),
diff --git a/lib/ssh/test/ssh_unicode_SUITE.erl b/lib/ssh/test/ssh_unicode_SUITE.erl
index cc916673b3..07d51335c6 100644
--- a/lib/ssh/test/ssh_unicode_SUITE.erl
+++ b/lib/ssh/test/ssh_unicode_SUITE.erl
@@ -55,6 +55,7 @@ all() ->
init_per_suite(Config) ->
+ catch crypto:stop(),
case {file:native_name_encoding(), (catch crypto:start())} of
{utf8, ok} ->
ssh:start(),
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 352563700b..fe0606b1a3 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,23 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 6.0</title>
+ <section><title>SSL 6.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Terminate gracefully when receving bad input to premaster
+ secret calculation</p>
+ <p>
+ Own Id: OTP-12783</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 6.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 1476336039..d100e41930 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,16 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
+ {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
+ {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
]
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index b538fefe53..12a17cb6ac 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -476,19 +476,27 @@ update_handshake_history({Handshake0, _Prev}, Data) ->
%% end.
premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) ->
- public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params);
-
+ try
+ public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params)
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
premaster_secret(PublicDhKey, PrivateDhKey, #server_dh_params{dh_p = Prime, dh_g = Base}) ->
- crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]);
+ try
+ crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base])
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
premaster_secret(#client_srp_public{srp_a = ClientPublicKey}, ServerKey, #srp_user{prime = Prime,
verifier = Verifier}) ->
case crypto:compute_key(srp, ClientPublicKey, ServerKey, {host, [Verifier, Prime, '6a']}) of
error ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
PremasterSecret ->
PremasterSecret
end;
-
premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Salt, srp_b = Public},
ClientKeys, {Username, Password}) ->
case ssl_srp_primes:check_srp_params(Generator, Prime) of
@@ -496,21 +504,19 @@ premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Sa
DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]),
case crypto:compute_key(srp, Public, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of
error ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
PremasterSecret ->
PremasterSecret
end;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end;
-
premaster_secret(#client_rsa_psk_identity{
identity = PSKIdentity,
exchange_keys = #encrypted_premaster_secret{premaster_secret = EncPMS}
}, #'RSAPrivateKey'{} = Key, PSKLookup) ->
PremasterSecret = premaster_secret(EncPMS, Key),
psk_secret(PSKIdentity, PSKLookup, PremasterSecret);
-
premaster_secret(#server_dhe_psk_params{
hint = IdentityHint,
dh_params = #server_dh_params{dh_y = PublicDhKey} = Params},
@@ -518,7 +524,6 @@ premaster_secret(#server_dhe_psk_params{
LookupFun) ->
PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params),
psk_secret(IdentityHint, LookupFun, PremasterSecret);
-
premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) ->
psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret).
@@ -527,13 +532,10 @@ premaster_secret(#client_dhe_psk_identity{
dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) ->
PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params),
psk_secret(PSKIdentity, PSKLookup, PremasterSecret).
-
premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) ->
psk_secret(PSKIdentity, PSKLookup);
-
premaster_secret({psk, PSKIdentity}, PSKLookup) ->
psk_secret(PSKIdentity, PSKLookup);
-
premaster_secret(#'ECPoint'{} = ECPoint, #'ECPrivateKey'{} = ECDHKeys) ->
public_key:compute_key(ECPoint, ECDHKeys);
premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) ->
@@ -2036,7 +2038,7 @@ psk_secret(PSKIdentity, PSKLookup) ->
#alert{} = Alert ->
Alert;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end.
psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
@@ -2048,7 +2050,7 @@ psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
#alert{} = Alert ->
Alert;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end.
handle_psk_identity(_PSKIdentity, LookupFun)
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a22af6b960..d23b42ace5 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -227,7 +227,10 @@ loop_conn_setup(World, Erts) ->
{tcp_closed, Erts} ->
ssl:close(World);
{ssl_closed, World} ->
- gen_tcp:close(Erts)
+ gen_tcp:close(Erts);
+ {ssl_error, World, _} ->
+
+ ssl:close(World)
end.
loop_conn(World, Erts) ->
@@ -241,7 +244,9 @@ loop_conn(World, Erts) ->
{tcp_closed, Erts} ->
ssl:close(World);
{ssl_closed, World} ->
- gen_tcp:close(Erts)
+ gen_tcp:close(Erts);
+ {ssl_error, World, _} ->
+ ssl:close(World)
end.
get_ssl_options(Type) ->
diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml
index ec1e43f29c..c853b402d4 100644
--- a/lib/stdlib/doc/src/orddict.xml
+++ b/lib/stdlib/doc/src/orddict.xml
@@ -48,8 +48,11 @@
<datatypes>
<datatype>
- <name name="orddict"/>
- <desc><p>As returned by new/0.</p></desc>
+ <name name="orddict" n_vars="2"/>
+ <desc><p>Dictionary as returned by <c>new/0</c>.</p></desc>
+ </datatype>
+ <datatype>
+ <name name="orddict" n_vars="0"/>
</datatype>
</datatypes>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index ffac1c0bd7..6ff477a42d 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -386,9 +386,15 @@
added to the supervisor and the function returns the same
value.</p>
<p>If the child process start function returns <c>ignore</c>,
- the child specification is added to the supervisor, the pid
- is set to <c>undefined</c>, and the function returns
- <c>{ok,undefined}</c>.</p>
+ the child specification is added to the supervisor (unless the
+ supervisor is a <c>simple_one_for_one</c> supervisor, see below),
+ the pid is set to <c>undefined</c> and the function returns
+ <c>{ok,undefined}</c>.
+ </p>
+ <p>In the case of a <c>simple_one_for_one</c> supervisor, when a child
+ process start function returns <c>ignore</c> the functions returns
+ <c>{ok,undefined}</c> and no child is added to the supervisor.
+ </p>
<p>If the child process start function returns an error tuple or
an erroneous value, or if it fails, the child specification is
discarded, and the function returns <c>{error,Error}</c> where
diff --git a/lib/stdlib/src/erl_anno.erl b/lib/stdlib/src/erl_anno.erl
index 9fb767fc93..fa83375c34 100644
--- a/lib/stdlib/src/erl_anno.erl
+++ b/lib/stdlib/src/erl_anno.erl
@@ -150,9 +150,7 @@ is_filename(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)
- catch _:_ -> false
- end.
+ is_list(T).
-spec column(Anno) -> column() | 'undefined' when
Anno :: anno().
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 714c260bda..b13848c501 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -609,22 +609,30 @@ pack_warnings(Ws) ->
%% add_warning(ErrorDescriptor, State) -> State'
%% add_warning(Line, Error, State) -> State'
-add_error(E, St) -> St#lint{errors=[{St#lint.file,E}|St#lint.errors]}.
+add_error(E, St) -> add_lint_error(E, St#lint.file, St).
add_error(Anno, E, St) ->
- {File,Location} = loc(Anno),
- add_error({Location,erl_lint,E}, St#lint{file = File}).
+ {File,Location} = loc(Anno, St),
+ add_lint_error({Location,erl_lint,E}, File, St).
-add_warning(W, St) -> St#lint{warnings=[{St#lint.file,W}|St#lint.warnings]}.
+add_lint_error(E, File, St) ->
+ St#lint{errors=[{File,E}|St#lint.errors]}.
+
+add_warning(W, St) -> add_lint_warning(W, St#lint.file, St).
add_warning(FileLine, W, St) ->
- {File,Location} = loc(FileLine),
- add_warning({Location,erl_lint,W}, St#lint{file = File}).
+ {File,Location} = loc(FileLine, St),
+ add_lint_warning({Location,erl_lint,W}, File, St).
+
+add_lint_warning(W, File, St) ->
+ St#lint{warnings=[{File,W}|St#lint.warnings]}.
-loc(Anno) ->
- File = erl_anno:file(Anno),
+loc(Anno, St) ->
Location = erl_anno:location(Anno),
- {File,Location}.
+ case erl_anno:file(Anno) of
+ undefined -> {St#lint.file,Location};
+ File -> {File,Location}
+ end.
%% forms([Form], State) -> State'
@@ -667,11 +675,21 @@ eval_file_attribute(Forms, St) ->
eval_file_attr([{attribute,_L,file,{File,_Line}}=Form | Forms], _File) ->
[Form | eval_file_attr(Forms, File)];
eval_file_attr([Form0 | Forms], File) ->
- Form = set_file(Form0, File),
+ Form = set_form_file(Form0, File),
[Form | eval_file_attr(Forms, File)];
eval_file_attr([], _File) ->
[].
+%% Sets the file only on the form. This is used on post-traversal.
+%% For the remaining of the AST we rely on #lint.file.
+
+set_form_file({attribute,L,K,V}, File) ->
+ {attribute,erl_anno:set_file(File, L),K,V};
+set_form_file({function,L,N,A,C}, File) ->
+ {function,erl_anno:set_file(File, L),N,A,C};
+set_form_file(Form, _File) ->
+ Form.
+
set_file(T, File) ->
F = fun(Anno) -> erl_anno:set_file(File, Anno) end,
erl_parse:map_anno(F, T).
@@ -796,10 +814,10 @@ disallowed_compile_flags(Forms, St0) ->
%% There are (still) no line numbers in St0#lint.compile.
Errors0 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
{attribute,A,compile,nowarn_bif_clash} <- Forms,
- {_,L} <- [loc(A)] ],
+ {_,L} <- [loc(A, St0)] ],
Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
{attribute,A,compile,{nowarn_bif_clash, {_,_}}} <- Forms,
- {_,L} <- [loc(A)] ],
+ {_,L} <- [loc(A, St0)] ],
Disabled = (not is_warn_enabled(bif_clash, St0)),
Errors = if
Disabled andalso Errors0 =:= [] ->
@@ -924,7 +942,7 @@ behaviour_conflicting(AllBfs, St) ->
behaviour_add_conflicts(R, St).
behaviour_add_conflicts([{Cb,[{FirstLoc,FirstB}|Cs]}|T], St0) ->
- FirstL = element(2, loc(FirstLoc)),
+ FirstL = element(2, loc(FirstLoc, St0)),
St = behaviour_add_conflict(Cs, Cb, FirstL, FirstB, St0),
behaviour_add_conflicts(T, St);
behaviour_add_conflicts([], St) -> St.
@@ -1142,7 +1160,7 @@ check_unused_records(Forms, St0) ->
end, St0#lint.records, UsedRecords),
Unused = [{Name,FileLine} ||
{Name,{FileLine,_Fields}} <- dict:to_list(URecs),
- element(1, loc(FileLine)) =:= FirstFile],
+ element(1, loc(FileLine, St0)) =:= FirstFile],
foldl(fun ({N,L}, St) ->
add_warning(L, {unused_record, N}, St)
end, St0, Unused);
@@ -1335,14 +1353,15 @@ check_on_load(St) -> St.
-spec call_function(line(), atom(), arity(), lint_state()) -> lint_state().
%% Add to both called and calls.
-call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
+call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St) ->
#usage{calls = Cs} = Usage0,
NA = {F,A},
Usage = case Cs of
undefined -> Usage0;
_ -> Usage0#usage{calls=dict:append(Func, NA, Cs)}
end,
- St#lint{called=[{NA,Line}|Cd], usage=Usage}.
+ Anno = erl_anno:set_file(File, Line),
+ St#lint{called=[{NA,Anno}|Cd], usage=Usage}.
%% function(Line, Name, Arity, Clauses, State) -> State.
@@ -2121,7 +2140,7 @@ expr({'receive',Line,Cs,To,ToEs}, Vt, St0) ->
{Cvt,St3} = icrt_clauses(Cs, Vt, St2),
%% Csvts = [vtnew(Tevt, Vt)|Cvt], %This is just NEW variables!
Csvts = [Tevt|Cvt],
- Rvt = icrt_export(Csvts, Vt, {'receive',Line}),
+ Rvt = icrt_export(Csvts, Vt, {'receive',Line}, St3),
{vtmerge([Tvt,Tevt,Rvt]),St3};
expr({'fun',Line,Body}, Vt, St) ->
%%No one can think funs export!
@@ -2824,10 +2843,9 @@ check_record_types([{type, _, field_type, [{atom, AL, FName}, Type]}|Left],
check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
{SeenVars, St}.
-used_type(TypePair, L, St) ->
- Usage = St#lint.usage,
+used_type(TypePair, L, #lint{usage = Usage, file = File} = St) ->
OldUsed = Usage#usage.used_types,
- UsedTypes = dict:store(TypePair, L, OldUsed),
+ UsedTypes = dict:store(TypePair, erl_anno:set_file(File, L), OldUsed),
St#lint{usage=Usage#usage{used_types=UsedTypes}}.
is_default_type({Name, NumberOfTypeVariables}) ->
@@ -2982,7 +3000,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
UsedTypes = gb_sets:from_list(L),
FoldFun =
fun(Type, #typeinfo{line = FileLine}, AccSt) ->
- case loc(FileLine) of
+ case loc(FileLine, AccSt) of
{FirstFile, _} ->
case gb_sets:is_member(Type, UsedTypes) of
true -> AccSt;
@@ -3020,7 +3038,7 @@ check_local_opaque_types(St) ->
icrt_clauses(Cs, In, Vt, St0) ->
{Csvt,St1} = icrt_clauses(Cs, Vt, St0),
- UpdVt = icrt_export(Csvt, Vt, In),
+ UpdVt = icrt_export(Csvt, Vt, In, St1),
{UpdVt,St1}.
%% icrt_clauses(Clauses, ImportVarTable, State) ->
@@ -3037,8 +3055,8 @@ icrt_clause({clause,_Line,H,G,B}, Vt0, St0) ->
{Bvt,St3} = exprs(B, vtupdate(Vt2, Vt0), St2),
{vtupdate(Bvt, Vt2),St3}.
-icrt_export(Vts, Vt, {Tag,Attrs}) ->
- {_File,Loc} = loc(Attrs),
+icrt_export(Vts, Vt, {Tag,Attrs}, St) ->
+ {_File,Loc} = loc(Attrs, St),
icrt_export(lists:merge(Vts), Vt, {Tag,Loc}, length(Vts), []).
icrt_export([{V,{{export,_},_,_}}|Vs0], [{V,{{export,_}=S0,_,Ls}}|Vt],
@@ -3395,7 +3413,7 @@ vtupdate(Uvt, Vt0) ->
%% Return all new variables in UpdVarTable as unsafe.
vtunsafe({Tag,FileLine}, Uvt, Vt) ->
- {_File,Line} = loc(FileLine),
+ Line = erl_anno:location(FileLine),
[{V,{{unsafe,{Tag,Line}},U,Ls}} || {V,{_,U,Ls}} <- vtnew(Uvt, Vt)].
%% vtmerge(VarTable, VarTable) -> VarTable.
diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl
index af5d917840..cbdf25d757 100644
--- a/lib/stdlib/src/orddict.erl
+++ b/lib/stdlib/src/orddict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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,11 +25,13 @@
-export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]).
-export([fold/3,map/2,filter/2,merge/3]).
--export_type([orddict/0]).
+-export_type([orddict/0, orddict/2]).
%%---------------------------------------------------------------------------
--type orddict() :: [{Key :: term(), Value :: term()}].
+-type orddict() :: orddict(_, _).
+
+-type orddict(Key, Value) :: [{Key, Value}].
%%---------------------------------------------------------------------------
@@ -38,8 +40,7 @@
new() -> [].
-spec is_key(Key, Orddict) -> boolean() when
- Key :: term(),
- Orddict :: orddict().
+ Orddict :: orddict(Key, Value :: term()).
is_key(Key, [{K,_}|_]) when Key < K -> false;
is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict);
@@ -47,14 +48,14 @@ is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K
is_key(_, []) -> false.
-spec to_list(Orddict) -> List when
- Orddict :: orddict(),
- List :: [{Key :: term(), Value :: term()}].
+ Orddict :: orddict(Key, Value),
+ List :: [{Key, Value}].
to_list(Dict) -> Dict.
-spec from_list(List) -> Orddict when
- List :: [{Key :: term(), Value :: term()}],
- Orddict :: orddict().
+ List :: [{Key, Value}],
+ Orddict :: orddict(Key, Value).
from_list([]) -> [];
from_list([{_,_}]=Pair) -> Pair;
@@ -73,17 +74,13 @@ is_empty([]) -> true;
is_empty([_|_]) -> false.
-spec fetch(Key, Orddict) -> Value when
- Key :: term(),
- Value :: term(),
- Orddict :: orddict().
+ Orddict :: orddict(Key, Value).
fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D);
fetch(Key, [{K,Value}|_]) when Key == K -> Value.
-spec find(Key, Orddict) -> {'ok', Value} | 'error' when
- Key :: term(),
- Orddict :: orddict(),
- Value :: term().
+ Orddict :: orddict(Key, Value).
find(Key, [{K,_}|_]) when Key < K -> error;
find(Key, [{K,_}|D]) when Key > K -> find(Key, D);
@@ -91,17 +88,16 @@ find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K
find(_, []) -> error.
-spec fetch_keys(Orddict) -> Keys when
- Orddict :: orddict(),
- Keys :: [term()].
+ Orddict :: orddict(Key, Value :: term()),
+ Keys :: [Key].
fetch_keys([{Key,_}|Dict]) ->
[Key|fetch_keys(Dict)];
fetch_keys([]) -> [].
-spec erase(Key, Orddict1) -> Orddict2 when
- Key :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict];
erase(Key, [{K,_}=E|Dict]) when Key > K ->
@@ -110,10 +106,8 @@ erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K
erase(_, []) -> [].
-spec store(Key, Value, Orddict1) -> Orddict2 when
- Key :: term(),
- Value :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
store(Key, New, [{K,_}|_]=Dict) when Key < K ->
[{Key,New}|Dict];
@@ -124,10 +118,8 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K
store(Key, New, []) -> [{Key,New}].
-spec append(Key, Value, Orddict1) -> Orddict2 when
- Key :: term(),
- Value :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
append(Key, New, [{K,_}|_]=Dict) when Key < K ->
[{Key,[New]}|Dict];
@@ -138,10 +130,9 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K
append(Key, New, []) -> [{Key,[New]}].
-spec append_list(Key, ValList, Orddict1) -> Orddict2 when
- Key :: term(),
- ValList :: [Value :: term()],
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ ValList :: [Value],
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
append_list(Key, NewList, [{K,_}|_]=Dict) when Key < K ->
[{Key,NewList}|Dict];
@@ -153,10 +144,9 @@ append_list(Key, NewList, []) ->
[{Key,NewList}].
-spec update(Key, Fun, Orddict1) -> Orddict2 when
- Key :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
update(Key, Fun, [{K,_}=E|Dict]) when Key > K ->
[E|update(Key, Fun, Dict)];
@@ -164,11 +154,10 @@ update(Key, Fun, [{K,Val}|Dict]) when Key == K ->
[{Key,Fun(Val)}|Dict].
-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when
- Key :: term(),
- Initial :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Initial :: Value,
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
update(Key, _, Init, [{K,_}|_]=Dict) when Key < K ->
[{Key,Init}|Dict];
@@ -179,10 +168,9 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K
update(Key, _, Init, []) -> [{Key,Init}].
-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when
- Key :: term(),
- Increment :: number(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value),
+ Increment :: number().
update_counter(Key, Incr, [{K,_}|_]=Dict) when Key < K ->
[{Key,Incr}|Dict];
@@ -193,28 +181,30 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K
update_counter(Key, Incr, []) -> [{Key,Incr}].
-spec fold(Fun, Acc0, Orddict) -> Acc1 when
- Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()),
- Acc0 :: term(),
- Acc1 :: term(),
- Orddict :: orddict().
+ Fun :: fun((Key, Value, AccIn) -> AccOut),
+ Orddict :: orddict(Key, Value),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc.
fold(F, Acc, [{Key,Val}|D]) ->
fold(F, F(Key, Val, Acc), D);
fold(F, Acc, []) when is_function(F, 3) -> Acc.
-spec map(Fun, Orddict1) -> Orddict2 when
- Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Fun :: fun((Key, Value1) -> Value2),
+ Orddict1 :: orddict(Key, Value1),
+ Orddict2 :: orddict(Key, Value2).
map(F, [{Key,Val}|D]) ->
[{Key,F(Key, Val)}|map(F, D)];
map(F, []) when is_function(F, 2) -> [].
-spec filter(Pred, Orddict1) -> Orddict2 when
- Pred :: fun((Key :: term(), Value :: term()) -> boolean()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Pred :: fun((Key, Value) -> boolean()),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
filter(F, [{Key,Val}=E|D]) ->
case F(Key, Val) of
@@ -224,10 +214,10 @@ filter(F, [{Key,Val}=E|D]) ->
filter(F, []) when is_function(F, 2) -> [].
-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when
- Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict(),
- Orddict3 :: orddict().
+ Fun :: fun((Key, Value1, Value2) -> Value),
+ Orddict1 :: orddict(Key, Value1),
+ Orddict2 :: orddict(Key, Value2),
+ Orddict3 :: orddict(Key, Value).
merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 ->
[E1|merge(F, D1, [E2|D2])];
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index ee87a8ddb2..b3569c2848 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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
@@ -17,9 +17,7 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
- {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0
+ [{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0-17.5
%% Down to - max one major revision back
- [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
- {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0
+ [{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0-17.5
}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 67655b1145..1d7396adee 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -381,7 +381,7 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
#child{mfargs = {M, F, A}} = Child,
Args = A ++ EArgs,
case do_start_child_i(M, F, Args) of
- {ok, undefined} when Child#child.restart_type =:= temporary ->
+ {ok, undefined} ->
{reply, {ok, undefined}, State};
{ok, Pid} ->
NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index 3c67bd67c6..f986c0081d 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -24,7 +24,7 @@
list_dir/1, list_dir/2, table/1, table/2,
t/1, tt/1]).
-%% unzipping peicemeal
+%% unzipping piecemeal
-export([openzip_open/1, openzip_open/2,
openzip_get/1, openzip_get/2,
openzip_t/1, openzip_tt/1,
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index 5248870744..8d26c77c9b 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -993,43 +993,51 @@ random_parts(X,N) ->
random_ref_comp(doc) ->
["Test pseudorandomly generated cases against reference imlementation"];
random_ref_comp(Config) when is_list(Config) ->
- ?line put(success_counter,0),
- ?line random:seed({1271,769940,559934}),
- ?line do_random_match_comp(5000,{1,40},{30,1000}),
+ put(success_counter,0),
+ random:seed({1271,769940,559934}),
+ Nr = {1,40},
+ Hr = {30,1000},
+ I1 = 1500,
+ I2 = 5,
+ do_random_match_comp(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_match_comp2(5000,{1,40},{30,1000}),
+ do_random_match_comp2(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_match_comp3(5000,{1,40},{30,1000}),
+ do_random_match_comp3(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_match_comp4(5000,{1,40},{30,1000}),
+ do_random_match_comp4(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_matches_comp(5000,{1,40},{30,1000}),
+ do_random_matches_comp(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_matches_comp2(5000,{1,40},{30,1000}),
+ do_random_matches_comp2(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_matches_comp3(5,{1,40},{30,1000}),
- ?line erts_debug:set_internal_state(available_internal_state,true),
- ?line io:format("oldlimit: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,100)]),
- ?line do_random_match_comp(5000,{1,40},{30,1000}),
- ?line do_random_matches_comp3(5,{1,40},{30,1000}),
- ?line io:format("limit was: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,default)]),
- ?line erts_debug:set_internal_state(available_internal_state,false),
+ do_random_matches_comp3(I2,Nr,Hr),
+ erts_debug:set_internal_state(available_internal_state,true),
+ io:format("oldlimit: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,100)]),
+ do_random_match_comp(I1,Nr,Hr),
+ do_random_matches_comp3(I2,Nr,Hr),
+ io:format("limit was: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,default)]),
+ erts_debug:set_internal_state(available_internal_state,false),
ok.
random_ref_sr_comp(doc) ->
["Test pseudorandomly generated cases against reference imlementation of split and replace"];
random_ref_sr_comp(Config) when is_list(Config) ->
- ?line put(success_counter,0),
- ?line random:seed({1271,769940,559934}),
- ?line do_random_split_comp(5000,{1,40},{30,1000}),
+ put(success_counter,0),
+ random:seed({1271,769940,559934}),
+ Nr = {1,40},
+ Hr = {30,1000},
+ I1 = 1500,
+ do_random_split_comp(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_replace_comp(5000,{1,40},{30,1000}),
+ do_random_replace_comp(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_split_comp2(5000,{1,40},{30,1000}),
+ do_random_split_comp2(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
- ?line do_random_replace_comp2(5000,{1,40},{30,1000}),
+ do_random_replace_comp2(I1,Nr,Hr),
io:format("Number of successes: ~p~n",[get(success_counter)]),
ok.
+
random_ref_fla_comp(doc) ->
["Test pseudorandomly generated cases against reference imlementation of split and replace"];
random_ref_fla_comp(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/erl_anno_SUITE.erl b/lib/stdlib/test/erl_anno_SUITE.erl
index 7632fbd324..d024f6907d 100644
--- a/lib/stdlib/test/erl_anno_SUITE.erl
+++ b/lib/stdlib/test/erl_anno_SUITE.erl
@@ -89,7 +89,6 @@ is_anno(_Config) ->
false = erl_anno:is_anno([{generated,true}]),
false = erl_anno:is_anno([{location,1},{file,nofile}]),
false = erl_anno:is_anno([{location,1},{text,notext}]),
- false = erl_anno:is_anno([{location,1},{text,[a,b,c]}]),
true = erl_anno:is_anno(erl_anno:new(1)),
A0 = erl_anno:new({1, 17}),
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index a88843bb6e..fff6b11a38 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -3061,13 +3061,13 @@ time_lookup(Config) when is_list(Config) ->
"~p ets lookups/s",[Values]))}.
time_lookup_do(Opts) ->
- ?line Tab = ets_new(foo,Opts),
- ?line fill_tab(Tab,foo),
- ?line ets:insert(Tab,{{a,key},foo}),
- ?line {Time,_} = ?t:timecall(test_server,do_times,
- [10000,ets,lookup,[Tab,{a,key}]]),
- ?line true = ets:delete(Tab),
- round(10000 / Time). % lookups/s
+ Tab = ets_new(foo,Opts),
+ fill_tab(Tab,foo),
+ ets:insert(Tab,{{a,key},foo}),
+ {Time,_} = ?t:timecall(test_server,do_times,
+ [100000,ets,lookup,[Tab,{a,key}]]),
+ true = ets:delete(Tab),
+ round(100000 / Time). % lookups/s
badlookup(doc) ->
["Check proper return values from bad lookups in existing/non existing "
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index 858a78b1d2..78432789cd 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -482,7 +482,7 @@ unicode_options_gen(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
AllModes = [utf8,utf16,{utf16,big},{utf16,little},
utf32,{utf32,big},{utf32,little}],
- FSize = 17*1024,
+ FSize = 9*1024,
NumItersRead = 2,
NumItersWrite = 2,
Dir = filename:join(PrivDir, "GENDATA1"),
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 348c308f5d..56829fac5c 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -6120,7 +6120,7 @@ otp_6964(Config) when is_list(Config) ->
lists:flatten(qlc:format_error(ErrReply)),
qlc_SUITE:install_error_logger(),
20000 = length(F(warning_msg)),
- {error, joining} = qlc_SUITE:read_error_logger(),
+ {warning, joining} = qlc_SUITE:read_error_logger(),
20000 = length(F(info_msg)),
{info, joining} = qlc_SUITE:read_error_logger(),
20000 = length(F(error_msg)),
@@ -6155,8 +6155,8 @@ otp_6964(Config) when is_list(Config) ->
{error, caching} = qlc_SUITE:read_error_logger(),
{error, caching} = qlc_SUITE:read_error_logger(),
1 = length(F(warning_msg)),
- {error, caching} = qlc_SUITE:read_error_logger(),
- {error, caching} = qlc_SUITE:read_error_logger(),
+ {warning, caching} = qlc_SUITE:read_error_logger(),
+ {warning, caching} = qlc_SUITE:read_error_logger(),
1 = length(F(info_msg)),
{info, caching} = qlc_SUITE:read_error_logger(),
{info, caching} = qlc_SUITE:read_error_logger(),
@@ -6188,7 +6188,7 @@ otp_6964(Config) when is_list(Config) ->
L = F(info_msg),
{info, sorting} = qlc_SUITE:read_error_logger(),
L = F(warning_msg),
- {error, sorting} = qlc_SUITE:read_error_logger(),
+ {warning, sorting} = qlc_SUITE:read_error_logger(),
qlc_SUITE:uninstall_error_logger(),
ets:delete(E1),
ets:delete(E2)">>],
@@ -6215,7 +6215,7 @@ otp_6964(Config) when is_list(Config) ->
R = lists:sort(F(error_msg)),
{error, caching} = qlc_SUITE:read_error_logger(),
R = lists:sort(F(warning_msg)),
- {error, caching} = qlc_SUITE:read_error_logger(),
+ {warning, caching} = qlc_SUITE:read_error_logger(),
qlc_SUITE:uninstall_error_logger(),
ErrReply = F(not_allowed),
{error,qlc,{tmpdir_usage,caching}} = ErrReply,
@@ -8178,6 +8178,8 @@ read_error_logger() ->
{error, Why};
{info, Why} ->
{info, Why};
+ {warning, Why} ->
+ {warning, Why};
{error, Pid, Tuple} ->
{error, Pid, Tuple}
after 1000 ->
@@ -8192,8 +8194,7 @@ read_error_logger() ->
init(Tester) ->
{ok, Tester}.
-handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester)
- when is_atom(Why) ->
+handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) ->
Tester ! {error, Why},
{ok, Tester};
handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) ->
@@ -8202,6 +8203,9 @@ handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) ->
handle_event({info_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) ->
Tester ! {info, Why},
{ok, Tester};
+handle_event({warning_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) ->
+ Tester ! {warning, Why},
+ {ok, Tester};
handle_event(_Event, State) ->
{ok, State}.
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index 9a1f37aa75..39ce1bd89a 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -33,7 +33,7 @@
-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(1)).
+-define(default_timeout, ?t:minutes(3)).
-define(LOOP, 1000000).
init_per_testcase(_Case, Config) ->
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 9dcf19707c..015b09f35e 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -37,6 +37,7 @@
sup_start_ignore_child/1, sup_start_ignore_temporary_child/1,
sup_start_ignore_temporary_child_start_child/1,
sup_start_ignore_temporary_child_start_child_simple/1,
+ sup_start_ignore_permanent_child_start_child_simple/1,
sup_start_error_return/1, sup_start_fail/1,
sup_start_map/1, sup_start_map_faulty_specs/1,
sup_stop_infinity/1, sup_stop_timeout/1, sup_stop_brutal_kill/1,
@@ -99,6 +100,7 @@ groups() ->
sup_start_ignore_child, sup_start_ignore_temporary_child,
sup_start_ignore_temporary_child_start_child,
sup_start_ignore_temporary_child_start_child_simple,
+ sup_start_ignore_permanent_child_start_child_simple,
sup_start_error_return, sup_start_fail]},
{sup_start_map, [],
[sup_start_map, sup_start_map_faulty_specs]},
@@ -250,6 +252,27 @@ sup_start_ignore_temporary_child_start_child_simple(Config)
[1,1,0,1] = get_child_counts(sup_test).
%%-------------------------------------------------------------------------
+%% Tests what happens if child's init-callback returns ignore for a
+%% permanent child when child is started with start_child/2, and the
+%% supervisor is simple_one_for_one.
+%% Child spec shall NOT be saved!!!
+sup_start_ignore_permanent_child_start_child_simple(Config)
+ when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, [ignore]},
+ permanent, 1000, worker, []},
+ {ok, Pid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child1]}}),
+
+ {ok, undefined} = supervisor:start_child(sup_test, []),
+ {ok, CPid2} = supervisor:start_child(sup_test, []),
+
+ [{undefined, CPid2, worker, []}] = supervisor:which_children(sup_test),
+ [1,1,0,1] = get_child_counts(sup_test),
+
+ %% Regression test: check that the supervisor terminates without error.
+ exit(Pid, shutdown),
+ check_exit_reason(Pid, shutdown).
+%%-------------------------------------------------------------------------
%% Tests what happens if init-callback returns a invalid value.
sup_start_error_return(Config) when is_list(Config) ->
process_flag(trap_exit, true),
diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl
index 613be99ccd..9f5d485df6 100644
--- a/lib/stdlib/test/unicode_SUITE.erl
+++ b/lib/stdlib/test/unicode_SUITE.erl
@@ -87,8 +87,9 @@ ex_binaries_errors_utf8(Config) when is_list(Config) ->
%% Now, try with longer binary (trapping)
BrokenPart = list_to_binary(lists:seq(128,255)),
BrokenSz = byte_size(BrokenPart),
+ Seq255 = lists:seq(1,255),
[ begin
- OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))),
+ OKList = lists:flatten(lists:duplicate(N,Seq255)),
OKBin = unicode:characters_to_binary(OKList),
OKLen = length(OKList),
%% Copy to avoid that the binary get's writable
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index f57f31c8de..a1f2a946b1 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 2.4
+STDLIB_VSN = 2.5
diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile
index 2c565cee7f..2e91adf8af 100644
--- a/lib/syntax_tools/src/Makefile
+++ b/lib/syntax_tools/src/Makefile
@@ -75,7 +75,8 @@ $(EBIN)/%.$(EMULATOR):%.erl
# special rules and dependencies to apply the transform to itself
$(EBIN)/merl_transform.beam: $(EBIN)/merl.beam ./merl_transform.beam \
- ../include/merl.hrl
+ ../include/merl.hrl \
+ $(EBIN)/erl_syntax.beam $(EBIN)/erl_syntax_lib.beam
./merl_transform.beam: ./merl_transform.erl $(EBIN)/merl.beam \
../include/merl.hrl
$(V_ERLC) -DMERL_NO_TRANSFORM $(ERL_COMPILE_FLAGS) -o ./ $<
diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src
index e207901def..dd4ac46055 100644
--- a/lib/syntax_tools/src/syntax_tools.app.src
+++ b/lib/syntax_tools/src/syntax_tools.app.src
@@ -17,4 +17,5 @@
{registered,[]},
{applications, [stdlib]},
{env, []},
- {runtime_dependencies, ["stdlib-2.5","kernel-3.0","erts-6.0"]}]}.
+ {runtime_dependencies,
+ ["compiler-6.0","erts-6.0","kernel-3.0","stdlib-2.5"]}]}.
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 1c42ef0ddb..403e90196e 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.18
+SYNTAX_TOOLS_VSN = 1.7
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index 010dffe138..de68486b1b 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -97,12 +97,12 @@ t_conflicts_file([File|Files]) ->
{ok, _} = lcnt:start(),
ok = lcnt:load(File),
ok = lcnt:conflicts(),
- THs = [-1, 0, 100, 1000],
+ THs = [-1, 5],
Print = [name , id , type , entry , tries , colls , ratio , time , duration],
Opts = [
[{sort, Sort}, {reverse, Rev}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, [Print]}] ||
- Sort <- [name , id , type , tries , colls , ratio , time , entry],
- ML <- [none, 1 , 32, 4096],
+ Sort <- [name , type , tries , colls , ratio , time],
+ ML <- [none, 32],
Combine <- [true, false],
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Rev <- [true, false]
@@ -131,12 +131,12 @@ t_locations_file([File|Files]) ->
{ok, _} = lcnt:start(),
ok = lcnt:load(File),
ok = lcnt:locations(),
- THs = [-1, 0, 100, 1000],
+ THs = [-1, 0, 100],
Print = [name , id , type , entry , tries , colls , ratio , time , duration],
Opts = [
[{full_id, Id}, {sort, Sort}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, Print}] ||
Sort <- [name , id , type , tries , colls , ratio , time , entry],
- ML <- [none, 1 , 64],
+ ML <- [none, 64],
Combine <- [true, false],
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Id <- [true, false]
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 3b3202d38b..68c3f6e29c 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.7.2
+TOOLS_VSN = 2.8
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index ce658e257b..74c0ccfc59 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.8
+TYPER_VSN = 0.9.9
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index 942d4c0d6f..09fb9f384c 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.3.3
+WX_VSN = 1.4
diff --git a/otp_versions.table b/otp_versions.table
index fbed2ce427..50a77237ff 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,5 @@
+OTP-17.5.6 : inets-5.10.9 ssh-3.2.4 ssl-6.0.1 # 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.2 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 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.5 : diameter-1.9.2 # 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 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 inets-5.10.8 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 ssh-3.2.3 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.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 :
diff --git a/system/doc/efficiency_guide/binaryhandling.xml b/system/doc/efficiency_guide/binaryhandling.xml
index 0ac1a7ee32..1d247bcdd5 100644
--- a/system/doc/efficiency_guide/binaryhandling.xml
+++ b/system/doc/efficiency_guide/binaryhandling.xml
@@ -416,6 +416,7 @@ non_opt_eq([], <<>>) ->
how you can find out whether your code can be optimized.</p>
<section>
+ <marker id="bin_opt_info"></marker>
<title>Option bin_opt_info</title>
<p>Use the <c>bin_opt_info</c> option to have the compiler print a lot of