diff options
585 files changed, 30809 insertions, 23073 deletions
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md index 8632f46264..551ecdd224 100644 --- a/HOWTO/INSTALL.md +++ b/HOWTO/INSTALL.md @@ -429,6 +429,10 @@ Some of the available `configure` options are: and **not supported**. This functionality **will** be subject to backward incompatible changes. Note that you should **not** enable the dirty scheduler functionality on production systems. It is only provided for testing. + This switch also imply `--enable-new-purge-strategy` (see below). +* `--enable-new-purge-strategy` - Enable the purge strategy that will be + introduced in ERTS version 9.0 (OTP 20). Note that this switch will be + removed in OTP 20. If you or your system has special requirements please read the `Makefile` for additional configuration information. diff --git a/OTP_VERSION b/OTP_VERSION index 6ea9a3bd47..02f94dcfc1 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -19.0 +19.0.5 diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot Binary files differindex ef962190a4..4a1f656c3c 100644 --- a/bootstrap/bin/start.boot +++ b/bootstrap/bin/start.boot diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot Binary files differindex ef962190a4..4a1f656c3c 100644 --- a/bootstrap/bin/start_clean.boot +++ b/bootstrap/bin/start_clean.boot diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam Binary files differindex c8bc82022c..481d43c967 100644 --- a/bootstrap/lib/compiler/ebin/beam_asm.beam +++ b/bootstrap/lib/compiler/ebin/beam_asm.beam diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam Binary files differindex ea91a62b56..b6a4b6f10f 100644 --- a/bootstrap/lib/compiler/ebin/beam_block.beam +++ b/bootstrap/lib/compiler/ebin/beam_block.beam diff --git a/bootstrap/lib/compiler/ebin/beam_bool.beam b/bootstrap/lib/compiler/ebin/beam_bool.beam Binary files differindex 1673c4aadb..37f368190e 100644 --- a/bootstrap/lib/compiler/ebin/beam_bool.beam +++ b/bootstrap/lib/compiler/ebin/beam_bool.beam diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam Binary files differindex 51abd68b00..48e8f80f79 100644 --- a/bootstrap/lib/compiler/ebin/beam_bsm.beam +++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam Binary files differindex 0f24e040e3..ed0827953c 100644 --- a/bootstrap/lib/compiler/ebin/beam_clean.beam +++ b/bootstrap/lib/compiler/ebin/beam_clean.beam diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam Binary files differindex d2dc2f7688..c473003687 100644 --- a/bootstrap/lib/compiler/ebin/beam_dead.beam +++ b/bootstrap/lib/compiler/ebin/beam_dead.beam diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam Binary files differindex fcb2202d2a..9d952f0a64 100644 --- a/bootstrap/lib/compiler/ebin/beam_validator.beam +++ b/bootstrap/lib/compiler/ebin/beam_validator.beam diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam Binary files differindex dea6cb4ee2..eecdd4e2dd 100644 --- a/bootstrap/lib/compiler/ebin/cerl.beam +++ b/bootstrap/lib/compiler/ebin/cerl.beam diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam Binary files differindex 8003d590c9..044ed03d93 100644 --- a/bootstrap/lib/compiler/ebin/compile.beam +++ b/bootstrap/lib/compiler/ebin/compile.beam diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app index 53891c7260..ebd5205219 100644 --- a/bootstrap/lib/compiler/ebin/compiler.app +++ b/bootstrap/lib/compiler/ebin/compiler.app @@ -19,7 +19,7 @@ {application, compiler, [{description, "ERTS CXC 138 10"}, - {vsn, "6.0.3"}, + {vsn, "7.0.1"}, {modules, [ beam_a, beam_asm, diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam Binary files differindex 0e47e00b15..47f9f53546 100644 --- a/bootstrap/lib/compiler/ebin/core_lint.beam +++ b/bootstrap/lib/compiler/ebin/core_lint.beam diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam Binary files differindex 920dbc922f..2599138a8c 100644 --- a/bootstrap/lib/compiler/ebin/core_parse.beam +++ b/bootstrap/lib/compiler/ebin/core_parse.beam diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam Binary files differindex 5f7b22ba8b..3017a4e026 100644 --- a/bootstrap/lib/compiler/ebin/core_scan.beam +++ b/bootstrap/lib/compiler/ebin/core_scan.beam diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam Binary files differindex 68cc4e3ff3..75a6570461 100644 --- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam +++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam Binary files differindex e8e120e145..54f507218f 100644 --- a/bootstrap/lib/compiler/ebin/v3_core.beam +++ b/bootstrap/lib/compiler/ebin/v3_core.beam diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam Binary files differindex 47eaf3b5e8..60cc6503eb 100644 --- a/bootstrap/lib/compiler/ebin/v3_kernel.beam +++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam Binary files differindex b220e475ee..d4b3f2a5e6 100644 --- a/bootstrap/lib/compiler/ebin/v3_life.beam +++ b/bootstrap/lib/compiler/ebin/v3_life.beam diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam Binary files differindex 2b3c8cf454..d46de4527a 100644 --- a/bootstrap/lib/kernel/ebin/application_controller.beam +++ b/bootstrap/lib/kernel/ebin/application_controller.beam diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam Binary files differindex 24492a6771..da770c5a84 100644 --- a/bootstrap/lib/kernel/ebin/application_master.beam +++ b/bootstrap/lib/kernel/ebin/application_master.beam diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam Binary files differindex cbe477830b..1ccad1a5a3 100644 --- a/bootstrap/lib/kernel/ebin/auth.beam +++ b/bootstrap/lib/kernel/ebin/auth.beam diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam Binary files differindex 6f99487ca7..7f073fe932 100644 --- a/bootstrap/lib/kernel/ebin/code_server.beam +++ b/bootstrap/lib/kernel/ebin/code_server.beam diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam Binary files differindex 5aa8bf7dc1..31fb2d609d 100644 --- a/bootstrap/lib/kernel/ebin/disk_log.beam +++ b/bootstrap/lib/kernel/ebin/disk_log.beam diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam Binary files differindex cb627c10a5..6b10d652de 100644 --- a/bootstrap/lib/kernel/ebin/disk_log_1.beam +++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam diff --git a/bootstrap/lib/kernel/ebin/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam Binary files differindex 50154045be..67cda715c4 100644 --- a/bootstrap/lib/kernel/ebin/disk_log_server.beam +++ b/bootstrap/lib/kernel/ebin/disk_log_server.beam diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam Binary files differindex d2c186f03a..56868dfe6d 100644 --- a/bootstrap/lib/kernel/ebin/dist_ac.beam +++ b/bootstrap/lib/kernel/ebin/dist_ac.beam diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam Binary files differindex eb09a06785..bf3572fe23 100644 --- a/bootstrap/lib/kernel/ebin/dist_util.beam +++ b/bootstrap/lib/kernel/ebin/dist_util.beam diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam Binary files differindex 8e8460514c..19be6babd1 100644 --- a/bootstrap/lib/kernel/ebin/erl_epmd.beam +++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam Binary files differindex 465dab3a52..601cb9176b 100644 --- a/bootstrap/lib/kernel/ebin/global.beam +++ b/bootstrap/lib/kernel/ebin/global.beam diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam Binary files differindex 03202f68eb..8bd51c2424 100644 --- a/bootstrap/lib/kernel/ebin/global_group.beam +++ b/bootstrap/lib/kernel/ebin/global_group.beam diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam Binary files differindex 38eb0e7a6c..c2c66f77ec 100644 --- a/bootstrap/lib/kernel/ebin/group.beam +++ b/bootstrap/lib/kernel/ebin/group.beam diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam Binary files differindex 86044153af..2ab7ef9d9f 100644 --- a/bootstrap/lib/kernel/ebin/heart.beam +++ b/bootstrap/lib/kernel/ebin/heart.beam diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam Binary files differindex 5a2f294951..f6715adbfe 100644 --- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam +++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam Binary files differindex 5ae4090a61..ff7f0ba7e6 100644 --- a/bootstrap/lib/kernel/ebin/inet.beam +++ b/bootstrap/lib/kernel/ebin/inet.beam diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam Binary files differindex 1be1dc1c57..98f70db13c 100644 --- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam +++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam Binary files differindex 2713c7fef6..b1bbf771a3 100644 --- a/bootstrap/lib/kernel/ebin/inet_res.beam +++ b/bootstrap/lib/kernel/ebin/inet_res.beam diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam Binary files differindex d06bdf709a..087274e89d 100644 --- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam +++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam Binary files differindex 8379f77e94..5e59fc2f82 100644 --- a/bootstrap/lib/kernel/ebin/kernel_config.beam +++ b/bootstrap/lib/kernel/ebin/kernel_config.beam diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam Binary files differindex 8afe1c0388..1197b4f681 100644 --- a/bootstrap/lib/kernel/ebin/net_kernel.beam +++ b/bootstrap/lib/kernel/ebin/net_kernel.beam diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam Binary files differindex c2b2808555..cded4b87be 100644 --- a/bootstrap/lib/kernel/ebin/user.beam +++ b/bootstrap/lib/kernel/ebin/user.beam diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam Binary files differindex 1d72927105..bd9b1a3bce 100644 --- a/bootstrap/lib/kernel/ebin/user_drv.beam +++ b/bootstrap/lib/kernel/ebin/user_drv.beam diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam Binary files differindex 91ef523b99..93f1f1ba72 100644 --- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam +++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam diff --git a/bootstrap/lib/kernel/include/dist_util.hrl b/bootstrap/lib/kernel/include/dist_util.hrl index 43e50d4325..320e916c04 100644 --- a/bootstrap/lib/kernel/include/dist_util.hrl +++ b/bootstrap/lib/kernel/include/dist_util.hrl @@ -63,7 +63,7 @@ f_getll, %% Get low level port or pid. f_address, %% The address of the "socket", %% generated from Socket,Node - %% These two are used in the tick loop, + %% These three are used in the tick loop, %% so they are not fun's to avoid holding old code. mf_tick, %% Takes the socket as parameters and %% sends a tick, this is no fun, it @@ -74,7 +74,11 @@ %% {ok, RecvCnt, SendCnt, SendPend} for %% a given socket. This is a {M,F}, %% returning {error, Reason on failure} - request_type = normal + request_type = normal, + + %% New in kernel-5.1 (OTP 19.1): + mf_setopts, %% netkernel:setopts on active connection + mf_getopts %% netkernel:getopts on active connection }). diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam Binary files differindex 538d169565..ff9022388e 100644 --- a/bootstrap/lib/stdlib/ebin/binary.beam +++ b/bootstrap/lib/stdlib/ebin/binary.beam diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam Binary files differindex 0dfaca7a48..8b9049c855 100644 --- a/bootstrap/lib/stdlib/ebin/dets.beam +++ b/bootstrap/lib/stdlib/ebin/dets.beam diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam Binary files differindex 502eb98318..0b0783bf9d 100644 --- a/bootstrap/lib/stdlib/ebin/dets_server.beam +++ b/bootstrap/lib/stdlib/ebin/dets_server.beam diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam Binary files differindex 37247c7176..2a3b22b7e0 100644 --- a/bootstrap/lib/stdlib/ebin/dets_utils.beam +++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam diff --git a/bootstrap/lib/stdlib/ebin/dets_v8.beam b/bootstrap/lib/stdlib/ebin/dets_v8.beam Binary files differindex 9a1416a316..39ddeebf85 100644 --- a/bootstrap/lib/stdlib/ebin/dets_v8.beam +++ b/bootstrap/lib/stdlib/ebin/dets_v8.beam diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam Binary files differindex 5bcc7cf8d9..b1a75a54eb 100644 --- a/bootstrap/lib/stdlib/ebin/epp.beam +++ b/bootstrap/lib/stdlib/ebin/epp.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex ebe80a32a8..6653cb9f49 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam Binary files differindex 33a0753965..d75b490162 100644 --- a/bootstrap/lib/stdlib/ebin/erl_parse.beam +++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam Binary files differindex e845392e4b..c8798a6094 100644 --- a/bootstrap/lib/stdlib/ebin/erl_pp.beam +++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam Binary files differindex 03d9020bb5..693c0b523d 100644 --- a/bootstrap/lib/stdlib/ebin/file_sorter.beam +++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam Binary files differindex 595574585b..0370d430b2 100644 --- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam +++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam Binary files differindex 3097207512..aa22d6aae4 100644 --- a/bootstrap/lib/stdlib/ebin/gen_server.beam +++ b/bootstrap/lib/stdlib/ebin/gen_server.beam diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam Binary files differindex 0c4b22c3df..d0e18955c7 100644 --- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam +++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam Binary files differindex a06ae1717a..cc6e536ccd 100644 --- a/bootstrap/lib/stdlib/ebin/pool.beam +++ b/bootstrap/lib/stdlib/ebin/pool.beam diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam Binary files differindex fa3240810d..a39ca1391b 100644 --- a/bootstrap/lib/stdlib/ebin/qlc.beam +++ b/bootstrap/lib/stdlib/ebin/qlc.beam diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam Binary files differindex 4a065ec57f..e9c760ba45 100644 --- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam +++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam Binary files differindex b0c98f4b98..3d7b53387b 100644 --- a/bootstrap/lib/stdlib/ebin/shell.beam +++ b/bootstrap/lib/stdlib/ebin/shell.beam diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam Binary files differindex 373d65f198..fa0fa8c960 100644 --- a/bootstrap/lib/stdlib/ebin/slave.beam +++ b/bootstrap/lib/stdlib/ebin/slave.beam diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam Binary files differindex 5099d4ecc0..7b8ab95876 100644 --- a/bootstrap/lib/stdlib/ebin/sofs.beam +++ b/bootstrap/lib/stdlib/ebin/sofs.beam diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app index ceb55c66fc..81b30526ab 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.app +++ b/bootstrap/lib/stdlib/ebin/stdlib.app @@ -20,7 +20,7 @@ %% {application, stdlib, [{description, "ERTS CXC 138 10"}, - {vsn, "3.0"}, + {vsn, "3.0.1"}, {modules, [array, base64, beam_lib, diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup index 737486d3c5..322a329931 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.appup +++ b/bootstrap/lib/stdlib/ebin/stdlib.appup @@ -16,7 +16,7 @@ %% limitations under the License. %% %% %CopyrightEnd% -{"3.0", +{"3.0.1", %% Up from - max one major revision back [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.* diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam Binary files differindex 3ae911ab29..c7534b5438 100644 --- a/bootstrap/lib/stdlib/ebin/supervisor.beam +++ b/bootstrap/lib/stdlib/ebin/supervisor.beam diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam Binary files differindex d2bfe6eea8..f52d5c407b 100644 --- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam +++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam Binary files differindex 6ef3732965..919166fe89 100644 --- a/bootstrap/lib/stdlib/ebin/zip.beam +++ b/bootstrap/lib/stdlib/ebin/zip.beam diff --git a/configure.in b/configure.in index 8a7f372a50..ea98dc9836 100644 --- a/configure.in +++ b/configure.in @@ -129,6 +129,13 @@ AC_PROG_CC AC_PROG_CXX AC_CHECK_TOOL(LD, [ld]) +_search_path=/bin:/usr/bin:/usr/local/bin:$PATH + +AC_PATH_PROG(ENV, [env], false, $_search_path) +if test "$ac_cv_path_ENV" = false; then + AC_MSG_ERROR([No 'env' command found]) +fi + # # We need GNU make, complain if we can't find it # diff --git a/erts/configure.in b/erts/configure.in index 81ecad4f51..883ce2db68 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -121,6 +121,7 @@ AS_HELP_STRING([--enable-bootstrap-only], enable_hipe=no enable_sctp=no enable_dirty_schedulers=no + enable_new_purge=no fi ]) @@ -139,6 +140,13 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]), *) enable_dirty_schedulers=yes ;; esac ], enable_dirty_schedulers=default) +AC_ARG_ENABLE(new-purge-strategy, +AS_HELP_STRING([--enable-new-purge-strategy], [enable new code purge strategy]), +[ case "$enableval" in + no) enable_new_purge=no ;; + *) enable_new_purge=yes ;; + esac ], enable_new_purge=default) + AC_ARG_ENABLE(smp-support, AS_HELP_STRING([--enable-smp-support], [enable smp support]) AS_HELP_STRING([--disable-smp-support], [disable smp support]), @@ -738,6 +746,11 @@ if test "$ac_cv_path_MKDIR" = false; then AC_MSG_ERROR([No 'mkdir' command found]) fi +AC_PATH_PROG(CP, cp, false, $_search_path) +if test "$ac_cv_path_CP" = false; then + AC_MSG_ERROR([No 'cp' command found]) +fi + _search_path= @@ -1023,6 +1036,27 @@ esac AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT) AC_SUBST(DIRTY_SCHEDULER_SUPPORT) +AC_MSG_CHECKING(whether the new code purge strategy should be enabled) +case $enable_new_purge-$enable_dirty_schedulers in + yes-*) + AC_MSG_RESULT(yes) + enable_new_purge=yes;; + default-yes) + AC_MSG_RESULT(yes; forced by dirty scheduler support) + enable_new_purge=yes;; + no-yes) + AC_MSG_ERROR([Dirty schedulers enabled, but new code purge strategy disabled]);; + *) + AC_MSG_RESULT(no) + enable_new_purge=no;; +esac + +if test $enable_new_purge = yes; then + AC_DEFINE(ERTS_NEW_PURGE_STRATEGY, 1, [Define if you want to use the new code purge strategy]) +fi +NEW_PURGE_STRATEGY=$enable_new_purge +AC_SUBST(NEW_PURGE_STRATEGY) + if test $ERTS_BUILD_SMP_EMU = yes; then if test $found_threads = no; then @@ -1676,7 +1710,8 @@ if test "x$enable_sctp" != "xno" ; then fi if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then - AC_CHECK_FUNCS([sctp_bindx sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs]) + AC_CHECK_LIB(sctp, sctp_bindx) + AC_CHECK_FUNCS([sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs]) AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT, SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED, SCTP_DELAYED_ACK_TIME, @@ -4816,8 +4851,7 @@ dnl Output the result. dnl ---------------------------------------------------------------------- dnl Note that the output files are relative to $srcdir - -AC_OUTPUT( +AC_CONFIG_FILES([ emulator/$host/Makefile:emulator/Makefile.in epmd/src/$host/Makefile:epmd/src/Makefile.in etc/common/$host/Makefile:etc/common/Makefile.in @@ -4827,15 +4861,22 @@ AC_OUTPUT( Makefile:Makefile.in ../make/$host/otp.mk:../make/otp.mk.in ../make/$host/otp_ded.mk:../make/otp_ded.mk.in +]) + +AC_CONFIG_FILES([../make/make_emakefile:../make/make_emakefile.in], + [chmod +x ../make/make_emakefile]) + dnl dnl The ones below should be moved to their respective lib dnl +dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in +AC_CONFIG_FILES([ ../lib/ic/c_src/$host/Makefile:../lib/ic/c_src/Makefile.in ../lib/os_mon/c_src/$host/Makefile:../lib/os_mon/c_src/Makefile.in -dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in ../lib/crypto/c_src/$host/Makefile:../lib/crypto/c_src/Makefile.in ../lib/orber/c_src/$host/Makefile:../lib/orber/c_src/Makefile.in ../lib/runtime_tools/c_src/$host/Makefile:../lib/runtime_tools/c_src/Makefile.in ../lib/tools/c_src/$host/Makefile:../lib/tools/c_src/Makefile.in - ) + ]) +AC_OUTPUT diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 0b04f8f70e..ab00d47425 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -26,144 +26,198 @@ <prepared>Arndt Jonasson</prepared> <responsible>Kenneth Lundin</responsible> <docno>1</docno> - <approved>Jultomten</approved> + <approved></approved> <checked></checked> - <date>00-12-01</date> + <date>2000-12-01</date> <rev>A</rev> <file>absform.xml</file> </header> - <p></p> - <p>This document describes the standard representation of parse trees for Erlang - programs as Erlang terms. This representation is known as the <em>abstract format</em>. - Functions dealing with such parse trees are <c>compile:forms/[1,2]</c> - and functions in the modules - <c>epp</c>, - <c>erl_eval</c>, - <c>erl_lint</c>, - <c>erl_pp</c>, - <c>erl_parse</c>, - and - <c>io</c>. - They are also used as input and output for parse transforms (see the module - <c>compile</c>).</p> + <p>This section describes the standard representation of parse trees for Erlang + programs as Erlang terms. This representation is known as the <em>abstract + format</em>. Functions dealing with such parse trees are + <seealso marker="compiler:compile#forms/1"> + <c>compile:forms/1,2</c></seealso> and functions in the following + modules:</p> + + <list type="bulleted"> + <item><seealso marker="stdlib:epp"> + <c>epp(3)</c></seealso></item> + <item><seealso marker="stdlib:erl_eval"> + <c>erl_eval(3)</c></seealso></item> + <item><seealso marker="stdlib:erl_lint"> + <c>erl_lint(3)</c></seealso></item> + <item><seealso marker="stdlib:erl_parse"> + <c>erl_parse(3)</c></seealso></item> + <item><seealso marker="stdlib:erl_pp"> + <c>erl_pp(3)</c></seealso></item> + <item><seealso marker="stdlib:io"> + <c>io(3)</c></seealso></item> + </list> + + <p>The functions are also used as input and output for parse transforms, see + the <seealso marker="compiler:compile"><c>compile(3)</c></seealso> + module.</p> + <p>We use the function <c>Rep</c> to denote the mapping from an Erlang source construct <c>C</c> to its abstract format representation <c>R</c>, and write - <c>R = Rep(C)</c>. - </p> - <p>The word <c>LINE</c> below represents an integer, and denotes the + <c>R = Rep(C)</c>.</p> + + <p>The word <c>LINE</c> in this section represents an integer, and denotes the number of the line in the source file where the construction occurred. - Several instances of <c>LINE</c> in the same construction may denote + Several instances of <c>LINE</c> in the same construction can denote different lines.</p> - <p>Since operators are not terms in their own right, when operators are - mentioned below, the representation of an operator should be taken to + + <p>As operators are not terms in their own right, when operators are + mentioned below, the representation of an operator is to be taken to be the atom with a printname consisting of the same characters as the - operator. - </p> + operator.</p> <section> <title>Module Declarations and Forms</title> - <p>A module declaration consists of a sequence of forms that are either + <p>A module declaration consists of a sequence of forms, which are either function declarations or attributes.</p> + <list type="bulleted"> - <item>If D is a module declaration consisting of the forms - <c>F_1</c>, ..., <c>F_k</c>, then - Rep(D) = <c>[Rep(F_1), ..., Rep(F_k)]</c>.</item> - <item>If F is an attribute <c>-export([Fun_1/A_1, ..., Fun_k/A_k])</c>, then - Rep(F) = <c>{attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}</c>.</item> - <item>If F is an attribute <c>-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k])</c>, then - Rep(F) = <c>{attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}</c>.</item> - <item>If F is an attribute <c>-module(Mod)</c>, then - Rep(F) = <c>{attribute,LINE,module,Mod}</c>.</item> - <item>If F is an attribute <c>-file(File,Line)</c>, then - Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</item> - <item>If F is a function declaration - <c>Name Fc_1 ; ... ; Name Fc_k</c>, - where each <c>Fc_i</c> is a function clause with a - pattern sequence of the same length <c>Arity</c>, then - Rep(F) = <c>{function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}</c>. - </item> - <item>If F is a function specification - <c>-Spec Name Ft_1; ...; Ft_k</c>, - where <c>Spec</c> is either the atom <c>spec</c> or the atom - <c>callback</c>, and each <c>Ft_i</c> is a possibly constrained - function type with an argument sequence of the same length - <c>Arity</c>, then Rep(F) = - <c>{attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}</c>. - </item> - <item>If F is a function specification - <c>-spec Mod:Name Ft_1; ...; Ft_k</c>, - where each <c>Ft_i</c> is a possibly constrained - function type with an argument sequence of the same length - <c>Arity</c>, then Rep(F) = - <c>{attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}</c>. - </item> - <item>If F is a record declaration - <c>-record(Name,{V_1, ..., V_k})</c>, - where each <c>V_i</c> is a record field, then Rep(F) = - <c>{attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}</c>. - For Rep(V), see below.</item> - <item>If F is a type declaration - <c>-Type Name(V_1, ..., V_k) :: T</c>, where - <c>Type</c> is either the atom <c>type</c> or the atom <c>opaque</c>, - each <c>V_i</c> is a variable, and <c>T</c> is a type, then Rep(F) = - <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}</c>. - </item> - <item>If F is a wild attribute <c>-A(T)</c>, then - Rep(F) = <c>{attribute,LINE,A,T}</c>. - <br></br></item> + <item> + <p>If D is a module declaration consisting of the forms + <c>F_1</c>, ..., <c>F_k</c>, then + Rep(D) = <c>[Rep(F_1), ..., Rep(F_k)]</c>.</p> + </item> + <item> + <p>If F is an attribute <c>-export([Fun_1/A_1, ..., Fun_k/A_k])</c>, + then Rep(F) = + <c>{attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}</c>.</p> + </item> + <item> + <p>If F is an attribute <c>-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k])</c>, + then Rep(F) = + <c>{attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., + {Fun_k,A_k}]}}</c>.</p> + </item> + <item> + <p>If F is an attribute <c>-module(Mod)</c>, then + Rep(F) = <c>{attribute,LINE,module,Mod}</c>.</p> + </item> + <item> + <p>If F is an attribute <c>-file(File,Line)</c>, then + Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</p> + </item> + <item> + <p>If F is a function declaration <c>Name Fc_1 ; ... ; Name Fc_k</c>, + where each <c>Fc_i</c> is a function clause with a pattern sequence of + the same length <c>Arity</c>, then Rep(F) = + <c>{function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}</c>.</p> + </item> + <item> + <p>If F is a function specification <c>-Spec Name Ft_1; ...; Ft_k</c>, + where <c>Spec</c> is either the atom <c>spec</c> or the atom + <c>callback</c>, and each <c>Ft_i</c> is a possibly constrained + function type with an argument sequence of the same length + <c>Arity</c>, then Rep(F) = + <c>{attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., + Rep(Ft_k)]}}</c>.</p> + </item> + <item> + <p>If F is a function specification + <c>-spec Mod:Name Ft_1; ...; Ft_k</c>, where each <c>Ft_i</c> is a + possibly constrained function type with an argument sequence of the + same length <c>Arity</c>, then Rep(F) = + <c>{attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., + Rep(Ft_k)]}}</c>.</p> + </item> + <item> + <p>If F is a record declaration <c>-record(Name,{V_1, ..., V_k})</c>, + where each <c>V_i</c> is a record field, then Rep(F) = + <c>{attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}</c>. + For Rep(V), see below.</p> + </item> + <item> + <p>If F is a type declaration <c>-Type Name(V_1, ..., V_k) :: T</c>, + where <c>Type</c> is either the atom <c>type</c> or the atom + <c>opaque</c>, each <c>V_i</c> is a variable, and <c>T</c> is a type, + then Rep(F) = + <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., + Rep(V_k)]}}</c>.</p> + </item> + <item> + <p>If F is a wild attribute <c>-A(T)</c>, then + Rep(F) = <c>{attribute,LINE,A,T}</c>.</p> + </item> </list> <section> <title>Record Fields</title> - <p>Each field in a record declaration may have an optional - explicit default initializer expression, as well as an + <p>Each field in a record declaration can have an optional, + explicit, default initializer expression, and an optional type.</p> + <list type="bulleted"> - <item>If V is <c>A</c>, then - Rep(V) = <c>{record_field,LINE,Rep(A)}</c>.</item> - <item>If V is <c>A = E</c>, - where <c>E</c> is an expression, then - Rep(V) = <c>{record_field,LINE,Rep(A),Rep(E)}</c>.</item> - <item>If V is <c>A :: T</c>, where <c>T</c> is a type, then Rep(V) = - <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}</c>. - </item> - <item>If V is <c>A = E :: T</c>, where - <c>E</c> is an expression and <c>T</c> is a type, then Rep(V) = - <c>{typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}</c>. + <item> + <p>If V is <c>A</c>, then + Rep(V) = <c>{record_field,LINE,Rep(A)}</c>.</p> + </item> + <item> + <p>If V is <c>A = E</c>, where <c>E</c> is an expression, then + Rep(V) = <c>{record_field,LINE,Rep(A),Rep(E)}</c>.</p> + </item> + <item> + <p>If V is <c>A :: T</c>, where <c>T</c> is a type, then Rep(V) = + <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}</c>.</p> + </item> + <item> + <p>If V is <c>A = E :: T</c>, where + <c>E</c> is an expression and <c>T</c> is a type, then Rep(V) = + <c>{typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}</c>. + </p> </item> </list> </section> <section> - <title>Representation of Parse Errors and End-of-file</title> + <title>Representation of Parse Errors and End-of-File</title> <p>In addition to the representations of forms, the list that represents - a module declaration (as returned by functions in <c>erl_parse</c> and - <c>epp</c>) may contain tuples <c>{error,E}</c> and - <c>{warning,W}</c>, denoting syntactically incorrect forms and - warnings, and <c>{eof,LINE}</c>, denoting an end-of-stream - encountered before a complete form had been parsed.</p> + a module declaration (as returned by functions in + <seealso marker="stdlib:epp"><c>epp(3)</c></seealso> and + <seealso marker="stdlib:erl_parse"><c>erl_parse(3)</c></seealso>) + can contain the following:</p> + + <list type="bulleted"> + <item>Tuples <c>{error,E}</c> and <c>{warning,W}</c>, denoting + syntactically incorrect forms and warnings</item> + <item><c>{eof,LINE}</c>, denoting an end-of-stream + encountered before a complete form had been parsed</item> + </list> </section> </section> <section> <title>Atomic Literals</title> <p>There are five kinds of atomic literals, which are represented in the - same way in patterns, expressions and guards:</p> + same way in patterns, expressions, and guards:</p> + <list type="bulleted"> - <item>If L is an atom literal, then - Rep(L) = <c>{atom,LINE,L}</c>.</item> - <item>If L is a character literal, then - Rep(L) = <c>{char,LINE,L}</c>.</item> - <item>If L is a float literal, then - Rep(L) = <c>{float,LINE,L}</c>.</item> - <item>If L is an integer literal, then - Rep(L) = <c>{integer,LINE,L}</c>.</item> - <item>If L is a string literal consisting of the characters - <c>C_1</c>, ..., <c>C_k</c>, then - Rep(L) = <c>{string,LINE,[C_1, ..., C_k]}</c>.</item> + <item> + <p>If L is an atom literal, then Rep(L) = <c>{atom,LINE,L}</c>.</p> + </item> + <item> + <p>If L is a character literal, then Rep(L) = <c>{char,LINE,L}</c>.</p> + </item> + <item> + <p>If L is a float literal, then Rep(L) = <c>{float,LINE,L}</c>.</p> + </item> + <item> + <p>If L is an integer literal, then + Rep(L) = <c>{integer,LINE,L}</c>.</p> + </item> + <item> + <p>If L is a string literal consisting of the characters + <c>C_1</c>, ..., <c>C_k</c>, then + Rep(L) = <c>{string,LINE,[C_1, ..., C_k]}</c>.</p> + </item> </list> - <p>Note that negative integer and float literals do not occur as such; they are - parsed as an application of the unary negation operator.</p> + + <p>Notice that negative integer and float literals do not occur as such; + they are parsed as an application of the unary negation operator.</p> </section> <section> @@ -171,288 +225,424 @@ <p>If Ps is a sequence of patterns <c>P_1, ..., P_k</c>, then Rep(Ps) = <c>[Rep(P_1), ..., Rep(P_k)]</c>. Such sequences occur as the list of arguments to a function or fun.</p> + <p>Individual patterns are represented as follows:</p> + <list type="bulleted"> - <item>If P is an atomic literal <c>L</c>, then Rep(P) = Rep(L).</item> - <item>If P is a bit string pattern - <c><<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>></c>, where each - <c>Size_i</c> is an expression that can be evaluated to an integer - and each <c>TSL_i</c> is a type specificer list, then - Rep(P) = <c>{bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}</c>. - For Rep(TSL), see below. - An omitted <c>Size_i</c> is represented by <c>default</c>. - An omitted <c>TSL_i</c> is represented by <c>default</c>.</item> - <item>If P is a compound pattern <c>P_1 = P_2</c>, then - Rep(P) = <c>{match,LINE,Rep(P_1),Rep(P_2)}</c>.</item> - <item>If P is a cons pattern <c>[P_h | P_t]</c>, then - Rep(P) = <c>{cons,LINE,Rep(P_h),Rep(P_t)}</c>.</item> - <item>If P is a map pattern <c>#{A_1, ..., A_k}</c>, where each - <c>A_i</c> is an association <c>P_i_1 := P_i_2</c>, then Rep(P) = - <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. For Rep(A), see - below.</item> - <item>If P is a nil pattern <c>[]</c>, then - Rep(P) = <c>{nil,LINE}</c>.</item> - <item>If P is an operator pattern <c>P_1 Op P_2</c>, - where <c>Op</c> is a binary operator (this is either an occurrence - of <c>++</c> applied to a literal string or character - list, or an occurrence of an expression that can be evaluated to a number - at compile time), - then Rep(P) = <c>{op,LINE,Op,Rep(P_1),Rep(P_2)}</c>.</item> - <item>If P is an operator pattern <c>Op P_0</c>, - where <c>Op</c> is a unary operator (this is an occurrence of - an expression that can be evaluated to a number at compile - time), then Rep(P) = <c>{op,LINE,Op,Rep(P_0)}</c>.</item> - <item>If P is a parenthesized pattern <c>( P_0 )</c>, then - Rep(P) = <c>Rep(P_0)</c>, - that is, parenthesized patterns cannot be distinguished from their - bodies.</item> - <item>If P is a record field index pattern <c>#Name.Field</c>, - where <c>Field</c> is an atom, then - Rep(P) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item> - <item>If P is a record pattern - <c>#Name{Field_1=P_1, ..., Field_k=P_k}</c>, - where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(P) = - <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}</c>.</item> - <item>If P is a tuple pattern <c>{P_1, ..., P_k}</c>, then - Rep(P) = <c>{tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}</c>.</item> - <item>If P is a universal pattern <c>_</c>, then - Rep(P) = <c>{var,LINE,'_'}</c>.</item> - <item>If P is a variable pattern <c>V</c>, then - Rep(P) = <c>{var,LINE,A}</c>, - where A is an atom with a printname consisting of the same characters as - <c>V</c>.</item> + <item> + <p>If P is an atomic literal <c>L</c>, then Rep(P) = Rep(L).</p> + </item> + <item> + <p>If P is a bitstring pattern + <c><<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>></c>, where each + <c>Size_i</c> is an expression that can be evaluated to an integer, + and each <c>TSL_i</c> is a type specificer list, then Rep(P) = + <c>{bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, + ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}</c>. + For Rep(TSL), see below. + An omitted <c>Size_i</c> is represented by <c>default</c>. + An omitted <c>TSL_i</c> is represented by <c>default</c>.</p> + </item> + <item> + <p>If P is a compound pattern <c>P_1 = P_2</c>, then Rep(P) = + <c>{match,LINE,Rep(P_1),Rep(P_2)}</c>.</p> + </item> + <item> + <p>If P is a cons pattern <c>[P_h | P_t]</c>, then Rep(P) = + <c>{cons,LINE,Rep(P_h),Rep(P_t)}</c>.</p> + </item> + <item> + <p>If P is a map pattern <c>#{A_1, ..., A_k}</c>, where each + <c>A_i</c> is an association <c>P_i_1 := P_i_2</c>, then Rep(P) = + <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. + For Rep(A), see below.</p> + </item> + <item> + <p>If P is a nil pattern <c>[]</c>, then Rep(P) = + <c>{nil,LINE}</c>.</p> + </item> + <item> + <p>If P is an operator pattern <c>P_1 Op P_2</c>, where <c>Op</c> is a + binary operator (this is either an occurrence of <c>++</c> applied to + a literal string or character list, or an occurrence of an expression + that can be evaluated to a number at compile time), then Rep(P) = + <c>{op,LINE,Op,Rep(P_1),Rep(P_2)}</c>.</p> + </item> + <item> + <p>If P is an operator pattern <c>Op P_0</c>, where <c>Op</c> is a + unary operator (this is an occurrence of an expression that can be + evaluated to a number at compile time), then Rep(P) = + <c>{op,LINE,Op,Rep(P_0)}</c>.</p> + </item> + <item> + <p>If P is a parenthesized pattern <c>( P_0 )</c>, then Rep(P) = + <c>Rep(P_0)</c>, that is, parenthesized patterns cannot be + distinguished from their bodies.</p> + </item> + <item> + <p>If P is a record field index pattern <c>#Name.Field</c>, + where <c>Field</c> is an atom, then Rep(P) = + <c>{record_index,LINE,Name,Rep(Field)}</c>.</p> + </item> + <item> + <p>If P is a record pattern <c>#Name{Field_1=P_1, ..., Field_k=P_k}</c>, + where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(P) = + <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., + {record_field,LINE,Rep(Field_k),Rep(P_k)}]}</c>.</p> + </item> + <item> + <p>If P is a tuple pattern <c>{P_1, ..., P_k}</c>, then Rep(P) = + <c>{tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}</c>.</p> + </item> + <item> + <p>If P is a universal pattern <c>_</c>, then Rep(P) = + <c>{var,LINE,'_'}</c>.</p></item> + <item> + <p>If P is a variable pattern <c>V</c>, then Rep(P) = + <c>{var,LINE,A}</c>, where A is an atom with a printname consisting + of the same characters as <c>V</c>.</p> + </item> </list> - <p>Note that every pattern has the same source form as some expression, and is - represented the same way as the corresponding expression.</p> + + <p>Notice that every pattern has the same source form as some expression, + and is represented in the same way as the corresponding expression.</p> </section> <section> <title>Expressions</title> - <p>A body B is a nonempty sequence of expressions <c>E_1, ..., E_k</c>, + <p>A body B is a non-empty sequence of expressions <c>E_1, ..., E_k</c>, and Rep(B) = <c>[Rep(E_1), ..., Rep(E_k)]</c>.</p> - <p>An expression E is one of the following alternatives:</p> + + <p>An expression E is one of the following:</p> + <list type="bulleted"> - <item>If E is an atomic literal <c>L</c>, then Rep(E) = Rep(L).</item> - <item>If E is a bit string comprehension - <c><<E_0 || Q_1, ..., Q_k>></c>, - where each <c>Q_i</c> is a qualifier, then - Rep(E) = <c>{bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>. - For Rep(Q), see below.</item> - <item>If E is a bit string constructor - <c><<E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>></c>, - where each <c>Size_i</c> is an expression and each - <c>TSL_i</c> is a type specificer list, then Rep(E) = - <c>{bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}</c>. - For Rep(TSL), see below. - An omitted <c>Size_i</c> is represented by <c>default</c>. - An omitted <c>TSL_i</c> is represented by <c>default</c>.</item> - <item>If E is a block expression <c>begin B end</c>, - where <c>B</c> is a body, then - Rep(E) = <c>{block,LINE,Rep(B)}</c>.</item> - <item>If E is a case expression <c>case E_0 of Cc_1 ; ... ; Cc_k end</c>, - where <c>E_0</c> is an expression and each <c>Cc_i</c> is a - case clause then Rep(E) = - <c>{'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</item> - <item>If E is a catch expression <c>catch E_0</c>, then - Rep(E) = <c>{'catch',LINE,Rep(E_0)}</c>.</item> - <item>If E is a cons skeleton <c>[E_h | E_t]</c>, then - Rep(E) = <c>{cons,LINE,Rep(E_h),Rep(E_t)}</c>.</item> - <item>If E is a fun expression <c>fun Name/Arity</c>, then - Rep(E) = <c>{'fun',LINE,{function,Name,Arity}}</c>.</item> - <item>If E is a fun expression - <c>fun Module:Name/Arity</c>, then Rep(E) = - <c>{'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}</c>. - (Before the R15 release: Rep(E) = - <c>{'fun',LINE,{function,Module,Name,Arity}}</c>.)</item> - <item>If E is a fun expression <c>fun Fc_1 ; ... ; Fc_k end</c>, - where each <c>Fc_i</c> is a function clause then Rep(E) = - <c>{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}</c>.</item> - <item>If E is a fun expression - <c>fun Name Fc_1 ; ... ; Name Fc_k end</c>, - where <c>Name</c> is a variable and each - <c>Fc_i</c> is a function clause then Rep(E) = - <c>{named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}</c>. - </item> - <item>If E is a function call <c>E_0(E_1, ..., E_k)</c>, then - Rep(E) = <c>{call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}</c>.</item> - <item>If E is a function call <c>E_m:E_0(E_1, ..., E_k)</c>, - then Rep(E) = - <c>{call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}</c>. - </item> - <item>If E is an if expression <c>if Ic_1 ; ... ; Ic_k end</c>, - where each <c>Ic_i</c> is an if clause then Rep(E) = - <c>{'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}</c>.</item> - <item>If E is a list comprehension <c>[E_0 || Q_1, ..., Q_k]</c>, - where each <c>Q_i</c> is a qualifier, then Rep(E) = - <c>{lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>. For Rep(Q), see - below.</item> - <item>If E is a map creation <c>#{A_1, ..., A_k}</c>, - where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c> - or <c>E_i_1 := E_i_2</c>, then Rep(E) = - <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. For Rep(A), see - below.</item> - <item>If E is a map update <c>E_0#{A_1, ..., A_k}</c>, - where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c> - or <c>E_i_1 := E_i_2</c>, then Rep(E) = - <c>{map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}</c>. - For Rep(A), see below.</item> - <item>If E is a match operator expression <c>P = E_0</c>, - where <c>P</c> is a pattern, then - Rep(E) = <c>{match,LINE,Rep(P),Rep(E_0)}</c>.</item> - <item>If E is nil, <c>[]</c>, then - Rep(E) = <c>{nil,LINE}</c>.</item> - <item>If E is an operator expression <c>E_1 Op E_2</c>, - where <c>Op</c> is a binary operator other than the match - operator <c>=</c>, then - Rep(E) = <c>{op,LINE,Op,Rep(E_1),Rep(E_2)}</c>.</item> - <item>If E is an operator expression <c>Op E_0</c>, - where <c>Op</c> is a unary operator, then - Rep(E) = <c>{op,LINE,Op,Rep(E_0)}</c>.</item> - <item>If E is a parenthesized expression <c>( E_0 )</c>, then - Rep(E) = <c>Rep(E_0)</c>, that is, parenthesized - expressions cannot be distinguished from their bodies.</item> - <item>If E is a receive expression <c>receive Cc_1 ; ... ; Cc_k end</c>, - where each <c>Cc_i</c> is a case clause then Rep(E) = - <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</item> - <item>If E is a receive expression - <c>receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end</c>, - where each <c>Cc_i</c> is a case clause, - <c>E_0</c> is an expression and <c>B_t</c> is a body, then Rep(E) = - <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}</c>.</item> - <item>If E is a record creation - <c>#Name{Field_1=E_1, ..., Field_k=E_k}</c>, - where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(E) = - <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</item> - <item>If E is a record field access <c>E_0#Name.Field</c>, - where <c>Field</c> is an atom, then - Rep(E) = <c>{record_field,LINE,Rep(E_0),Name,Rep(Field)}</c>.</item> - <item>If E is a record field index <c>#Name.Field</c>, - where <c>Field</c> is an atom, then - Rep(E) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item> - <item>If E is a record update - <c>E_0#Name{Field_1=E_1, ..., Field_k=E_k}</c>, - where each <c>Field_i</c> is an atom, then Rep(E) = - <c>{record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</item> - <item>If E is a tuple skeleton <c>{E_1, ..., E_k}</c>, then - Rep(E) = <c>{tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}</c>.</item> - <item>If E is a try expression <c>try B catch Tc_1 ; ... ; Tc_k end</c>, - where <c>B</c> is a body and each <c>Tc_i</c> is a catch clause then - Rep(E) = - <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}</c>.</item> - <item>If E is a try expression - <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end</c>, - where <c>B</c> is a body, - each <c>Cc_i</c> is a case clause and - each <c>Tc_j</c> is a catch clause then Rep(E) = - <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}</c>.</item> - <item>If E is a try expression <c>try B after A end</c>, - where <c>B</c> and <c>A</c> are bodies then Rep(E) = - <c>{'try',LINE,Rep(B),[],[],Rep(A)}</c>.</item> - <item>If E is a try expression - <c>try B of Cc_1 ; ... ; Cc_k after A end</c>, - where <c>B</c> and <c>A</c> are a bodies and - each <c>Cc_i</c> is a case clause then Rep(E) = - <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}</c>.</item> - <item>If E is a try expression - <c>try B catch Tc_1 ; ... ; Tc_k after A end</c>, - where <c>B</c> and <c>A</c> are bodies and - each <c>Tc_i</c> is a catch clause then Rep(E) = - <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}</c>.</item> - <item>If E is a try expression - <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end</c>, - where <c>B</c> and <c>A</c> are a bodies, - each <c>Cc_i</c> is a case clause, and - each <c>Tc_j</c> is a catch clause then - Rep(E) = - <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}</c>.</item> - <item>If E is a variable <c>V</c>, then Rep(E) = <c>{var,LINE,A}</c>, - where <c>A</c> is an atom with a printname consisting of the same - characters as <c>V</c>.</item> + <item> + <p>If E is an atomic literal <c>L</c>, then Rep(E) = Rep(L).</p> + </item> + <item> + <p>If E is a bitstring comprehension + <c><<E_0 || Q_1, ..., Q_k>></c>, + where each <c>Q_i</c> is a qualifier, then Rep(E) = + <c>{bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>. + For Rep(Q), see below.</p> + </item> + <item> + <p>If E is a bitstring constructor + <c><<E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>></c>, + where each <c>Size_i</c> is an expression and each + <c>TSL_i</c> is a type specificer list, then Rep(E) = + <c>{bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, + ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}</c>. + For Rep(TSL), see below. + An omitted <c>Size_i</c> is represented by <c>default</c>. + An omitted <c>TSL_i</c> is represented by <c>default</c>.</p> + </item> + <item> + <p>If E is a block expression <c>begin B end</c>, + where <c>B</c> is a body, then Rep(E) = + <c>{block,LINE,Rep(B)}</c>.</p> + </item> + <item> + <p>If E is a case expression <c>case E_0 of Cc_1 ; ... ; Cc_k end</c>, + where <c>E_0</c> is an expression and each <c>Cc_i</c> is a + case clause, then Rep(E) = + <c>{'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</p> + </item> + <item> + <p>If E is a catch expression <c>catch E_0</c>, then Rep(E) = + <c>{'catch',LINE,Rep(E_0)}</c>.</p> + </item> + <item> + <p>If E is a cons skeleton <c>[E_h | E_t]</c>, then Rep(E) = + <c>{cons,LINE,Rep(E_h),Rep(E_t)}</c>.</p> + </item> + <item> + <p>>If E is a fun expression <c>fun Name/Arity</c>, then Rep(E) = + <c>{'fun',LINE,{function,Name,Arity}}</c>.</p> + </item> + <item> + <p>If E is a fun expression <c>fun Module:Name/Arity</c>, then Rep(E) = + <c>{'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}</c>. + (Before Erlang/OTP R15: Rep(E) = + <c>{'fun',LINE,{function,Module,Name,Arity}}</c>.)</p> + </item> + <item> + <p>If E is a fun expression <c>fun Fc_1 ; ... ; Fc_k end</c>, + where each <c>Fc_i</c> is a function clause, then Rep(E) = + <c>{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}</c>.</p> + </item> + <item> + <p>If E is a fun expression <c>fun Name Fc_1 ; ... ; Name Fc_k end</c>, + where <c>Name</c> is a variable and each + <c>Fc_i</c> is a function clause, then Rep(E) = + <c>{named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}</c>.</p> + </item> + <item> + <p>If E is a function call <c>E_0(E_1, ..., E_k)</c>, then Rep(E) = + <c>{call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}</c>.</p> + </item> + <item> + <p>If E is a function call <c>E_m:E_0(E_1, ..., E_k)</c>, then Rep(E) = + <c>{call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., + Rep(E_k)]}</c>.</p> + </item> + <item> + <p>If E is an if expression <c>if Ic_1 ; ... ; Ic_k end</c>, + where each <c>Ic_i</c> is an if clause, then Rep(E) = + <c>{'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}</c>.</p> + </item> + <item> + <p>If E is a list comprehension <c>[E_0 || Q_1, ..., Q_k]</c>, + where each <c>Q_i</c> is a qualifier, then Rep(E) = + <c>{lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>. + For Rep(Q), see below.</p> + </item> + <item> + <p>If E is a map creation <c>#{A_1, ..., A_k}</c>, + where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c> + or <c>E_i_1 := E_i_2</c>, then Rep(E) = + <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. + For Rep(A), see below.</p> + </item> + <item> + <p>If E is a map update <c>E_0#{A_1, ..., A_k}</c>, + where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c> + or <c>E_i_1 := E_i_2</c>, then Rep(E) = + <c>{map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}</c>. + For Rep(A), see below.</p> + </item> + <item> + <p>If E is a match operator expression <c>P = E_0</c>, + where <c>P</c> is a pattern, then Rep(E) = + <c>{match,LINE,Rep(P),Rep(E_0)}</c>.</p> + </item> + <item> + <p>If E is nil, <c>[]</c>, then Rep(E) = <c>{nil,LINE}</c>.</p> + </item> + <item> + <p>If E is an operator expression <c>E_1 Op E_2</c>, + where <c>Op</c> is a binary operator other than match operator + <c>=</c>, then Rep(E) = + <c>{op,LINE,Op,Rep(E_1),Rep(E_2)}</c>.</p> + </item> + <item> + <p>If E is an operator expression <c>Op E_0</c>, + where <c>Op</c> is a unary operator, then Rep(E) = + <c>{op,LINE,Op,Rep(E_0)}</c>.</p> + </item> + <item> + <p>If E is a parenthesized expression <c>( E_0 )</c>, then Rep(E) = + <c>Rep(E_0)</c>, that is, parenthesized expressions cannot be + distinguished from their bodies.</p> + </item> + <item> + <p>If E is a receive expression <c>receive Cc_1 ; ... ; Cc_k end</c>, + where each <c>Cc_i</c> is a case clause, then Rep(E) = + <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</p> + </item> + <item> + <p>If E is a receive expression + <c>receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end</c>, + where each <c>Cc_i</c> is a case clause, <c>E_0</c> is an expression, + and <c>B_t</c> is a body, then Rep(E) = + <c>{'receive',LINE,[Rep(Cc_1), ..., + Rep(Cc_k)],Rep(E_0),Rep(B_t)}</c>.</p> + </item> + <item> + <p>If E is a record creation + <c>#Name{Field_1=E_1, ..., Field_k=E_k}</c>, + where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(E) = + <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, + ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</p> + </item> + <item> + <p>If E is a record field access <c>E_0#Name.Field</c>, + where <c>Field</c> is an atom, then Rep(E) = + <c>{record_field,LINE,Rep(E_0),Name,Rep(Field)}</c>.</p> + </item> + <item> + <p>If E is a record field index <c>#Name.Field</c>, + where <c>Field</c> is an atom, then Rep(E) = + <c>{record_index,LINE,Name,Rep(Field)}</c>.</p></item> + <item> + <p>If E is a record update + <c>E_0#Name{Field_1=E_1, ..., Field_k=E_k}</c>, + where each <c>Field_i</c> is an atom, then Rep(E) = + <c>{record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, + ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</p> + </item> + <item> + <p>If E is a tuple skeleton <c>{E_1, ..., E_k}</c>, then Rep(E) = + <c>{tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}</c>.</p> + </item> + <item> + <p>If E is a try expression <c>try B catch Tc_1 ; ... ; Tc_k end</c>, + where <c>B</c> is a body and each <c>Tc_i</c> is a catch clause, + then Rep(E) = + <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}</c>.</p> + </item> + <item> + <p>If E is a try expression + <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end</c>, + where <c>B</c> is a body, each <c>Cc_i</c> is a case clause, and + each <c>Tc_j</c> is a catch clause, then Rep(E) = + <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., + Rep(Tc_n)],[]}</c>.</p> + </item> + <item> + <p>If E is a try expression <c>try B after A end</c>, + where <c>B</c> and <c>A</c> are bodies, then Rep(E) = + <c>{'try',LINE,Rep(B),[],[],Rep(A)}</c>.</p> + </item> + <item> + <p>If E is a try expression + <c>try B of Cc_1 ; ... ; Cc_k after A end</c>, + where <c>B</c> and <c>A</c> are a bodies, + and each <c>Cc_i</c> is a case clause, then Rep(E) = + <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., + Rep(Cc_k)],[],Rep(A)}</c>.</p> + </item> + <item> + <p>If E is a try expression + <c>try B catch Tc_1 ; ... ; Tc_k after A end</c>, + where <c>B</c> and <c>A</c> are bodies, + and each <c>Tc_i</c> is a catch clause, then Rep(E) = + <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., + Rep(Tc_k)],Rep(A)}</c>.</p> + </item> + <item> + <p>If E is a try expression + <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A + end</c>, where <c>B</c> and <c>A</c> are a bodies, + each <c>Cc_i</c> is a case clause, + and each <c>Tc_j</c> is a catch clause, then Rep(E) = + <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., + Rep(Tc_n)],Rep(A)}</c>.</p> + </item> + <item> + <p>If E is a variable <c>V</c>, then Rep(E) = <c>{var,LINE,A}</c>, + where <c>A</c> is an atom with a printname consisting of the same + characters as <c>V</c>.</p> + </item> </list> <section> <title>Qualifiers</title> - <p>A qualifier Q is one of the following alternatives:</p> + <p>A qualifier Q is one of the following:</p> + <list type="bulleted"> - <item>If Q is a filter <c>E</c>, where <c>E</c> is an expression, then - Rep(Q) = <c>Rep(E)</c>.</item> - <item>If Q is a generator <c>P <- E</c>, where <c>P</c> is - a pattern and <c>E</c> is an expression, then - Rep(Q) = <c>{generate,LINE,Rep(P),Rep(E)}</c>.</item> - <item>If Q is a bit string generator - <c>P <= E</c>, where <c>P</c> is - a pattern and <c>E</c> is an expression, then - Rep(Q) = <c>{b_generate,LINE,Rep(P),Rep(E)}</c>.</item> + <item> + <p>If Q is a filter <c>E</c>, where <c>E</c> is an expression, then + Rep(Q) = <c>Rep(E)</c>.</p> + </item> + <item> + <p>If Q is a generator <c>P <- E</c>, where <c>P</c> is + a pattern and <c>E</c> is an expression, then Rep(Q) = + <c>{generate,LINE,Rep(P),Rep(E)}</c>.</p> + </item> + <item> + <p>If Q is a bitstring generator <c>P <= E</c>, where <c>P</c> is + a pattern and <c>E</c> is an expression, then Rep(Q) = + <c>{b_generate,LINE,Rep(P),Rep(E)}</c>.</p> + </item> </list> </section> <section> - <title>Bit String Element Type Specifiers</title> - <p>A type specifier list TSL for a bit string element is a sequence + <title>Bitstring Element Type Specifiers</title> + <p>A type specifier list TSL for a bitstring element is a sequence of type specifiers <c>TS_1 - ... - TS_k</c>, and Rep(TSL) = <c>[Rep(TS_1), ..., Rep(TS_k)]</c>.</p> + <list type="bulleted"> - <item>If TS is a type specifier <c>A</c>, where <c>A</c> is an atom, - then Rep(TS) = <c>A</c>.</item> - <item>If TS is a type specifier <c>A:Value</c>, - where <c>A</c> is an atom and <c>Value</c> is an integer, - then Rep(TS) = <c>{A,Value}</c>.</item> + <item> + <p>If TS is a type specifier <c>A</c>, where <c>A</c> is an atom, + then Rep(TS) = <c>A</c>.</p> + </item> + <item> + <p>If TS is a type specifier <c>A:Value</c>, + where <c>A</c> is an atom and <c>Value</c> is an integer, + then Rep(TS) = <c>{A,Value}</c>.</p> + </item> </list> </section> <section> <title>Associations</title> - <p>An association A is one of the following alternatives:</p> + <p>An association A is one of the following:</p> + <list type="bulleted"> - <item>If A is an association <c>K => V</c>, - then Rep(A) = <c>{map_field_assoc,LINE,Rep(K),Rep(V)}</c>. - </item> - <item>If A is an association <c>K := V</c>, - then Rep(A) = <c>{map_field_exact,LINE,Rep(K),Rep(V)}</c>. - </item> + <item> + <p>If A is an association <c>K => V</c>, + then Rep(A) = <c>{map_field_assoc,LINE,Rep(K),Rep(V)}</c>.</p> + </item> + <item> + <p>If A is an association <c>K := V</c>, + then Rep(A) = <c>{map_field_exact,LINE,Rep(K),Rep(V)}</c>.</p> + </item> </list> </section> </section> <section> <title>Clauses</title> - <p>There are function clauses, if clauses, case clauses + <p>There are function clauses, if clauses, case clauses, and catch clauses.</p> - <p>A clause <c>C</c> is one of the following alternatives:</p> + + <p>A clause C is one of the following:</p> + <list type="bulleted"> - <item>If C is a case clause <c>P -> B</c>, - where <c>P</c> is a pattern and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[Rep(P)],[],Rep(B)}</c>.</item> - <item>If C is a case clause <c>P when Gs -> B</c>, - where <c>P</c> is a pattern, - <c>Gs</c> is a guard sequence and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}</c>.</item> - <item>If C is a catch clause <c>P -> B</c>, - where <c>P</c> is a pattern and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</item> - <item>If C is a catch clause <c>X : P -> B</c>, - where <c>X</c> is an atomic literal or a variable pattern, - <c>P</c> is a pattern, and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</item> - <item>If C is a catch clause <c>P when Gs -> B</c>, - where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence, - and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</item> - <item>If C is a catch clause <c>X : P when Gs -> B</c>, - where <c>X</c> is an atomic literal or a variable pattern, - <c>P</c> is a pattern, <c>Gs</c> is a guard sequence, - and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</item> - <item>If C is a function clause <c>( Ps ) -> B</c>, - where <c>Ps</c> is a pattern sequence and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,Rep(Ps),[],Rep(B)}</c>.</item> - <item>If C is a function clause <c>( Ps ) when Gs -> B</c>, - where <c>Ps</c> is a pattern sequence, - <c>Gs</c> is a guard sequence and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}</c>.</item> - <item>If C is an if clause <c>Gs -> B</c>, - where <c>Gs</c> is a guard sequence and <c>B</c> is a body, then - Rep(C) = <c>{clause,LINE,[],Rep(Gs),Rep(B)}</c>.</item> + <item> + <p>If C is a case clause <c>P -> B</c>, + where <c>P</c> is a pattern and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[Rep(P)],[],Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a case clause <c>P when Gs -> B</c>, + where <c>P</c> is a pattern, + <c>Gs</c> is a guard sequence, and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a catch clause <c>P -> B</c>, + where <c>P</c> is a pattern and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a catch clause <c>X : P -> B</c>, + where <c>X</c> is an atomic literal or a variable pattern, + <c>P</c> is a pattern, and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a catch clause <c>P when Gs -> B</c>, + where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence, + and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a catch clause <c>X : P when Gs -> B</c>, + where <c>X</c> is an atomic literal or a variable pattern, + <c>P</c> is a pattern, <c>Gs</c> is a guard sequence, + and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a function clause <c>( Ps ) -> B</c>, + where <c>Ps</c> is a pattern sequence and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,Rep(Ps),[],Rep(B)}</c>.</p> + </item> + <item> + <p>If C is a function clause <c>( Ps ) when Gs -> B</c>, + where <c>Ps</c> is a pattern sequence, + <c>Gs</c> is a guard sequence and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}</c>.</p> + </item> + <item> + <p>If C is an if clause <c>Gs -> B</c>, + where <c>Gs</c> is a guard sequence and <c>B</c> is a body, then + Rep(C) = <c>{clause,LINE,[],Rep(Gs),Rep(B)}</c>.</p> + </item> </list> </section> @@ -460,205 +650,292 @@ <title>Guards</title> <p>A guard sequence Gs is a sequence of guards <c>G_1; ...; G_k</c>, and Rep(Gs) = <c>[Rep(G_1), ..., Rep(G_k)]</c>. If the guard sequence is - empty, Rep(Gs) = <c>[]</c>.</p> - <p>A guard G is a nonempty sequence of guard tests + empty, then Rep(Gs) = <c>[]</c>.</p> + + <p>A guard G is a non-empty sequence of guard tests <c>Gt_1, ..., Gt_k</c>, and Rep(G) = <c>[Rep(Gt_1), ..., Rep(Gt_k)]</c>.</p> - <p>A guard test <c>Gt</c> is one of the following alternatives:</p> + + <p>A guard test Gt is one of the following:</p> + <list type="bulleted"> - <item>If Gt is an atomic literal <c>L</c>, then Rep(Gt) = Rep(L).</item> - <item>If Gt is a bit string constructor - <c><<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>></c>, - where each <c>Size_i</c> is a guard test and each - <c>TSL_i</c> is a type specificer list, then - Rep(Gt) = <c>{bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}</c>. - For Rep(TSL), see above. - An omitted <c>Size_i</c> is represented by <c>default</c>. - An omitted <c>TSL_i</c> is represented by <c>default</c>.</item> - <item>If Gt is a cons skeleton <c>[Gt_h | Gt_t]</c>, then - Rep(Gt) = <c>{cons,LINE,Rep(Gt_h),Rep(Gt_t)}</c>.</item> - <item>If Gt is a function call <c>A(Gt_1, ..., Gt_k)</c>, - where <c>A</c> is an atom, then Rep(Gt) = - <c>{call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item> - <item>If Gt is a function call <c>A_m:A(Gt_1, ..., Gt_k)</c>, - where <c>A_m</c> is the atom <c>erlang</c> and <c>A</c> is - an atom or an operator, then Rep(Gt) = - <c>{call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item> - <item>If Gt is a map creation <c>#{A_1, ..., A_k}</c>, - where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c> - or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) = - <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. For Rep(A), see - above.</item> - <item>If Gt is a map update <c>Gt_0#{A_1, ..., A_k}</c>, where each - <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c> - or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) = - <c>{map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}</c>. - For Rep(A), see above.</item> - <item>If Gt is nil, <c>[]</c>, - then Rep(Gt) = <c>{nil,LINE}</c>.</item> - <item>If Gt is an operator guard test <c>Gt_1 Op Gt_2</c>, - where <c>Op</c> is a binary operator other than the match - operator <c>=</c>, then - Rep(Gt) = <c>{op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}</c>.</item> - <item>If Gt is an operator guard test <c>Op Gt_0</c>, - where <c>Op</c> is a unary operator, then - Rep(Gt) = <c>{op,LINE,Op,Rep(Gt_0)}</c>.</item> - <item>If Gt is a parenthesized guard test <c>( Gt_0 )</c>, then - Rep(Gt) = <c>Rep(Gt_0)</c>, that is, parenthesized - guard tests cannot be distinguished from their bodies.</item> - <item>If Gt is a record creation - <c>#Name{Field_1=Gt_1, ..., Field_k=Gt_k}</c>, - where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(Gt) = - <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}</c>.</item> - <item>If Gt is a record field access <c>Gt_0#Name.Field</c>, - where <c>Field</c> is an atom, then - Rep(Gt) = <c>{record_field,LINE,Rep(Gt_0),Name,Rep(Field)}</c>.</item> - <item>If Gt is a record field index <c>#Name.Field</c>, - where <c>Field</c> is an atom, then - Rep(Gt) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item> - <item>If Gt is a tuple skeleton <c>{Gt_1, ..., Gt_k}</c>, then - Rep(Gt) = <c>{tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item> - <item>If Gt is a variable pattern <c>V</c>, then - Rep(Gt) = <c>{var,LINE,A}</c>, where A is an atom with - a printname consisting of the same characters as <c>V</c>.</item> + <item> + <p>If Gt is an atomic literal <c>L</c>, then Rep(Gt) = Rep(L).</p> + </item> + <item> + <p>If Gt is a bitstring constructor + <c><<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>></c>, + where each <c>Size_i</c> is a guard test and each + <c>TSL_i</c> is a type specificer list, then Rep(Gt) = + <c>{bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, + ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}</c>. + For Rep(TSL), see above. + An omitted <c>Size_i</c> is represented by <c>default</c>. + An omitted <c>TSL_i</c> is represented by <c>default</c>.</p> + </item> + <item> + <p>If Gt is a cons skeleton <c>[Gt_h | Gt_t]</c>, then Rep(Gt) = + <c>{cons,LINE,Rep(Gt_h),Rep(Gt_t)}</c>.</p> + </item> + <item> + <p>If Gt is a function call <c>A(Gt_1, ..., Gt_k)</c>, + where <c>A</c> is an atom, then Rep(Gt) = + <c>{call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</p> + </item> + <item> + <p>If Gt is a function call <c>A_m:A(Gt_1, ..., Gt_k)</c>, + where <c>A_m</c> is the atom <c>erlang</c> and <c>A</c> is + an atom or an operator, then Rep(Gt) = + <c>{call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., + Rep(Gt_k)]}</c>.</p> + </item> + <item> + <p>If Gt is a map creation <c>#{A_1, ..., A_k}</c>, + where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c> + or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) = + <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. + For Rep(A), see above.</p> + </item> + <item> + <p>If Gt is a map update <c>Gt_0#{A_1, ..., A_k}</c>, + where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c> + or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) = + <c>{map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}</c>. + For Rep(A), see above.</p> + </item> + <item> + <p>If Gt is nil, <c>[]</c>, then Rep(Gt) = <c>{nil,LINE}</c>.</p> + </item> + <item> + <p>If Gt is an operator guard test <c>Gt_1 Op Gt_2</c>, + where <c>Op</c> is a binary operator other than match + operator <c>=</c>, then Rep(Gt) = + <c>{op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}</c>.</p> + </item> + <item> + <p>If Gt is an operator guard test <c>Op Gt_0</c>, + where <c>Op</c> is a unary operator, then Rep(Gt) = + <c>{op,LINE,Op,Rep(Gt_0)}</c>.</p> + </item> + <item> + <p>If Gt is a parenthesized guard test <c>( Gt_0 )</c>, then Rep(Gt) = + <c>Rep(Gt_0)</c>, that is, parenthesized + guard tests cannot be distinguished from their bodies.</p> + </item> + <item> + <p>If Gt is a record creation + <c>#Name{Field_1=Gt_1, ..., Field_k=Gt_k}</c>, + where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(Gt) = + <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, + ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}</c>.</p> + </item> + <item> + <p>If Gt is a record field access <c>Gt_0#Name.Field</c>, + where <c>Field</c> is an atom, then Rep(Gt) = + <c>{record_field,LINE,Rep(Gt_0),Name,Rep(Field)}</c>.</p> + </item> + <item> + <p>If Gt is a record field index <c>#Name.Field</c>, + where <c>Field</c> is an atom, then Rep(Gt) = + <c>{record_index,LINE,Name,Rep(Field)}</c>.</p> + </item> + <item> + <p>If Gt is a tuple skeleton <c>{Gt_1, ..., Gt_k}</c>, then Rep(Gt) = + <c>{tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</p> + </item> + <item> + <p>If Gt is a variable pattern <c>V</c>, then Rep(Gt) = + <c>{var,LINE,A}</c>, where A is an atom with + a printname consisting of the same characters as <c>V</c>.</p> + </item> </list> - <p>Note that every guard test has the same source form as some expression, - and is represented the same way as the corresponding expression.</p> + + <p>Notice that every guard test has the same source form as some expression, + and is represented in the same way as the corresponding expression.</p> </section> <section> <title>Types</title> <list type="bulleted"> - <item>If T is an annotated type <c>A :: T_0</c>, - where <c>A</c> is a variable, then Rep(T) = - <c>{ann_type,LINE,[Rep(A),Rep(T_0)]}</c>.</item> - <item>If T is an atom or integer literal L, then Rep(T) = Rep(L). - </item> - <item>If T is a bit string type <c><<_:M,_:_*N>></c>, - where <c>M</c> and <c>N</c> are singleton integer types, then Rep(T) = - <c>{type,LINE,binary,[Rep(M),Rep(N)]}</c>.</item> - <item>If T is the empty list type <c>[]</c>, then Rep(T) = - <c>{type,Line,nil,[]}</c>.</item> - <item>If T is a fun type <c>fun()</c>, then Rep(T) = - <c>{type,LINE,'fun',[]}</c>.</item> - <item>If T is a fun type <c>fun((...) -> T_0)</c>, then - Rep(T) = <c>{type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}</c>. - </item> - <item>If T is a fun type <c>fun(Ft)</c>, where - <c>Ft</c> is a function type, - then Rep(T) = <c>Rep(Ft)</c>. For Rep(Ft), see below.</item> - <item>If T is an integer range type <c>L .. H</c>, - where <c>L</c> and <c>H</c> are singleton integer types, then - Rep(T) = <c>{type,LINE,range,[Rep(L),Rep(H)]}</c>.</item> - <item>If T is a map type <c>map()</c>, then Rep(T) = - <c>{type,LINE,map,any}</c>.</item> - <item>If T is a map type <c>#{A_1, ..., A_k}</c>, where each - <c>A_i</c> is an association type, then Rep(T) = - <c>{type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}</c>. - For Rep(A), see below.</item> - <item>If T is an operator type <c>T_1 Op T_2</c>, - where <c>Op</c> is a binary operator (this is an occurrence of - an expression that can be evaluated to an integer at compile - time), then - Rep(T) = <c>{op,LINE,Op,Rep(T_1),Rep(T_2)}</c>.</item> - <item>If T is an operator type <c>Op T_0</c>, where <c>Op</c> is a - unary operator (this is an occurrence of - an expression that can be evaluated to an integer at compile time), - then Rep(T) = <c>{op,LINE,Op,Rep(T_0)}</c>.</item> - <item>If T is <c>( T_0 )</c>, then Rep(T) = <c>Rep(T_0)</c>, - that is, parenthesized types cannot be distinguished from their - bodies.</item> - <item>If T is a predefined (or built-in) type <c>N(T_1, ..., T_k)</c>, - then Rep(T) = - <c>{type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</item> - <item>If T is a record type <c>#Name{F_1, ..., F_k}</c>, - where each <c>F_i</c> is a record field type, then Rep(T) = - <c>{type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}</c>. - For Rep(F), see below.</item> - <item>If T is a remote type <c>M:N(T_1, ..., T_k)</c>, then Rep(T) = - <c>{remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ..., Rep(T_k)]]}</c>. - </item> - <item>If T is a tuple type <c>tuple()</c>, then Rep(T) = - <c>{type,LINE,tuple,any}</c>.</item> - <item>If T is a tuple type <c>{T_1, ..., T_k}</c>, then Rep(T) = - <c>{type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}</c>.</item> - <item>If T is a type union <c>T_1 | ... | T_k</c>, then Rep(T) = - <c>{type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}</c>.</item> - <item>If T is a type variable <c>V</c>, then Rep(T) = - <c>{var,LINE,A}</c>, where <c>A</c> is an atom with a printname - consisting of the same characters as <c>V</c>. A type variable - is any variable except underscore (<c>_</c>).</item> - <item>If T is a user-defined type <c>N(T_1, ..., T_k)</c>, - then Rep(T) = - <c>{user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</item> + <item> + <p>If T is an annotated type <c>A :: T_0</c>, + where <c>A</c> is a variable, then Rep(T) = + <c>{ann_type,LINE,[Rep(A),Rep(T_0)]}</c>.</p> + </item> + <item> + <p>If T is an atom or integer literal L, then Rep(T) = Rep(L).</p> + </item> + <item> + <p>If T is a bitstring type <c><<_:M,_:_*N>></c>, + where <c>M</c> and <c>N</c> are singleton integer types, then Rep(T) = + <c>{type,LINE,binary,[Rep(M),Rep(N)]}</c>.</p> + </item> + <item> + <p>If T is the empty list type <c>[]</c>, then Rep(T) = + <c>{type,Line,nil,[]}</c>.</p> + </item> + <item> + <p>If T is a fun type <c>fun()</c>, then Rep(T) = + <c>{type,LINE,'fun',[]}</c>.</p> + </item> + <item> + <p>If T is a fun type <c>fun((...) -> T_0)</c>, then Rep(T) = + <c>{type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}</c>.</p> + </item> + <item> + <p>If T is a fun type <c>fun(Ft)</c>, where + <c>Ft</c> is a function type, then Rep(T) = <c>Rep(Ft)</c>. + For Rep(Ft), see below.</p> + </item> + <item> + <p>If T is an integer range type <c>L .. H</c>, + where <c>L</c> and <c>H</c> are singleton integer types, then Rep(T) = + <c>{type,LINE,range,[Rep(L),Rep(H)]}</c>.</p> + </item> + <item> + <p>If T is a map type <c>map()</c>, then Rep(T) = + <c>{type,LINE,map,any}</c>.</p> + </item> + <item> + <p>If T is a map type <c>#{A_1, ..., A_k}</c>, where each + <c>A_i</c> is an association type, then Rep(T) = + <c>{type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}</c>. + For Rep(A), see below.</p> + </item> + <item> + <p>If T is an operator type <c>T_1 Op T_2</c>, + where <c>Op</c> is a binary operator (this is an occurrence of + an expression that can be evaluated to an integer at compile + time), then Rep(T) = + <c>{op,LINE,Op,Rep(T_1),Rep(T_2)}</c>.</p> + </item> + <item> + <p>If T is an operator type <c>Op T_0</c>, where <c>Op</c> is a + unary operator (this is an occurrence of an expression that can + be evaluated to an integer at compile time), then Rep(T) = + <c>{op,LINE,Op,Rep(T_0)}</c>.</p> + </item> + <item> + <p>If T is <c>( T_0 )</c>, then Rep(T) = <c>Rep(T_0)</c>, that is, + parenthesized types cannot be distinguished from their bodies.</p> + </item> + <item> + <p>If T is a predefined (or built-in) type <c>N(T_1, ..., T_k)</c>, + then Rep(T) = <c>{type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</p> + </item> + <item> + <p>If T is a record type <c>#Name{F_1, ..., F_k}</c>, + where each <c>F_i</c> is a record field type, then Rep(T) = + <c>{type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}</c>. + For Rep(F), see below.</p> + </item> + <item> + <p>If T is a remote type <c>M:N(T_1, ..., T_k)</c>, then Rep(T) = + <c>{remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ..., + Rep(T_k)]]}</c>.</p> + </item> + <item> + <p>If T is a tuple type <c>tuple()</c>, then Rep(T) = + <c>{type,LINE,tuple,any}</c>.</p> + </item> + <item> + <p>If T is a tuple type <c>{T_1, ..., T_k}</c>, then Rep(T) = + <c>{type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}</c>.</p> + </item> + <item> + <p>If T is a type union <c>T_1 | ... | T_k</c>, then Rep(T) = + <c>{type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}</c>.</p> + </item> + <item> + <p>If T is a type variable <c>V</c>, then Rep(T) = + <c>{var,LINE,A}</c>, where <c>A</c> is an atom with a printname + consisting of the same characters as <c>V</c>. A type variable + is any variable except underscore (<c>_</c>).</p> + </item> + <item> + <p>If T is a user-defined type <c>N(T_1, ..., T_k)</c>, then Rep(T) = + <c>{user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</p> + </item> </list> <section> <title>Function Types</title> - <p>A function type Ft is one of the following alternatives:</p> + <p>A function type Ft is one of the following:</p> + <list type="bulleted"> - <item>If Ft is a constrained function type <c>Ft_1 when Fc</c>, - where <c>Ft_1</c> is a function type and - <c>Fc</c> is a function constraint, then Rep(T) = - <c>{type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}</c>. - For Rep(Fc), see below.</item> - <item>If Ft is a function type <c>(T_1, ..., T_n) -> T_0</c>, - where each <c>T_i</c> is a type, then - Rep(Ft) = <c>{type,LINE,'fun',[{type,LINE,product,[Rep(T_1), - ..., Rep(T_n)]},Rep(T_0)]}</c>.</item> + <item> + <p>If Ft is a constrained function type <c>Ft_1 when Fc</c>, + where <c>Ft_1</c> is a function type and + <c>Fc</c> is a function constraint, then Rep(T) = + <c>{type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}</c>. + For Rep(Fc), see below.</p> + </item> + <item> + <p>If Ft is a function type <c>(T_1, ..., T_n) -> T_0</c>, + where each <c>T_i</c> is a type, then Rep(Ft) = + <c>{type,LINE,'fun',[{type,LINE,product,[Rep(T_1), ..., + Rep(T_n)]},Rep(T_0)]}</c>.</p> + </item> </list> </section> <section> <title>Function Constraints</title> - <p>A function constraint Fc is a nonempty sequence of constraints - <c>C_1, ..., C_k</c>, and - Rep(Fc) = <c>[Rep(C_1), ..., Rep(C_k)]</c>.</p> + <p>A function constraint Fc is a non-empty sequence of constraints + <c>C_1, ..., C_k</c>, and + Rep(Fc) = <c>[Rep(C_1), ..., Rep(C_k)]</c>.</p> + <list type="bulleted"> - <item>If C is a constraint <c>is_subtype(V, T)</c> or <c>V :: T</c>, - where <c>V</c> is a type variable and <c>T</c> is a type, then - Rep(C) = <c>{type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}</c>. - </item> + <item>If C is a constraint <c>is_subtype(V, T)</c> or <c>V :: T</c>, + where <c>V</c> is a type variable + and <c>T</c> is a type, then Rep(C) = + <c>{type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}</c>. + </item> </list> </section> <section> <title>Association Types</title> <list type="bulleted"> - <item>If A is an association type <c>K => V</c>, where - <c>K</c> and <c>V</c> are types, then Rep(A) = - <c>{type,LINE,map_field_assoc,[Rep(K),Rep(V)]}</c>.</item> - <item>If A is an association type <c>K := V</c>, where - <c>K</c> and <c>V</c> are types, then Rep(A) = - <c>{type,LINE,map_field_exact,[Rep(K),Rep(V)]}</c>.</item> + <item> + <p>If A is an association type <c>K => V</c>, + where <c>K</c> and <c>V</c> are types, then Rep(A) = + <c>{type,LINE,map_field_assoc,[Rep(K),Rep(V)]}</c>.</p> + </item> + <item> + <p>If A is an association type <c>K := V</c>, + where <c>K</c> and <c>V</c> are types, then Rep(A) = + <c>{type,LINE,map_field_exact,[Rep(K),Rep(V)]}</c>.</p> + </item> </list> </section> <section> <title>Record Field Types</title> <list type="bulleted"> - <item>If F is a record field type <c>Name :: Type</c>, - where <c>Type</c> is a type, then Rep(F) = - <c>{type,LINE,field_type,[Rep(Name),Rep(Type)]}</c>.</item> + <item>If F is a record field type <c>Name :: Type</c>, + where <c>Type</c> is a type, then Rep(F) = + <c>{type,LINE,field_type,[Rep(Name),Rep(Type)]}</c>. + </item> </list> </section> </section> <section> - <title>The Abstract Format After Preprocessing</title> - <p>The compilation option <c>debug_info</c> can be given to the + <title>The Abstract Format after Preprocessing</title> + <p>The compilation option <c>debug_info</c> can be specified to the compiler to have the abstract code stored in - the <c>abstract_code</c> chunk in the BEAM file + the <c>abstract_code</c> chunk in the Beam file (for debugging purposes).</p> - <p>In OTP R9C and later, the <c>abstract_code</c> chunk will - contain</p> - <p><c>{raw_abstract_v1,AbstractCode}</c></p> - <p>where <c>AbstractCode</c> is the abstract code as described - in this document.</p> - <p>In releases of OTP prior to R9C, the abstract code after some more - processing was stored in the BEAM file. The first element of the - tuple would be either <c>abstract_v1</c> (R7B) or <c>abstract_v2</c> - (R8B).</p> + + <p>As from Erlang/OTP R9C, the <c>abstract_code</c> chunk contains + <c>{raw_abstract_v1,AbstractCode}</c>, where <c>AbstractCode</c> is the + abstract code as described in this section.</p> + + <p>In OTP releases before R9C, the abstract code after some more + processing was stored in the Beam file. The first element of the + tuple would be either <c>abstract_v1</c> (in OTP R7B) or + <c>abstract_v2</c> (in OTP R8B).</p> </section> </chapter> diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml index e283acc1b4..be969a8267 100644 --- a/erts/doc/src/alt_dist.xml +++ b/erts/doc/src/alt_dist.xml @@ -22,7 +22,8 @@ </legalnotice> - <title>How to implement an alternative carrier for the Erlang distribution</title> + <title>How to Implement an Alternative Carrier for the Erlang Distribution + </title> <prepared>Patrik Nyblom</prepared> <responsible></responsible> <docno></docno> @@ -32,203 +33,270 @@ <rev>PA2</rev> <file>alt_dist.xml</file> </header> - <p>This document describes how one can implement ones own carrier + <p>This section describes how to implement an alternative carrier protocol for the Erlang distribution. The distribution is normally - carried by the TCP/IP protocol. What's explained here is the method for - replacing TCP/IP with another protocol. </p> - <p>The document is a step by step explanation of the <c><![CDATA[uds_dist]]></c> example - application (seated in the kernel applications <c><![CDATA[examples]]></c> directory). - The <c><![CDATA[uds_dist]]></c> application implements distribution over Unix domain - sockets and is written for the Sun Solaris 2 operating environment. The - mechanisms are however general and applies to any operating system Erlang - runs on. The reason the C code is not made portable, is simply readability.</p> - <note><p>This document was written a long time ago. Most of it is still - valid, but some things have changed since it was first written. - Most notably the driver interface. There have been some updates - to the documentation of the driver presented in this documentation, - but more could be done and are planned for the future. The - reader is encouraged to also read the - <seealso marker="erl_driver">erl_driver</seealso>, and the - <seealso marker="erl_driver">driver_entry</seealso> documentation. - </p></note> + carried by TCP/IP. Here is explained a method for replacing TCP/IP + with another protocol.</p> + + <p>The section is a step-by-step explanation of the + <c><![CDATA[uds_dist]]></c> example application (in the + Kernel application <c><![CDATA[examples]]></c> directory). The + <c><![CDATA[uds_dist]]></c> application implements distribution over Unix + domain sockets and is written for the Sun Solaris 2 operating environment. + The mechanisms are however general and apply to any operating system Erlang + runs on. The reason the C code is not made portable, is simply + readability.</p> + + <note> + <p>This section was written a long time ago. Most of it is still + valid, but some things have changed since then. + Most notably is the driver interface. Some updates have been made + to the documentation of the driver presented here, + but more can be done and is planned for the future. + The reader is encouraged to read the + <seealso marker="erl_driver"><c>erl_driver</c></seealso> and + <seealso marker="driver_entry"><c>driver_entry</c></seealso> + documentation also.</p> + </note> <section> <title>Introduction</title> - <p>To implement a new carrier for the Erlang distribution, one must first - make the protocol available to the Erlang machine, which involves writing - an Erlang driver. There is no way one can use a port program, - there <em>has</em> to - be an Erlang driver. Erlang drivers can either be statically - linked - to the emulator, which can be an alternative when using the open source - distribution of Erlang, or dynamically loaded into the Erlang machines - address space, which is the only alternative if a precompiled version of - Erlang is to be used. </p> - <p>Writing an Erlang driver is by no means easy. The driver is written - as a couple of call-back functions called by the Erlang emulator when - data is sent to the driver or the driver has any data available on a file - descriptor. As the driver call-back routines execute in the main - thread of the Erlang machine, the call-back functions can perform - no blocking activity whatsoever. The call-backs should only set up - file descriptors for waiting and/or read/write available data. All - I/O has to be non blocking. Driver call-backs are however executed - in sequence, why a global state can safely be updated within the - routines. </p> - <p>When the driver is implemented, one would preferably write an - Erlang interface for the driver to be able to test the - functionality of the driver separately. This interface can then - be used by the distribution module which will cover the details of - the protocol from the <c><![CDATA[net_kernel]]></c>. The easiest path is to - mimic the <c><![CDATA[inet]]></c> and <c><![CDATA[inet_tcp]]></c> interfaces, but a lot of - functionality in those modules need not be implemented. In the - example application, only a few of the usual interfaces are - implemented, and they are much simplified.</p> - <p>When the protocol is available to Erlang through a driver and an - Erlang interface module, a distribution module can be - written. The distribution module is a module with well defined - call-backs, much like a <c><![CDATA[gen_server]]></c> (there is no compiler support - for checking the call-backs though). The details of finding other - nodes (i.e. talking to epmd or something similar), creating a - listen port (or similar), connecting to other nodes and performing - the handshakes/cookie verification are all implemented by this - module. There is however a utility module, <c><![CDATA[dist_util]]></c>, that - will do most of the hard work of handling handshakes, cookies, - timers and ticking. Using <c><![CDATA[dist_util]]></c> makes implementing a - distribution module much easier and that's what we are doing in - the example application.</p> - <p>The last step is to create boot scripts to make the protocol - implementation available at boot time. The implementation can be - debugged by starting the distribution when all of the system is - running, but in a real system the distribution should start very - early, why a boot-script and some command line parameters are - necessary. This last step also implies that the Erlang code in the - interface and distribution modules is written in such a way that - it can be run in the startup phase. Most notably there can be no - calls to the <c><![CDATA[application]]></c> module or to any modules not - loaded at boot-time (i.e. only <c><![CDATA[kernel]]></c>, <c><![CDATA[stdlib]]></c> and the - application itself can be used).</p> + <p>To implement a new carrier for the Erlang distribution, the main + steps are as follows.</p> + + <section> + <title>Writing an Erlang Driver</title> + <p>First, the protocol must be available to the Erlang machine, which + involves writing an Erlang driver. A port program cannot be used, + an Erlang driver is required. Erlang drivers can be:</p> + + <list type="bulleted"> + <item> + <p>Statically linked to the emulator, which can be an alternative + when using the open source distribution of Erlang, or</p> + </item> + <item> + <p>Dynamically loaded into the Erlang machines address space, + which is the only alternative if a precompiled version of + Erlang is to be used</p> + </item> + </list> + + <p>Writing an Erlang driver is not easy. The driver is written + as some callback functions called by the Erlang emulator when + data is sent to the driver, or the driver has any data available on + a file descriptor. As the driver callback routines execute in the main + thread of the Erlang machine, the callback functions can perform + no blocking activity whatsoever. The callbacks are only to set up + file descriptors for waiting and/or read/write available data. All + I/O must be non-blocking. Driver callbacks are however executed + in sequence, why a global state can safely be updated within the + routines.</p> + </section> + + <section> + <title>Writing an Erlang Interface for the Driver</title> + <p>When the driver is implemented, one would preferably write an + Erlang interface for the driver to be able to test the + functionality of the driver separately. This interface can then + be used by the distribution module, which will cover the details of + the protocol from the <c><![CDATA[net_kernel]]></c>.</p> + + <p>The easiest path + is to mimic the <c><![CDATA[inet]]></c> and <c><![CDATA[inet_tcp]]></c> + interfaces, but not much + functionality in those modules needs to be implemented. In the + example application, only a few of the usual interfaces are + implemented, and they are much simplified.</p> + </section> + + <section> + <title>Writing a Distribution Module</title> + <p>When the protocol is available to Erlang through a driver and an + Erlang interface module, a distribution module can be written. + The distribution module is a module with well-defined callbacks, + much like a <c><![CDATA[gen_server]]></c> (there is no compiler support + for checking the callbacks, though). This module implements:</p> + + <list type="bulleted"> + <item>The details of finding other nodes (that is, talking to + <c>epmd</c> or something similar)</item> + <item>Creating a listen port (or similar)</item> + <item>Connecting to other nodes</item> + <item>Performing the handshakes/cookie verification</item> + </list> + + <p>There is however a utility module, <c><![CDATA[dist_util]]></c>, which + does most of the hard work of handling handshakes, cookies, timers, + and ticking. Using <c><![CDATA[dist_util]]></c> makes implementing a + distribution module much easier and that is done in + the example application.</p> + </section> + + <section> + <title>Creating Boot Scripts</title> + <p>The last step is to create boot scripts to make the protocol + implementation available at boot time. The implementation can be + debugged by starting the distribution when all the system is + running, but in a real system the distribution is to start very + early, why a boot script and some command-line parameters are + necessary.</p> + + <p>This step also implies that the Erlang code in the + interface and distribution modules is written in such a way that + it can be run in the startup phase. In particular, there can be no + calls to the <c><![CDATA[application]]></c> module or to any modules + not loaded at boot time. That is, only <c><![CDATA[Kernel]]></c>, + <c><![CDATA[STDLIB]]></c>, and the application itself can be used.</p> + </section> </section> <section> - <title>The driver</title> - <p>Although Erlang drivers in general may be beyond the scope of this - document, a brief introduction seems to be in place.</p> + <title>The Driver</title> + <p>Although Erlang drivers in general can be beyond the scope of this + section, a brief introduction seems to be in place.</p> <section> - <title>Drivers in general</title> + <title>Drivers in General</title> <p>An Erlang driver is a native code module written in C (or - assembler) which serves as an interface for some special operating + assembler), which serves as an interface for some special operating system service. This is a general mechanism that is used throughout the Erlang emulator for all kinds of I/O. An Erlang driver can be dynamically linked (or loaded) to the Erlang emulator at runtime by using the <c><![CDATA[erl_ddll]]></c> Erlang module. Some of the drivers in OTP are however statically linked - to the runtime system, but that's more an optimization than a + to the runtime system, but that is more an optimization than a necessity.</p> - <p>The driver data-types and the functions available to the driver - writer are defined in the header file <c><![CDATA[erl_driver.h]]></c> (there - is also an deprecated version called <c><![CDATA[driver.h]]></c>, don't use - that one.) seated in Erlang's include directory (and in - $ERL_TOP/erts/emulator/beam in the source code - distribution). Refer to that file for function prototypes etc.</p> + + <p>The driver data types and the functions available to the driver + writer are defined in header file <c><![CDATA[erl_driver.h]]></c> + seated in Erlang's include directory. See the + <seealso marker="erts:erl_driver">erl_driver</seealso> documentation + for details of which functions are available.</p> + <p>When writing a driver to make a communications protocol available to Erlang, one should know just about everything worth knowing - about that particular protocol. All operation has to be non - blocking and all possible situations should be accounted for in - the driver. A non stable driver will affect and/or crash the - whole Erlang runtime system, which is seldom what's wanted. </p> + about that particular protocol. All operation must be + non-blocking and all possible situations are to be accounted for in + the driver. A non-stable driver will affect and/or crash the + whole Erlang runtime system.</p> + <p>The emulator calls the driver in the following situations:</p> + <list type="bulleted"> - <item>When the driver is loaded. This call-back has to have a - special name and will inform the emulator of what call-backs should - be used by returning a pointer to a <c><![CDATA[ErlDrvEntry]]></c> struct, - which should be properly filled in (see below).</item> - <item>When a port to the driver is opened (by a <c><![CDATA[open_port]]></c> - call from Erlang). This routine should set up internal data - structures and return an opaque data entity of the type - <c><![CDATA[ErlDrvData]]></c>, which is a data-type large enough to hold a - pointer. The pointer returned by this function will be the first - argument to all other call-backs concerning this particular - port. It is usually called the port handle. The emulator only - stores the handle and does never try to interpret it, why it can - be virtually anything (well anything not larger than a pointer - that is) and can point to anything if it is a pointer. Usually - this pointer will refer to a structure holding information about - the particular port, as i t does in our example.</item> - <item>When an Erlang process sends data to the port. The data will - arrive as a buffer of bytes, the interpretation is not defined, - but is up to the implementor. This call-back returns nothing to the - caller, answers are sent to the caller as messages (using a - routine called <c><![CDATA[driver_output]]></c> available to all - drivers). There is also a way to talk in a synchronous way to - drivers, described below. There can be an additional call-back - function for handling data that is fragmented (sent in a deep - io-list). That interface will get the data in a form suitable for - Unix <c><![CDATA[writev]]></c> rather than in a single buffer. There is no - need for a distribution driver to implement such a call-back, so - we wont.</item> - <item>When a file descriptor is signaled for input. This call-back - is called when the emulator detects input on a file descriptor - which the driver has marked for monitoring by using the interface - <c><![CDATA[driver_select]]></c>. The mechanism of driver select makes it - possible to read non blocking from file descriptors by calling - <c><![CDATA[driver_select]]></c> when reading is needed and then do the actual - reading in this call-back (when reading is actually possible). The - typical scenario is that <c><![CDATA[driver_select]]></c> is called when an - Erlang process orders a read operation, and that this routine - sends the answer when data is available on the file descriptor.</item> - <item>When a file descriptor is signaled for output. This call-back - is called in a similar way as the previous, but when writing to a - file descriptor is possible. The usual scenario is that Erlang - orders writing on a file descriptor and that the driver calls - <c><![CDATA[driver_select]]></c>. When the descriptor is ready for output, - this call-back is called an the driver can try to send the - output. There may of course be queuing involved in such - operations, and there are some convenient queue routines available - to the driver writer to use in such situations.</item> - <item>When a port is closed, either by an Erlang process or by the - driver calling one of the <c><![CDATA[driver_failure_XXX]]></c> routines. This - routine should clean up everything connected to one particular - port. Note that when other call-backs call a - <c><![CDATA[driver_failure_XXX]]></c> routine, this routine will be - immediately called and the call-back routine issuing the error can - make no more use of the data structures for the port, as this - routine surely has freed all associated data and closed all file - descriptors. If the queue utility available to driver writes is - used, this routine will however <em>not</em> be called until the - queue is empty.</item> - <item>When an Erlang process calls <c>erlang:port_control/3</c>, - which is a synchronous interface to drivers. The control interface - is used to set driver options, change states of ports etc. We'll - use this interface quite a lot in our example.</item> - <item>When a timer expires. The driver can set timers with the - function <c><![CDATA[driver_set_timer]]></c>. When such timers expire, a - specific call-back function is called. We will not use timers in - our example.</item> - <item>When the whole driver is unloaded. Every resource allocated - by the driver should be freed.</item> + <item> + <p>When the driver is loaded. This callback must have a special + name and inform the emulator of what callbacks are to be used + by returning a pointer to a <c><![CDATA[ErlDrvEntry]]></c> struct, + which is to be properly filled in (see below).</p> + </item> + <item> + <p>When a port to the driver is opened (by a + <c><![CDATA[open_port]]></c> call from Erlang). This routine is to + set up internal data structures and return an opaque data entity of + the type <c><![CDATA[ErlDrvData]]></c>, which is a data type large + enough to hold a pointer. + The pointer returned by this function is the first + argument to all other callbacks concerning this particular + port. It is usually called the port handle. The emulator only + stores the handle and does never try to interpret it, why it can + be virtually anything (anything not larger than a pointer + that is) and can point to anything if it is a pointer. Usually + this pointer refers to a structure holding information about + the particular port, as it does in the example.</p> + </item> + <item> + <p>When an Erlang process sends data to the port. The data + arrives as a buffer of bytes, the interpretation is not defined, + but is up to the implementor. This callback returns nothing to the + caller, answers are sent to the caller as messages (using a + routine called <c><![CDATA[driver_output]]></c> available to all + drivers). There is also a way to talk in a synchronous way to + drivers, described below. There can be an additional callback + function for handling data that is fragmented (sent in a deep + io-list). That interface gets the data in a form suitable for + Unix <c><![CDATA[writev]]></c> rather than in a single buffer. + There is no need for a distribution driver to implement such a + callback, so we will not.</p> + </item> + <item> + <p>When a file descriptor is signaled for input. This callback + is called when the emulator detects input on a file descriptor + that the driver has marked for monitoring by using the interface + <c><![CDATA[driver_select]]></c>. The mechanism of driver select + makes it possible to read non-blocking from file descriptors by + calling <c><![CDATA[driver_select]]></c> when reading is needed, and + then do the reading in this callback (when reading is possible). + The typical scenario is that <c><![CDATA[driver_select]]></c> is + called when an Erlang process orders a read operation, and that + this routine sends the answer when data is available on the file + descriptor.</p> + </item> + <item> + <p>When a file descriptor is signaled for output. This callback + is called in a similar way as the previous, but when writing to a + file descriptor is possible. The usual scenario is that Erlang + orders writing on a file descriptor and that the driver calls + <c><![CDATA[driver_select]]></c>. When the descriptor is ready for + output, this callback is called and the driver can try to send the + output. Queuing can be involved in such operations, and there are + convenient queue routines available to the driver writer to use.</p> + </item> + <item> + <p>When a port is closed, either by an Erlang process or by the + driver calling one of the <c><![CDATA[driver_failure_XXX]]></c> + routines. This routine is to clean up everything connected to one + particular port. When other callbacks call a + <c><![CDATA[driver_failure_XXX]]></c> routine, this routine is + immediately called. The callback routine issuing the error can + make no more use of the data structures for the port, as this + routine surely has freed all associated data and closed all file + descriptors. If the queue utility available to driver writer is + used, this routine is however <em>not</em> called until the + queue is empty.</p> + </item> + <item> + <p>When an Erlang process calls + <seealso marker="erlang#port_control/3"> + <c>erlang:port_control/3</c></seealso>, + which is a synchronous interface to drivers. The control interface + is used to set driver options, change states of ports, and so on. + This interface is used a lot in the example.</p> + </item> + <item> + <p>When a timer expires. The driver can set timers with the function + <c><![CDATA[driver_set_timer]]></c>. When such timers expire, a + specific callback function is called. No timers are used in + the example.</p> + </item> + <item> + <p>When the whole driver is unloaded. Every resource allocated + by the driver is to be freed.</p> + </item> </list> </section> <section> - <title>The distribution driver's data structures</title> - <p>The driver used for Erlang distribution should implement a - reliable, order maintaining, variable length packet oriented - protocol. All error correction, re-sending and such need to be + <title>The Data Structures of the Distribution Driver</title> + <p>The driver used for Erlang distribution is to implement a + reliable, order maintaining, variable length packet-oriented + protocol. All error correction, resending and such need to be implemented in the driver or by the underlying communications - protocol. If the protocol is stream oriented (as is the case with + protocol. If the protocol is stream-oriented (as is the case with both TCP/IP and our streamed Unix domain sockets), some mechanism for packaging is needed. We will use the simple method of having a - header of four bytes containing the length of the package in a big - endian 32 bit integer (as Unix domain sockets only can be used - between processes on the same machine, we actually don't need to - code the integer in some special endianess, but I'll do it anyway - because in most situation you do need to do it. Unix domain - sockets are reliable and order maintaining, so we don't need to - implement resends and such in our driver.</p> - <p>Lets start writing our example Unix domain sockets driver by - declaring prototypes and filling in a static ErlDrvEntry - structure.</p> + header of four bytes containing the length of the package in a + big-endian 32-bit integer. As Unix domain sockets only can be used + between processes on the same machine, we do not need to + code the integer in some special endianess, but we will do it anyway + because in most situation you need to do it. Unix domain + sockets are reliable and order maintaining, so we do not need to + implement resends and such in the driver.</p> + + <p>We start writing the example Unix domain sockets driver by + declaring prototypes and filling in a static <c>ErlDrvEntry</c> + structure:</p> + <code type="none"><![CDATA[ ( 1) #include <stdio.h> ( 2) #include <stdlib.h> @@ -286,94 +354,122 @@ (51) NULL, /* process_exit callback */ (52) NULL /* stop_select callback */ (53) };]]></code> - <p>On line 1 to 10 we have included the OS headers needed for our - driver. As this driver is written for Solaris, we know that the - header <c><![CDATA[uio.h]]></c> exists, why we can define the preprocessor - variable <c><![CDATA[HAVE_UIO_H]]></c> before we include <c><![CDATA[erl_driver.h]]></c> - at line 12. The definition of <c><![CDATA[HAVE_UIO_H]]></c> will make the + + <p>On line 1-10 the OS headers needed for the driver are included. + As this driver is written for Solaris, we know that the + header <c><![CDATA[uio.h]]></c> exists. So the preprocessor variable + <c><![CDATA[HAVE_UIO_H]]></c> can be defined before + <c><![CDATA[erl_driver.h]]></c> is included on line 12. + The definition of <c><![CDATA[HAVE_UIO_H]]></c> will make the I/O vectors used in Erlang's driver queues to correspond to the operating systems ditto, which is very convenient.</p> - <p>The different call-back functions are declared ("forward - declarations") on line 16 to 23.</p> - <p>The driver structure is similar for statically linked in - drivers and dynamically loaded. However some of the fields - should be left empty (i.e. initialized to NULL) in the + + <p>On line 16-23 the different callback functions are declared ("forward + declarations").</p> + + <p>The driver structure is similar for statically linked-in + drivers and dynamically loaded. However, some of the fields + are to be left empty (that is, initialized to NULL) in the different types of drivers. The first field (the <c><![CDATA[init]]></c> function pointer) is always left blank in a dynamically loaded - driver, which can be seen on line 26. The NULL on line 37 - should always be there, the field is no longer used and is - retained for backward compatibility. We use no timers in this - driver, why no call-back for timers is needed. The <c>outputv</c> field + driver, see line 26. <c>NULL</c> on line 37 + is always to be there, the field is no longer used and is + retained for backward compatibility. No timers are used in this + driver, why no callback for timers is needed. The <c>outputv</c> field (line 40) can be used to implement an interface similar to Unix <c><![CDATA[writev]]></c> for output. The Erlang runtime - system could previously not use <c>outputv</c> for the - distribution, but since erts version 5.7.2 it can. - Since this driver was written before erts version 5.7.2 it does - not use the <c>outputv</c> callback. Using the <c>outputv</c> - callback is preferred since it reduces copying of data. (We - will however use scatter/gather I/O internally in the driver).</p> - <p>As of erts version 5.5.3 the driver interface was extended with - version control and the possibility to pass capability information. - Capability flags are present at line 48. As of erts version 5.7.4 - the - <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_SOFT_BUSY</seealso> - flag is required for drivers that are to be used by the distribution. - The soft busy flag implies that the driver is capable of handling - calls to the <c>output</c> and <c>outputv</c> callbacks even though - it has marked itself as busy. This has always been a requirement - on drivers used by the distribution, but there have previously not - been any capability information available about this. For more - information see - <seealso marker="erl_driver#set_busy_port">set_busy_port()</seealso>). -</p> + system could previously not use <c>outputv</c> for the + distribution, but it can as from ERTS 5.7.2. + As this driver was written before ERTS 5.7.2 it does + not use the <c>outputv</c> callback. Using the <c>outputv</c> + callback is preferred, as it reduces copying of data. (We + will however use scatter/gather I/O internally in the driver.)</p> + + <p>As from ERTS 5.5.3 the driver interface was extended with + version control and the possibility to pass capability information. + Capability flags are present on line 48. As from ERTS 5.7.4 flag + <seealso marker="driver_entry#driver_flags"> + <c>ERL_DRV_FLAG_SOFT_BUSY</c></seealso> is required for drivers that + are to be used by the distribution. The soft busy flag implies that the + driver can handle calls to the <c>output</c> and <c>outputv</c> + callbacks although it has marked itself as busy. This has always been a + requirement on drivers used by the distribution, but no capability + information has been available about this previously. For more + information. see <seealso marker="erl_driver#set_busy_port"> + <c>erl_driver:set_busy_port()</c></seealso>).</p> + <p>This driver was written before the runtime system had SMP support. - The driver will still function in the runtime system with SMP support, - but performance will suffer from lock contention on the driver lock - used for the driver. This can be alleviated by reviewing and perhaps - rewriting the code so that each instance of the driver safely can - execute in parallel. When instances safely can execute in parallel it - is safe to enable instance specific locking on the driver. This is done - by passing - <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_USE_PORT_LOCKING</seealso> - as a driver flag. This is left as an exercise for the reader.</p> - <p>Our defined call-backs thus are:</p> - <list type="bulleted"> - <item>uds_start, which shall initiate data for a port. We wont - create any actual sockets here, just initialize data structures.</item> - <item>uds_stop, the function called when a port is closed.</item> - <item>uds_command, which will handle messages from Erlang. The - messages can either be plain data to be sent or more subtle - instructions to the driver. We will use this function mostly for - data pumping.</item> - <item>uds_input, this is the call-back which is called when we have - something to read from a socket.</item> - <item>uds_output, this is the function called when we can write to a - socket.</item> - <item>uds_finish, which is called when the driver is unloaded. A - distribution driver will actually (or hopefully) never be unloaded, - but we include this for completeness. Being able to clean up after - oneself is always a good thing.</item> - <item>uds_control, the <c>erlang:port_control/2</c> call-back, which - will be used a lot in this implementation.</item> - </list> - <p>The ports implemented by this driver will operate in two major - modes, which i will call the <em>command</em> and <em>data</em> - modes. In command mode, only passive reading and writing (like - gen_tcp:recv/gen_tcp:send) can be - done, and this is the mode the port will be in during the - distribution handshake. When the connection is up, the port will - be switched to data mode and all data will be immediately read and - passed further to the Erlang emulator. In data mode, no data - arriving to the uds_command will be interpreted, but just packaged - and sent out on the socket. The uds_control call-back will do the - switching between those two modes.</p> - <p>While the <c><![CDATA[net_kernel]]></c> informs different subsystems that the - connection is coming up, the port should accept data to send, but - not receive any data, to avoid that data arrives from another node - before every kernel subsystem is prepared to handle it. We have a - third mode for this intermediate stage, lets call it the - <em>intermediate</em> mode.</p> - <p>Lets define an enum for the different types of ports we have:</p> + The driver will still function in the runtime system with SMP support, + but performance will suffer from lock contention on the driver lock + used for the driver. This can be alleviated by reviewing and perhaps + rewriting the code so that each instance of the driver safely can + execute in parallel. When instances safely can execute in parallel, it + is safe to enable instance-specific locking on the driver. This is done + by passing <seealso marker="driver_entry#driver_flags"> + <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></seealso> as a driver flag. This + is left as an exercise for the reader.</p> + + <p>Thus, the defined callbacks are as follows:</p> + + <taglist> + <tag><c>uds_start</c></tag> + <item> + <p>Must initiate data for a port. We do not create any sockets + here, only initialize data structures.</p> + </item> + <tag><c>uds_stop</c></tag> + <item> + <p>Called when a port is closed.</p> + </item> + <tag><c>uds_command</c></tag> + <item> + <p>Handles messages from Erlang. The + messages can either be plain data to be sent or more subtle + instructions to the driver. This function is here mostly for + data pumping.</p> + </item> + <tag><c>uds_input</c></tag> + <item> + <p>Called when there is something to read from a socket.</p> + </item> + <tag><c>uds_output</c></tag> + <item> + <p>Called when it is possible to write to a socket.</p> + </item> + <tag><c>uds_finish</c></tag> + <item> + <p>Called when the driver is unloaded. A distribution driver will + never be unloaded, but we include this for completeness. To be + able to clean up after oneself is always a good thing.</p> + </item> + <tag><c>uds_control</c></tag> + <item> + <p>The <seealso marker="erlang#port_control/3"> + <c>erlang:port_control/3</c></seealso> callback, which is + used a lot in this implementation.</p> + </item> + </taglist> + + <p>The ports implemented by this driver operate in two major modes, + named <c>command</c> and <c>data</c>. In <c>command</c> mode, + only passive reading and writing (like + <c>gen_tcp:recv</c>/<c>gen_tcp:send</c>) can be done. The port is in + this mode during the distribution handshake. When the connection is up, + the port is switched to <c>data</c> mode and all data is immediately + read and passed further to the Erlang emulator. In <c>data</c> + mode, no data arriving to <c>uds_command</c> is interpreted, only + packaged and sent out on the socket. The <c>uds_control</c> callback + does the switching between those two modes.</p> + + <p>While <c><![CDATA[net_kernel]]></c> informs different subsystems + that the connection is coming up, the port is to accept data to send. + However, the port should not receive any data, to avoid that data + arrives from another node before every kernel subsystem is prepared + to handle it. A third mode, named <c>intermediate</c>, is used for this + intermediate stage.</p> + + <p>An enum is defined for the different types of ports:</p> + <code type="none"><![CDATA[ ( 1) typedef enum { ( 2) portTypeUnknown, /* An uninitialized port */ @@ -384,40 +480,63 @@ ( 7) portTypeCommand, /* A connected open port in command mode */ ( 8) portTypeIntermediate, /* A connected open port in special ( 9) half active mode */ -(10) portTypeData /* A connectec open port in data mode */ +(10) portTypeData /* A connected open port in data mode */ (11) } PortType; ]]></code> - <p>Lets look at the different types:</p> - <list type="bulleted"> - <item>portTypeUnknown - The type a port has when it's opened, but - not actually bound to any file descriptor.</item> - <item>portTypeListener - A port that is connected to a listen - socket. This port will not do especially much, there will be no data - pumping done on this socket, but there will be read data available - when one is trying to do an accept on the port.</item> - <item>portTypeAcceptor - This is a port that is to represent the - result of an accept operation. It is created when one wants to - accept from a listen socket, and it will be converted to a - portTypeCommand when the accept succeeds.</item> - <item>portTypeConnector - Very similar to portTypeAcceptor, an - intermediate stage between the request for a connect operation and - that the socket is really connected to an accepting ditto in the - other end. As soon as the sockets are connected, the port will - switch type to portTypeCommand.</item> - <item>portTypeCommand - A connected socket (or accepted socket if - you want) that is in the command mode mentioned earlier.</item> - <item>portTypeIntermediate - The intermediate stage for a connected - socket. There should be no processing of input for this socket.</item> - <item>portTypeData - The mode where data is pumped through the port - and the uds_command routine will regard every call as a call where - sending is wanted. In this mode all input available will be read and - sent to Erlang as soon as it arrives on the socket, much like in the - active mode of a <c><![CDATA[gen_tcp]]></c> socket.</item> - </list> - <p>Now lets look at the state we'll need for our ports. One can note - that not all fields are used for all types of ports and that one - could save some space by using unions, but that would clutter the - code with multiple indirections, so i simply use one struct for - all types of ports, for readability.</p> + + <p>The different types are as follows:</p> + + <taglist> + <tag><c>portTypeUnknown</c></tag> + <item> + <p>The type a port has when it is opened, but + not bound to any file descriptor.</p> + </item> + <tag><c>portTypeListener</c></tag> + <item> + <p>A port that is connected to a listen socket. This port does not + do much, no data pumping is done on this socket, but read data is + available when one is trying to do an accept on the port.</p> + </item> + <tag><c>portTypeAcceptor</c></tag> + <item> + <p>This port is to represent the result of an accept operation. It is + created when one wants to accept from a listen socket, and it is + converted to a <c>portTypeCommand</c> when the accept succeeds.</p> + </item> + <tag><c>portTypeConnector</c></tag> + <item> + <p>Very similar to <c>portTypeAcceptor</c>, an + intermediate stage between the request for a connect operation and + that the socket is connected to an accepting ditto in the + other end. When the sockets are connected, the port + switches type to <c>portTypeCommand</c>.</p> + </item> + <tag><c>portTypeCommand</c></tag> + <item> + <p>A connected socket (or accepted socket) in <c>command</c> mode + mentioned earlier.</p> + </item> + <tag><c>portTypeIntermediate</c></tag> + <item> + <p>The intermediate stage for a connected socket. + There is to be no processing of input for this socket.</p> + </item> + <tag><c>portTypeData</c></tag> + <item> + <p>The mode where data is pumped through the port and the + <c>uds_command</c> routine regards every call as a call where + sending is wanted. In this mode, all input available is read and + sent to Erlang when it arrives on the socket, much like in the + active mode of a <c><![CDATA[gen_tcp]]></c> socket.</p> + </item> + </taglist> + + <p>We study the state that is needed for the ports. Notice + that not all fields are used for all types of ports. Some space + could be saved by using unions, but that would clutter the + code with multiple indirections, so here is used one struct for + all types of ports, for readability:</p> + <code type="none"><![CDATA[ ( 1) typedef unsigned char Byte; ( 2) typedef unsigned int Word; @@ -428,7 +547,7 @@ ( 6) int lockfd; /* The file descriptor for a lock file in ( 7) case of listen sockets */ ( 8) Byte creation; /* The creation serial derived from the -( 9) lockfile */ +( 9) lock file */ (10) PortType type; /* Type of port */ (11) char *name; /* Short name of socket for unlink */ (12) Word sent; /* Bytes sent */ @@ -442,114 +561,154 @@ (20) input buffer */ (21) Byte *buffer; /* The actual input buffer */ (22) } UdsData; ]]></code> + <p>This structure is used for all types of ports although some fields are useless for some types. The least memory consuming solution would be to arrange this structure as a union of - structures, but the multiple indirections in the code to - access a field in such a structure will clutter the code to + structures. However, the multiple indirections in the code to + access a field in such a structure would clutter the code too much for an example.</p> - <p>Let's look at the fields in our structure:</p> - <list type="bulleted"> - <item>fd - The file descriptor of the socket associated with the - port.</item> - <item>port - The port identifier for the port which this structure - corresponds to. It is needed for most <c><![CDATA[driver_XXX]]></c> - calls from the driver back to the emulator.</item> + + <p>The fields in the structure are as follows:</p> + + <taglist> + <tag><c>fd</c></tag> <item> - <p>lockfd - If the socket is a listen socket, we use a separate + <p>The file descriptor of the socket associated with the port.</p> + </item> + <tag><c>port</c></tag> + <item> + <p>The port identifier for the port that this structure + corresponds to. It is needed for most <c><![CDATA[driver_XXX]]></c> + calls from the driver back to the emulator.</p> + </item> + <tag><c>lockfd</c></tag> + <item> + <p>If the socket is a listen socket, we use a separate (regular) file for two purposes:</p> <list type="bulleted"> - <item>We want a locking mechanism that gives no race - conditions, so that we can be sure of if another Erlang - node uses the listen socket name we require or if the - file is only left there from a previous (crashed) - session.</item> <item> - <p>We store the <em>creation</em> serial number in the - file. The <em>creation</em> is a number that should + <p>We want a locking mechanism that gives no race + conditions, to be sure if another Erlang + node uses the listen socket name we require or if the + file is only left there from a previous (crashed) session.</p> + </item> + <item> + <p>We store the <c>creation</c> serial number in the + file. The <c>creation</c> is a number that is to change between different instances of different Erlang emulators with the same name, so that process - identifiers from one emulator won't be valid when sent + identifiers from one emulator do not become valid when sent to a new emulator with the same distribution name. The - creation can be between 0 and 3 (two bits) and is stored - in every process identifier sent to another node. </p> - <p>In a system with TCP based distribution, this data is + creation can be from 0 through 3 (two bits) and is stored + in every process identifier sent to another node.</p> + <p>In a system with TCP-based distribution, this data is kept in the <em>Erlang port mapper daemon</em> (<c><![CDATA[epmd]]></c>), which is contacted when a distributed - node starts. The lock-file and a convention for the UDS - listen socket's name will remove the need for + node starts. The lock file and a convention for the UDS + listen socket's name remove the need for <c><![CDATA[epmd]]></c> when using this distribution module. UDS is always restricted to one host, why avoiding a port mapper is easy.</p> </item> </list> </item> - <item>creation - The creation number for a listen socket, which is - calculated as (the value found in the lock-file + 1) rem - 4. This creation value is also written back into the - lock-file, so that the next invocation of the emulator will - found our value in the file.</item> - <item>type - The current type/state of the port, which can be one - of the values declared above.</item> - <item>name - The name of the socket file (the path prefix - removed), which allows for deletion (<c><![CDATA[unlink]]></c>) when the - socket is closed.</item> - <item>sent - How many bytes that have been sent over the - socket. This may wrap, but that's no problem for the - distribution, as the only thing that interests the Erlang - distribution is if this value has changed (the Erlang - net_kernel <em>ticker</em> uses this value by calling the - driver to fetch it, which is done through the - <c>erlang:port_control</c> routine).</item> - <item>received - How many bytes that are read (received) from the - socket, used in similar ways as <c><![CDATA[sent]]></c>.</item> - <item>partner - A pointer to another port structure, which is - either the listen port from which this port is accepting a - connection or the other way around. The "partner relation" - is always bidirectional.</item> - <item>next - Pointer to next structure in a linked list of all - port structures. This list is used when accepting - connections and when the driver is unloaded.</item> - <item>buffer_size, buffer_pos, header_pos, buffer - data for input - buffering. Refer to the source code (in the kernel/examples - directory) for details about the input buffering. That - certainly goes beyond the scope of this document.</item> - </list> + <tag><c>creation</c></tag> + <item> + <p>The creation number for a listen socket, which is + calculated as (the value found in the lock-file + 1) rem 4. + This creation value is also written back into the + lock file, so that the next invocation of the emulator + finds our value in the file.</p> + </item> + <tag><c>type</c></tag> + <item> + <p>The current type/state of the port, which can be one + of the values declared above.</p> + </item> + <tag><c>name</c></tag> + <item> + <p>The name of the socket file (the path prefix removed), + which allows for deletion (<c><![CDATA[unlink]]></c>) when the + socket is closed.</p> + </item> + <tag><c>sent</c></tag> + <item> + <p>How many bytes that have been sent over the + socket. This can wrap, but that is no problem for the + distribution, as the Erlang distribution is only interested in + if this value has changed. (The Erlang + <c>net_kernel</c> <c>ticker</c> uses this value by calling the + driver to fetch it, which is done through the + <seealso marker="erlang#port_control/3"> + <c>erlang:port_control/3</c></seealso> routine.)</p> + </item> + <tag><c>received</c></tag> + <item> + <p>How many bytes that are read (received) from the + socket, used in similar ways as <c><![CDATA[sent]]></c>.</p> + </item> + <tag><c>partner</c></tag> + <item> + <p>A pointer to another port structure, which is + either the listen port from which this port is accepting a + connection or conversely. The "partner relation" + is always bidirectional.</p> + </item> + <tag><c>next</c></tag> + <item> + <p>Pointer to next structure in a linked list of all + port structures. This list is used when accepting + connections and when the driver is unloaded.</p> + </item> + <tag><c>buffer_size</c>, <c>buffer_pos</c>, <c>header_pos</c>, + <c>buffer</c></tag> + <item> + <p>Data for input buffering. For details about the input buffering, + see the source code in directory <c>kernel/examples</c>. That + certainly goes beyond the scope of this section.</p> + </item> + </taglist> </section> <section> - <title>Selected parts of the distribution driver implementation</title> - <p>The distribution drivers implementation is not completely - covered in this text, details about buffering and other things + <title>Selected Parts of the Distribution Driver Implementation</title> + <p>The implemenation of the distribution driver is not completely + covered here, details about buffering and other things unrelated to driver writing are not explained. Likewise are some peculiarities of the UDS protocol not explained in detail. The chosen protocol is not important.</p> - <p>Prototypes for the driver call-back routines can be found in + + <p>Prototypes for the driver callback routines can be found in the <c><![CDATA[erl_driver.h]]></c> header file.</p> + <p>The driver initialization routine is (usually) declared with a macro to make the driver easier to port between different - operating systems (and flavours of systems). This is the only - routine that has to have a well defined name. All other - call-backs are reached through the driver structure. The macro + operating systems (and flavors of systems). This is the only + routine that must have a well-defined name. All other + callbacks are reached through the driver structure. The macro to use is named <c><![CDATA[DRIVER_INIT]]></c> and takes the driver name - as parameter.</p> + as parameter:</p> + <code type="none"><![CDATA[ (1) /* Beginning of linked list of ports */ (2) static UdsData *first_data; - (3) DRIVER_INIT(uds_drv) (4) { (5) first_data = NULL; (6) return &uds_driver_entry; (7) } ]]></code> + <p>The routine initializes the single global data structure and - returns a pointer to the driver entry. The routine will be - called when <c><![CDATA[erl_ddll:load_driver]]></c> is called from Erlang.</p> - <p>The <c><![CDATA[uds_start]]></c> routine is called when a port is opened - from Erlang. In our case, we only allocate a structure and + returns a pointer to the driver entry. The routine is called + when <c><![CDATA[erl_ddll:load_driver]]></c> is called from Erlang.</p> + + <p>The <c><![CDATA[uds_start]]></c> routine is called when a port is + opened from Erlang. In this case, we only allocate a structure and initialize it. Creating the actual socket is left to the <c><![CDATA[uds_command]]></c> routine.</p> + <code type="none"><![CDATA[ ( 1) static ErlDrvData uds_start(ErlDrvPort port, char *buff) ( 2) { @@ -574,15 +733,18 @@ (21) (22) return((ErlDrvData) ud); (23) } ]]></code> - <p>Every data item is initialized, so that no problems will arise + + <p>Every data item is initialized, so that no problems arise when a newly created port is closed (without there being any corresponding socket). This routine is called when - <c><![CDATA[open_port({spawn, "uds_drv"},[])]]></c> is called from Erlang.</p> - <p>The <c><![CDATA[uds_command]]></c> routine is the routine called when an - Erlang process sends data to the port. All asynchronous - commands when the port is in <em>command mode</em> as well as - the sending of all data when the port is in <em>data mode</em> - is handled in this9s routine. Let's have a look at it:</p> + <c><![CDATA[open_port({spawn, "uds_drv"},[])]]></c> is called from + Erlang.</p> + + <p>The <c><![CDATA[uds_command]]></c> routine is the routine called when + an Erlang process sends data to the port. This routine handles all + asynchronous commands when the port is in <c>command</c> mode and + the sending of all data when the port is in <c>data</c> mode:</p> + <code type="none"><![CDATA[ ( 1) static void uds_command(ErlDrvData handle, char *buff, int bufflen) ( 2) { @@ -636,57 +798,77 @@ (49) return; (50) } (51) } ]]></code> - <p>The command routine takes three parameters; the handle - returned for the port by <c><![CDATA[uds_start]]></c>, which is a pointer - to the internal port structure, the data buffer and the length + + <p>The command routine takes three parameters; the handle returned for + the port by <c><![CDATA[uds_start]]></c>, which is a pointer + to the internal port structure, the data buffer, and the length of the data buffer. The buffer is the data sent from Erlang - (a list of bytes) converted to an C array (of bytes). </p> - <p>If Erlang sends i.e. the list <c><![CDATA[[$a,$b,$c]]]></c> to the port, - the <c><![CDATA[bufflen]]></c> variable will be <c><![CDATA[3]]></c> ant the - <c><![CDATA[buff]]></c> variable will contain <c><![CDATA[{'a','b','c'}]]></c> (no - null termination). Usually the first byte is used as an - opcode, which is the case in our driver to (at least when the - port is in command mode). The opcodes are defined as:</p> - <list type="bulleted"> - <item>'L'<socketname>: Create and listen on socket with the - given name.</item> - <item>'A'<listennumber as 32 bit bigendian>: Accept from the - listen socket identified by the given identification - number. The identification number is retrieved with the - uds_control routine.</item> - <item>'C'<socketname>: Connect to the socket named - <socketname>.</item> - <item>'S'<data>: Send the data <data> on the - connected/accepted socket (in command mode). The sending is - acked when the data has left this process.</item> - <item>'R': Receive one packet of data.</item> - </list> - <p>One may wonder what is meant by "one packet of data" in the - 'R' command. This driver always sends data packeted with a 4 - byte header containing a big endian 32 bit integer that + (a list of bytes) converted to an C array (of bytes).</p> + + <p>If Erlang sends, for example, the list <c><![CDATA[[$a,$b,$c]]]></c> + to the port, the <c><![CDATA[bufflen]]></c> variable is + <c><![CDATA[3]]></c> and the <c><![CDATA[buff]]></c> variable contains + <c><![CDATA[{'a','b','c'}]]></c> (no + <c>NULL</c> termination). Usually the first byte is used as an + opcode, which is the case in this driver too (at least when the + port is in <c>command</c> mode). The opcodes are defined as follows:</p> + + <taglist> + <tag><c>'L'<socket name></c></tag> + <item> + <p>Creates and listens on socket with the specified name.</p> + </item> + <tag><c>'A'<listen number as 32-bit big-endian></c></tag> + <item> + <p>Accepts from the listen socket identified by the specified + identification number. The identification number is retrieved with + the <c>uds_control</c> routine.</p> + </item> + <tag><c>'C'<socket name></c></tag> + <item> + <p>Connects to the socket named <socket name>.</p> + </item> + <tag><c>'S'<data></c></tag> + <item> + <p>Sends the data <data> on the + connected/accepted socket (in <c>command</c> mode). The sending is + acknowledged when the data has left this process.</p> + </item> + <tag><c>'R'</c></tag> + <item> + <p>Receives one packet of data.</p> + </item> + </taglist> + + <p>"One packet of data" in command <c>'R'</c> can be explained + as follows. This driver always sends data packaged with a 4 + byte header containing a big-endian 32-bit integer that represents the length of the data in the packet. There is no need for different packet sizes or some kind of streamed - mode, as this driver is for the distribution only. One may - wonder why the header word is coded explicitly in big endian - when an UDS socket is local to the host. The answer simply is - that I see it as a good practice when writing a distribution - driver, as distribution in practice usually cross the host - boundaries. </p> - <p>On line 4-8 we handle the case where the port is in data or - intermediate mode, the rest of the routine handles the - different commands. We see (first on line 15) that the routine - uses the <c><![CDATA[driver_failure_posix()]]></c> routine to report - errors. One important thing to remember is that the failure - routines make a call to our <c><![CDATA[uds_stop]]></c> routine, which - will remove the internal port data. The handle (and the casted - handle <c><![CDATA[ud]]></c>) is therefore <em>invalid pointers</em> after a - <c><![CDATA[driver_failure]]></c> call and we should <em>immediately return</em>. The runtime system will send exit signals to all + mode, as this driver is for the distribution only. + Why is the header word coded explicitly in big-endian when a UDS + socket is local to the host? It is good practice when writing a + distribution driver, as distribution in practice usually crosses + the host boundaries.</p> + + <p>On line 4-8 is handled the case where the port is in <c>data</c> mode + or <c>intermediate</c> mode and the remaining routine handles the + different commands. The routine uses the + <c><![CDATA[driver_failure_posix()]]></c> routine to report errors + (see, for example, line 15). Notice that the failure routines make + a call to the <c><![CDATA[uds_stop]]></c> routine, which will + remove the internal port data. The handle (and the casted handle + <c><![CDATA[ud]]></c>) is therefore <em>invalid pointers</em> after a + <c><![CDATA[driver_failure]]></c> call and we should <em>return + immediately</em>. The runtime system will send exit signals to all linked processes.</p> - <p>The uds_input routine gets called when data is available on a - file descriptor previously passed to the <c><![CDATA[driver_select]]></c> - routine. Typically this happens when a read command is issued - and no data is available. Lets look at the <c><![CDATA[do_recv]]></c> - routine:</p> + + <p>The <c>uds_input</c> routine is called when data is available on a + file descriptor previously passed to the + <c><![CDATA[driver_select]]></c> routine. This occurs typically when + a read command is issued and no data is available. The + <c><![CDATA[do_recv]]></c> routine is as follows:</p> + <code type="none"><![CDATA[ ( 1) static void do_recv(UdsData *ud) ( 2) { @@ -716,29 +898,34 @@ (26) } (27) } (28) } ]]></code> + <p>The routine tries to read data until a packet is read or the <c><![CDATA[buffered_read_package]]></c> routine returns a - <c><![CDATA[NORMAL_READ_FAILURE]]></c> (an internally defined constant for - the module that means that the read operation resulted in an - <c><![CDATA[EWOULDBLOCK]]></c>). If the port is in command mode, the - reading stops when one package is read, but if it is in data - mode, the reading continues until the socket buffer is empty - (read failure). If no more data can be read and more is wanted - (always the case when socket is in data mode) driver_select is - called to make the <c><![CDATA[uds_input]]></c> call-back be called when - more data is available for reading.</p> - <p>When the port is in data mode, all data is sent to Erlang in a - format that suits the distribution, in fact the raw data will + <c><![CDATA[NORMAL_READ_FAILURE]]></c> (an internally defined constant + for the module, which means that the read operation resulted in an + <c><![CDATA[EWOULDBLOCK]]></c>). If the port is in <c>command</c> mode, + the reading stops when one package is read. If the port is in + <c>data</c> mode, the reading continues until the socket buffer is empty + (read failure). If no more data can be read and more is wanted (which + is always the case when the socket is in <c>data</c> mode), + <c>driver_select</c> is called to make the <c><![CDATA[uds_input]]></c> + callback be called when more data is available for reading.</p> + + <p>When the port is in <c>data</c> mode, all data is sent to Erlang in a + format that suits the distribution. In fact, the raw data will never reach any Erlang process, but will be translated/interpreted by the emulator itself and then delivered in the correct format to the correct processes. In - the current emulator version, received data should be tagged - with a single byte of 100. Thats what the macro - <c><![CDATA[DIST_MAGIC_RECV_TAG]]></c> is defined to. The tagging of data - in the distribution will possibly change in the future.</p> - <p>The <c><![CDATA[uds_input]]></c> routine will handle other input events - (like nonblocking <c><![CDATA[accept]]></c>), but most importantly handle + the current emulator version, received data is to be tagged + with a single byte of 100. That is what the macro + <c><![CDATA[DIST_MAGIC_RECV_TAG]]></c> is defined to. The tagging of + data in the distribution can be changed in the future.</p> + + <p>The <c><![CDATA[uds_input]]></c> routine handles other input events + (like non-blocking <c><![CDATA[accept]]></c>), but most importantly + handle data arriving at the socket by calling <c><![CDATA[do_recv]]></c>:</p> + <code type="none"><![CDATA[ ( 1) static void uds_input(ErlDrvData handle, ErlDrvEvent event) ( 2) { @@ -768,13 +955,16 @@ (24) } (25) do_recv(ud); (26) } ]]></code> - <p>The important line here is the last line in the function, the - <c><![CDATA[do_read]]></c> routine is called to handle new input. The rest - of the function handles input on a listen socket, which means - that there should be possible to do an accept on the + + <p>The important line is the last line in the function: the + <c><![CDATA[do_read]]></c> routine is called to handle new input. + The remaining function handles input on a listen socket, which means + that it is to be possible to do an accept on the socket, which is also recognized as a read event.</p> - <p>The output mechanisms are similar to the input. Lets first - look at the <c><![CDATA[do_send]]></c> routine:</p> + + <p>The output mechanisms are similar to the input. + The <c><![CDATA[do_send]]></c> routine is as follows:</p> + <code type="none"><![CDATA[ ( 1) static void do_send(UdsData *ud, char *buff, int bufflen) ( 2) { @@ -816,33 +1006,36 @@ (37) driver_enqv(ud->port, &eio, written); (38) send_out_queue(ud); (39) } ]]></code> + <p>This driver uses the <c><![CDATA[writev]]></c> system call to send data - onto the socket. A combination of writev and the driver output - queues is very convenient. An <em>ErlIOVec</em> structure - contains a <em>SysIOVec</em> (which is equivalent to the - <c><![CDATA[struct iovec]]></c> structure defined in <c><![CDATA[uio.h]]></c>. The - ErlIOVec also contains an array of <em>ErlDrvBinary</em> + onto the socket. A combination of <c>writev</c> and the driver output + queues is very convenient. An <c>ErlIOVec</c> structure + contains a <c>SysIOVec</c> (which is equivalent to the + <c><![CDATA[struct iovec]]></c> structure defined in + <c><![CDATA[uio.h]]></c>. The + <c>ErlIOVec</c> also contains an array of <c>ErlDrvBinary</c> pointers, of the same length as the number of buffers in the I/O vector itself. One can use this to allocate the binaries - for the queue "manually" in the driver, but we'll just fill - the binary array with NULL values (line 7) , which will make - the runtime system allocate its own buffers when we call - <c><![CDATA[driver_enqv]]></c> (line 37).</p> - <p></p> + for the queue "manually" in the driver, but here + the binary array is filled with <c>NULL</c> values (line 7). + The runtime system then allocates its own buffers when + <c><![CDATA[driver_enqv]]></c> is called (line 37).</p> + <p>The routine builds an I/O vector containing the header bytes and the buffer (the opcode has been removed and the buffer length decreased by the output routine). If the queue is - empty, we'll write the data directly to the socket (or at + empty, we write the data directly to the socket (or at least try to). If any data is left, it is stored in the queue - and then we try to send the queue (line 38). An ack is sent - when the message is delivered completely (line 22). The - <c><![CDATA[send_out_queue]]></c> will send acks if the sending is - completed there. If the port is in command mode, the Erlang + and then we try to send the queue (line 38). An acknowledgement + is sent when the message is delivered completely (line 22). The + <c><![CDATA[send_out_queue]]></c> sends acknowledgements if the sending + is completed there. If the port is in <c>command</c> mode, the Erlang code serializes the send operations so that only one packet - can be waiting for delivery at a time. Therefore the ack can - be sent simply whenever the queue is empty.</p> - <p></p> - <p>A short look at the <c><![CDATA[send_out_queue]]></c> routine:</p> + can be waiting for delivery at a time. Therefore the acknowledgement + can be sent whenever the queue is empty.</p> + + <p>The <c><![CDATA[send_out_queue]]></c> routine is as follows:</p> + <code type="none"><![CDATA[ ( 1) static int send_out_queue(UdsData *ud) ( 2) { @@ -874,20 +1067,24 @@ (28) ud->sent += wrote; (29) } (30) } ]]></code> - <p>What we do is simply to pick out an I/O vector from the queue - (which is the whole queue as an <em>SysIOVec</em>). If the I/O - vector is to long (IO_VECTOR_MAX is defined to 16), the vector + + <p>We simply pick out an I/O vector from the queue + (which is the whole queue as a <c>SysIOVec</c>). If the I/O + vector is too long (<c>IO_VECTOR_MAX</c> is defined to 16), the vector length is decreased (line 15), otherwise the <c><![CDATA[writev]]></c> - (line 17) call will - fail. Writing is tried and anything written is dequeued (line - 27). If the write fails with <c><![CDATA[EWOULDBLOCK]]></c> (note that all - sockets are in nonblocking mode), <c><![CDATA[driver_select]]></c> is + call (line 17) fails. Writing is tried and anything written is dequeued + (line 27). + If the write fails with <c><![CDATA[EWOULDBLOCK]]></c> (notice that all + sockets are in non-blocking mode), <c><![CDATA[driver_select]]></c> is called to make the <c><![CDATA[uds_output]]></c> routine be called when there is space to write again.</p> - <p>We will continue trying to write until the queue is empty or - the writing would block.</p> - <p>The routine above are called from the <c><![CDATA[uds_output]]></c> - routine, which looks like this:</p> + + <p>We continue trying to write until the queue is empty or + the writing blocks.</p> + + <p>The routine above is called from the <c><![CDATA[uds_output]]></c> + routine:</p> + <code type="none"><![CDATA[ ( 1) static void uds_output(ErlDrvData handle, ErlDrvEvent event) ( 2) { @@ -900,51 +1097,80 @@ ( 9) } (10) send_out_queue(ud); (11) } ]]></code> - <p>The routine is simple, it first handles the fact that the + + <p>The routine is simple: it first handles the fact that the output select will concern a socket in the business of connecting (and the connecting blocked). If the socket is in - a connected state it simply sends the output queue, this - routine is called when there is possible to write to a socket + a connected state, it simply sends the output queue. This + routine is called when it is possible to write to a socket where we have an output queue, so there is no question what to do.</p> + <p>The driver implements a control interface, which is a synchronous interface called when Erlang calls - <c><![CDATA[erlang:port_control/3]]></c>. This is the only interface - that can control the driver when it is in data mode and it may + <seealso marker="erlang#port_control/3"> + <c>erlang:port_control/3</c></seealso>. Only this interface + can control the driver when it is in <c>data</c> mode. It can be called with the following opcodes:</p> - <list type="bulleted"> - <item>'C': Set port in command mode.</item> - <item>'I': Set port in intermediate mode.</item> - <item>'D': Set port in data mode.</item> - <item>'N': Get identification number for listen port, this - identification number is used in an accept command to the - driver, it is returned as a big endian 32 bit integer, which - happens to be the file identifier for the listen socket.</item> - <item>'S': Get statistics, which is the number of bytes received, - the number of bytes sent and the number of bytes pending in - the output queue. This data is used when the distribution - checks that a connection is alive (ticking). The statistics - is returned as 3 32 bit big endian integers.</item> - <item>'T': Send a tick message, which is a packet of length - 0. Ticking is done when the port is in data mode, so the - command for sending data cannot be used (besides it ignores - zero length packages in command mode). This is used by the - ticker to send dummy data when no other traffic is present. - <em>Note</em> that it is important that the interface for - sending ticks is not blocking. This implementation uses - <c>erlang:port_control/3</c> which does not block the caller. - If <c>erlang:port_command</c> is used, use - <c>erlang:port_command/3</c> and pass <c>[force]</c> as - option list; otherwise, the caller can be blocked indefinitely - on a busy port and prevent the system from taking down a - connection that is not functioning.</item> - <item>'R': Get creation number of listen socket, which is used to - dig out the number stored in the lock file to differentiate - between invocations of Erlang nodes with the same name.</item> - </list> + + <taglist> + <tag><c>'C'</c></tag> + <item> + <p>Sets port in <c>command</c> mode.</p> + </item> + <tag><c>'I'</c></tag> + <item> + <p>Sets port in <c>intermediate</c> mode.</p> + </item> + <tag><c>'D'</c></tag> + <item> + <p>Sets port in <c>data</c> mode.</p> + </item> + <tag><c>'N'</c></tag> + <item> + <p>Gets identification number for listen port. This + identification number is used in an accept command to the + driver. It is returned as a big-endian 32-bit integer, which + is the file identifier for the listen socket.</p> + </item> + <tag><c>'S'</c></tag> + <item> + <p>Gets statistics, which is the number of bytes received, + the number of bytes sent, and the number of bytes pending in + the output queue. This data is used when the distribution + checks that a connection is alive (ticking). The statistics + is returned as three 32-bit big-endian integers.</p> + </item> + <tag><c>'T'</c></tag> + <item> + <p>Sends a tick message, which is a packet of length 0. + Ticking is done when the port is in <c>data</c> mode, so the + command for sending data cannot be used (besides it ignores + zero length packages in <c>command</c> mode). This is used by the + ticker to send dummy data when no other traffic is present.</p> + <p><em>Note:</em> It is important that the interface for + sending ticks is not blocking. This implementation uses + <seealso marker="erlang#port_control/3"> + <c>erlang:port_control/3</c></seealso>, which does not block the + caller. If <c>erlang:port_command</c> is used, use + <seealso marker="erlang#port_command/3"> + <c>erlang:port_command/3</c></seealso> and pass <c>[force]</c> as + option list; otherwise the caller can be blocked indefinitely + on a busy port and prevent the system from taking down a + connection that is not functioning.</p> + </item> + <tag><c>'R'</c></tag> + <item> + <p>Gets creation number of a listen socket, which is used to + dig out the number stored in the lock file to differentiate + between invocations of Erlang nodes with the same name.</p> + </item> + </taglist> + <p>The control interface gets a buffer to return its value in, - but is free to allocate its own buffer is the provided one is - to small. Here is the code for <c><![CDATA[uds_control]]></c>:</p> + but is free to allocate its own buffer if the provided one is + too small. The <c><![CDATA[uds_control]]></c> code is as follows:</p> + <code type="none"><![CDATA[ ( 1) static int uds_control(ErlDrvData handle, unsigned int command, ( 2) char* buf, int count, char** res, int res_size) @@ -1025,47 +1251,55 @@ (75) } (76) #undef ENSURE (77) } ]]></code> - <p>The macro <c><![CDATA[ENSURE]]></c> (line 5 to 10) is used to ensure that - the buffer is large enough for our answer. We switch on the - command and take actions, there is not much to say about this - routine. Worth noting is that we always has read select active - on a port in data mode (achieved by calling <c><![CDATA[do_recv]]></c> on - line 45), but turn off read selection in intermediate and - command modes (line 27 and 36).</p> - <p>The rest of the driver is more or less UDS specific and not of + + <p>The macro <c><![CDATA[ENSURE]]></c> (line 5-10) is used to ensure that + the buffer is large enough for the answer. We switch on the command and + take actions. We always have read select active on a port in <c>data</c> + mode (achieved by calling <c><![CDATA[do_recv]]></c> on line 45), but + we turn off read selection in <c>intermediate</c> and <c>command</c> + modes (line 27 and 36).</p> + + <p>The rest of the driver is more or less UDS-specific and not of general interest.</p> </section> </section> <section> - <title>Putting it all together</title> - <p>To test the distribution, one can use the - <c><![CDATA[net_kernel:start/1]]></c> function, which is useful as it starts - the distribution on a running system, where tracing/debugging - can be performed. The <c><![CDATA[net_kernel:start/1]]></c> routine takes a - list as its single argument. The lists first element should be - the node name (without the "@hostname") as an atom, and the second (and - last) element should be one of the atoms <c><![CDATA[shortnames]]></c> or - <c><![CDATA[longnames]]></c>. In the example case <c><![CDATA[shortnames]]></c> is - preferred. </p> - <p>For net kernel to find out which distribution module to use, the - command line argument <c><![CDATA[-proto_dist]]></c> is used. The argument - is followed by one or more distribution module names, with the - "_dist" suffix removed, i.e. uds_dist as a distribution module + <title>Putting It All Together</title> + <p>To test the distribution, the <c><![CDATA[net_kernel:start/1]]></c> + function can be used. It is useful, as it starts the distribution on a + running system, where tracing/debugging can be performed. + The <c><![CDATA[net_kernel:start/1]]></c> routine takes a + list as its single argument. The list first element in the list is to be + the node name (without the "@hostname") as an atom. The second (and + last) element is to be one of the atoms <c><![CDATA[shortnames]]></c> or + <c><![CDATA[longnames]]></c>. In the example case, + <c><![CDATA[shortnames]]></c> is preferred.</p> + + <p>For <c>net_kernel</c> to find out which distribution module to use, + command-line argument <c><![CDATA[-proto_dist]]></c> is used. It + is followed by one or more distribution module names, with suffix + "_dist" removed, that is, <c>uds_dist</c> as a distribution module is specified as <c><![CDATA[-proto_dist uds]]></c>.</p> - <p>If no epmd (TCP port mapper daemon) is used, one should also - specify the command line option <c><![CDATA[-no_epmd]]></c>, which will make - Erlang skip the epmd startup, both as a OS process and as an + + <p>If no <c>epmd</c> (TCP port mapper daemon) is used, also command-line + option <c><![CDATA[-no_epmd]]></c> is to be specified, which makes + Erlang skip the <c>epmd</c> startup, both as an OS process and as an Erlang ditto.</p> + <p>The path to the directory where the distribution modules reside - must be known at boot, which can either be achieved by - specifying <c><![CDATA[-pa <path>]]></c> on the command line or by building - a boot script containing the applications used for your - distribution protocol (in the uds_dist protocol, it's only the - uds_dist application that needs to be added to the script).</p> - <p>The distribution will be started at boot if all the above is + must be known at boot. This can be achieved either by + specifying <c><![CDATA[-pa <path>]]></c> on the command line or by + building a boot script containing the applications used for your + distribution protocol. (In the <c>uds_dist</c> protocol, only the + <c>uds_dist</c> application needs to be added to the script.)</p> + + <p>The distribution starts at boot if all the above is specified and an <c><![CDATA[-sname <name>]]></c> flag is present at the - command line, here follows two examples: </p> + command line.</p> + + <p><em>Example 1:</em></p> + <pre> $ <input>erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds -no_epmd</input> Erlang (BEAM) emulator version 5.0 @@ -1074,7 +1308,9 @@ Eshell V5.0 (abort with ^G) 1> <input>net_kernel:start([bing,shortnames]).</input> {ok,<0.30.0>} (bing@hador)2></pre> - <p>...</p> + + <p><em>Example 2:</em></p> + <pre> $ <input>erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds \ </input> <input> -no_epmd -sname bong</input> @@ -1082,8 +1318,10 @@ Erlang (BEAM) emulator version 5.0 Eshell V5.0 (abort with ^G) (bong@hador)1></pre> - <p>One can utilize the ERL_FLAGS environment variable to store the + + <p>The <c>ERL_FLAGS</c> environment variable can be used to store the complicated parameters in:</p> + <pre> $ <input>ERL_FLAGS=-pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin \ </input> <input> -proto_dist uds -no_epmd</input> @@ -1093,8 +1331,8 @@ Erlang (BEAM) emulator version 5.0 Eshell V5.0 (abort with ^G) (bang@hador)1></pre> - <p>The <c><![CDATA[ERL_FLAGS]]></c> should preferably not include the name of - the node.</p> + + <p><c><![CDATA[ERL_FLAGS]]></c> should not include the node name.</p> </section> </chapter> diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml index 1eb05310e9..7e18a73aa8 100644 --- a/erts/doc/src/communication.xml +++ b/erts/doc/src/communication.xml @@ -26,63 +26,72 @@ <prepared>Rickard Green</prepared> <responsible></responsible> <docno></docno> - <approved></approved> + <approved>uy</approved> <checked></checked> <date>2012-12-03</date> <rev>PA1</rev> <file>communication.xml</file> </header> <p>Communication in Erlang is conceptually performed using - asynchronous signaling. All different executing entities - such as processes, and ports communicate via asynchronous + asynchronous signaling. All different executing entities, + such as processes and ports, communicate through asynchronous signals. The most commonly used signal is a message. Other - common signals are exit, link, unlink, monitor, demonitor + common signals are exit, link, unlink, monitor, and demonitor signals.</p> + <section> <title>Passing of Signals</title> - <p>The amount of time that passes between a signal being sent + <p>The amount of time that passes between a signal is sent and the arrival of the signal at the destination is unspecified - but positive. If the receiver has terminated, the signal will - not arrive, but it is possible that it triggers another signal. - For example, a link signal sent to a non-existing process will - trigger an exit signal which will be sent back to where the link + but positive. If the receiver has terminated, the signal does + not arrive, but it can trigger another signal. + For example, a link signal sent to a non-existing process + triggers an exit signal, which is sent back to where the link signal originated from. When communicating over the distribution, - signals may be lost if the distribution channel goes down.</p> - <p>The only signal ordering guarantee given is the following. If + signals can be lost if the distribution channel goes down.</p> + + <p>The only signal ordering guarantee given is the following: if an entity sends multiple signals to the same destination entity, - the order will be preserved. That is, if <c>A</c> sends + the order is preserved; that is, if <c>A</c> sends a signal <c>S1</c> to <c>B</c>, and later sends - the signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to + signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to arrive after <c>S2</c>.</p> </section> + <section> <title>Synchronous Communication</title> <p>Some communication is synchronous. If broken down into pieces, - a synchronous communication operation, consists of two asynchronous - signals. One request signal and one reply signal. An example of - such a synchronous communication is a call to <c>process_info/2</c> - when the first argument is not <c>self()</c>. The caller will send - an asynchronous signal requesting information, and will then - wait for the reply signal containing the requested information. When - the request signal reaches its destination the destination process + a synchronous communication operation consists of two asynchronous + signals; one request signal and one reply signal. An example of + such a synchronous communication is a call to + <seealso marker="erlang:process_info/2"> + <c>erlang:process_info/2</c></seealso> + when the first argument is not <c>self()</c>. The caller sends + an asynchronous signal requesting information, and then + waits for the reply signal containing the requested information. When + the request signal reaches its destination, the destination process replies with the requested information.</p> </section> + <section> <title>Implementation</title> - <p>The implementation of different asynchronous signals in the - VM may vary over time, but the behaviour will always respect this + <p>The implementation of different asynchronous signals in the virtual + machine can vary over time, but the behavior always respects this concept of asynchronous signals being passed between entities as described above.</p> - <p>By inspecting the implementation you might notice that some - specific signal actually gives a stricter guarantee than described + + <p>By inspecting the implementation, you might notice that some + specific signal gives a stricter guarantee than described above. It is of vital importance that such knowledge about the - implementation is <em>not</em> used by Erlang code, since the - implementation might change at any time without prior notice.</p> - <p>Some example of major implementation changes:</p> + implementation is <em>not</em> used by Erlang code, as the + implementation can change at any time without prior notice.</p> + + <p>Examples of major implementation changes:</p> + <list type="bulleted"> - <item>As of ERTS version 5.5.2 exit signals to processes are truly + <item>As from ERTS 5.5.2 exit signals to processes are truly asynchronously delivered.</item> - <item>As of ERTS version 5.10 all signals from processes to ports + <item>As from ERTS 5.10 all signals from processes to ports are truly asynchronously delivered.</item> </list> </section> diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 0b827ae583..a9aeb1888c 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -22,7 +22,7 @@ </legalnotice> - <title>How to interpret the Erlang crash dumps</title> + <title>How to Interpret the Erlang Crash Dumps</title> <prepared>Patrik Nyblom</prepared> <responsible></responsible> <docno></docno> @@ -32,401 +32,529 @@ <rev>PA1</rev> <file>crash_dump.xml</file> </header> - <p>This document describes the <c><![CDATA[erl_crash.dump]]></c> file generated - upon abnormal exit of the Erlang runtime system.</p> - <p><em>Important:</em> For OTP release R9C the Erlang crash dump has - had a major facelift. This means that the information in this - document will not be directly applicable for older dumps. However, - if you use the Crashdump Viewer tool on older dumps, the crash - dumps are translated into a format similar to this.</p> - <p>The system will write the crash dump in the current directory of + <p>This section describes the <c><![CDATA[erl_crash.dump]]></c> file + generated upon abnormal exit of the Erlang runtime system.</p> + + <note> + <p>The Erlang crash dump had a major facelift in Erlang/OTP R9C. The + information in this section is therefore not directly applicable for + older dumps. However, if you use <seealso marker="observer:crashdump_viewer"> + <c>crashdump_viewer(3)</c></seealso> on older dumps, + the crash dumps are translated into a format similar to this.</p> + </note> + + <p>The system writes the crash dump in the current directory of the emulator or in the file pointed out by the environment variable (whatever that means on the current operating system) - ERL_CRASH_DUMP. For a crash dump to be written, there has to be a - writable file system mounted.</p> + <c>ERL_CRASH_DUMP</c>. For a crash dump to be written, a + writable file system must be mounted.</p> + <p>Crash dumps are written mainly for one of two reasons: either the - builtin function <c><![CDATA[erlang:halt/1]]></c> is called explicitly with a - string argument from running Erlang code, or else the runtime + built-in function <c><![CDATA[erlang:halt/1]]></c> is called explicitly + with a string argument from running Erlang code, or the runtime system has detected an error that cannot be handled. The most - usual reason that the system can't handle the error is that the + usual reason that the system cannot handle the error is that the cause is external limitations, such as running out of memory. A - crash dump due to an internal error may be caused by the system + crash dump caused by an internal error can be caused by the system reaching limits in the emulator itself (like the number of atoms - in the system, or too many simultaneous ets tables). Usually the + in the system, or too many simultaneous ETS tables). Usually the emulator or the operating system can be reconfigured to avoid the crash, which is why interpreting the crash dump correctly is important.</p> + <p>On systems that support OS signals, it is also possible to stop - the runtime system and generate a crash dump by sending the SIGUSR1.</p> - <p>The erlang crash dump is a readable text file, but it might not be - very easy to read. Using the Crashdump Viewer tool in the - <c><![CDATA[observer]]></c> application will simplify the task. This is an - wx-widget based tool for browsing Erlang crash dumps.</p> + the runtime system and generate a crash dump by sending the <c>SIGUSR1</c> + signal.</p> + + <p>The Erlang crash dump is a readable text file, but it can be difficult + to read. Using the Crashdump Viewer tool in the + <c><![CDATA[Observer]]></c> application simplifies the task. This is a + wx-widget-based tool for browsing Erlang crash dumps.</p> <section> <marker id="general_info"></marker> - <title>General information</title> - <p>The first part of the dump shows the creation time for the dump, - a slogan indicating the reason for the dump, the system version, - of the node from which the dump originates, the compile time of - the emulator running the originating node, the number of - atoms in the atom table and the runtime system thread that caused - the crash dump to happen. - </p> + <title>General Information</title> + <p>The first part of the crash dump shows the following:</p> + + <list type="bulleted"> + <item>The creation time for the dump</item> + <item>A slogan indicating the reason for the dump</item> + <item>The system version of the node from which the dump originates</item> + <item>The compile time of the emulator running the originating node</item> + <item>The number of atoms in the atom table</item> + <item>The runtime system thread that caused the crash dump</item> + </list> <section> - <title>Reasons for crash dumps (slogan)</title> - <p>The reason for the dump is noted in the beginning of the file - as <em>Slogan: <reason></em> (the word "slogan" has historical - roots). If the system is halted by the BIF + <title>Reasons for Crash Dumps (Slogan)</title> + <p>The reason for the dump is shown in the beginning of the file as:</p> + + <pre> +Slogan: <reason></pre> + + <p>If the system is halted by the BIF <c><![CDATA[erlang:halt/1]]></c>, the slogan is the string parameter passed to the BIF, otherwise it is a description generated by the emulator or the (Erlang) kernel. Normally the message - should be enough to understand the problem, but nevertheless - some messages are described here. Note however that the - suggested reasons for the crash are <em>only suggestions</em>. The exact reasons for the errors may vary + is enough to understand the problem, but + some messages are described here. Notice that the + suggested reasons for the crash are <em>only suggestions</em>. + The exact reasons for the errors can vary depending on the local applications and the underlying operating system.</p> - <list type="bulleted"> - <item>"<em><A></em>: Cannot allocate <em><N></em> - bytes of memory (of type "<em><T></em>")." - The system - has run out of memory. <A> is the allocator that failed - to allocate memory, <N> is the number of bytes that - <A> tried to allocate, and <T> is the memory block - type that the memory was needed for. The most common case is - that a process stores huge amounts of data. In this case - <T> is most often <c><![CDATA[heap]]></c>, <c><![CDATA[old_heap]]></c>, - <c><![CDATA[heap_frag]]></c>, or <c><![CDATA[binary]]></c>. For more information on - allocators see - <seealso marker="erts_alloc">erts_alloc(3)</seealso>.</item> - <item>"<em><A></em>: Cannot reallocate <em><N></em> - bytes of memory (of type "<em><T></em>")." - Same as - above with the exception that memory was being reallocated - instead of being allocated when the system ran out of memory.</item> - <item>"Unexpected op code <em>N</em>" - Error in compiled - code, <c><![CDATA[beam]]></c> file damaged or error in the compiler.</item> - <item>"Module <em>Name</em> undefined" <c><![CDATA[|]]></c> "Function - <em>Name</em> undefined" <c><![CDATA[|]]></c> "No function - <em>Name</em>:<em>Name</em>/1" <c><![CDATA[|]]></c> "No function - <em>Name</em>:start/2" - The kernel/stdlib applications are - damaged or the start script is damaged.</item> - <item>"Driver_select called with too large file descriptor - <c><![CDATA[N]]></c>" - The number of file descriptors for sockets - exceed 1024 (Unix only). The limit on file-descriptors in - some Unix flavors can be set to over 1024, but only 1024 - sockets/pipes can be used simultaneously by Erlang (due to - limitations in the Unix <c><![CDATA[select]]></c> call). The number of - open regular files is not affected by this.</item> - <item>"Received SIGUSR1" - Sending the SIGUSR1 signal to a - Erlang machine (Unix only) forces a crash dump. This slogan reflects - that the Erlang machine crash-dumped due to receiving that signal.</item> - <item>"Kernel pid terminated (<em>Who</em>) - (<em>Exit-reason</em>)" - The kernel supervisor has detected - a failure, usually that the <c><![CDATA[application_controller]]></c> - has shut down (<c><![CDATA[Who]]></c> = <c><![CDATA[application_controller]]></c>, - <c><![CDATA[Why]]></c> = <c><![CDATA[shutdown]]></c>). The application controller - may have shut down for a number of reasons, the most usual - being that the node name of the distributed Erlang node is - already in use. A complete supervisor tree "crash" (i.e., - the top supervisors have exited) will give about the same - result. This message comes from the Erlang code and not from - the virtual machine itself. It is always due to some kind of - failure in an application, either within OTP or a - "user-written" one. Looking at the error log for your - application is probably the first step to take.</item> - <item>"Init terminating in do_boot ()" - The primitive Erlang boot - sequence was terminated, most probably because the boot - script has errors or cannot be read. This is usually a - configuration error - the system may have been started with - a faulty <c><![CDATA[-boot]]></c> parameter or with a boot script from - the wrong version of OTP.</item> - <item>"Could not start kernel pid (<em>Who</em>) ()" - One of the - kernel processes could not start. This is probably due to - faulty arguments (like errors in a <c><![CDATA[-config]]></c> argument) - or faulty configuration files. Check that all files are in - their correct location and that the configuration files (if - any) are not damaged. Usually there are also messages - written to the controlling terminal and/or the error log - explaining what's wrong.</item> - </list> - <p>Other errors than the ones mentioned above may occur, as the - <c><![CDATA[erlang:halt/1]]></c> BIF may generate any message. If the + + <taglist> + <tag><em><A>: Cannot allocate <N> bytes of memory (of type + "<T>")</em></tag> + <item> + <p>The system has run out of memory. <A> is the allocator that + failed to allocate memory, <N> is the number of bytes that + <A> tried to allocate, and <T> is the memory block + type that the memory was needed for. The most common case is + that a process stores huge amounts of data. In this case + <T> is most often <c><![CDATA[heap]]></c>, + <c><![CDATA[old_heap]]></c>, <c><![CDATA[heap_frag]]></c>, or + <c><![CDATA[binary]]></c>. For more information on allocators, see + <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p> + </item> + <tag><em><A>: Cannot reallocate <N> bytes of memory (of + type "<T>")</em></tag> + <item> + <p>Same as above except that memory was reallocated + instead of allocated when the system ran out of memory.</p> + </item> + <tag><em>Unexpected op code <N></em></tag> + <item> + <p>Error in compiled code, <c><![CDATA[beam]]></c> file damaged, or + error in the compiler.</p> + </item> + <tag><em>Module <Name> undefined <c><![CDATA[|]]></c> Function + <Name> undefined <c><![CDATA[|]]></c> No function + <Name>:<Name>/1 <c><![CDATA[|]]></c> No function + <Name>:start/2</em></tag> + <item> + <p>The Kernel/STDLIB applications are + damaged or the start script is damaged.</p> + </item> + <tag><em>Driver_select called with too large file descriptor + <c><![CDATA[N]]></c></em></tag> + <item> + <p>The number of file descriptors for sockets + exceeds 1024 (Unix only). The limit on file descriptors in + some Unix flavors can be set to over 1024, but only 1024 + sockets/pipes can be used simultaneously by Erlang (because of + limitations in the Unix <c><![CDATA[select]]></c> call). The number + of open regular files is not affected by this.</p> + </item> + <tag><em>Received SIGUSR1</em></tag> + <item> + <p>Sending the <c>SIGUSR1</c> signal to an Erlang machine (Unix only) + forces a crash dump. This slogan reflects that the Erlang machine + crash-dumped because of receiving that signal.</p> + </item> + <tag><em>Kernel pid terminated (<Who>) (<Exit + reason>)</em></tag> + <item> + <p>The kernel supervisor has detected a failure, usually that the + <c><![CDATA[application_controller]]></c> has shut down + (<c><![CDATA[Who]]></c> = <c><![CDATA[application_controller]]></c>, + <c><![CDATA[Why]]></c> = <c><![CDATA[shutdown]]></c>). + The application controller + can have shut down for many reasons, the most usual + is that the node name of the distributed Erlang node is + already in use. A complete supervisor tree "crash" (that is, + the top supervisors have exited) gives about the same + result. This message comes from the Erlang code and not from + the virtual machine itself. It is always because of some + failure in an application, either within OTP or a + "user-written" one. Looking at the error log for your + application is probably the first step to take.</p> + </item> + <tag><em>Init terminating in do_boot ()</em></tag> + <item> + <p>The primitive Erlang boot sequence was terminated, most probably + because the boot script has errors or cannot be read. This is + usually a configuration error; the system can have been started + with a faulty <c><![CDATA[-boot]]></c> parameter or with a boot + script from the wrong OTP version.</p> + </item> + <tag><em>Could not start kernel pid (<Who>) ()</em></tag> + <item> + <p>One of the kernel processes could not start. This is probably + because of faulty arguments (like errors in a + <c><![CDATA[-config]]></c> argument) + or faulty configuration files. Check that all files are in + their correct location and that the configuration files (if + any) are not damaged. Usually messages are also + written to the controlling terminal and/or the error log + explaining what is wrong.</p> + </item> + </taglist> + + <p>Other errors than these can occur, as the + <c><![CDATA[erlang:halt/1]]></c> BIF can generate any message. If the message is not generated by the BIF and does not occur in the - list above, it may be due to an error in the emulator. There - may however be unusual messages that I haven't mentioned, that - still are connected to an application failure. There is a lot - more information available, so more thorough reading of the - crash dump may reveal the crash reason. The size of processes, - the number of ets tables and the Erlang data on each process - stack can be useful for tracking down the problem.</p> + list above, it can be because of an error in the emulator. There + can however be unusual messages, not mentioned here, which + are still connected to an application failure. There is much + more information available, so a thorough reading of the + crash dump can reveal the crash reason. The size of processes, + the number of ETS tables, and the Erlang data on each process + stack can be useful to find the problem.</p> </section> <section> - <title>Number of atoms</title> + <title>Number of Atoms</title> <p>The number of atoms in the system at the time of the crash is shown as <em>Atoms: <number></em>. Some ten thousands atoms is - perfectly normal, but more could indicate that the BIF - <c><![CDATA[erlang:list_to_atom/1]]></c> is used to dynamically generate a - lot of <em>different</em> atoms, which is never a good idea.</p> + perfectly normal, but more can indicate that the BIF + <c><![CDATA[erlang:list_to_atom/1]]></c> is used to generate many + <em>different</em> atoms dynamically, which is never a good idea.</p> </section> </section> <section> <marker id="scheduler"></marker> - <title>Scheduler information</title> - <p>Under the tag <em>=scheduler</em> information about the current state - and statistics of the schedulers in the runtime system is displayed. - On OSs that do allow instant suspension of other threads, the data within - this section will reflect what the runtime system looks like at the moment - when the crash happens.</p> + <title>Scheduler Information</title> + <p>Under the tag <em>=scheduler</em> is shown information about the current + state and statistics of the schedulers in the runtime system. On + operating systems that allow suspension of other threads, the + data within this section reflects what the runtime system looks like + when a crash occurs.</p> + <p>The following fields can exist for a process:</p> + <taglist> <tag><em>=scheduler:id</em></tag> - <item>Header, states the scheduler identifier.</item> + <item> + <p>Heading. States the scheduler identifier.</p> + </item> <tag><em>Scheduler Sleep Info Flags</em></tag> - <item>If empty the scheduler was doing some work. - If not empty the scheduler is either in some state of sleep, - or suspended. This entry is only present in a SMP enabled emulator</item> + <item> + <p>If empty, the scheduler was doing some work. + If not empty, the scheduler is either in some state of sleep, + or suspended. This entry is only present in an SMP-enabled + emulator.</p> + </item> <tag><em>Scheduler Sleep Info Aux Work</em></tag> - <item>If not empty, a scheduler internal auxiliary work is scheduled - to be done.</item> + <item> + <p>If not empty, a scheduler internal auxiliary work is scheduled + to be done.</p> + </item> <tag><em>Current Port</em></tag> - <item>The port identifier of the port that is currently being - executed by the scheduler.</item> + <item> + <p>The port identifier of the port that is currently + executed by the scheduler.</p> + </item> <tag><em>Current Process</em></tag> - <item>The process identifier of the process that is currently being - executed by the scheduler. If there is such a process this entry is - followed by the <em>State</em>,<em>Internal State</em>, - <em>Program Counter</em>, <em>CP</em> of that same process. See - <seealso marker="#processes">Process Information</seealso> for a - description what the different entries mean. Keep in mind that - this is a snapshot of what the entries are exactly when the crash - dump is starting to be generated. Therefore they will most likely - be different (and more telling) then the entries for the same - processes found in the <em>=proc</em> section. If there is no currently - running process, only the <em>Current Process</em> entry will be printed. + <item> + <p>The process identifier of the process that is currently + executed by the scheduler. If there is such a process, this entry is + followed by the <em>State</em>, <em>Internal State</em>, + <em>Program Counter</em>, and <em>CP</em> of that same process. + The entries are described in section + <seealso marker="#processes">Process Information</seealso>.</p> + <p>Notice that this is a snapshot of what the entries are exactly when + the crash dump is starting to be generated. Therefore they are most + likely different (and more telling) than the entries for the same + processes found in the <em>=proc</em> section. If there is no + currently running process, only the <em>Current Process</em> entry is + shown.</p> </item> <tag><em>Current Process Limited Stack Trace</em></tag> - <item>This entry only shows up if there is a current process. It is very - similar to <seealso marker="#proc_data"><em>=proc_stack</em></seealso>, - except that only the function frames are printed (i.e. the stack variables - are omited). It is also limited to only print the top and bottom part - of the stack. If the stack is small (less that 512 slots) then the - entire stack will be printed. If not, an entry stating - <code>skipping ## slots</code> will be printed where ## is - replaced by the number of slots that has been skipped.</item> + <item> + <p>This entry is shown only if there is a current process. It is + similar to <seealso marker="#proc_data"> + <em>=proc_stack</em></seealso>, except that only the function frames + are shown (that is, the stack variables are omitted). + Also, only the top and bottom part of the stack are shown. If the + stack is small (< 512 slots), the entire stack is shown. Otherwise + the entry <em>skipping ## slots</em> is shown, where <c>##</c> + is replaced by the number of slots that has been skipped.</p> + </item> <tag><em>Run Queue</em></tag> - <item>Displays statistics about how many processes and ports - of different priorities are scheduled on this scheduler.</item> + <item> + <p>Shows statistics about how many processes and ports + of different priorities are scheduled on this scheduler.</p> + </item> <tag><em>** crashed **</em></tag> - <item>This entry is normally not printed. It signifies that getting - the rest of the information about this scheduler failed for some reason. + <item> + <p>This entry is normally not shown. It signifies that getting the rest + of the information about this scheduler failed for some reason.</p> </item> </taglist> </section> <section> <marker id="memory"></marker> - <title>Memory information</title> - <p>Under the tag <em>=memory</em> you will find information similar - to what you can obtain on a living node with - <seealso marker="erts:erlang#erlang:memory/0">erlang:memory()</seealso>.</p> + <title>Memory Information</title> + <p>Under the tag <em>=memory</em> is shown information similar + to what can be obtainted on a living node with + <seealso marker="erts:erlang#erlang:memory/0"> + <c>erlang:memory()</c></seealso>.</p> </section> <section> <marker id="internal_tables"></marker> - <title>Internal table information</title> - <p>The tags <em>=hash_table:<table_name></em> and - <em>=index_table:<table_name></em> presents internal - tables. These are mostly of interest for runtime system - developers.</p> + <title>Internal Table Information</title> + <p>Under the tags <em>=hash_table:<table_name></em> and + <em>=index_table:<table_name></em> is shown internal + tables. These are mostly of interest for runtime system developers.</p> </section> <section> <marker id="allocated_areas"></marker> - <title>Allocated areas</title> - <p>Under the tag <em>=allocated_areas</em> you will find information - similar to what you can obtain on a living node with - <seealso marker="erts:erlang#system_info_allocated_areas">erlang:system_info(allocated_areas)</seealso>.</p> + <title>Allocated Areas</title> + <p>Under the tag <em>=allocated_areas</em> is shown information + similar to what can be obtained on a living node with + <seealso marker="erts:erlang#system_info_allocated_areas"> + <c>erlang:system_info(allocated_areas)</c></seealso>.</p> </section> <section> <marker id="allocator"></marker> <title>Allocator</title> - <p>Under the tag <em>=allocator:<A></em> you will find + <p>Under the tag <em>=allocator:<A></em> is shown various information about allocator <A>. The information - is similar to what you can obtain on a living node with - <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, <A>})</seealso>. - For more information see the documentation of - <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, <A>})</seealso>, - and the - <seealso marker="erts_alloc">erts_alloc(3)</seealso> - documentation.</p> + is similar to what can be obtained on a living node with + <seealso marker="erts:erlang#system_info_allocator_tuple"> + <c>erlang:system_info({allocator, <A>})</c></seealso>. + For more information, see also + <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p> </section> <section> <marker id="processes"></marker> - <title>Process information</title> + <title>Process Information</title> <p>The Erlang crashdump contains a listing of each living Erlang - process in the system. The process information for one process - may look like this (line numbers have been added): - </p> - <p>The following fields can exist for a process:</p> + process in the system. The following fields can exist for a process:</p> + <taglist> <tag><em>=proc:<pid></em></tag> - <item>Heading, states the process identifier</item> + <item> + <p>Heading. States the process identifier.</p> + </item> <tag><em>State</em></tag> <item> <p>The state of the process. This can be one of the following:</p> - <list type="bulleted"> - <item><em>Scheduled</em> - The process was scheduled to run - but not currently running ("in the run queue").</item> - <item><em>Waiting</em> - The process was waiting for - something (in <c><![CDATA[receive]]></c>).</item> - <item><em>Running</em> - The process was currently - running. If the BIF <c><![CDATA[erlang:halt/1]]></c> was called, this was - the process calling it.</item> - <item><em>Exiting</em> - The process was on its way to - exit.</item> - <item><em>Garbing</em> - This is bad luck, the process was - garbage collecting when the crash dump was written, the rest - of the information for this process is limited.</item> - <item><em>Suspended</em> - The process is suspended, either - by the BIF <c><![CDATA[erlang:suspend_process/1]]></c> or because it is - trying to write to a busy port.</item> - </list> + <taglist> + <tag><em>Scheduled</em></tag> + <item>The process was scheduled to run + but is currently not running ("in the run queue").</item> + <tag><em>Waiting</em></tag> + <item>The process was waiting for + something (in <c><![CDATA[receive]]></c>).</item> + <tag><em>Running</em></tag> + <item>The process was currently running. + If the BIF <c><![CDATA[erlang:halt/1]]></c> was called, this was + the process calling it.</item> + <tag><em>Exiting</em></tag> + <item>The process was on its way to exit.</item> + <tag><em>Garbing</em></tag> + <item>This is bad luck, the process was + garbage collecting when the crash dump was written. The rest + of the information for this process is limited.</item> + <tag><em>Suspended</em></tag> + <item>The process is suspended, either + by the BIF <c><![CDATA[erlang:suspend_process/1]]></c> or because + it tries to write to a busy port.</item> + </taglist> </item> <tag><em>Registered name</em></tag> - <item>The registered name of the process, if any.</item> + <item> + <p>The registered name of the process, if any.</p> + </item> <tag><em>Spawned as</em></tag> - <item>The entry point of the process, i.e., what function was - referenced in the <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c> call that - started the process.</item> + <item> + <p>The entry point of the process, that is, what function was + referenced in the <c><![CDATA[spawn]]></c> or + <c><![CDATA[spawn_link]]></c> call that + started the process.</p> + </item> <tag><em>Last scheduled in for | Current call</em></tag> - <item>The current function of the process. These fields will not - always exist.</item> + <item> + <p>The current function of the process. These fields do not + always exist.</p> + </item> <tag><em>Spawned by</em></tag> - <item>The parent of the process, i.e. the process which executed - <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c>.</item> + <item> + <p>The parent of the process, that is, the process that executed + <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c>.</p> + </item> <tag><em>Started</em></tag> - <item>The date and time when the process was started.</item> + <item> + <p>The date and time when the process was started.</p> + </item> <tag><em>Message queue length</em></tag> - <item>The number of messages in the process' message queue.</item> + <item> + <p>The number of messages in the process' message queue.</p> + </item> <tag><em>Number of heap fragments</em></tag> - <item>The number of allocated heap fragments.</item> + <item> + <p>The number of allocated heap fragments.</p> + </item> <tag><em>Heap fragment data</em></tag> - <item>Size of fragmented heap data. This is data either created by - messages being sent to the process or by the Erlang BIFs. This - amount depends on so many things that this field is utterly - uninteresting.</item> + <item> + <p>Size of fragmented heap data. This is data either created by + messages sent to the process or by the Erlang BIFs. This + amount depends on so many things that this field is utterly + uninteresting.</p> + </item> <tag><em>Link list</em></tag> - <item>Process id's of processes linked to this one. May also contain - ports. If process monitoring is used, this field also tells in - which direction the monitoring is in effect, i.e., a link - being "to" a process tells you that the "current" process was - monitoring the other and a link "from" a process tells you - that the other process was monitoring the current one.</item> + <item> + <p>Process IDs of processes linked to this one. Can also contain + ports. If process monitoring is used, this field also tells in + which direction the monitoring is in effect. That is, a link + "to" a process tells you that the "current" process was + monitoring the other, and a link "from" a process tells you + that the other process was monitoring the current one.</p> + </item> <tag><em>Reductions</em></tag> - <item>The number of reductions consumed by the process.</item> + <item> + <p>The number of reductions consumed by the process.</p> + </item> <tag><em>Stack+heap</em></tag> - <item>The size of the stack and heap (they share memory segment)</item> + <item> + <p>The size of the stack and heap (they share memory segment).</p> + </item> <tag><em>OldHeap</em></tag> - <item>The size of the "old heap". The Erlang virtual machine uses - generational garbage collection with two generations. There is - one heap for new data items and one for the data that have - survived two garbage collections. The assumption (which is - almost always correct) is that data that survive two garbage - collections can be "tenured" to a heap more seldom garbage - collected, as they will live for a long period. This is a - quite usual technique in virtual machines. The sum of the - heaps and stack together constitute most of the process's - allocated memory.</item> + <item> + <p>The size of the "old heap". The Erlang virtual machine uses + generational garbage collection with two generations. There is + one heap for new data items and one for the data that has + survived two garbage collections. The assumption (which is + almost always correct) is that data surviving two garbage + collections can be "tenured" to a heap more seldom garbage + collected, as they will live for a long period. This is a + usual technique in virtual machines. The sum of the + heaps and stack together constitute most of the + allocated memory of the process.</p> + </item> <tag><em>Heap unused, OldHeap unused</em></tag> - <item>The amount of unused memory on each heap. This information is - usually useless.</item> - <tag><em>Stack</em></tag> - <item>If the system uses shared heap, the fields - <em>Stack+heap</em>, <em>OldHeap</em>, <em>Heap unused</em> - and <em>OldHeap unused</em> do not exist. Instead this field - presents the size of the process' stack.</item> + <item> + <p>The amount of unused memory on each heap. This information is + usually useless.</p> + </item> <tag><em>Memory</em></tag> - <item>The total memory used by this process. This includes call stack, - heap and internal structures. Same as <seealso marker="erlang#process_info-2">erlang:process_info(Pid,memory)</seealso>. + <item> + <p>The total memory used by this process. This includes call stack, + heap, and internal structures. Same as + <seealso marker="erlang#process_info-2"> + <c>erlang:process_info(Pid,memory)</c></seealso>.</p> </item> <tag><em>Program counter</em></tag> - <item>The current instruction pointer. This is only interesting for - runtime system developers. The function into which the program - counter points is the current function of the process.</item> + <item> + <p>The current instruction pointer. This is only of interest for + runtime system developers. The function into which the program + counter points is the current function of the process.</p> + </item> <tag><em>CP</em></tag> - <item>The continuation pointer, i.e. the return address for the - current call. Usually useless for other than runtime system - developers. This may be followed by the function into which - the CP points, which is the function calling the current - function.</item> + <item> + <p>The continuation pointer, that is, the return address for the + current call. Usually useless for other than runtime system + developers. This can be followed by the function into which + the CP points, which is the function calling the current + function.</p> + </item> <tag><em>Arity</em></tag> - <item>The number of live argument registers. The argument registers, - if any are live, will follow. These may contain the arguments - of the function if they are not yet moved to the stack.</item> - <item><em>Internal State</em></item> - <item>A more detailed internal represantation of the state of - this process.</item> + <item> + <p>The number of live argument registers. The argument registers + if any are live will follow. These can contain the arguments + of the function if they are not yet moved to the stack.</p> + </item> + <tag><em>Internal State</em></tag> + <item> + <p>A more detailed internal representation of the state of + this process.</p> + </item> </taglist> - <p>See also the section about <seealso marker="#proc_data">process data</seealso>.</p> + <p>See also section <seealso marker="#proc_data">Process Data</seealso>.</p> </section> <section> <marker id="ports"></marker> - <title>Port information</title> + <title>Port Information</title> <p>This section lists the open ports, their owners, any linked - processed, and the name of their driver or external process.</p> + processes, and the name of their driver or external process.</p> </section> <section> <marker id="ets_tables"></marker> - <title>ETS tables</title> + <title>ETS Tables</title> <p>This section contains information about all the ETS tables in - the system. The following fields are interesting for each table:</p> + the system. The following fields are of interest for each table:</p> + <taglist> <tag><em>=ets:<owner></em></tag> - <item>Heading, states the owner of the table (a process identifier)</item> + <item> + <p>Heading. States the table owner (a process identifier).</p> + </item> <tag><em>Table</em></tag> - <item>The identifier for the table. If the table is a - <c><![CDATA[named_table]]></c>, this is the name.</item> + <item> + <p>The identifier for the table. If the table is a + <c><![CDATA[named_table]]></c>, this is the name.</p> + </item> <tag><em>Name</em></tag> - <item>The name of the table, regardless of whether it is a - <c><![CDATA[named_table]]></c> or not.</item> + <item> + <p>The table name, regardless of if it is a + <c><![CDATA[named_table]]></c> or not.</p> + </item> <tag><em>Hash table, Buckets</em></tag> - <item>This occurs if the table is a hash table, i.e. if it is not an - <c><![CDATA[ordered_set]]></c>.</item> + <item> + <p>If the table is a hash table, that is, if it is not an + <c><![CDATA[ordered_set]]></c>.</p> + </item> <tag><em>Hash table, Chain Length</em></tag> - <item>Only applicable for hash tables. Contains statistics about the - hash table, such as the max, min and avg chain length. Having a max much - larger than the avg, and a std dev much larger that - the expected std dev is a sign that the hashing of the terms is - behaving badly for some reason.</item> + <item> + <p>If the table is a hash table. Contains statistics about the + table, such as the maximum, minimum, and average chain length. + Having a maximum much larger than the average, and a standard + deviation much larger than the expected standard deviation is + a sign that the hashing of the terms + behaves badly for some reason.</p> + </item> <tag><em>Ordered set (AVL tree), Elements</em></tag> - <item>This occurs only if the table is an <c><![CDATA[ordered_set]]></c>. (The - number of elements is the same as the number of objects in the - table.)</item> + <item> + <p>If the table is an <c><![CDATA[ordered_set]]></c>. (The + number of elements is the same as the number of objects in the + table.)</p> + </item> <tag><em>Fixed</em></tag> - <item>If the table is fixed using ets:safe_fixtable or some internal - mechanism.</item> + <item> + <p>If the table is fixed using + <seealso marker="stdlib:ets#safe_fixtable/2"> + <c>ets:safe_fixtable/2</c></seealso> or some internal mechanism.</p> + </item> <tag><em>Objects</em></tag> - <item>The number of objects in the table</item> + <item> + <p>The number of objects in the table.</p> + </item> <tag><em>Words</em></tag> - <item>The number of words (usually 4 bytes/word) allocated to data - in the table.</item> + <item> + <p>The number of words (usually 4 bytes/word) allocated to data + in the table.</p> + </item> <tag><em>Type</em></tag> - <item>The type of the table, i.e. <c>set</c>, <c>bag</c>, - <c>dublicate_bag</c> or <c>ordered_set</c>.</item> + <item> + <p>The table type, that is, <c>set</c>, <c>bag</c>, + <c>dublicate_bag</c>, or <c>ordered_set</c>.</p> + </item> <tag><em>Compressed</em></tag> - <item>If this table was compressed.</item> + <item> + <p>If the table was compressed.</p> + </item> <tag><em>Protection</em></tag> - <item>The protection of this table.</item> + <item> + <p>The protection of the table.</p> + </item> <tag><em>Write Concurrency</em></tag> - <item>If write_concurrency was enabled for this table.</item> + <item> + <p>If <c>write_concurrency</c> was enabled for the table.</p> + </item> <tag><em>Read Concurrency</em></tag> - <item>If read_concurrency was enabled for this table.</item> + <item> + <p>If <c>read_concurrency</c> was enabled for the table.</p> + </item> </taglist> </section> @@ -435,167 +563,252 @@ <title>Timers</title> <p>This section contains information about all the timers started with the BIFs <c><![CDATA[erlang:start_timer/3]]></c> and - <c><![CDATA[erlang:send_after/3]]></c>. The following fields exists for each - timer:</p> + <c><![CDATA[erlang:send_after/3]]></c>. The following fields exist + for each timer:</p> + <taglist> <tag><em>=timer:<owner></em></tag> - <item>Heading, states the owner of the timer (a process identifier) - i.e. the process to receive the message when the timer - expires.</item> + <item> + <p>Heading. States the timer owner (a process identifier), + that is, the process to receive the message when the timer + expires.</p> + </item> <tag><em>Message</em></tag> - <item>The message to be sent.</item> + <item> + <p>The message to be sent.</p> + </item> <tag><em>Time left</em></tag> - <item>Number of milliseconds left until the message would have been - sent.</item> + <item> + <p>Number of milliseconds left until the message would have been + sent.</p> + </item> </taglist> </section> <section> <marker id="distribution_info"></marker> - <title>Distribution information</title> - <p>If the Erlang node was alive, i.e., set up for communicating + <title>Distribution Information</title> + <p>If the Erlang node was alive, that is, set up for communicating with other nodes, this section lists the connections that were active. The following fields can exist:</p> + <taglist> <tag><em>=node:<node_name></em></tag> - <item>The name of the node</item> + <item> + <p>The node name.</p> + </item> <tag><em>no_distribution</em></tag> - <item>This will only occur if the node was not distributed.</item> + <item> + <p>If the node was not distributed.</p> + </item> <tag><em>=visible_node:<channel></em></tag> - <item>Heading for a visible nodes, i.e. an alive node with a - connection to the node that crashed. States the channel number - for the node.</item> + <item> + <p>Heading for a visible node, that is, an alive node with a + connection to the node that crashed. States the channel number + for the node.</p> + </item> <tag><em>=hidden_node:<channel></em></tag> - <item>Heading for a hidden node. A hidden node is the same as a - visible node, except that it is started with the "-hidden" - flag. States the channel number for the node.</item> + <item> + <p>Heading for a hidden node. A hidden node is the same as a + visible node, except that it is started with the <c>"-hidden"</c> + flag. States the channel number for the node.</p> + </item> <tag><em>=not_connected:<channel></em></tag> - <item>Heading for a node which is has been connected to the crashed - node earlier. References (i.e. process or port identifiers) - to the not connected node existed at the time of the crash. - exist. States the channel number for the node.</item> + <item> + <p>Heading for a node that was connected to the crashed + node earlier. References (that is, process or port identifiers) + to the not connected node existed at the time of the crash. + States the channel number for the node.</p> + </item> <tag><em>Name</em></tag> - <item>The name of the remote node.</item> + <item> + <p>The name of the remote node.</p> + </item> <tag><em>Controller</em></tag> - <item>The port which controls the communication with the remote node.</item> + <item> + <p>The port controlling communication with the remote node.</p> + </item> <tag><em>Creation</em></tag> - <item>An integer (1-3) which together with the node name identifies - a specific instance of the node.</item> - <tag><em>Remote monitoring: <local_proc> <remote_proc></em></tag> - <item>The local process was monitoring the remote process at the - time of the crash.</item> - <tag><em>Remotely monitored by: <local_proc> <remote_proc></em></tag> - <item>The remote process was monitoring the local process at the - time of the crash.</item> + <item> + <p>An integer (1-3) that together with the node name identifies + a specific instance of the node.</p> + </item> + <tag><em>Remote monitoring: <local_proc> <remote_proc></em> + </tag> + <item> + <p>The local process was monitoring the remote process at the + time of the crash.</p> + </item> + <tag><em>Remotely monitored by: <local_proc> + <remote_proc></em></tag> + <item> + <p>The remote process was monitoring the local process at the + time of the crash.</p> + </item> <tag><em>Remote link: <local_proc> <remote_proc></em></tag> - <item>A link existed between the local process and the remote - process at the time of the crash.</item> + <item> + <p>A link existed between the local process and the remote + process at the time of the crash.</p> + </item> </taglist> </section> <section> <marker id="loaded_modules"></marker> - <title>Loaded module information</title> - <p>This section contains information about all loaded modules. - First, the memory usage by loaded code is summarized. There is - one field for "Current code" which is code that is the current - latest version of the modules. There is also a field for "Old - code" which is code where there exists a newer version in the - system, but the old version is not yet purged. The memory usage - is in bytes.</p> - <p>All loaded modules are then listed. The following fields exist:</p> + <title>Loaded Module Information</title> + <p>This section contains information about all loaded modules.</p> + + <p>First, the memory use by the loaded code is summarized:</p> + + <taglist> + <tag><em>Current code</em></tag> + <item> + <p>Code that is the current latest version of the modules.</p> + </item> + <tag><em>Old code</em></tag> + <item> + <p>Code where there exists a newer version in the + system, but the old version is not yet purged.</p> + </item> + </taglist> + + <p>The memory use is in bytes.</p> + + <p>Then, all loaded modules are listed. The following fields exist:</p> + <taglist> <tag><em>=mod:<module_name></em></tag> - <item>Heading, and the name of the module.</item> + <item> + <p>Heading. States the module name.</p> + </item> <tag><em>Current size</em></tag> - <item>Memory usage for the loaded code in bytes</item> + <item> + <p>Memory use for the loaded code, in bytes.</p> + </item> <tag><em>Old size</em></tag> - <item>Memory usage for the old code, if any.</item> + <item> + <p>Memory use for the old code, if any.</p> + </item> <tag><em>Current attributes</em></tag> - <item>Module attributes for the current code. This field is decoded - when looked at by the Crashdump Viewer tool.</item> + <item> + <p>Module attributes for the current code. This field is decoded + when looked at by the Crashdump Viewer tool.</p> + </item> <tag><em>Old attributes</em></tag> - <item>Module attributes for the old code, if any. This field is - decoded when looked at by the Crashdump Viewer tool.</item> + <item> + <p>Module attributes for the old code, if any. This field is + decoded when looked at by the Crashdump Viewer tool.</p> + </item> <tag><em>Current compilation info</em></tag> - <item>Compilation information (options) for the current code. This - field is decoded when looked at by the Crashdump Viewer tool.</item> + <item> + <p>Compilation information (options) for the current code. This + field is decoded when looked at by the Crashdump Viewer tool.</p> + </item> <tag><em>Old compilation info</em></tag> - <item>Compilation information (options) for the old code, if - any. This field is decoded when looked at by the Crashdump - Viewer tool.</item> + <item> + <p>Compilation information (options) for the old code, if + any. This field is decoded when looked at by the Crashdump + Viewer tool.</p> + </item> </taglist> </section> <section> <marker id="funs"></marker> - <title>Fun information</title> - <p>In this section, all funs are listed. The following fields exist - for each fun:</p> + <title>Fun Information</title> + <p>This section lists all funs. The following fields exist for each fun:</p> + <taglist> <tag><em>=fun</em></tag> - <item>Heading</item> + <item> + <p>Heading.</p> + </item> <tag><em>Module</em></tag> - <item>The name of the module where the fun was defined.</item> + <item> + <p>The name of the module where the fun was defined.</p> + </item> <tag><em>Uniq, Index</em></tag> - <item>Identifiers</item> + <item> + <p>Identifiers.</p> + </item> <tag><em>Address</em></tag> - <item>The address of the fun's code.</item> + <item> + <p>The address of the fun's code.</p> + </item> <tag><em>Native_address</em></tag> - <item>The address of the fun's code when HiPE is enabled.</item> + <item> + <p>The address of the fun's code when HiPE is enabled.</p> + </item> <tag><em>Refc</em></tag> - <item>The number of references to the fun.</item> + <item> + <p>The number of references to the fun.</p> + </item> </taglist> </section> <section> <marker id="proc_data"></marker> <title>Process Data</title> - <p>For each process there will be at least one <em>=proc_stack</em> - and one <em>=proc_heap</em> tag followed by the raw memory + <p>For each process there is at least one <em>=proc_stack</em> + and one <em>=proc_heap</em> tag, followed by the raw memory information for the stack and heap of the process.</p> - <p>For each process there will also be a <em>=proc_messages</em> - tag if the process' message queue is non-empty and a - <em>=proc_dictionary</em> tag if the process' dictionary (the - <c><![CDATA[put/2]]></c> and <c><![CDATA[get/1]]></c> thing) is non-empty.</p> + + <p>For each process there is also a <em>=proc_messages</em> + tag if the process message queue is non-empty, and a + <em>=proc_dictionary</em> tag if the process dictionary (the + <c><![CDATA[put/2]]></c> and <c><![CDATA[get/1]]></c> thing) is + non-empty.</p> + <p>The raw memory information can be decoded by the Crashdump - Viewer tool. You will then be able to see the stack dump, the - message queue (if any) and the dictionary (if any).</p> + Viewer tool. You can then see the stack dump, the + message queue (if any), and the dictionary (if any).</p> + <p>The stack dump is a dump of the Erlang process stack. Most of - the live data (i.e., variables currently in use) are placed on - the stack; thus this can be quite interesting. One has to - "guess" what's what, but as the information is symbolic, - thorough reading of this information can be very useful. As an - example we can find the state variable of the Erlang primitive - loader on line <c><![CDATA[(5)]]></c> in the example below:</p> + the live data (that is, variables currently in use) are placed on + the stack; thus this can be interesting. One has to + "guess" what is what, but as the information is symbolic, + thorough reading of this information can be useful. As an + example, we can find the state variable of the Erlang primitive + loader online <c><![CDATA[(5)]]></c> and <c><![CDATA[(6)]]></c> + in the following example:</p> + <code type="none"><![CDATA[ (1) 3cac44 Return addr 0x13BF58 (<terminate process normally>) -(2) y(0) ["/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin","/view/siri_r10_dev/ -(3) clearcase/otp/erts/lib/stdlib/ebin"] +(2) y(0) ["/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin", +(3) "/view/siri_r10_dev/clearcase/otp/erts/lib/stdlib/ebin"] (4) y(1) <0.1.0> -(5) y(2) {state,[],none,#Fun<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>,#Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>} -(6) y(3) infinity ]]></code> +(5) y(2) {state,[],none,#Fun<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>, +(6) #Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>} +(7) y(3) infinity ]]></code> + <p>When interpreting the data for a process, it is helpful to know - that anonymous function objects (funs) are given a name - constructed from the name of the function in which they are - created, and a number (starting with 0) indicating the number of - that fun within that function.</p> + that anonymous function objects (funs) are given the following:</p> + + <list type="bulleted"> + <item>A name constructed from the name of the function in which they are + created + </item> + <item>A number (starting with 0) indicating the number of that fun within + that function + </item> + </list> </section> <section> <marker id="atoms"></marker> <title>Atoms</title> - <p>Now all the atoms in the system are written. This is only - interesting if one suspects that dynamic generation of atoms could + <p>This section presents all the atoms in the system. This is only + of interest if one suspects that dynamic generation of atoms can be a problem, otherwise this section can be ignored.</p> - <p>Note that the last created atom is printed first.</p> + + <p>Notice that the last created atom is shown first.</p> </section> <section> <title>Disclaimer</title> - <p>The format of the crash dump evolves between releases of - OTP. Some information here may not apply to your - version. A description as this will never be complete; it is meant as + <p>The format of the crash dump evolves between OTP releases. + Some information described here may not apply to your + version. A description like this will never be complete; it is meant as an explanation of the crash dump in general and as a help when trying to find application errors, not as a complete specification.</p> diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index 4bef5e1388..8f31df4cad 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -22,103 +22,115 @@ </legalnotice> - <title>How to implement a driver</title> + <title>How to Implement a Driver</title> <prepared>Jakob C</prepared> <docno></docno> <date>2000-11-28</date> <rev>PA1</rev> <file>driver.xml</file> </header> - - <note><p>This document was written a long time ago. A lot of it is still - interesting since it explains important concepts, but it was - written for an older driver interface so the examples do not - work anymore. The reader is encouraged to read - <seealso marker="erl_driver">erl_driver</seealso> and the - <seealso marker="driver_entry">driver_entry</seealso> documentation. - </p></note> + <note> + <p>This section was written a long time ago. Most of it is still + valid, as it explains important concepts, but this was + written for an older driver interface so the examples do not + work anymore. The reader is encouraged to read the + <seealso marker="erl_driver"><c>erl_driver</c></seealso> and + <seealso marker="driver_entry"><c>driver_entry</c></seealso> + documentation also.</p> + </note> <section> <title>Introduction</title> - <p>This chapter tells you how to build your own driver for erlang.</p> - <p>A driver in Erlang is a library written in C, that is linked to - the Erlang emulator and called from erlang. Drivers can be used - when C is more suitable than Erlang, to speed things up, or to - provide access to OS resources not directly accessible from - Erlang.</p> + <p>This section describes how to build your own driver for Erlang.</p> + + <p>A driver in Erlang is a library written in C, which is linked to + the Erlang emulator and called from Erlang. Drivers can be used + when C is more suitable than Erlang, to speed up things, or to + provide access to OS resources not directly accessible from Erlang.</p> + <p>A driver can be dynamically loaded, as a shared library (known as - a DLL on windows), or statically loaded, linked with the emulator + a DLL on Windows), or statically loaded, linked with the emulator when it is compiled and linked. Only dynamically loaded drivers are described here, statically linked drivers are beyond the scope - of this chapter.</p> - <p>When a driver is loaded it is executed in the context of the - emulator, shares the same memory and the same thread. This means - that all operations in the driver must be non-blocking, and that - any crash in the driver will bring the whole emulator down. In - short: you have to be extremely careful!</p> - <p></p> + of this section.</p> + + <warning> + <p>When a driver is loaded it is executed in the context of the + emulator, shares the same memory and the same thread. This means + that all operations in the driver must be non-blocking, and that + any crash in the driver brings the whole emulator down. In short, + be careful.</p> + </warning> </section> <section> - <title>Sample driver</title> - <p>This is a simple driver for accessing a postgres + <title>Sample Driver</title> + <p>This section describes a simple driver for accessing a postgres database using the libpq C client library. Postgres - is used because it's free and open source. For information - on postgres, refer to the website - <url href="http://www.postgres.org">www.postgres.org</url>.</p> + is used because it is free and open source. For information on postgres, + see <url href="http://www.postgres.org">www.postgres.org</url>.</p> + <p>The driver is synchronous, it uses the synchronous calls of - the client library. This is only for simplicity, and is - generally not good, since it will - halt the emulator while waiting for the database. - This will be improved on below with an asynchronous - sample driver.</p> - <p>The code is quite straight-forward: all + the client library. This is only for simplicity, but not good, as it + halts the emulator while waiting for the database. + This is improved below with an asynchronous sample driver.</p> + + <p>The code is straightforward: all communication between Erlang and the driver is done with <c><![CDATA[port_control/3]]></c>, and the driver returns data back using the <c><![CDATA[rbuf]]></c>.</p> + <p>An Erlang driver only exports one function: the driver entry function. This is defined with a macro, - <c><![CDATA[DRIVER_INIT]]></c>, and returns a pointer to a + <c><![CDATA[DRIVER_INIT]]></c>, which returns a pointer to a C <c><![CDATA[struct]]></c> containing the entry points that are called from the emulator. The <c><![CDATA[struct]]></c> defines the entries that the emulator calls to call the driver, with a <c><![CDATA[NULL]]></c> pointer for entries that are not defined and used by the driver.</p> + <p>The <c><![CDATA[start]]></c> entry is called when the driver is opened as a port with <c><![CDATA[open_port/2]]></c>. Here we allocate memory for a user data structure. - This user data will be passed every time the emulator - calls us. First we store the driver handle, because it - is needed in subsequent calls. We allocate memory for + This user data is passed every time the emulator + calls us. First we store the driver handle, as it + is needed in later calls. We allocate memory for the connection handle that is used by LibPQ. We also set the port to return allocated driver binaries, by - setting the flag <c><![CDATA[PORT_CONTROL_FLAG_BINARY]]></c>, calling + setting flag <c><![CDATA[PORT_CONTROL_FLAG_BINARY]]></c>, calling <c><![CDATA[set_port_control_flags]]></c>. (This is because - we don't know whether our data will fit in the - result buffer of <c><![CDATA[control]]></c>, which has a default size - set up by the emulator, currently 64 bytes.)</p> - <p>There is an entry <c><![CDATA[init]]></c> which is called when - the driver is loaded, but we don't use this, since + we do not know if our data will fit in the + result buffer of <c><![CDATA[control]]></c>, which has a default size, + 64 bytes, set up by the emulator.)</p> + + <p>An entry <c><![CDATA[init]]></c> is called when + the driver is loaded. However, we do not use this, as it is executed only once, and we want to have the possibility of several instances of the driver.</p> + <p>The <c><![CDATA[stop]]></c> entry is called when the port is closed.</p> + <p>The <c><![CDATA[control]]></c> entry is called from the emulator when the Erlang code calls <c><![CDATA[port_control/3]]></c>, to do the actual work. We have defined a simple set of - commands: <c><![CDATA[connect]]></c> to login to the database, <c><![CDATA[disconnect]]></c> - to log out and <c><![CDATA[select]]></c> to send a SQL-query and get the result. + commands: <c><![CDATA[connect]]></c> to log in to the database, + <c><![CDATA[disconnect]]></c> to log out, and <c><![CDATA[select]]></c> + to send a SQL-query and get the result. All results are returned through <c><![CDATA[rbuf]]></c>. - The library <c><![CDATA[ei]]></c> in <c><![CDATA[erl_interface]]></c> is used - to encode data in binary term format. The result is returned + The library <c><![CDATA[ei]]></c> in <c><![CDATA[erl_interface]]></c> is + used to encode data in binary term format. The result is returned to the emulator as binary terms, so <c><![CDATA[binary_to_term]]></c> is called in Erlang to convert the result to term form.</p> - <p>The code is available in <c><![CDATA[pg_sync.c]]></c> in the <c><![CDATA[sample]]></c> - directory of <c><![CDATA[erts]]></c>.</p> + + <p>The code is available in <c><![CDATA[pg_sync.c]]></c> in the + <c><![CDATA[sample]]></c> directory of <c><![CDATA[erts]]></c>.</p> + <p>The driver entry contains the functions that - will be called by the emulator. In our simple - example, we only provide <c><![CDATA[start]]></c>, <c><![CDATA[stop]]></c> - and <c><![CDATA[control]]></c>.</p> + will be called by the emulator. In this example, + only <c><![CDATA[start]]></c>, <c><![CDATA[stop]]></c>, + and <c><![CDATA[control]]></c> are provided:</p> + <code type="none"><![CDATA[ /* Driver interface declarations */ static ErlDrvData start(ErlDrvPort port, char *command); @@ -128,15 +140,15 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf, static ErlDrvEntry pq_driver_entry = { NULL, /* init */ - start, - stop, + start, + stop, NULL, /* output */ NULL, /* ready_input */ - NULL, /* ready_output */ + NULL, /* ready_output */ "pg_sync", /* the name of the driver */ NULL, /* finish */ NULL, /* handle */ - control, + control, NULL, /* timeout */ NULL, /* outputv */ NULL, /* ready_async */ @@ -145,14 +157,18 @@ static ErlDrvEntry pq_driver_entry = { NULL /* event */ }; ]]></code> + <p>We have a structure to store state needed by the driver, - in this case we only need to keep the database connection.</p> + in this case we only need to keep the database connection:</p> + <code type="none"><![CDATA[ typedef struct our_data_s { PGconn* conn; } our_data_t; ]]></code> - <p>These are control codes we have defined.</p> + + <p>The control codes that we have defined are as follows:</p> + <code type="none"><![CDATA[ /* Keep the following definitions in alignment with the * defines in erl_pq_sync.erl @@ -162,10 +178,12 @@ typedef struct our_data_s { #define DRV_DISCONNECT 'D' #define DRV_SELECT 'S' ]]></code> - <p>This just returns the driver structure. The macro + + <p>This returns the driver structure. The macro <c><![CDATA[DRIVER_INIT]]></c> defines the only exported function. All the other functions are static, and will not be exported from the library.</p> + <code type="none"><![CDATA[ /* INITIALIZATION AFTER LOADING */ @@ -180,9 +198,11 @@ DRIVER_INIT(pq_drv) return &pq_driver_entry; } ]]></code> - <p>Here we do some initialization, <c><![CDATA[start]]></c> is called from - <c><![CDATA[open_port]]></c>. The data will be passed to <c><![CDATA[control]]></c> - and <c><![CDATA[stop]]></c>.</p> + + <p>Here some initialization is done, <c><![CDATA[start]]></c> is called from + <c><![CDATA[open_port]]></c>. The data will be passed to + <c><![CDATA[control]]></c> and <c><![CDATA[stop]]></c>.</p> + <code type="none"><![CDATA[ /* DRIVER INTERFACE */ static ErlDrvData start(ErlDrvPort port, char *command) @@ -195,8 +215,10 @@ static ErlDrvData start(ErlDrvPort port, char *command) return (ErlDrvData)data; } ]]></code> + <p>We call disconnect to log out from the database. (This should have been done from Erlang, but just in case.)</p> + <code type="none"><![CDATA[ static int do_disconnect(our_data_t* data, ei_x_buff* x); @@ -208,22 +230,27 @@ static void stop(ErlDrvData drv_data) driver_free(data); } ]]></code> + <p>We use the binary format only to return data to the emulator; - input data is a string paramater for <c><![CDATA[connect]]></c> and + input data is a string parameter for <c><![CDATA[connect]]></c> and <c><![CDATA[select]]></c>. The returned data consists of Erlang terms.</p> - <p>The functions <c><![CDATA[get_s]]></c> and <c><![CDATA[ei_x_to_new_binary]]></c> are - utilities that are used to make the code shorter. <c><![CDATA[get_s]]></c> - duplicates the string and zero-terminates it, since the + + <p>The functions <c><![CDATA[get_s]]></c> and + <c><![CDATA[ei_x_to_new_binary]]></c> are utilities that are used to + make the code shorter. <c><![CDATA[get_s]]></c> + duplicates the string and zero-terminates it, as the postgres client library wants that. <c><![CDATA[ei_x_to_new_binary]]></c> - takes an <c><![CDATA[ei_x_buff]]></c> buffer and allocates a binary and - copies the data there. This binary is returned in <c><![CDATA[*rbuf]]></c>. - (Note that this binary is freed by the emulator, not by us.)</p> + takes an <c><![CDATA[ei_x_buff]]></c> buffer, allocates a binary, and + copies the data there. This binary is returned in + <c><![CDATA[*rbuf]]></c>. + (Notice that this binary is freed by the emulator, not by us.)</p> + <code type="none"><![CDATA[ static char* get_s(const char* buf, int len); static int do_connect(const char *s, our_data_t* data, ei_x_buff* x); static int do_select(const char* s, our_data_t* data, ei_x_buff* x); -/* Since we are operating in binary mode, the return value from control +/* As we are operating in binary mode, the return value from control * is irrelevant, as long as it is not negative. */ static int control(ErlDrvData drv_data, unsigned int command, char *buf, @@ -246,10 +273,12 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf, return r; } ]]></code> - <p><c><![CDATA[do_connect]]></c> is where we log in to the database. If the connection - was successful we store the connection handle in our driver - data, and return ok. Otherwise, we return the error message - from postgres, and store <c><![CDATA[NULL]]></c> in the driver data.</p> + + <p><c><![CDATA[do_connect]]></c> is where we log in to the database. If the + connection was successful, we store the connection handle in the driver + data, and return <c>'ok'</c>. Otherwise, we return the error message + from postgres and store <c><![CDATA[NULL]]></c> in the driver data.</p> + <code type="none"><![CDATA[ static int do_connect(const char *s, our_data_t* data, ei_x_buff* x) { @@ -265,10 +294,13 @@ static int do_connect(const char *s, our_data_t* data, ei_x_buff* x) return 0; } ]]></code> - <p>If we are connected (if the connection handle is not <c><![CDATA[NULL]]></c>), + + <p>If we are connected (and if the connection handle is not + <c><![CDATA[NULL]]></c>), we log out from the database. We need to check if we should - encode an ok, since we might get here from the <c><![CDATA[stop]]></c> - function, which doesn't return data to the emulator.</p> + encode an <c>'ok'</c>, as we can get here from function + <c><![CDATA[stop]]></c>, which does not return data to the emulator:</p> + <code type="none"><![CDATA[ static int do_disconnect(our_data_t* data, ei_x_buff* x) { @@ -281,9 +313,11 @@ static int do_disconnect(our_data_t* data, ei_x_buff* x) return 0; } ]]></code> - <p>We execute a query and encode the result. Encoding is done - in another C module, <c><![CDATA[pg_encode.c]]></c> which is also provided + + <p>We execute a query and encode the result. Encoding is done in + another C module, <c><![CDATA[pg_encode.c]]></c>, which is also provided as sample code.</p> + <code type="none"><![CDATA[ static int do_select(const char* s, our_data_t* data, ei_x_buff* x) { @@ -293,12 +327,14 @@ static int do_select(const char* s, our_data_t* data, ei_x_buff* x) return 0; } ]]></code> - <p>Here we simply check the result from postgres, and - if it's data we encode it as lists of lists with + + <p>Here we check the result from postgres. + If it is data, we encode it as lists of lists with column data. Everything from postgres is C strings, - so we just use <c><![CDATA[ei_x_encode_string]]></c> to send + so we use <c><![CDATA[ei_x_encode_string]]></c> to send the result as strings to Erlang. (The head of the list contains the column names.)</p> + <code type="none"><![CDATA[ void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn) { @@ -338,33 +374,36 @@ void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn) </section> <section> - <title>Compiling and linking the sample driver</title> - <p>The driver should be compiled and linked to a shared - library (DLL on windows). With gcc this is done - with the link flags <c><![CDATA[-shared]]></c> and <c><![CDATA[-fpic]]></c>. - Since we use the <c><![CDATA[ei]]></c> library we should include + <title>Compiling and Linking the Sample Driver</title> + <p>The driver is to be compiled and linked to a shared + library (DLL on Windows). With gcc, this is done with + link flags <c><![CDATA[-shared]]></c> and <c><![CDATA[-fpic]]></c>. + As we use the <c><![CDATA[ei]]></c> library, we should include it too. There are several versions of <c><![CDATA[ei]]></c>, compiled for debug or non-debug and multi-threaded or single-threaded. - In the makefile for the samples the <c><![CDATA[obj]]></c> directory + In the makefile for the samples, the <c><![CDATA[obj]]></c> directory is used for the <c><![CDATA[ei]]></c> library, meaning that we use the non-debug, single-threaded version.</p> </section> <section> - <title>Calling a driver as a port in Erlang</title> + <title>Calling a Driver as a Port in Erlang</title> <p>Before a driver can be called from Erlang, it must be loaded and opened. Loading is done using the <c><![CDATA[erl_ddll]]></c> module (the <c><![CDATA[erl_ddll]]></c> driver that loads dynamic - driver, is actually a driver itself). If loading is ok + driver is actually a driver itself). If loading is successfull, the port can be opened with <c><![CDATA[open_port/2]]></c>. The port name must match the name of the shared library and the name in the driver entry structure.</p> + <p>When the port has been opened, the driver can be called. In - the <c><![CDATA[pg_sync]]></c> example, we don't have any data from + the <c><![CDATA[pg_sync]]></c> example, we do not have any data from the port, only the return value from the <c><![CDATA[port_control]]></c>.</p> + <p>The following code is the Erlang part of the synchronous - postgres driver, <c><![CDATA[pg_sync.erl]]></c>.</p> + postgres driver, <c><![CDATA[pg_sync.erl]]></c>:</p> + <code type="none"><![CDATA[ -module(pg_sync). @@ -394,20 +433,35 @@ disconnect(Port) -> select(Port, Query) -> binary_to_term(port_control(Port, ?DRV_SELECT, Query)). ]]></code> - <p>The API is simple: <c><![CDATA[connect/1]]></c> loads the driver, opens it - and logs on to the database, returning the Erlang port - if successful, <c><![CDATA[select/2]]></c> sends a query to the driver, - and returns the result, <c><![CDATA[disconnect/1]]></c> closes the - database connection and the driver. (It does not unload it, - however.) The connection string should be a connection - string for postgres.</p> - <p>The driver is loaded with <c><![CDATA[erl_ddll:load_driver/2]]></c>, - and if this is successful, or if it's already loaded, + + <p>The API is simple:</p> + + <list type="bulleted"> + <item> + <p><c><![CDATA[connect/1]]></c> loads the driver, opens it, + and logs on to the database, returning the Erlang port + if successful.</p> + </item> + <item> + <p><c><![CDATA[select/2]]></c> sends a query to the driver + and returns the result.</p> + </item> + <item> + <p><c><![CDATA[disconnect/1]]></c> closes the database + connection and the driver. (However, it does not unload it.)</p> + </item> + </list> + + <p>The connection string is to be a connection string for postgres.</p> + + <p>The driver is loaded with <c><![CDATA[erl_ddll:load_driver/2]]></c>. + If this is successful, or if it is already loaded, it is opened. This will call the <c><![CDATA[start]]></c> function in the driver.</p> + <p>We use the <c><![CDATA[port_control/3]]></c> function for all - calls into the driver, the result from the driver is - returned immediately, and converted to terms by calling + calls into the driver. The result from the driver is + returned immediately and converted to terms by calling <c><![CDATA[binary_to_term/1]]></c>. (We trust that the terms returned from the driver are well-formed, otherwise the <c><![CDATA[binary_to_term]]></c> calls could be contained in a @@ -415,16 +469,18 @@ select(Port, Query) -> </section> <section> - <title>Sample asynchronous driver</title> - <p>Sometimes database queries can take long time to + <title>Sample Asynchronous Driver</title> + <p>Sometimes database queries can take a long time to complete, in our <c><![CDATA[pg_sync]]></c> driver, the emulator halts while the driver is doing its job. This is - often not acceptable, since no other Erlang process + often not acceptable, as no other Erlang process gets a chance to do anything. To improve on our - postgres driver, we reimplement it using the asynchronous + postgres driver, we re-implement it using the asynchronous calls in LibPQ.</p> - <p>The asynchronous version of the driver is in the - sample files <c><![CDATA[pg_async.c]]></c> and <c><![CDATA[pg_asyng.erl]]></c>.</p> + + <p>The asynchronous version of the driver is in the sample files + <c><![CDATA[pg_async.c]]></c> and <c><![CDATA[pg_asyng.erl]]></c>.</p> + <code type="none"><![CDATA[ /* Driver interface declarations */ static ErlDrvData start(ErlDrvPort port, char *command); @@ -459,22 +515,26 @@ typedef struct our_data_t { int connecting; } our_data_t; ]]></code> - <p>Here some things have changed from <c><![CDATA[pg_sync.c]]></c>: we use the - entry <c><![CDATA[ready_io]]></c> for <c><![CDATA[ready_input]]></c> and - <c><![CDATA[ready_output]]></c> which will be called from the emulator only - when there is input to be read from the socket. (Actually, the + + <p>Some things have changed from <c><![CDATA[pg_sync.c]]></c>: we use + the entry <c><![CDATA[ready_io]]></c> for <c><![CDATA[ready_input]]></c> + and <c><![CDATA[ready_output]]></c>, which is called from the emulator + only when there is input to be read from the socket. (Actually, the socket is used in a <c><![CDATA[select]]></c> function inside - the emulator, and when the socket is signalled, - indicating there is data to read, the <c><![CDATA[ready_input]]></c> entry - is called. More on this below.)</p> + the emulator, and when the socket is signaled, + indicating there is data to read, the <c><![CDATA[ready_input]]></c> + entry is called. More about this below.)</p> + <p>Our driver data is also extended, we keep track of the socket used for communication with postgres, and also the port, which is needed when we send data to the port with - <c><![CDATA[driver_output]]></c>. We have a flag <c><![CDATA[connecting]]></c> to tell + <c><![CDATA[driver_output]]></c>. We have a flag + <c><![CDATA[connecting]]></c> to tell whether the driver is waiting for a connection or waiting - for the result of a query. (This is needed since the entry - <c><![CDATA[ready_io]]></c> will be called both when connecting and + for the result of a query. (This is needed, as the entry + <c><![CDATA[ready_io]]></c> is called both when connecting and when there is a query result.)</p> + <code type="none"><![CDATA[ static int do_connect(const char *s, our_data_t* data) { @@ -498,16 +558,20 @@ static int do_connect(const char *s, our_data_t* data) return 0; } ]]></code> - <p>The <c><![CDATA[connect]]></c> function looks a bit different too. We connect - using the asynchronous <c><![CDATA[PQconnectStart]]></c> function. After the - connection is started, we retrieve the socket for the connection + + <p>The <c><![CDATA[connect]]></c> function looks a bit different too. We + connect using the asynchronous <c><![CDATA[PQconnectStart]]></c> function. + After the connection is started, we retrieve the socket for the connection with <c><![CDATA[PQsocket]]></c>. This socket is used with the <c><![CDATA[driver_select]]></c> function to wait for connection. When - the socket is ready for input or for output, the <c><![CDATA[ready_io]]></c> - function will be called.</p> - <p>Note that we only return data (with <c><![CDATA[driver_output]]></c>) if there + the socket is ready for input or for output, the + <c><![CDATA[ready_io]]></c> function is called.</p> + + <p>Notice that we only return data (with <c><![CDATA[driver_output]]></c>) + if there is an error here, otherwise we wait for the connection to be completed, - in which case our <c><![CDATA[ready_io]]></c> function will be called.</p> + in which case our <c><![CDATA[ready_io]]></c> function is called.</p> + <code type="none"><![CDATA[ static int do_select(const char* s, our_data_t* data) { @@ -525,9 +589,11 @@ static int do_select(const char* s, our_data_t* data) return 0; } ]]></code> + <p>The <c><![CDATA[do_select]]></c> function initiates a select, and returns - if there is no immediate error. The actual result will be returned + if there is no immediate error. The result is returned when <c><![CDATA[ready_io]]></c> is called.</p> + <code type="none"><![CDATA[ static void ready_io(ErlDrvData drv_data, ErlDrvEvent event) { @@ -566,26 +632,31 @@ static void ready_io(ErlDrvData drv_data, ErlDrvEvent event) ei_x_free(&x); } ]]></code> - <p>The <c><![CDATA[ready_io]]></c> function will be called when the socket + + <p>The <c><![CDATA[ready_io]]></c> function is called when the socket we got from postgres is ready for input or output. Here we first check if we are connecting to the database. In that - case we check connection status and return ok if the - connection is successful, or error if it's not. If the - connection is not yet established, we simply return; <c><![CDATA[ready_io]]></c> - will be called again.</p> + case, we check connection status and return OK if the + connection is successful, or error if it is not. If the + connection is not yet established, we simply return; + <c><![CDATA[ready_io]]></c> is called again.</p> + <p>If we have a result from a connect, indicated by having data in the <c><![CDATA[x]]></c> buffer, we no longer need to select on output (<c><![CDATA[ready_output]]></c>), so we remove this by calling <c><![CDATA[driver_select]]></c>.</p> - <p>If we're not connecting, we're waiting for results from a + + <p>If we are not connecting, we wait for results from a <c><![CDATA[PQsendQuery]]></c>, so we get the result and return it. The encoding is done with the same functions as in the earlier example.</p> - <p>We should add error handling here, for instance checking - that the socket is still open, but this is just a simple - example.</p> + + <p>Error handling is to be added here, for example, checking + that the socket is still open, but this is only a simple example.</p> + <p>The Erlang part of the asynchronous driver consists of the sample file <c><![CDATA[pg_async.erl]]></c>.</p> + <code type="none"><![CDATA[ -module(pg_async). @@ -626,45 +697,50 @@ return_port_data(Port) -> binary_to_term(Data) end. ]]></code> - <p>The Erlang code is slightly different, this is because we - don't return the result synchronously from <c><![CDATA[port_control]]></c>, + + <p>The Erlang code is slightly different, as we do not + return the result synchronously from <c><![CDATA[port_control]]></c>, instead we get it from <c><![CDATA[driver_output]]></c> as data in the message queue. The function <c><![CDATA[return_port_data]]></c> above - receives data from the port. Since the data is in + receives data from the port. As the data is in binary format, we use <c><![CDATA[binary_to_term/1]]></c> to convert - it to an Erlang term. Note that the driver is opened in - binary mode (<c><![CDATA[open_port/2]]></c> is called with the option + it to an Erlang term. Notice that the driver is opened in + binary mode (<c><![CDATA[open_port/2]]></c> is called with option <c><![CDATA[[binary]]]></c>). This means that data sent from the driver - to the emulator is sent as binaries. Without the <c><![CDATA[binary]]></c> - option, they would have been lists of integers.</p> + to the emulator is sent as binaries. Without option + <c><![CDATA[binary]]></c>, they would have been lists of integers.</p> </section> <section> - <title>An asynchronous driver using driver_async</title> - <p>As a final example we demonstrate the use of <c><![CDATA[driver_async]]></c>. + <title>An Asynchronous Driver Using driver_async</title> + <p>As a final example we demonstrate the use of + <c><![CDATA[driver_async]]></c>. We also use the driver term interface. The driver is written - in C++. This enables us to use an algorithm from STL. We will - use the <c><![CDATA[next_permutation]]></c> algorithm to get the next permutation - of a list of integers. For large lists (more than 100000 - elements), this will take some time, so we will perform this + in C++. This enables us to use an algorithm from STL. We use + the <c><![CDATA[next_permutation]]></c> algorithm to get the next + permutation of a list of integers. For large lists (> 100,000 + elements), this takes some time, so we perform this as an asynchronous task.</p> - <p>The asynchronous API for drivers is quite complicated. First - of all, the work must be prepared. In our example we do this - in <c><![CDATA[output]]></c>. We could have used <c><![CDATA[control]]></c> just as well, - but we want some variation in our examples. In our driver, we allocate - a structure that contains anything that's needed for the asynchronous task - to do the work. This is done in the main emulator thread. + + <p>The asynchronous API for drivers is complicated. First, + the work must be prepared. In the example, this is done in + <c><![CDATA[output]]></c>. We could have used <c><![CDATA[control]]></c>, + but we want some variation in the examples. In our driver, we allocate + a structure that contains anything that is needed for the asynchronous + task to do the work. This is done in the main emulator thread. Then the asynchronous function is called from a driver thread, - separate from the main emulator thread. Note that the driver-functions - are not reentrant, so they shouldn't be used. + separate from the main emulator thread. Notice that the driver functions + are not re-entrant, so they are not to be used. Finally, after the function is completed, the driver callback <c><![CDATA[ready_async]]></c> is called from the main emulator thread, - this is where we return the result to Erlang. (We can't - return the result from within the asynchronous function, since - we can't call the driver-functions.)</p> - <p>The code below is from the sample file <c><![CDATA[next_perm.cc]]></c>.</p> - <p>The driver entry looks like before, but also contains the - call-back <c><![CDATA[ready_async]]></c>.</p> + this is where we return the result to Erlang. (We cannot + return the result from within the asynchronous function, as + we cannot call the driver functions.)</p> + + <p>The following code is from the sample file + <c><![CDATA[next_perm.cc]]></c>. The driver entry looks like before, + but also contains the callback <c><![CDATA[ready_async]]></c>.</p> + <code type="none"><![CDATA[ static ErlDrvEntry next_perm_driver_entry = { NULL, /* init */ @@ -685,17 +761,21 @@ static ErlDrvEntry next_perm_driver_entry = { NULL /* event */ }; ]]></code> - <p>The <c><![CDATA[output]]></c> function allocates the work-area of the - asynchronous function. Since we use C++, we use a struct, - and stuff the data in it. We have to copy the original data, + + <p>The <c><![CDATA[output]]></c> function allocates the work area of the + asynchronous function. As we use C++, we use a struct, + and stuff the data in it. We must copy the original data, it is not valid after we have returned from the <c><![CDATA[output]]></c> - function, and the <c><![CDATA[do_perm]]></c> function will be called later, - and from another thread. We return no data here, instead it will - be sent later from the <c><![CDATA[ready_async]]></c> call-back.</p> - <p>The <c><![CDATA[async_data]]></c> will be passed to the <c><![CDATA[do_perm]]></c> function. - We do not use a <c><![CDATA[async_free]]></c> function (the last argument to - <c><![CDATA[driver_async]]></c>), it's only used if the task is cancelled + function, and the <c><![CDATA[do_perm]]></c> function is called + later, and from another thread. We return no data here, instead it + is sent later from the <c><![CDATA[ready_async]]></c> callback.</p> + + <p>The <c><![CDATA[async_data]]></c> is passed to the + <c><![CDATA[do_perm]]></c> function. We do not use a + <c><![CDATA[async_free]]></c> function (the last argument to + <c><![CDATA[driver_async]]></c>), it is only used if the task is cancelled programmatically.</p> + <code type="none"><![CDATA[ struct our_async_data { bool prev; @@ -720,8 +800,10 @@ static void output(ErlDrvData drv_data, char *buf, int len) driver_async(port, NULL, do_perm, async_data, do_free); } ]]></code> - <p>In the <c><![CDATA[do_perm]]></c> we simply do the work, operating + + <p>In the <c><![CDATA[do_perm]]></c> we do the work, operating on the structure that was allocated in <c><![CDATA[output]]></c>.</p> + <code type="none"><![CDATA[ static void do_perm(void* async_data) { @@ -732,13 +814,17 @@ static void do_perm(void* async_data) next_permutation(d->data.begin(), d->data.end()); } ]]></code> - <p>In the <c><![CDATA[ready_async]]></c> function, the output is sent back to the + + <p>In the <c><![CDATA[ready_async]]></c> function the output is sent back + to the emulator. We use the driver term format instead of <c><![CDATA[ei]]></c>. - This is the only way to send Erlang terms directly to a driver, - without having the Erlang code to call <c><![CDATA[binary_to_term/1]]></c>. In - our simple example this works well, and we don't need to use + This is the only way to send Erlang terms directly to a driver, without + having the Erlang code to call <c><![CDATA[binary_to_term/1]]></c>. In + the simple example this works well, and we do not need to use <c><![CDATA[ei]]></c> to handle the binary term format.</p> - <p>When the data is returned we deallocate our data.</p> + + <p>When the data is returned, we deallocate our data.</p> + <code type="none"><![CDATA[ static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data) { @@ -759,12 +845,15 @@ static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data) delete d; } ]]></code> - <p>This driver is called like the others from Erlang, however, since + + <p>This driver is called like the others from Erlang. However, as we use <c><![CDATA[driver_output_term]]></c>, there is no need to call - binary_to_term. The Erlang code is in the sample file + <c>binary_to_term</c>. The Erlang code is in the sample file <c><![CDATA[next_perm.erl]]></c>.</p> + <p>The input is changed into a list of integers and sent to the driver.</p> + <code type="none"><![CDATA[ -module(next_perm). diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index ae7f264d0c..2421e0a8d9 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -4,7 +4,7 @@ <cref> <header> <copyright> - <year>2001</year><year>2015</year> + <year>2001</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,55 +33,64 @@ <file>driver_entry.xml</file> </header> <lib>driver_entry</lib> - <libsummary>The driver-entry structure used by erlang drivers.</libsummary> + <libsummary>The driver-entry structure used by Erlang drivers.</libsummary> <description> <marker id="WARNING"/> - <warning><p><em>Use this functionality with extreme care!</em></p> + <warning> + <p><em>Use this functionality with extreme care.</em></p> <p>A driver callback is executed as a direct extension of the - native code of the VM. Execution is not made in a safe environment. - The VM can <em>not</em> provide the same services as provided when - executing Erlang code, such as preemptive scheduling or memory - protection. If the driver callback function doesn't behave well, - the whole VM will misbehave.</p> - <list> - <item><p>A driver callback that crash will crash the whole VM.</p></item> - <item><p>An erroneously implemented driver callback might cause - a VM internal state inconsistency which may cause a crash of the VM, - or miscellaneous misbehaviors of the VM at any point after the call - to the driver callback.</p></item> - <item><p>A driver callback that do - <seealso marker="erl_driver#lengthy_work">lengthy work</seealso> - before returning will degrade responsiveness of the VM, - and may cause miscellaneous strange behaviors. Such strange behaviors - include, but are not limited to, extreme memory usage, and bad load - balancing between schedulers. Strange behaviors that might occur due - to lengthy work may also vary between OTP releases.</p></item> + native code of the VM. Execution is not made in a safe environment. + The VM <em>cannot</em> provide the same services as provided when + executing Erlang code, such as pre-emptive scheduling or memory + protection. If the driver callback function does not behave well, + the whole VM will misbehave.</p> + <list type="bulleted"> + <item> + <p>A driver callback that crash will crash the whole VM.</p> + </item> + <item> + <p>An erroneously implemented driver callback can cause a VM + internal state inconsistency, which can cause a crash of the VM, + or miscellaneous misbehaviors of the VM at any point after the + call to the driver callback.</p> + </item> + <item> + <p>A driver callback doing + <seealso marker="erl_driver#lengthy_work">lengthy work</seealso> + before returning degrades responsiveness of the VM, and can cause + miscellaneous strange behaviors. Such strange behaviors + include, but are not limited to, extreme memory usage, and bad + load balancing between schedulers. Strange behaviors that can + occur because of lengthy work can also vary between Erlang/OTP + releases.</p> + </item> </list> - </warning> - <p> - As of erts version 5.9 (OTP release R15B) the driver interface + </warning> + + <p>As from ERTS 5.9 (Erlang/OTP R15B) the driver interface has been changed with larger types for the callbacks - <seealso marker="#output">output</seealso>, - <seealso marker="#control">control</seealso> and - <seealso marker="#call">call</seealso>. + <seealso marker="#output"><c>output</c></seealso>, + <seealso marker="#control"><c>control</c></seealso>, and + <seealso marker="#call"><c>call</c></seealso>. See driver <seealso marker="erl_driver#version_management"> version management</seealso> in - <seealso marker="erl_driver">erl_driver</seealso>. - </p> + <seealso marker="erl_driver"><c>erl_driver</c></seealso>.</p> + <note> <p>Old drivers (compiled with an <c>erl_driver.h</c> from an - earlier erts version than 5.9) have to be updated and have - to use the extended interface (with - <seealso marker="erl_driver#version_management">version management - </seealso>).</p> + ERTS version earlier than 5.9) must be updated and have + to use the extended interface (with + <seealso marker="erl_driver#version_management">version management + </seealso>).</p> </note> - <p>The <c>driver_entry</c> structure is a C struct that all erlang - drivers define. It contains entry points for the erlang driver - that are called by the erlang emulator when erlang code accesses + + <p>The <c>driver_entry</c> structure is a C struct that all Erlang + drivers define. It contains entry points for the Erlang driver, + which are called by the Erlang emulator when Erlang code accesses the driver.</p> - <p> - <marker id="emulator"></marker> - The <seealso marker="erl_driver">erl_driver</seealso> driver + + <p><marker id="emulator"></marker> + The <seealso marker="erl_driver"><c>erl_driver</c></seealso> driver API functions need a port handle that identifies the driver instance (and the port in the emulator). This is only passed to the <c>start</c> function, but @@ -90,416 +99,466 @@ common practice is to have the <c>start</c> function allocate some application-defined structure and stash the <c>port</c> handle in it, to use it later with the driver API functions.</p> - <p>The driver call-back functions are called synchronously from the - erlang emulator. If they take too long before completing, they - can cause timeouts in the emulator. Use the queue or - asynchronous calls if necessary, since the emulator must be + + <p>The driver callback functions are called synchronously from the + Erlang emulator. If they take too long before completing, they + can cause time-outs in the emulator. Use the queue or + asynchronous calls if necessary, as the emulator must be responsive.</p> - <p>The driver structure contains the name of the driver and some - 15 function pointers. These pointers are called at different + + <p>The driver structure contains the driver name and some + 15 function pointers, which are called at different times by the emulator.</p> + <p>The only exported function from the driver is <c>driver_init</c>. This function returns the <c>driver_entry</c> structure that points to the other functions in the driver. The - <c>driver_init</c> function is declared with a macro - <c>DRIVER_INIT(drivername)</c>. (This is because different OS's - have different names for it.)</p> - <p>When writing a driver in C++, the driver entry should be of - <c>"C"</c> linkage. One way to do this is to put this line - somewhere before the driver entry: - <c>extern "C" DRIVER_INIT(drivername);</c>.</p> + <c>driver_init</c> function is declared with a macro, + <c>DRIVER_INIT(drivername)</c>. (This is because different + operating systems have different names for it.)</p> + + <p>When writing a driver in C++, the driver entry is to be of + <c>"C"</c> linkage. One way to do this is to put the + following line somewhere before the driver entry:</p> + + <pre> +extern "C" DRIVER_INIT(drivername);</pre> + <p>When the driver has passed the <c>driver_entry</c> over to the emulator, the driver is <em>not</em> allowed to modify the <c>driver_entry</c>.</p> - <p>If compiling a driver for static inclusion via --enable-static-drivers you - have to define STATIC_ERLANG_DRIVER before the DRIVER_INIT declaration.</p> + + <p>If compiling a driver for static inclusion through + <c>--enable-static-drivers</c>, you must define + <c>STATIC_ERLANG_DRIVER</c> before the <c>DRIVER_INIT</c> declaration.</p> + <note> - <p>Do <em>not</em> declare the <c>driver_entry</c> <c>const</c>. This since the emulator needs to - modify the <c>handle</c>, and the <c>handle2</c> - fields. A statically allocated, and <c>const</c> - declared <c>driver_entry</c> may be located in - read only memory which will cause the emulator - to crash.</p> + <p>Do <em>not</em> declare the <c>driver_entry</c> <c>const</c>. + This because the emulator must + modify the <c>handle</c> and the <c>handle2</c> + fields. A statically allocated, and <c>const</c>-declared + <c>driver_entry</c> can be located in + read-only memory, which causes the emulator to crash.</p> </note> </description> <section> - <title>DATA TYPES</title> - <taglist> - <tag><em>ErlDrvEntry</em></tag> - <item> - <p/> + <title>Data Types</title> + <p><c>ErlDrvEntry</c></p> <code type="none"> typedef struct erl_drv_entry { - int (*init)(void); /* called at system start up for statically + int (*init)(void); /* Called at system startup for statically linked drivers, and after loading for - dynamically loaded drivers */ - + dynamically loaded drivers */ #ifndef ERL_SYS_DRV ErlDrvData (*start)(ErlDrvPort port, char *command); - /* called when open_port/2 is invoked. - return value -1 means failure. */ + /* Called when open_port/2 is invoked, + return value -1 means failure */ #else ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts); - /* special options, only for system driver */ + /* Special options, only for system driver */ #endif void (*stop)(ErlDrvData drv_data); - /* called when port is closed, and when the - emulator is halted. */ + /* Called when port is closed, and when the + emulator is halted */ void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len); - /* called when we have output from erlang to + /* Called when we have output from Erlang to the port */ void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event); - /* called when we have input from one of + /* Called when we have input from one of the driver's handles */ void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event); - /* called when output is possible to one of + /* Called when output is possible to one of the driver's handles */ - char *driver_name; /* name supplied as command - in open_port XXX ? */ - void (*finish)(void); /* called before unloading the driver - - DYNAMIC DRIVERS ONLY */ - void *handle; /* Reserved -- Used by emulator internally */ + char *driver_name; /* Name supplied as command in + erlang:open_port/2 */ + void (*finish)(void); /* Called before unloading the driver - + dynamic drivers only */ + void *handle; /* Reserved, used by emulator internally */ ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen); - /* "ioctl" for drivers - invoked by + /* "ioctl" for drivers - invoked by port_control/3 */ - void (*timeout)(ErlDrvData drv_data); /* Handling of timeout in driver */ + void (*timeout)(ErlDrvData drv_data); + /* Handling of time-out in driver */ void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev); - /* called when we have output from erlang + /* Called when we have output from Erlang to the port */ void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); void (*flush)(ErlDrvData drv_data); - /* called when the port is about to be - closed, and there is data in the - driver queue that needs to be flushed + /* Called when the port is about to be + closed, and there is data in the + driver queue that must be flushed before 'stop' can be called */ ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags); /* Works mostly like 'control', a synchronous - call into the driver. */ + call into the driver */ void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data); - /* Called when an event selected by + /* Called when an event selected by driver_event() has occurred */ int extended_marker; /* ERL_DRV_EXTENDED_MARKER */ int major_version; /* ERL_DRV_EXTENDED_MAJOR_VERSION */ int minor_version; /* ERL_DRV_EXTENDED_MINOR_VERSION */ int driver_flags; /* ERL_DRV_FLAGs */ - void *handle2; /* Reserved -- Used by emulator internally */ + void *handle2; /* Reserved, used by emulator internally */ void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor); /* Called when a process monitor fires */ void (*stop_select)(ErlDrvEvent event, void* reserved); /* Called to close an event object */ - } ErlDrvEntry; - </code> - <p/> + } ErlDrvEntry;</code> <taglist> - <tag><marker id="init"/>int (*init)(void)</tag> - <item> - <p>This is called directly after the driver has been loaded by - <c>erl_ddll:load_driver/2</c>. (Actually when the driver is - added to the driver list.) The driver should return 0, or if - the driver can't initialize, -1.</p> + <tag><marker id="init"/><c>int (*init)(void)</c></tag> + <item> + <p>Called directly after the driver has been loaded by + <seealso marker="kernel:erl_ddll#load_driver/2"> + <c>erl_ddll:load_driver/2</c></seealso> (actually when the driver is + added to the driver list). The driver is to return <c>0</c>, or, if + the driver cannot initialize, <c>-1</c>.</p> </item> - <tag><marker id="start"/>ErlDrvData (*start)(ErlDrvPort port, char* command)</tag> + <tag><marker id="start"/> + <c>ErlDrvData (*start)(ErlDrvPort port, char* command)</c></tag> <item> - <p>This is called when the driver is instantiated, when - <c>open_port/2</c> is called. The driver should return a - number >= 0 or a pointer, or if the driver can't be started, - one of three error codes should be returned:</p> - <p>ERL_DRV_ERROR_GENERAL - general error, no error code</p> - <p>ERL_DRV_ERROR_ERRNO - error with error code in <c>errno</c></p> - <p>ERL_DRV_ERROR_BADARG - error, badarg</p> - <p>If an error code is returned, the port isn't started.</p> + <p>Called when the driver is instantiated, when + <seealso marker="erlang#open_port/2"> + <c>erlang:open_port/2</c></seealso> is called. + The driver is to return a number >= 0 or a pointer, or, if the + driver cannot be started, one of three error codes:</p> + <taglist> + <tag><c>ERL_DRV_ERROR_GENERAL</c></tag> + <item>General error, no error code</item> + <tag><c>ERL_DRV_ERROR_ERRNO</c></tag> + <item>Error with error code in <c>errno</c></item> + <tag><c>ERL_DRV_ERROR_BADARG</c></tag> + <item>Error, <c>badarg</c></item> + </taglist> + <p>If an error code is returned, the port is not started.</p> </item> - <tag><marker id="stop"/>void (*stop)(ErlDrvData drv_data)</tag> + <tag><marker id="stop"/><c>void (*stop)(ErlDrvData drv_data)</c></tag> <item> - <p>This is called when the port is closed, with - <c>port_close/1</c> or <c>Port ! {self(), close}</c>. Note - that terminating the port owner process also closes the + <p>Called when the port is closed, with + <seealso marker="erlang#port_close/1"> + <c>erlang:port_close/1</c></seealso> or <c>Port ! {self(), close}</c>. + Notice that terminating the port owner process also closes the port. If <c>drv_data</c> is a pointer to memory allocated in - <c>start</c>, then <c>stop</c> is the place to deallocate that - memory.</p> + <c>start</c>, then <c>stop</c> is the place to deallocate that + memory.</p> </item> - <tag><marker id="output"/>void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)</tag> + <tag><marker id="output"/> + <c>void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)</c> + </tag> <item> - <p>This is called when an erlang process has sent data to the - port. The data is pointed to by <c>buf</c>, and is - <c>len</c> bytes. Data is sent to the port with <c>Port ! {self(), {command, Data}}</c>, or with - <c>port_command/2</c>. Depending on how the port was opened, - it should be either a list of integers 0...255 or a - binary. See <c>open_port/3</c> and <c>port_command/2</c>.</p> + <p>Called when an Erlang process has sent data to the port. The data is + pointed to by <c>buf</c>, and is <c>len</c> bytes. Data is sent to + the port with <c>Port ! {self(), {command, Data}}</c> or with + <c>erlang:port_command/2</c>. Depending on how the port was + opened, it is to be either a list of integers <c>0...255</c> or a + binary. See <seealso marker="erlang#open_port/2"> + <c>erlang:open_port/2</c></seealso> and + <seealso marker="erlang#port_command/2"> + <c>erlang:port_command/2</c></seealso>.</p> </item> - - <tag><marker id="ready_input"/>void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)</tag> - <item/> - <tag><marker id="ready_output"/>void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)</tag> + <tag><marker id="ready_input"/> + <c>void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)</c> + </tag> + <item></item> + <tag><marker id="ready_output"/> + <c>void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)</c> + </tag> <item> - <p>This is called when a driver event (given in the - <c>event</c> parameter) is signaled. This is used to help - asynchronous drivers "wake up" when something happens.</p> - <p>On unix the <c>event</c> is a pipe or socket handle (or + <p>Called when a driver event (specified in parameter + <c>event</c>) is signaled. This is used to help + asynchronous drivers "wake up" when something occurs.</p> + <p>On Unix the <c>event</c> is a pipe or socket handle (or something that the <c>select</c> system call understands).</p> - <p>On Windows the <c>event</c> is an Event or Semaphore (or - something that the <c>WaitForMultipleObjects</c> API + <p>On Windows the <c>event</c> is an <c>Event</c> or <c>Semaphore</c> + (or something that the <c>WaitForMultipleObjects</c> API function understands). (Some trickery in the emulator allows more than the built-in limit of 64 <c>Events</c> to be used.)</p> <p>To use this with threads and asynchronous routines, create a - pipe on unix and an Event on Windows. When the routine + pipe on Unix and an <c>Event</c> on Windows. When the routine completes, write to the pipe (use <c>SetEvent</c> on - Windows), this will make the emulator call + Windows), this makes the emulator call <c>ready_input</c> or <c>ready_output</c>.</p> - <p>Spurious events may happen. That is, calls to <c>ready_input</c> - or <c>ready_output</c> even though no real events are signaled. In - reality it should be rare (and OS dependant), but a robust driver + <p>False events can occur. That is, calls to <c>ready_input</c> + or <c>ready_output</c> although no real events are signaled. In + reality, it is rare (and OS-dependant), but a robust driver must nevertheless be able to handle such cases.</p> </item> - <tag><marker id="driver_name"/>char *driver_name</tag> + <tag><marker id="driver_name"/><c>char *driver_name</c></tag> <item> - <p>This is the name of the driver, it must correspond to the - atom used in <c>open_port</c>, and the name of the driver + <p>The driver name. It must correspond to the atom used in + <seealso marker="erlang#open_port/2"> + <c>erlang:open_port/2</c></seealso>, and the name of the driver library file (without the extension).</p> </item> - <tag><marker id="finish"/>void (*finish)(void)</tag> + <tag><marker id="finish"/><c>void (*finish)(void)</c></tag> <item> - <p>This function is called by the <c>erl_ddll</c> driver when the + <p>Called by the <c>erl_ddll</c> driver when the driver is unloaded. (It is only called in dynamic drivers.)</p> <p>The driver is only unloaded as a result of calling - <c>unload_driver/1</c>, or when the emulator halts.</p> + <seealso marker="kernel:erl_ddll#unload_driver/1"> + <c>erl_ddll:unload_driver/1</c></seealso>, + or when the emulator halts.</p> </item> - <tag>void *handle</tag> + <tag><c>void *handle</c></tag> <item> <p>This field is reserved for the emulator's internal use. The - emulator will modify this field; therefore, it is important - that the <c>driver_entry</c> isn't declared <c>const</c>.</p> + emulator will modify this field, so it is important + that the <c>driver_entry</c> is not declared <c>const</c>.</p> </item> - <tag><marker id="control"></marker>ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)</tag> + <tag><marker id="control"></marker> + <c>ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, + char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)</c></tag> <item> - <p>This is a special routine invoked with the erlang function - <c>port_control/3</c>. It works a little like an "ioctl" for - erlang drivers. The data given to <c>port_control/3</c> - arrives in <c>buf</c> and <c>len</c>. The driver may send + <p>A special routine invoked with + <seealso marker="erlang#port_control/3"> + <c>erlang:port_control/3</c></seealso>. + It works a little like an "ioctl" for + Erlang drivers. The data specified to <c>port_control/3</c> + arrives in <c>buf</c> and <c>len</c>. The driver can send data back, using <c>*rbuf</c> and <c>rlen</c>.</p> <p>This is the fastest way of calling a driver and get a - response. It won't make any context switch in the erlang - emulator, and requires no message passing. It is suitable - for calling C function to get faster execution, when erlang + response. It makes no context switch in the Erlang + emulator and requires no message passing. It is suitable + for calling C function to get faster execution, when Erlang is too slow.</p> - <p>If the driver wants to return data, it should return it in + <p>If the driver wants to return data, it is to return it in <c>rbuf</c>. When <c>control</c> is called, <c>*rbuf</c> points to a default buffer of <c>rlen</c> bytes, which - can be used to return data. Data is returned different depending on + can be used to return data. Data is returned differently depending on the port control flags (those that are set with - <seealso marker="erl_driver#set_port_control_flags">set_port_control_flags</seealso>). - </p> + <seealso marker="erl_driver#set_port_control_flags"> + <c>erl_driver:set_port_control_flags</c></seealso>).</p> <p>If the flag is set to <c>PORT_CONTROL_FLAG_BINARY</c>, - a binary will be returned. Small binaries can be returned by writing - the raw data into the default buffer. A binary can also be - returned by setting <c>*rbuf</c> to point to a binary allocated with - <seealso marker="erl_driver#driver_alloc_binary">driver_alloc_binary</seealso>. - This binary will be freed automatically after <c>control</c> has returned. + a binary is returned. Small binaries can be returned by writing + the raw data into the default buffer. A binary can also be + returned by setting <c>*rbuf</c> to point to a binary allocated with + <seealso marker="erl_driver#driver_alloc_binary"> + <c>erl_driver:driver_alloc_binary</c></seealso>. + This binary is freed automatically after <c>control</c> has returned. The driver can retain the binary for <em>read only</em> access with - <seealso marker="erl_driver#driver_binary_inc_refc">driver_binary_inc_refc</seealso> to be freed later with - <seealso marker="erl_driver#driver_free_binary">driver_free_binary</seealso>. - It is never allowed to alter the binary after <c>control</c> has returned. - If <c>*rbuf</c> is set to NULL, an empty list will be returned. - </p> + <seealso marker="erl_driver#driver_binary_inc_refc"> + <c>erl_driver:driver_binary_inc_refc</c></seealso> to be freed later + with <seealso marker="erl_driver#driver_free_binary"> + <c>erl_driver:driver_free_binary</c></seealso>. + It is never allowed to change the binary after <c>control</c> has + returned. If <c>*rbuf</c> is set to <c>NULL</c>, an empty list is + returned.</p> <p>If the flag is set to <c>0</c>, data is returned as a list of integers. Either use the default buffer or set <c>*rbuf</c> to point to a larger buffer allocated with - <seealso marker="erl_driver#driver_alloc">driver_alloc</seealso>. - The buffer will be freed automatically after <c>control</c> has returned.</p> + <seealso marker="erl_driver#driver_alloc"> + <c>erl_driver:driver_alloc</c></seealso>. The + buffer is freed automatically after <c>control</c> has returned.</p> <p>Using binaries is faster if more than a few bytes are returned.</p> - <p>The return value is the number of bytes returned in - <c>*rbuf</c>.</p> + <p>The return value is the number of bytes returned in <c>*rbuf</c>.</p> </item> - - <tag><marker id="timeout"/>void (*timeout)(ErlDrvData drv_data)</tag> + <tag><marker id="timeout"/><c>void (*timeout)(ErlDrvData drv_data)</c> + </tag> <item> - <p>This function is called any time after the driver's timer - reaches 0. The timer is activated with - <c>driver_set_timer</c>. There are no priorities or ordering - among drivers, so if several drivers time out at the same - time, any one of them is called first.</p> + <p>Called any time after the driver's timer reaches <c>0</c>. + The timer is activated with + <seealso marker="erl_driver#driver_set_timer"> + <c>erl_driver:driver_set_timer</c></seealso>. No priorities or + ordering exist among drivers, so if several drivers time out at + the same time, anyone of them is called first.</p> </item> - - <tag><marker id="outputv"/>void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)</tag> + <tag><marker id="outputv"/> + <c>void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)</c></tag> <item> - <p>This function is called whenever the port is written to. If + <p>Called whenever the port is written to. If it is <c>NULL</c>, the <c>output</c> function is called - instead. This function is faster than <c>output</c>, because + instead. This function is faster than <c>output</c>, as it takes an <c>ErlIOVec</c> directly, which requires no - copying of the data. The port should be in binary mode, see - <c>open_port/2</c>.</p> - <p>The <c>ErlIOVec</c> contains both a <c>SysIOVec</c>, + copying of the data. The port is to be in binary mode, see + <seealso marker="erlang#open_port/2"> + <c>erlang:open_port/2</c></seealso>.</p> + <p><c>ErlIOVec</c> contains both a <c>SysIOVec</c>, suitable for <c>writev</c>, and one or more binaries. If - these binaries should be retained, when the driver returns - from <c>outputv</c>, they can be queued (using <seealso marker="erl_driver#driver_enq_bin">driver_enq_bin</seealso> - for instance), or if they are kept in a static or global + these binaries are to be retained when the driver returns + from <c>outputv</c>, they can be queued (using, for example, + <seealso marker="erl_driver#driver_enq_bin"> + <c>erl_driver:driver_enq_bin</c></seealso>) + or, if they are kept in a static or global variable, the reference counter can be incremented.</p> </item> - <tag><marker id="ready_async"/>void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data)</tag> + <tag><marker id="ready_async"/> + <c>void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData + thread_data)</c></tag> <item> - <p>This function is called after an asynchronous call has - completed. The asynchronous call is started with <seealso marker="erl_driver#driver_async">driver_async</seealso>. - This function is called from the erlang emulator thread, as + <p>Called after an asynchronous call has completed. + The asynchronous call is started with + <seealso marker="erl_driver#driver_async"> + <c>erl_driver:driver_async</c></seealso>. + This function is called from the Erlang emulator thread, as opposed to the asynchronous function, which is called in - some thread (if multithreading is enabled).</p> + some thread (if multi-threading is enabled).</p> + </item> + <tag><c>void (*flush)(ErlDrvData drv_data)</c></tag> + <item> + <p>Called when the port is about to be closed, + and there is data in the driver queue that must be flushed + before 'stop' can be called.</p> </item> - <tag><marker id="call"/>ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags)</tag> + <tag><marker id="call"/><c>ErlDrvSSizeT (*call)(ErlDrvData drv_data, + unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, + ErlDrvSizeT rlen, unsigned int *flags)</c></tag> <item> - <p>This function is called from <c>erlang:port_call/3</c>. It - works a lot like the <c>control</c> call-back, but uses the + <p>Called from <seealso marker="erlang#port_call/3"> + <c>erlang:port_call/3</c></seealso>. + It works a lot like the <c>control</c> callback, but uses the external term format for input and output.</p> <p><c>command</c> is an integer, obtained from the call from - erlang (the second argument to <c>erlang:port_call/3</c>).</p> + Erlang (the second argument to <c>erlang:port_call/3</c>).</p> <p><c>buf</c> and <c>len</c> provide the arguments to the call (the third argument to <c>erlang:port_call/3</c>). They can be decoded using <c>ei</c> functions.</p> <p><c>rbuf</c> points to a return buffer, <c>rlen</c> bytes - long. The return data should be a valid erlang term in the - external (binary) format. This is converted to an erlang + long. The return data is to be a valid Erlang term in the + external (binary) format. This is converted to an Erlang term and returned by <c>erlang:port_call/3</c> to the - caller. If more space than <c>rlen</c> bytes is needed to + caller. If more space than <c>rlen</c> bytes is needed to return data, <c>*rbuf</c> can be set to memory allocated with - <c>driver_alloc</c>. This memory will be freed automatically - after <c>call</c> has returned.</p> + <seealso marker="erl_driver#driver_alloc"> + <c>erl_driver:driver_alloc</c></seealso>. + This memory is freed automatically after <c>call</c> has returned.</p> <p>The return value is the number of bytes returned in <c>*rbuf</c>. If <c>ERL_DRV_ERROR_GENERAL</c> is returned - (or in fact, anything < 0), <c>erlang:port_call/3</c> will - throw a <c>BAD_ARG</c>.</p> + (or in fact, anything < 0), <c>erlang:port_call/3</c> + throws a <c>BAD_ARG</c>.</p> </item> - <tag>void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data)</tag> + <tag><c>void (*event)(ErlDrvData drv_data, ErlDrvEvent event, + ErlDrvEventData event_data)</c></tag> <item> <p>Intentionally left undocumented.</p> </item> - <tag><marker id="extended_marker"/>int extended_marker</tag> + <tag><marker id="extended_marker"/><c>int extended_marker</c></tag> <item> - <p> - This field should either be equal to <c>ERL_DRV_EXTENDED_MARKER</c> + <p>This field is either to be equal to <c>ERL_DRV_EXTENDED_MARKER</c> or <c>0</c>. An old driver (not aware of the extended driver - interface) should set this field to <c>0</c>. If this field is - equal to <c>0</c>, all the fields following this field also - <em>have</em> to be <c>0</c>, or <c>NULL</c> in case it is a - pointer field. - </p> + interface) is to set this field to <c>0</c>. If this field is + <c>0</c>, all the following fields <em>must</em> also be <c>0</c>, + or <c>NULL</c> if it is a pointer field.</p> </item> - <tag>int major_version</tag> + <tag><c>int major_version</c></tag> <item> - <p>This field should equal <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> if - the <c>extended_marker</c> field equals + <p>This field is to equal <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> if + field <c>extended_marker</c> equals <c>ERL_DRV_EXTENDED_MARKER</c>.</p> </item> - <tag>int minor_version</tag> + <tag><c>int minor_version</c></tag> <item> - <p> - This field should equal <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> if - the <c>extended_marker</c> field equals - <c>ERL_DRV_EXTENDED_MARKER</c>. - </p> + <p>This field is to equal <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> if + field <c>extended_marker</c> equals + <c>ERL_DRV_EXTENDED_MARKER</c>.</p> </item> - - <tag><marker id="driver_flags"/>int driver_flags</tag> + <tag><marker id="driver_flags"/><c>int driver_flags</c></tag> <item> <p>This field is used to pass driver capability and other - information to the runtime system. If the - <c>extended_marker</c> field equals <c>ERL_DRV_EXTENDED_MARKER</c>, - it should contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>) - ored bitwise. Currently the following driver flags exist: - </p> + information to the runtime system. If + field <c>extended_marker</c> equals <c>ERL_DRV_EXTENDED_MARKER</c>, + it is to contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>) + OR'ed bitwise. The following driver flags exist:</p> <taglist> <tag><c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></tag> <item> - The runtime system will use port level locking on - all ports executing this driver instead of driver - level locking when the driver is run in a runtime - system with SMP support. For more information see the - <seealso marker="erl_driver#smp_support">erl_driver</seealso> - documentation. - </item> + <p>The runtime system uses port-level locking on + all ports executing this driver instead of driver-level + locking when the driver is run in a runtime + system with SMP support. For more information, see + <seealso marker="erl_driver#smp_support"> + <c>erl_driver</c></seealso>.</p> + </item> <tag><c>ERL_DRV_FLAG_SOFT_BUSY</c></tag> <item> - Marks that driver instances can handle being called - in the <seealso marker="#output">output</seealso> and/or - <seealso marker="#outputv">outputv</seealso> callbacks even - though a driver instance has marked itself as busy (see - <seealso marker="erl_driver#set_busy_port">set_busy_port()</seealso>). - Since erts version 5.7.4 this flag is required for drivers used - by the Erlang distribution (the behaviour has always been - required by drivers used by the distribution). + <p>Marks that driver instances can handle being called + in the <seealso marker="#output"><c>output</c></seealso> and/or + <seealso marker="#outputv"><c>outputv</c></seealso> callbacks + although a driver instance has marked itself as busy (see + <seealso marker="erl_driver#set_busy_port"> + <c>erl_driver:set_busy_port</c></seealso>). + As from ERTS 5.7.4 this flag is required for drivers used + by the Erlang distribution (the behavior has always been + required by drivers used by the distribution).</p> </item> <tag><c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c></tag> - <item>Disable busy port message queue functionality. For - more information, see the documentation of the - <seealso marker="erl_driver#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso> - function. + <item> + <p>Disables busy port message queue functionality. For + more information, see + <seealso marker="erl_driver#erl_drv_busy_msgq_limits"> + <c>erl_driver:erl_drv_busy_msgq_limits</c></seealso>.</p> </item> <tag><c>ERL_DRV_FLAG_USE_INIT_ACK</c></tag> - <item>When this flag is given the linked-in driver has to manually - acknowledge that the port has been successfully started using - <seealso marker="erl_driver#erl_drv_init_ack">erl_drv_init_ack()</seealso>. - This allows the implementor to make the erlang:open_port exit with - badarg after some initial asynchronous initialization has been done. + <item> + <p>When this flag is specified, the linked-in driver must manually + acknowledge that the port has been successfully started using + <seealso marker="erl_driver#erl_drv_init_ack"> + <c>erl_driver:erl_drv_init_ack()</c></seealso>. + This allows the implementor to make the + <c>erlang:open_port</c> exit with <c>badarg</c> after some + initial asynchronous initialization has been done.</p> </item> </taglist> </item> - <tag>void *handle2</tag> + <tag><c>void *handle2</c></tag> <item> - <p> - This field is reserved for the emulator's internal use. The - emulator will modify this field; therefore, it is important - that the <c>driver_entry</c> isn't declared <c>const</c>. - </p> + <p>This field is reserved for the emulator's internal use. The + emulator modifies this field, so it is important + that the <c>driver_entry</c> is not declared <c>const</c>.</p> </item> - <tag><marker id="process_exit"/>void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)</tag> + <tag><marker id="process_exit"/> + <c>void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)</c> + </tag> <item> - <p>This callback is called when a monitored process exits. The + <p>Called when a monitored process exits. The <c>drv_data</c> is the data associated with the port for which - the process is monitored (using <seealso marker="erl_driver#driver_monitor_process">driver_monitor_process</seealso>) - and the <c>monitor</c> corresponds to the <c>ErlDrvMonitor</c> + the process is monitored (using + <seealso marker="erl_driver#driver_monitor_process"> + <c>erl_driver:driver_monitor_process</c></seealso>) + and the <c>monitor</c> corresponds to the <c>ErlDrvMonitor</c> structure filled in when creating the monitor. The driver interface function - <seealso marker="erl_driver#driver_get_monitored_process">driver_get_monitored_process</seealso> - can be used to retrieve the process id of the exiting process as + <seealso marker="erl_driver#driver_get_monitored_process"> + <c>erl_driver:driver_get_monitored_process</c></seealso> + can be used to retrieve the process ID of the exiting process as an <c>ErlDrvTermData</c>.</p> </item> - <tag><marker id="stop_select"/>void (*stop_select)(ErlDrvEvent event, void* reserved)</tag> + <tag><marker id="stop_select"/> + <c>void (*stop_select)(ErlDrvEvent event, void* reserved)</c></tag> <item> - <p>This function is called on behalf of - <seealso marker="erl_driver#driver_select">driver_select</seealso> - when it is safe to close an event object.</p> + <p>Called on behalf of + <seealso marker="erl_driver#driver_select"> + <c>erl_driver:driver_select</c></seealso> + when it is safe to close an event object.</p> <p>A typical implementation on Unix is to do - <c>close((int)event)</c>.</p> - <p>Argument <c>reserved</c> is intended for future use and should be ignored.</p> - <p>In contrast to most of the other call-back functions, - <c>stop_select</c> is called independent of any port. No - <c>ErlDrvData</c> argument is passed to the function. No - driver lock or port lock is guaranteed to be held. The port that - called <c>driver_select</c> might even be closed at the - time <c>stop_select</c> is called. But it could also be - the case that <c>stop_select</c> is called directly by - <c>driver_select</c>.</p> + <c>close((int)event)</c>.</p> + <p>Argument <c>reserved</c> is intended for future use and is to be + ignored.</p> + <p>In contrast to most of the other callback functions, + <c>stop_select</c> is called independent of any port. No + <c>ErlDrvData</c> argument is passed to the function. No + driver lock or port lock is guaranteed to be held. The port that + called <c>driver_select</c> can even be closed at the + time <c>stop_select</c> is called. But it can also be + the case that <c>stop_select</c> is called directly by + <c>erl_driver:driver_select</c>.</p> <p>It is not allowed to call any functions in the - <seealso marker="erl_driver">driver API</seealso> from - <c>stop_select</c>. This strict limitation is due to the - volatile context that <c>stop_select</c> may be called.</p> + <seealso marker="erl_driver">driver API</seealso> from + <c>stop_select</c>. This strict limitation is because the + volatile context that <c>stop_select</c> can be called.</p> </item> - - </taglist> - </item> - </taglist> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="erl_driver">erl_driver(3)</seealso>, - <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>, - <seealso marker="erlang">erlang(3)</seealso>, - kernel(3)</p> + <title>See Also</title> + <p><seealso marker="erl_driver"><c>erl_driver(3)</c></seealso>, + <seealso marker="erlang"><c>erlang(3)</c></seealso>, + <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso></p> </section> </cref> diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index d9f580d081..311483022d 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -28,242 +28,259 @@ <docno>1</docno> <approved></approved> <checked></checked> - <date>98-01-05</date> + <date>1998-01-05</date> <rev>A</rev> <file>epmd.xml</file> </header> <com>epmd</com> - <comsummary> - <p>Erlang Port Mapper Daemon</p> - <taglist> - <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag> - <item> - <p>Starts the port mapper daemon</p> - </item> - <tag><c><![CDATA[epmd [-d|-debug] [-port No] [-names|-kill|-stop Name]]]></c></tag> - <item> - <p>Communicates with a running port mapper daemon</p> - </item> - </taglist> - </comsummary> + <comsummary>Erlang Port Mapper Daemon</comsummary> + <description> - <p>This daemon acts as a name server on all hosts involved in - distributed Erlang computations. When an Erlang node - starts, the node has a name and it obtains an address from the host - OS kernel. - The name and the address are sent to the + <taglist> + <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses] + [-port No] [-daemon] [-relaxed_command_check]]]></c></tag> + <item> + <p>Starts the port mapper daemon.</p> + </item> + <tag><c><![CDATA[epmd [-d|-debug] [-port No] + [-names|-kill|-stop Name]]]></c></tag> + <item> + <p>Communicates with a running port mapper daemon.</p> + </item> + </taglist> + + <p>This daemon acts as a name server on all hosts involved in + distributed Erlang computations. When an Erlang node starts, + the node has a name and it obtains an address from the host + OS kernel. The name and address are sent to the <c><![CDATA[epmd]]></c> daemon running on the local host. In a TCP/IP environment, the address consists - of the IP address and a port number. The name of the node is + of the IP address and a port number. The node name is an atom on the form of <c><![CDATA[Name@Node]]></c>. The job of the <c><![CDATA[epmd]]></c> daemon is to keep track of which node name listens on which address. Hence, <c><![CDATA[epmd]]></c> maps symbolic node names to machine addresses.</p> - <p>The TCP/IP <c>epmd</c> daemon actually only keeps track of + <p>The TCP/IP <c>epmd</c> daemon only keeps track of the <c>Name</c> (first) part of an Erlang node name. The <c>Host</c> part (whatever is after the <c><![CDATA[@]]></c>) is implicit in the - node name where the <c>epmd</c> daemon was actually contacted, + node name where the <c>epmd</c> daemon was contacted, as is the IP address where the Erlang node can be reached. Consistent and correct TCP naming services are therefore required for an Erlang network to function correctly.</p> <taglist> - <tag>Starting the port mapper daemon</tag> - <item> - - <p>The daemon is started automatically by the <c>erl</c> - command if the node is to be distributed and there is no - running instance present. If automatically launched, - environment variables have to be used to alter the behavior of - the daemon. See the <seealso - marker="#environment_variables">Environment - variables</seealso> section below.</p> - - <p>If the -daemon argument is not given, - <c><![CDATA[epmd]]></c> runs as a normal program with the - controlling terminal of the shell in which it is - started. Normally, it should run as a daemon.</p> - - <p>Regular start-up options are described in the - <seealso marker="#daemon_flags">Regular options</seealso> - section below.</p> - - <p>The <c>DbgExtra</c> options are described in the - <seealso marker="#debug_flags">DbgExtra options</seealso> - section below.</p> - - </item> - <tag>Communicating with a running port mapper daemon</tag> - <item> - - <p>Communicating with the running epmd daemon by means of the - <c>epmd</c> program is done primarily for debugging - purposes.</p> - - <p>The different queries are described in the <seealso - marker="#interactive_flags">Interactive options</seealso> - section below.</p> - - </item> - </taglist> + <tag>Starting the port mapper daemon</tag> + <item> + <p>The daemon is started automatically by command + <seealso marker="erl"><c>erl(1)</c></seealso> + if the node is to be distributed and no running + instance is present. If automatically launched + environment variables must be used to change the behavior + of the daemon; see section + <seealso marker="#environment_variables">Environment + Variables</seealso>.</p> + <p>If argument <c>-daemon</c> is not specified, + <c><![CDATA[epmd]]></c> runs as a normal program with the + controlling terminal of the shell in which it is + started. Normally, it is to be run as a daemon.</p> + <p>Regular startup options are described in section + <seealso marker="#daemon_flags">Regular Options</seealso>.</p> + <p>The <c>DbgExtra</c> options are described in section + <seealso marker="#debug_flags">DbgExtra Options</seealso>.</p> + </item> + <tag>Communicating with a running port mapper daemon</tag> + <item> + <p>Communicating with the running <c>epmd</c> daemon by the + <c>epmd</c> program is done primarily for debugging purposes.</p> + <p>The different queries are described in section <seealso + marker="#interactive_flags">Interactive options</seealso>.</p> + </item> + </taglist> </description> + <section> <marker id="daemon_flags"></marker> - <title>Regular options</title> + <title>Regular Options</title> + <p>These options are available when starting the name server. The name + server is normally started automatically by command + <seealso marker="erl"><c>erl(1)</c></seealso> (if not already available), + but it can also be started at system startup.</p> - <p>These options are available when starting the actual name server. The name server is normally started automatically by the <c>erl</c> command (if not already available), but it can also be started at i.e. system start-up.</p> <taglist> - <tag><c><![CDATA[-address List]]></c></tag> - <item> - <p>Let this instance of <c>epmd</c> listen only on the - comma-separated list of IP addresses and on the loopback address - (which is implicitly added to the list if it has not been - specified). This can also be set using the - <c><![CDATA[ERL_EPMD_ADDRESS]]></c> environment variable. See the - section <seealso marker="#environment_variables">Environment - variables</seealso> below.</p> - </item> - <tag><c><![CDATA[-port No]]></c></tag> - <item> - <p>Let this instance of epmd listen to another TCP port than - default 4369. This can also be set using the - <c><![CDATA[ERL_EPMD_PORT]]></c> environment variable. See the - section <seealso marker="#environment_variables">Environment - variables</seealso> below</p> - </item> - <tag><c><![CDATA[-d | -debug]]></c></tag> - <item> - - <p>Enable debug output. The more <c>-d</c> flags given, the more - debug output you will get (to a certain limit). This option is - most useful when the epmd daemon is not started as a daemon.</p> - </item> - <tag><c><![CDATA[-daemon]]></c></tag> - <item> - <p>Start epmd detached from the controlling terminal. Logging will end up in syslog when available and correctly configured. If the epmd daemon is started at boot, this option should definitely be used. It is also used when the <c>erl</c> command automatically starts <c>epmd</c>.</p> - </item> - <tag><c><![CDATA[-relaxed_command_check]]></c></tag> - <item> - <p>Start the epmd program with relaxed command checking (mostly for backward compatibility). This affects the following:</p> - <list type="bulleted"> - <item> - <p>With relaxed command checking, the <c>epmd</c> daemon can be killed from the localhost with i.e. <c>epmd -kill</c> even if there are active nodes registered. Normally only daemons with an empty node database can be killed with the <c>epmd -kill</c> command.</p> + <tag><c><![CDATA[-address List]]></c></tag> + <item> + <p>Lets this instance of <c>epmd</c> listen only on the + comma-separated list of IP addresses and on the loopback address + (which is implicitly added to the list if it has not been + specified). This can also be set using environment variable + <c><![CDATA[ERL_EPMD_ADDRESS]]></c>; see section <seealso + marker="#environment_variables">Environment Variables</seealso>.</p> + </item> + <tag><c><![CDATA[-port No]]></c></tag> + <item> + <p>Lets this instance of <c>epmd</c> listen to another TCP port than + default 4369. This can also be set using environment variable + <c><![CDATA[ERL_EPMD_PORT]]></c>; see section <seealso + marker="#environment_variables">Environment Variables</seealso>.</p> + </item> + <tag><c><![CDATA[-d | -debug]]></c></tag> + <item> + <p>Enables debug output. The more <c>-d</c> flags specified, the more + debug output you will get (to a certain limit). This option is most + useful when the <c>epmd</c> daemon is not started as a daemon.</p> + </item> + <tag><c><![CDATA[-daemon]]></c></tag> + <item> + <p>Starts <c>epmd</c> detached from the controlling terminal. Logging + ends up in syslog when available and correctly configured. If the + <c>epmd</c> daemon is started at boot, this option is definitely + to be used. It is also used when command <c>erl</c> automatically + starts <c>epmd</c>.</p> + </item> + <tag><c><![CDATA[-relaxed_command_check]]></c></tag> + <item> + <p>Starts the <c>epmd</c> program with relaxed command checking + (mostly for backward compatibility). This affects the following:</p> + <list type="bulleted"> + <item> + <p>With relaxed command checking, the <c>epmd</c> daemon can be + killed from the local host with, for example, command + <c>epmd -kill</c> even if active nodes are registered. Normally + only daemons with an empty node database can be killed with + <c>epmd -kill</c>.</p> </item> <item> - <p>The <c>epmd -stop</c> command (and the corresponding messages to epmd, as can be given using <c>erl_interface/ei</c>) is normally always ignored, as it opens up the possibility of a strange situation where two nodes of the same name can be alive at the same time. A node unregisters itself by just closing the connection to epmd, which is why the <c>stop</c> command was only intended for use in debugging situations.</p> - <p>With relaxed command checking enabled, you can forcibly unregister live nodes.</p> + <p>Command <c>epmd -stop</c> (and the corresponding messages to + <c>epmd</c>, as can be specified using <seealso + marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seealso>) is + normally always ignored. This because it can cause a strange + situation where two nodes of the same name can be alive at the + same time. A node unregisters itself by only closing the + connection to <c>epmd</c>, which is why command <c>stop</c> + was only intended for use in debugging situations.</p> + <p>With relaxed command checking enabled, you can forcibly + unregister live nodes.</p> </item> - </list> - <p>Relaxed command checking can also be enabled by setting the environment variable <c>ERL_EPMD_RELAXED_COMMAND_CHECK</c> prior to starting <c>epmd</c>.</p> - <p>Only use relaxed command checking on systems with very limited interactive usage.</p> - </item> - </taglist> + </list> + <p>Relaxed command checking can also be enabled by setting environment + variable <c>ERL_EPMD_RELAXED_COMMAND_CHECK</c> before starting + <c>epmd</c>.</p> + <p>Use relaxed command checking only on systems with very limited + interactive usage.</p> + </item> + </taglist> </section> <section> <marker id="debug_flags"></marker> - <title>DbgExtra options</title> - <p>These options are purely for debugging and testing epmd clients. They should not be used in normal operation.</p> + <title>DbgExtra Options</title> + <note> + <p>These options are only for debugging and testing <c>epmd</c> clients. + They are not to be used in normal operation.</p> + </note> <taglist> - <tag><c><![CDATA[-packet_timeout Seconds]]></c></tag> - <item> - <p>Set the number of seconds a connection can be - inactive before epmd times out and closes the - connection (default 60).</p> - </item> - <tag><c><![CDATA[-delay_accept Seconds]]></c></tag> - <item> - <p>To simulate a busy server you can insert a delay between when epmd - gets notified that a new connection is requested and - when the connection gets accepted.</p> - </item> - <tag><c><![CDATA[-delay_write Seconds]]></c></tag> - <item> - <p>Also a simulation of a busy server. Inserts - a delay before a reply is sent.</p> - </item> + <tag><c><![CDATA[-packet_timeout Seconds]]></c></tag> + <item> + <p>Sets the number of seconds a connection can be + inactive before <c>epmd</c> times out and closes the + connection. Defaults to 60.</p> + </item> + <tag><c><![CDATA[-delay_accept Seconds]]></c></tag> + <item> + <p>To simulate a busy server, you can insert a delay between when + <c>epmd</c> gets notified that a new connection is requested and + when the connection gets accepted.</p> + </item> + <tag><c><![CDATA[-delay_write Seconds]]></c></tag> + <item> + <p>Also a simulation of a busy server. Inserts + a delay before a reply is sent.</p> + </item> </taglist> </section> + <section> <marker id="interactive_flags"></marker> - <title>Interactive options</title> - <p>These options make <c>epmd</c> run as an interactive command, displaying the results of sending queries to an already running instance of <c>epmd</c>. The epmd contacted is always on the local node, but the <c>-port</c> option can be used to select between instances if several are running using different ports on the host.</p> - <taglist> - <tag><c><![CDATA[-port No]]></c></tag> - <item> - <p>Contacts the <c>epmd</c> listening on the given TCP port number - (default 4369). This can also be set using the - <c><![CDATA[ERL_EPMD_PORT]]></c> environment variable. See the - section <seealso marker="#environment_variables">Environment - variables</seealso> below.</p> - </item> - <tag><c><![CDATA[-names]]></c></tag> - <item> - <p>List names registered with the currently running epmd</p> - </item> - <tag><c><![CDATA[-kill]]></c></tag> - <item> - <p>Kill the currently running <c>epmd</c>.</p> + <title>Interactive Options</title> + <p>These options make <c>epmd</c> run as an interactive command, + displaying the results of sending queries to an already running + instance of <c>epmd</c>. The <c>epmd</c> contacted is always on the + local node, but option <c>-port</c> can be used to select between + instances if several are running using different ports on the host.</p> - <p>Killing the running <c>epmd</c> is only allowed if <c>epmd - -names</c> shows an empty database or - <c>-relaxed_command_check</c> was given when the running - instance of <c>epmd</c> was started. Note that - <c>-relaxed_command_check</c> is given when starting the - daemon that is to accept killing when it has live nodes - registered. When running epmd interactively, - <c>-relaxed_command_check</c> has no effect. A daemon that is - started without relaxed command checking has to be killed - using i.e. signals or some other OS specific method if it has - active clients registered.</p> - </item> - <tag><c><![CDATA[-stop Name]]></c></tag> - <item> - <p>Forcibly unregister a live node from <c>epmd</c>'s database</p> - - <p>This command can only be used when contacting <c>epmd</c> - instances started with the <c>-relaxed_command_check</c> - flag. Note that relaxed command checking has to be enabled for - the <c>epmd</c> daemon contacted. When running epmd - interactively, - <c>-relaxed_command_check</c> has no effect.</p> - </item> - </taglist> + <taglist> + <tag><c><![CDATA[-port No]]></c></tag> + <item> + <p>Contacts the <c>epmd</c> listening on the specified TCP port + number (default 4369). This can also be set using environment + variable <c><![CDATA[ERL_EPMD_PORT]]></c>; see section <seealso + marker="#environment_variables">Environment Variables</seealso>.</p> + </item> + <tag><c><![CDATA[-names]]></c></tag> + <item> + <p>Lists names registered with the currently running <c>epmd</c>.</p> + </item> + <tag><c><![CDATA[-kill]]></c></tag> + <item> + <p>Kills the currently running <c>epmd</c>.</p> + <p>Killing the running <c>epmd</c> is only allowed if + <c>epmd -names</c> shows an empty database or if + <c>-relaxed_command_check</c> was specified when the running + instance of <c>epmd</c> was started.</p> + <p>Notice that <c>-relaxed_command_check</c> is specified when + starting the daemon that is to accept killing when it has live + nodes registered. When running <c>epmd</c> interactively, + <c>-relaxed_command_check</c> has no effect. A daemon that is + started without relaxed command checking must be killed using, + for example, signals or some other OS-specific method if it has + active clients registered.</p> + </item> + <tag><c><![CDATA[-stop Name]]></c></tag> + <item> + <p>Forcibly unregisters a live node from the <c>epmd</c> database.</p> + <p>This command can only be used when contacting <c>epmd</c> + instances started with flag <c>-relaxed_command_check</c>.</p> + <p>Notice that relaxed command checking must enabled for the + <c>epmd</c> daemon contacted. When running <c>epmd</c> + interactively, <c>-relaxed_command_check</c> has no effect.</p> + </item> + </taglist> </section> + <section> <marker id="environment_variables"></marker> - <title>Environment variables</title> + <title>Environment Variables</title> <taglist> <tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag> <item> - <p>This environment variable may be set to a comma-separated - list of IP addresses, in which case the <c>epmd</c> daemon - will listen only on the specified address(es) and on the - loopback address (which is implicitly added to the list if it - has not been specified). The default behaviour is to listen on - all available IP addresses.</p> + <p>Can be set to a comma-separated + list of IP addresses, in which case the <c>epmd</c> daemon + will listen only on the specified address(es) and on the + loopback address (which is implicitly added to the list if it + has not been specified). The default behavior is to listen on + all available IP addresses.</p> </item> <tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag> <item> - <p>This environment variable can contain the port number epmd will use. - The default port will work fine in most cases. A different port can - be specified to allow several instances of epmd, representing - independent clusters of nodes, to co-exist on the same host. - All nodes in a cluster must use the same epmd port number.</p> + <p>Can contain the port number <c>epmd</c> will use. + The default port will work fine in most cases. A different port can + be specified to allow several instances of <c>epmd</c>, representing + independent clusters of nodes, to co-exist on the same host. + All nodes in a cluster must use the same <c>epmd</c> port number.</p> </item> <tag><c><![CDATA[ERL_EPMD_RELAXED_COMMAND_CHECK]]></c></tag> <item> - <p>If set prior to start, the <c>epmd</c> daemon will behave - as if the <c>-relaxed_command_check</c> option was given at - start-up. Consequently, if this option is set before starting - the Erlang virtual machine, the automatically started - <c>epmd</c> will accept the <c>-kill</c> and <c>-stop</c> - commands without restrictions.</p> + <p>If set before start, the <c>epmd</c> daemon behaves + as if option <c>-relaxed_command_check</c> was specified at + startup. Consequently, if this option is set before starting + the Erlang virtual machine, the automatically started + <c>epmd</c> accepts the <c>-kill</c> and <c>-stop</c> + commands without restrictions.</p> </item> </taglist> </section> @@ -271,41 +288,45 @@ <section> <title>Logging</title> <p>On some operating systems <em>syslog</em> will be used for - error reporting when epmd runs as an daemon. To enable - the error logging you have to edit <path unix="" windows="">/etc/syslog.conf</path> - file and add an entry</p> + error reporting when <c>epmd</c> runs as a daemon. To enable + the error logging, you must edit the + <path unix="" windows="">/etc/syslog.conf</path> file and add an + entry:</p> + <code type="none"><![CDATA[ - !epmd - *.*<TABs>/var/log/epmd.log - ]]></code> - <p>where <TABs> are at least one real tab character. Spaces will - silently be ignored. - </p> + !epmd + *.*<TABs>/var/log/epmd.log +]]></code> + + <p>where <c><TABs></c> are at least one real tab character. + Spaces are silently ignored.</p> </section> + <section> - <title>Access restrictions</title> - <p>The <c>epmd</c> daemon accepts messages from both localhost and - remote hosts. However, only the query commands are answered (and - acted upon) if the query comes from a remote host. It is always an - error to try to register a nodename if the client is not a process - located on the same host as the <c>epmd</c> instance is running on- - such requests are considered hostile and the connection is - immediately closed.</p> + <title>Access Restrictions</title> + <p>The <c>epmd</c> daemon accepts messages from both the local host and + remote hosts. However, only the query commands are answered (and + acted upon) if the query comes from a remote host. It is always an + error to try to register a node name if the client is not a process + on the same host as the <c>epmd</c> instance is running on. Such + requests are considered hostile and the connection is closed + immediately.</p> - <p>The queries accepted from remote nodes are:</p> - <list type="bulleted"> - <item> - <p>Port queries - i.e. on which port does the node with a given - name listen</p> - </item> - <item> - <p>Name listing - i.e. give a list of all names registered on + <p>The following queries are accepted from remote nodes:</p> + + <list type="bulleted"> + <item> + <p>Port queries, that is, on which port the node with a specified + name listens</p> + </item> + <item> + <p>Name listing, that is, gives a list of all names registered on the host</p> - </item> - </list> - <p>To restrict access further, firewall software has to be used.</p> - </section> + </item> + </list> + <p>To restrict access further, firewall software must be used.</p> + </section> </comref> diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 37387f2c59..f62d3fb170 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -30,86 +30,92 @@ <file>erl.xml</file> </header> <com>erl</com> - <comsummary>The Erlang Emulator</comsummary> + <comsummary>The Erlang emulator.</comsummary> <description> <p>The <c><![CDATA[erl]]></c> program starts an Erlang runtime system. - The exact details (for example, whether <c><![CDATA[erl]]></c> is a script or - a program and which other programs it calls) are system-dependent.</p> - <p>Windows users probably wants to use the <c><![CDATA[werl]]></c> program + The exact details (for example, whether <c><![CDATA[erl]]></c> is a + script or a program and which other programs it calls) are + system-dependent.</p> + + <p>Windows users probably want to use the <c><![CDATA[werl]]></c> program instead, which runs in its own window with scrollbars and supports - command-line editing. The <c><![CDATA[erl]]></c> program on Windows provides - no line editing in its shell, and on Windows 95 there is no way - to scroll back to text which has scrolled off the screen. - The <c><![CDATA[erl]]></c> program must be used, however, in pipelines or if + command-line editing. The <c><![CDATA[erl]]></c> program on Windows + provides no line editing in its shell, and on Windows 95 there is no way + to scroll back to text that has scrolled off the screen. The + <c><![CDATA[erl]]></c> program must be used, however, in pipelines or if you want to redirect standard input or output.</p> - <note><p>As of ERTS version 5.9 (OTP-R15B) the runtime system will by - default <em>not</em> bind schedulers to logical processors. - For more information see documentation of the - <seealso marker="#+sbt">+sbt</seealso> system flag. - </p> - </note> + + <note> + <p>As from ERTS 5.9 (Erlang/OTP R15B) the runtime system does by + default <em>not</em> bind schedulers to logical processors. + For more information, see system flag + <seealso marker="#+sbt"><c>+sbt</c></seealso>.</p> + </note> </description> + <funcs> <func> <name>erl <arguments></name> - <fsummary>Start an Erlang runtime system</fsummary> + <fsummary>Start an Erlang runtime system.</fsummary> <desc> - <p>Starts an Erlang runtime system.</p> - <p>The arguments can be divided into <em>emulator flags</em>, - <em>flags</em> and <em>plain arguments</em>:</p> - <list type="bulleted"> - <item> - <p>Any argument starting with the character <c><![CDATA[+]]></c> is - interpreted as an <seealso marker="#emu_flags">emulator flag</seealso>.</p> - <p>As indicated by the name, emulator flags controls - the behavior of the emulator.</p> - </item> - <item> - <p>Any argument starting with the character <c><![CDATA[-]]></c> - (hyphen) is interpreted as a - <seealso marker="#init_flags">flag</seealso> which should - be passed to the Erlang part of the runtime system, more - specifically to the <c><![CDATA[init]]></c> system process, see - <seealso marker="init">init(3)</seealso>.</p> - <p>The <c><![CDATA[init]]></c> process itself interprets some of these - flags, the <em>init flags</em>. It also stores any - remaining flags, the <em>user flags</em>. The latter can - be retrieved by calling <c><![CDATA[init:get_argument/1]]></c>.</p> - <p>It can be noted that there are a small number of "-" - flags which now actually are emulator flags, see - the description below.</p> - </item> - <item> - <p>Plain arguments are not interpreted in any way. They are - also stored by the <c><![CDATA[init]]></c> process and can be - retrieved by calling <c><![CDATA[init:get_plain_arguments/0]]></c>. - Plain arguments can occur before the first flag, or after - a <c><![CDATA[--]]></c> flag. Additionally, the flag <c><![CDATA[-extra]]></c> - causes everything that follows to become plain arguments.</p> - </item> - </list> - <p>Example:</p> - <pre> + <p>Starts an Erlang runtime system.</p> + <p>The arguments can be divided into <em>emulator flags</em>, + <em>flags</em>, and <em>plain arguments</em>:</p> + <list type="bulleted"> + <item> + <p>Any argument starting with character <c><![CDATA[+]]></c> is + interpreted as an + <seealso marker="#emu_flags">emulator flag</seealso>.</p> + <p>As indicated by the name, emulator flags control + the behavior of the emulator.</p> + </item> + <item> + <p>Any argument starting with character <c><![CDATA[-]]></c> + (hyphen) is interpreted as a + <seealso marker="#init_flags">flag</seealso>, which is to + be passed to the Erlang part of the runtime system, more + specifically to the <c><![CDATA[init]]></c> system process, see + <seealso marker="init"><c>init(3)</c></seealso>.</p> + <p>The <c><![CDATA[init]]></c> process itself interprets some of + these flags, the <em>init flags</em>. It also stores any + remaining flags, the <em>user flags</em>. The latter can be + retrieved by calling <c><![CDATA[init:get_argument/1]]></c>.</p> + <p>A small number of "-" flags exist, which now actually are + emulator flags, see the description below.</p> + </item> + <item> + <p>Plain arguments are not interpreted in any way. They are also + stored by the <c><![CDATA[init]]></c> process and can be retrieved + by calling <c><![CDATA[init:get_plain_arguments/0]]></c>. + Plain arguments can occur before the first flag, or after a + <c><![CDATA[--]]></c> flag. Also, the <c><![CDATA[-extra]]></c> + flag causes everything that follows to become plain arguments.</p> + </item> + </list> + <p><em>Examples:</em></p> + <pre> % <input>erl +W w -sname arnie +R 9 -s my_init -extra +bertie</input> (arnie@host)1> <input>init:get_argument(sname).</input> {ok,[["arnie"]]} (arnie@host)2> <input>init:get_plain_arguments().</input> ["+bertie"]</pre> - <p>Here <c><![CDATA[+W w]]></c> and <c><![CDATA[+R 9]]></c> are emulator flags. - <c><![CDATA[-s my_init]]></c> is an init flag, interpreted by <c><![CDATA[init]]></c>. - <c><![CDATA[-sname arnie]]></c> is a user flag, stored by <c><![CDATA[init]]></c>. - It is read by Kernel and will cause the Erlang runtime system - to become distributed. Finally, everything after <c><![CDATA[-extra]]></c> - (that is, <c><![CDATA[+bertie]]></c>) is considered as plain arguments.</p> - <pre> + <p>Here <c><![CDATA[+W w]]></c> and <c><![CDATA[+R 9]]></c> are + emulator flags. <c><![CDATA[-s my_init]]></c> is an init flag, + interpreted by <c><![CDATA[init]]></c>. + <c><![CDATA[-sname arnie]]></c> is a user flag, stored by + <c><![CDATA[init]]></c>. It is read by Kernel and causes the + Erlang runtime system to become distributed. Finally, everything after + <c><![CDATA[-extra]]></c> (that is, <c><![CDATA[+bertie]]></c>) is + considered as plain arguments.</p> + <pre> % <input>erl -myflag 1</input> 1> <input>init:get_argument(myflag).</input> {ok,[["1"]]} 2> <input>init:get_plain_arguments().</input> []</pre> - <p>Here the user flag <c><![CDATA[-myflag 1]]></c> is passed to and stored - by the <c><![CDATA[init]]></c> process. It is a user defined flag, - presumably used by some user defined application.</p> + <p>Here the user flag <c><![CDATA[-myflag 1]]></c> is passed to and + stored by the <c><![CDATA[init]]></c> process. It is a user-defined + flag, presumably used by some user-defined application.</p> </desc> </func> </funcs> @@ -117,50 +123,54 @@ <section> <marker id="init_flags"></marker> <title>Flags</title> - <p>In the following list, init flags are marked (init flag). + <p>In the following list, init flags are marked "(init flag)". Unless otherwise specified, all other flags are user flags, for which the values can be retrieved by calling - <c><![CDATA[init:get_argument/1]]></c>. Note that the list of user flags is - not exhaustive, there may be additional, application specific - flags which instead are documented in the corresponding + <c><![CDATA[init:get_argument/1]]></c>. Notice that the list of user + flags is not exhaustive, there can be more application-specific + flags that instead are described in the corresponding application documentation.</p> <taglist> - <tag><c><![CDATA[--]]></c>(init flag)</tag> + <tag><c><![CDATA[--]]></c> (init flag)</tag> <item> <p>Everything following <c><![CDATA[--]]></c> up to the next flag - (<c><![CDATA[-flag]]></c> or <c><![CDATA[+flag]]></c>) is considered plain arguments - and can be retrieved using <c><![CDATA[init:get_plain_arguments/0]]></c>.</p> + (<c><![CDATA[-flag]]></c> or <c><![CDATA[+flag]]></c>) is considered + plain arguments and can be retrieved using + <c><![CDATA[init:get_plain_arguments/0]]></c>.</p> </item> <tag><c><![CDATA[-Application Par Val]]></c></tag> <item> - <p>Sets the application configuration parameter <c><![CDATA[Par]]></c> to - the value <c><![CDATA[Val]]></c> for the application <c><![CDATA[Application]]></c>, - see <seealso marker="kernel:app">app(4)</seealso> and - <seealso marker="kernel:application">application(3)</seealso>.</p> + <p>Sets the application configuration parameter <c><![CDATA[Par]]></c> + to the value <c><![CDATA[Val]]></c> for the application + <c><![CDATA[Application]]></c>; see + <seealso marker="kernel:app"><c>app(4)</c></seealso> and + <seealso marker="kernel:application"> + <c>application(3)</c></seealso>.</p> </item> <tag><marker id="args_file"/><c><![CDATA[-args_file FileName]]></c></tag> <item> - <p>Command line arguments are read from the file <c><![CDATA[FileName]]></c>. - The arguments read from the file replace the - '<c><![CDATA[-args_file FileName]]></c>' flag on the resulting command line.</p> - <p>The file <c><![CDATA[FileName]]></c> should be a plain text file and may - contain comments and command line arguments. A comment begins - with a # character and continues until next end of line character. - Backslash (\\) is used as quoting character. All command line - arguments accepted by <c><![CDATA[erl]]></c> are allowed, also the - <c><![CDATA[-args_file FileName]]></c> flag. Be careful not to cause circular - dependencies between files containing the <c><![CDATA[-args_file]]></c> flag, - though.</p> - <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends - at the end of the file. Arguments following an <c><![CDATA[-extra]]></c> - flag are moved on the command line into the <c><![CDATA[-extra]]></c> section, - i.e. the end of the command line following after an <c><![CDATA[-extra]]></c> - flag.</p> + <p>Command-line arguments are read from the file + <c><![CDATA[FileName]]></c>. The arguments read from the file replace + flag '<c><![CDATA[-args_file FileName]]></c>' on the resulting + command line.</p> + <p>The file <c><![CDATA[FileName]]></c> is to be a plain text file and + can contain comments and command-line arguments. A comment begins + with a <c>#</c> character and continues until the next end of line + character. Backslash (\\) is used as quoting character. All + command-line arguments accepted by <c><![CDATA[erl]]></c> are allowed, + also flag <c><![CDATA[-args_file FileName]]></c>. Be careful not to + cause circular dependencies between files containing flag + <c><![CDATA[-args_file]]></c>, though.</p> + <p>The flag <c><![CDATA[-extra]]></c> is treated in special way. Its + scope ends at the end of the file. Arguments following an + <c><![CDATA[-extra]]></c> flag are moved on the command line into the + <c><![CDATA[-extra]]></c> section, that is, the end of the command + line following after an <c><![CDATA[-extra]]></c> flag.</p> </item> <tag><c><![CDATA[-async_shell_start]]></c></tag> <item> <p>The initial Erlang shell does not read user input until - the system boot procedure has been completed (Erlang 5.4 and + the system boot procedure has been completed (Erlang/OTP 5.4 and later). This flag disables the start synchronization feature and lets the shell start in parallel with the rest of the system.</p> @@ -168,52 +178,56 @@ <tag><c><![CDATA[-boot File]]></c></tag> <item> <p>Specifies the name of the boot file, <c><![CDATA[File.boot]]></c>, - which is used to start the system. See - <seealso marker="init">init(3)</seealso>. Unless + which is used to start the system; see + <seealso marker="init"><c>init(3)</c></seealso>. Unless <c><![CDATA[File]]></c> contains an absolute path, the system searches - for <c><![CDATA[File.boot]]></c> in the current and <c><![CDATA[$ROOT/bin]]></c> - directories.</p> + for <c><![CDATA[File.boot]]></c> in the current and + <c><![CDATA[$ROOT/bin]]></c> directories.</p> <p>Defaults to <c><![CDATA[$ROOT/bin/start.boot]]></c>.</p> </item> <tag><c><![CDATA[-boot_var Var Dir]]></c></tag> <item> - <p>If the boot script contains a path variable <c><![CDATA[Var]]></c> other - than <c><![CDATA[$ROOT]]></c>, this variable is expanded to <c><![CDATA[Dir]]></c>. - Used when applications are installed in another directory - than <c><![CDATA[$ROOT/lib]]></c>, see - <seealso marker="sasl:systools#make_script/1">systools:make_script/1,2</seealso>.</p> + <p>If the boot script contains a path variable <c><![CDATA[Var]]></c> + other than <c><![CDATA[$ROOT]]></c>, this variable is expanded to + <c><![CDATA[Dir]]></c>. Used when applications are installed in + another directory than <c><![CDATA[$ROOT/lib]]></c>; see + <seealso marker="sasl:systools#make_script/1"> + <c>systools:make_script/1,2</c></seealso> in SASL.</p> </item> <tag><c><![CDATA[-code_path_cache]]></c></tag> <item> - <p>Enables the code path cache of the code server, see - <seealso marker="kernel:code">code(3)</seealso>.</p> + <p>Enables the code path cache of the code server; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-compile Mod1 Mod2 ...]]></c></tag> <item> <p>Compiles the specified modules and then terminates (with non-zero exit code if the compilation of some file did not - succeed). Implies <c><![CDATA[-noinput]]></c>. Not recommended - use - <seealso marker="erlc">erlc</seealso> instead.</p> + succeed). Implies <c><![CDATA[-noinput]]></c>.</p> + <p>Not recommended; use <seealso marker="erlc"><c>erlc</c></seealso> + instead.</p> </item> <tag><c><![CDATA[-config Config]]></c></tag> <item> <p>Specifies the name of a configuration file, <c><![CDATA[Config.config]]></c>, which is used to configure - applications. See - <seealso marker="kernel:app">app(4)</seealso> and - <seealso marker="kernel:application">application(3)</seealso>.</p> + applications; see + <seealso marker="kernel:app"><c>app(4)</c></seealso> and + <seealso marker="kernel:application"> + <c>application(3)</c></seealso>.</p> </item> <tag><marker id="connect_all"/><c><![CDATA[-connect_all false]]></c></tag> <item> - <p>If this flag is present, <c><![CDATA[global]]></c> will not maintain a - fully connected network of distributed Erlang nodes, and then - global name registration cannot be used. See - <seealso marker="kernel:global">global(3)</seealso>.</p> + <p>If this flag is present, <c><![CDATA[global]]></c> does not maintain + a fully connected network of distributed Erlang nodes, and then + global name registration cannot be used; see + <seealso marker="kernel:global"><c>global(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-cookie Cookie]]></c></tag> <item> <p>Obsolete flag without any effect and common misspelling for - <c><![CDATA[-setcookie]]></c>. Use <c><![CDATA[-setcookie]]></c> instead.</p> + <c><![CDATA[-setcookie]]></c>. Use <c><![CDATA[-setcookie]]></c> + instead.</p> </item> <tag><c><![CDATA[-detached]]></c></tag> <item> @@ -223,8 +237,7 @@ </item> <tag><c><![CDATA[-emu_args]]></c></tag> <item> - <p>Useful for debugging. Prints out the actual arguments - sent to the emulator.</p> + <p>Useful for debugging. Prints the arguments sent to the emulator.</p> </item> <tag><c><![CDATA[-env Variable Value]]></c></tag> <item> @@ -234,14 +247,16 @@ <pre> % <input>erl -env DISPLAY gin:0</input></pre> <p>In this example, an Erlang runtime system is started with - the <c><![CDATA[DISPLAY]]></c> environment variable set to <c><![CDATA[gin:0]]></c>.</p> + environment variable <c><![CDATA[DISPLAY]]></c> set to + <c><![CDATA[gin:0]]></c>.</p> </item> - <tag><c><![CDATA[-eval Expr]]></c>(init flag)</tag> + <tag><c><![CDATA[-eval Expr]]></c> (init flag)</tag> <item> - <p>Makes <c><![CDATA[init]]></c> evaluate the expression <c><![CDATA[Expr]]></c>, see - <seealso marker="init">init(3)</seealso>.</p> + <p>Makes <c><![CDATA[init]]></c> evaluate the expression + <c><![CDATA[Expr]]></c>; see + <seealso marker="init"><c>init(3)</c></seealso>.</p> </item> - <tag><c><![CDATA[-extra]]></c>(init flag)</tag> + <tag><c><![CDATA[-extra]]></c> (init flag)</tag> <item> <p>Everything following <c><![CDATA[-extra]]></c> is considered plain arguments and can be retrieved using @@ -249,8 +264,9 @@ </item> <tag><c><![CDATA[-heart]]></c></tag> <item> - <p>Starts heart beat monitoring of the Erlang runtime system. - See <seealso marker="kernel:heart">heart(3)</seealso>.</p> + <p>Starts heartbeat monitoring of the Erlang runtime system; + see <seealso marker="kernel:heart"> + <c>heart(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-hidden]]></c></tag> <item> @@ -258,91 +274,99 @@ run as a distributed node. Hidden nodes always establish hidden connections to all other nodes except for nodes in the same global group. Hidden connections are not published on - either of the connected nodes, i.e. neither of the connected - nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the other - node. See also hidden global groups, - <seealso marker="kernel:global_group">global_group(3)</seealso>.</p> + any of the connected nodes, that is, none of the connected + nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the + other node. See also hidden global groups; + <seealso marker="kernel:global_group"> + <c>global_group(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-hosts Hosts]]></c></tag> <item> - <p>Specifies the IP addresses for the hosts on which Erlang - boot servers are running, see - <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>. - This flag is mandatory if the <c><![CDATA[-loader inet]]></c> flag is - present.</p> - <p>The IP addresses must be given in the standard form (four - decimal numbers separated by periods, for example - <c><![CDATA["150.236.20.74"]]></c>. Hosts names are not acceptable, but - a broadcast address (preferably limited to the local network) + <p>Specifies the IP addresses for the hosts on which Erlang boot servers + are running, see <seealso marker="kernel:erl_boot_server"> + <c>erl_boot_server(3)</c></seealso>. This flag + is mandatory if flag <c><![CDATA[-loader inet]]></c> is present.</p> + <p>The IP addresses must be specified in the standard form (four + decimal numbers separated by periods, for example, + <c><![CDATA["150.236.20.74"]]></c>. Hosts names are not acceptable, + but a broadcast address (preferably limited to the local network) is.</p> </item> <tag><c><![CDATA[-id Id]]></c></tag> <item> <p>Specifies the identity of the Erlang runtime system. If it is run as a distributed node, <c><![CDATA[Id]]></c> must be identical to - the name supplied together with the <c><![CDATA[-sname]]></c> or - <c><![CDATA[-name]]></c> flag.</p> + the name supplied together with flag <c><![CDATA[-sname]]></c> or + <c><![CDATA[-name]]></c>.</p> </item> <tag><c><![CDATA[-init_debug]]></c></tag> <item> <p>Makes <c><![CDATA[init]]></c> write some debug information while interpreting the boot script.</p> </item> - <tag><marker id="instr"/><c><![CDATA[-instr]]></c>(emulator flag)</tag> + <tag><marker id="instr"/><c><![CDATA[-instr]]></c> (emulator flag)</tag> <item> <p>Selects an instrumented Erlang runtime system (virtual machine) to run, instead of the ordinary one. When running an instrumented runtime system, some resource usage data can be - obtained and analysed using the module <c><![CDATA[instrument]]></c>. + obtained and analyzed using the <c><![CDATA[instrument]]></c> module. Functionally, it behaves exactly like an ordinary Erlang runtime system.</p> </item> <tag><c><![CDATA[-loader Loader]]></c></tag> <item> - <p>Specifies the method used by <c><![CDATA[erl_prim_loader]]></c> to load - Erlang modules into the system. See - <seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>. - Two <c><![CDATA[Loader]]></c> methods are supported, <c><![CDATA[efile]]></c> and - <c><![CDATA[inet]]></c>. <c><![CDATA[efile]]></c> means use the local file system, - this is the default. <c><![CDATA[inet]]></c> means use a boot server on - another machine, and the <c><![CDATA[-id]]></c>, <c><![CDATA[-hosts]]></c> and - <c><![CDATA[-setcookie]]></c> flags must be specified as well. If - <c><![CDATA[Loader]]></c> is something else, the user supplied + <p>Specifies the method used by <c><![CDATA[erl_prim_loader]]></c> to + load Erlang modules into the system; see + <seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>. + Two <c><![CDATA[Loader]]></c> methods are supported:</p> + <list type="bulleted"> + <item> + <p><c><![CDATA[efile]]></c>, which means use the local file system, + this is the default.</p> + </item> + <item> + <p><c><![CDATA[inet]]></c>, which means use a boot server on + another machine. The flags <c><![CDATA[-id]]></c>, + <c><![CDATA[-hosts]]></c> and <c><![CDATA[-setcookie]]></c> must + also be specified.</p> + </item> + </list> + <p>If <c><![CDATA[Loader]]></c> is something else, the user-supplied <c><![CDATA[Loader]]></c> port program is started.</p> </item> <tag><c><![CDATA[-make]]></c></tag> <item> - <p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c> in - the current working directory and then terminate. See - <seealso marker="tools:make">make(3)</seealso>. Implies + <p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c> + in the current working directory and then terminate; see + <seealso marker="tools:make"><c>make(3)</c></seealso>. Implies <c><![CDATA[-noinput]]></c>.</p> </item> <tag><c><![CDATA[-man Module]]></c></tag> <item> - <p>Displays the manual page for the Erlang module <c><![CDATA[Module]]></c>. - Only supported on Unix.</p> + <p>Displays the manual page for the Erlang module + <c><![CDATA[Module]]></c>. Only supported on Unix.</p> </item> <tag><c><![CDATA[-mode interactive | embedded]]></c></tag> <item> - <p>Indicates if the system should load code dynamically - (<c><![CDATA[interactive]]></c>), or if all code should be loaded - during system initialization (<c><![CDATA[embedded]]></c>), see - <seealso marker="kernel:code">code(3)</seealso>. Defaults to - <c><![CDATA[interactive]]></c>.</p> + <p>Indicates if the system is to load code dynamically + (<c><![CDATA[interactive]]></c>), or if all code is to be loaded + during system initialization (<c><![CDATA[embedded]]></c>); see + <seealso marker="kernel:code"><c>code(3)</c></seealso>. + Defaults to <c><![CDATA[interactive]]></c>.</p> </item> <tag><c><![CDATA[-name Name]]></c></tag> <item> <p>Makes the Erlang runtime system into a distributed node. This flag invokes all network servers necessary for a node to - become distributed. See - <seealso marker="kernel:net_kernel">net_kernel(3)</seealso>. - It is also ensured that <c><![CDATA[epmd]]></c> runs on the current host - before Erlang is started. See - <seealso marker="epmd">epmd(1)</seealso> and the + become distributed; see <seealso marker="kernel:net_kernel"> + <c>net_kernel(3)</c></seealso>. It is also ensured that + <c><![CDATA[epmd]]></c> runs on the current host before Erlang is + started; see <seealso marker="epmd"><c>epmd(1)</c></seealso>.and the <seealso marker="#start_epmd"><c>-start_epmd</c></seealso> option.</p> - <p>The name of the node will be <c><![CDATA[Name@Host]]></c>, where - <c><![CDATA[Host]]></c> is the fully qualified host name of the current - host. For short names, use the <c><![CDATA[-sname]]></c> flag instead.</p> + <p>The node name will be <c><![CDATA[Name@Host]]></c>, where + <c><![CDATA[Host]]></c> is the fully qualified host name of the + current host. For short names, use flag <c><![CDATA[-sname]]></c> + instead.</p> </item> <tag><c><![CDATA[-noinput]]></c></tag> <item> @@ -353,115 +377,111 @@ <item> <p>Starts an Erlang runtime system with no shell. This flag makes it possible to have the Erlang runtime system as a - component in a series of UNIX pipes.</p> + component in a series of Unix pipes.</p> </item> <tag><c><![CDATA[-nostick]]></c></tag> <item> <p>Disables the sticky directory facility of the Erlang code - server, see - <seealso marker="kernel:code">code(3)</seealso>.</p> + server; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-oldshell]]></c></tag> <item> - <p>Invokes the old Erlang shell from Erlang 3.3. The old shell + <p>Invokes the old Erlang shell from Erlang/OTP 3.3. The old shell can still be used.</p> </item> <tag><c><![CDATA[-pa Dir1 Dir2 ...]]></c></tag> <item> <p>Adds the specified directories to the beginning of the code - path, similar to <c><![CDATA[code:add_pathsa/1]]></c>. See - <seealso marker="kernel:code">code(3)</seealso>. + path, similar to <c><![CDATA[code:add_pathsa/1]]></c>; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>. As an alternative to <c>-pa</c>, if several directories are to be prepended to the code path and the directories have a - common parent directory, that parent directory could be - specified in the <c>ERL_LIBS</c> environment variable. - See <seealso marker="kernel:code">code(3)</seealso>.</p> + common parent directory, that parent directory can be + specified in environment variable <c>ERL_LIBS</c>; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-pz Dir1 Dir2 ...]]></c></tag> <item> <p>Adds the specified directories to the end of the code path, - similar to <c><![CDATA[code:add_pathsz/1]]></c>. See - <seealso marker="kernel:code">code(3)</seealso>.</p> + similar to <c><![CDATA[code:add_pathsz/1]]></c>; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-path Dir1 Dir2 ...]]></c></tag> <item> - <p>Replaces the path specified in the boot script. See - <seealso marker="sasl:script">script(4)</seealso>.</p> + <p>Replaces the path specified in the boot script; see + <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p> </item> <tag><c><![CDATA[-proto_dist Proto]]></c></tag> <item> - <p>Specify a protocol for Erlang distribution.</p> - <taglist> - <tag><c>inet_tcp</c></tag> - <item> - <p>TCP over IPv4 (the default)</p> - </item> - <tag><c>inet_tls</c></tag> - <item> - <p>distribution over TLS/SSL</p> - </item> - <tag><c>inet6_tcp</c></tag> - <item> - <p>TCP over IPv6</p> - </item> + <p>Specifies a protocol for Erlang distribution:</p> + <taglist> + <tag><c>inet_tcp</c></tag> + <item>TCP over IPv4 (the default)</item> + <tag><c>inet_tls</c></tag> + <item>Distribution over TLS/SSL</item> + <tag><c>inet6_tcp</c></tag> + <item>TCP over IPv6</item> </taglist> <p>For example, to start up IPv6 distributed nodes:</p> <pre> -% <input>erl -name [email protected] -proto_dist inet6_tcp</input> -</pre> +% <input>erl -name [email protected] -proto_dist inet6_tcp</input></pre> </item> <tag><c><![CDATA[-remsh Node]]></c></tag> <item> - <p>Starts Erlang with a remote shell connected to <c><![CDATA[Node]]></c>.</p> + <p>Starts Erlang with a remote shell connected to + <c><![CDATA[Node]]></c>.</p> </item> <tag><c><![CDATA[-rsh Program]]></c></tag> <item> - <p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a slave - node on a remote host. See - <seealso marker="stdlib:slave">slave(3)</seealso>.</p> + <p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a + slave node on a remote host; see + <seealso marker="stdlib:slave"><c>slave(3)</c></seealso>.</p> </item> - <tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c>(init flag)</tag> + <tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c> (init + flag)</tag> <item> - <p>Makes <c><![CDATA[init]]></c> call the specified function. <c><![CDATA[Func]]></c> - defaults to <c><![CDATA[start]]></c>. If no arguments are provided, - the function is assumed to be of arity 0. Otherwise it is - assumed to be of arity 1, taking the list - <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are passed - as strings. See - <seealso marker="init">init(3)</seealso>.</p> + <p>Makes <c><![CDATA[init]]></c> call the specified function. + <c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>. + If no arguments are provided, the function is assumed to be of + arity 0. Otherwise it is assumed to be of arity 1, taking the list + <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are + passed as strings. See <seealso marker="init"> + <c>init(3)</c></seealso>.</p> </item> - <tag><c><![CDATA[-s Mod [Func [Arg1, Arg2, ...]]]]></c>(init flag)</tag> + <tag><c><![CDATA[-s Mod [Func [Arg1, Arg2, ...]]]]></c> (init flag)</tag> <item> - <p>Makes <c><![CDATA[init]]></c> call the specified function. <c><![CDATA[Func]]></c> - defaults to <c><![CDATA[start]]></c>. If no arguments are provided, - the function is assumed to be of arity 0. Otherwise it is - assumed to be of arity 1, taking the list - <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are passed - as atoms. See - <seealso marker="init">init(3)</seealso>.</p> + <p>Makes <c><![CDATA[init]]></c> call the specified function. + <c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>. + If no arguments are provided, the function is assumed to be of + arity 0. Otherwise it is assumed to be of arity 1, taking the list + <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are + passed as atoms. See <seealso marker="init"> + <c>init(3)</c></seealso>.</p> </item> <tag><c><![CDATA[-setcookie Cookie]]></c></tag> <item> - <p>Sets the magic cookie of the node to <c><![CDATA[Cookie]]></c>, see - <seealso marker="erlang#set_cookie/2">erlang:set_cookie/2</seealso>.</p> + <p>Sets the magic cookie of the node to <c><![CDATA[Cookie]]></c>; see + <seealso marker="erlang#set_cookie/2"> + <c>erlang:set_cookie/2</c></seealso>.</p> </item> <tag><c><![CDATA[-shutdown_time Time]]></c></tag> <item> <p>Specifies how long time (in milliseconds) the <c><![CDATA[init]]></c> process is allowed to spend shutting down the system. If - <c><![CDATA[Time]]></c> ms have elapsed, all processes still existing are - killed. Defaults to <c><![CDATA[infinity]]></c>.</p> + <c><![CDATA[Time]]></c> milliseconds have elapsed, all processes still + existing are killed. Defaults to <c><![CDATA[infinity]]></c>.</p> </item> <tag><c><![CDATA[-sname Name]]></c></tag> <item> - <p>Makes the Erlang runtime system into a distributed node, - similar to <c><![CDATA[-name]]></c>, but the host name portion of the node + <p>Makes the Erlang runtime system into a distributed node, similar to + <c><![CDATA[-name]]></c>, but the host name portion of the node name <c><![CDATA[Name@Host]]></c> will be the short name, not fully qualified.</p> <p>This is sometimes the only way to run distributed Erlang if - the DNS (Domain Name System) is not running. There can be no - communication between nodes running with the <c><![CDATA[-sname]]></c> - flag and those running with the <c><![CDATA[-name]]></c> flag, as node + the Domain Name System (DNS) is not running. No communication can + exist between nodes running with flag <c><![CDATA[-sname]]></c> + and those running with flag <c><![CDATA[-name]]></c>, as node names must be unique in distributed Erlang systems.</p> </item> <tag><marker id="start_epmd"/><c>-start_epmd true | false</c></tag> @@ -481,19 +501,21 @@ </item> <tag><marker id="smp"/><c><![CDATA[-smp [enable|auto|disable]]]></c></tag> <item> - <p><c>-smp enable</c> and <c>-smp</c> starts the Erlang runtime - system with SMP support enabled. This may fail if no runtime + <p><c>-smp enable</c> and <c>-smp</c> start the Erlang runtime + system with SMP support enabled. This can fail if no runtime system with SMP support is available. <c>-smp auto</c> starts the Erlang runtime system with SMP support enabled if it is - available and more than one logical processor are detected. + available and more than one logical processor is detected. <c>-smp disable</c> starts a runtime system without SMP support.</p> - <p><em>NOTE</em>: The runtime system with SMP support will not - be available on all supported platforms. See also the - <seealso marker="#+S">+S</seealso> flag.</p> + <note> + <p>The runtime system with SMP support is not available on all + supported platforms. See also flag + <seealso marker="#+S"><c>+S</c></seealso>.</p> + </note> </item> - <tag><c><![CDATA[-version]]></c>(emulator flag)</tag> + <tag><c><![CDATA[-version]]></c> (emulator flag)</tag> <item> - <p>Makes the emulator print out its version number. The same + <p>Makes the emulator print its version number. The same as <c><![CDATA[erl +V]]></c>.</p> </item> </taglist> @@ -505,133 +527,169 @@ <p><c><![CDATA[erl]]></c> invokes the code for the Erlang emulator (virtual machine), which supports the following flags:</p> <taglist> - <tag><marker id="async_thread_stack_size"/><c><![CDATA[+a size]]></c></tag> + <tag><marker id="async_thread_stack_size"/> + <c><![CDATA[+a size]]></c></tag> <item> <p>Suggested stack size, in kilowords, for threads in the - async-thread pool. Valid range is 16-8192 kilowords. The - default suggested stack size is 16 kilowords, i.e, 64 + async thread pool. Valid range is 16-8192 kilowords. The + default suggested stack size is 16 kilowords, that is, 64 kilobyte on 32-bit architectures. This small default size - has been chosen since the amount of async-threads might - be quite large. The default size is enough for drivers - delivered with Erlang/OTP, but might not be sufficiently - large for other dynamically linked in drivers that use the - <seealso marker="erl_driver#driver_async">driver_async()</seealso> - functionality. Note that the value passed is only a - suggestion, and it might even be ignored on some - platforms.</p> + has been chosen because the number of async threads can + be large. The default size is enough for drivers + delivered with Erlang/OTP, but might not be large + enough for other dynamically linked-in drivers that use the + <seealso marker="erl_driver#driver_async"> + <c>driver_async()</c></seealso> functionality. + Notice that the value passed is only a suggestion, + and it can even be ignored on some platforms.</p> </item> <tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag> <item> - <p>Sets the number of threads in async thread pool, valid range - is 0-1024. If thread support is available, the default is 10.</p> + <p>Sets the number of threads in async thread pool. Valid range + is 0-1024. Defaults to 10 if thread support is available.</p> </item> <tag><c><![CDATA[+B [c | d | i]]]></c></tag> <item> - <p>The <c><![CDATA[c]]></c> option makes <c><![CDATA[Ctrl-C]]></c> interrupt the current - shell instead of invoking the emulator break handler. - The <c><![CDATA[d]]></c> option (same as specifying <c><![CDATA[+B]]></c> without an - extra option) disables the break handler. The <c><![CDATA[i]]></c> option - makes the emulator ignore any break signal.</p> - <p>If the <c><![CDATA[c]]></c> option is used with <c><![CDATA[oldshell]]></c> on Unix, - <c><![CDATA[Ctrl-C]]></c> will restart the shell process rather than - interrupt it.</p> - <p>Note that on Windows, this flag is only applicable for - <c><![CDATA[werl]]></c>, not <c><![CDATA[erl]]></c> (<c><![CDATA[oldshell]]></c>). Note also that - <c><![CDATA[Ctrl-Break]]></c> is used instead of <c><![CDATA[Ctrl-C]]></c> on Windows.</p> + <p>Option <c><![CDATA[c]]></c> makes <c><![CDATA[Ctrl-C]]></c> + interrupt the current shell instead of invoking the emulator break + handler. Option <c><![CDATA[d]]></c> (same as specifying + <c><![CDATA[+B]]></c> without an extra option) disables the break + handler. Option <c><![CDATA[i]]></c> makes the emulator ignore any + break signal.</p> + <p>If option <c><![CDATA[c]]></c> is used with + <c><![CDATA[oldshell]]></c> on Unix, <c><![CDATA[Ctrl-C]]></c> will + restart the shell process rather than interrupt it.</p> + <p>Notice that on Windows, this flag is only applicable for + <c><![CDATA[werl]]></c>, not <c><![CDATA[erl]]></c> + (<c><![CDATA[oldshell]]></c>). Notice also that + <c><![CDATA[Ctrl-Break]]></c> is used instead of + <c><![CDATA[Ctrl-C]]></c> on Windows.</p> </item> <tag><marker id="+c"/><c><![CDATA[+c true | false]]></c></tag> <item> - <p>Enable or disable - <seealso marker="time_correction#Time_Correction">time correction</seealso>:</p> + <p>Enables or disables + <seealso marker="time_correction#Time_Correction">time + correction</seealso>:</p> <taglist> <tag><c>true</c></tag> - <item><p>Enable time correction. This is the default if - time correction is supported on the specific platform.</p></item> - - <tag><c>false</c></tag> - <item><p>Disable time correction.</p></item> - </taglist> - <p>For backwards compatibility, the boolean value can be omitted. - This is interpreted as <c>+c false</c>. - </p> - </item> - <tag><marker id="+C_"/><c><![CDATA[+C no_time_warp | single_time_warp | multi_time_warp]]></c></tag> - <item> - <p>Set - <seealso marker="time_correction#Time_Warp_Modes">time warp mode</seealso>: - </p> - <taglist> - <tag><c>no_time_warp</c></tag> - <item><p><seealso marker="time_correction#No_Time_Warp_Mode">No Time Warp Mode</seealso> (the default)</p></item> - <tag><c>single_time_warp</c></tag> - <item><p><seealso marker="time_correction#Single_Time_Warp_Mode">Single Time Warp Mode</seealso></p></item> - <tag><c>multi_time_warp</c></tag> - <item><p><seealso marker="time_correction#Multi_Time_Warp_Mode">Multi Time Warp Mode</seealso></p></item> - </taglist> + <item>Enables time correction. This is the default if + time correction is supported on the specific platform.</item> + <tag><c>false</c></tag> + <item>Disables time correction.</item> + </taglist> + <p>For backward compatibility, the boolean value can be omitted. + This is interpreted as <c>+c false</c>.</p> + </item> + <tag><marker id="+C_"/><c><![CDATA[+C no_time_warp | single_time_warp | + multi_time_warp]]></c></tag> + <item> + <p>Sets <seealso marker="time_correction#Time_Warp_Modes">time warp + mode</seealso>:</p> + <taglist> + <tag><c>no_time_warp</c></tag> + <item><seealso marker="time_correction#No_Time_Warp_Mode"> + No time warp mode</seealso> (the default)</item> + <tag><c>single_time_warp</c></tag> + <item><seealso marker="time_correction#Single_Time_Warp_Mode"> + Single time warp mode</seealso></item> + <tag><c>multi_time_warp</c></tag> + <item><seealso marker="time_correction#Multi_Time_Warp_Mode"> + Multi-time warp mode</seealso></item> + </taglist> </item> <tag><c><![CDATA[+d]]></c></tag> <item> <p>If the emulator detects an internal error (or runs out of memory), - it will by default generate both a crash dump and a core dump. - The core dump will, however, not be very useful since the content - of process heaps is destroyed by the crash dump generation.</p> - - <p>The <c>+d</c> option instructs the emulator to only produce a - core dump and no crash dump if an internal error is detected.</p> - - <p>Calling <c>erlang:halt/1</c> with a string argument will still - produce a crash dump. On Unix systems, sending an emulator process - a SIGUSR1 signal will also force a crash dump.</p> + it, by default, generates both a crash dump and a core dump. + The core dump is, however, not very useful as the content + of process heaps is destroyed by the crash dump generation.</p> + <p>Option <c>+d</c> instructs the emulator to produce only a + core dump and no crash dump if an internal error is detected.</p> + <p>Calling <seealso marker="erlang:halt/1"> + <c>erlang:halt/1</c></seealso> with a string argument still + produces a crash dump. On Unix systems, sending an emulator process + a <c>SIGUSR1</c> signal also forces a crash dump.</p> </item> <tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag> <item> - <p>Set max number of ETS tables.</p> + <p>Sets the maximum number of ETS tables.</p> </item> <tag><c><![CDATA[+ec]]></c></tag> <item> - <p>Force the <c>compressed</c> option on all ETS tables. - Only intended for test and evaluation.</p> + <p>Forces option <c>compressed</c> on all ETS tables. + Only intended for test and evaluation.</p> </item> - <tag><marker id="file_name_encoding"></marker><c><![CDATA[+fnl]]></c></tag> + <tag><marker id="file_name_encoding"></marker> + <c><![CDATA[+fnl]]></c></tag> <item> - <p>The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255.</p> - <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p> + <p>The virtual machine works with filenames as if they are encoded + using the ISO Latin-1 encoding, disallowing Unicode characters with + code points > 255.</p> + <p>For more information about Unicode filenames, see section + <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode + Filenames</seealso> in the STDLIB User's Guide. Notice that + this value also applies to command-line parameters and environment + variables (see section <seealso + marker="stdlib:unicode_usage#unicode_in_environment_and_parameters"> + Unicode in Enviroment and Parameters</seealso> in the STDLIB + User's Guide).</p> </item> <tag><c><![CDATA[+fnu[{w|i|e}]]]></c></tag> <item> - <p>The VM works with file names as if they are encoded using - UTF-8 (or some other system specific Unicode encoding). This - is the default on operating systems that enforce Unicode - encoding, i.e. Windows and MacOS X.</p> - <p>The <c>+fnu</c> switch can be followed by <c>w</c>, - <c>i</c>, or <c>e</c> to control the way wrongly encoded file - names are to be reported. <c>w</c> means that a warning is - sent to the <c>error_logger</c> whenever a wrongly encoded - file name is "skipped" in directory listings, <c>i</c> means - that those wrongly encoded file names are silently ignored and - <c>e</c> means that the API function will return an error - whenever a wrongly encoded file (or directory) name is - encountered. <c>w</c> is the default. Note that - <c>file:read_link/1</c> will always return an error if the - link points to an invalid file name.</p> - <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p> + <p>The virtual machine works with filenames as if they are encoded + using UTF-8 (or some other system-specific Unicode encoding). This is + the default on operating systems that enforce Unicode encoding, that + is, Windows and MacOS X.</p> + <p>The <c>+fnu</c> switch can be followed by <c>w</c>, <c>i</c>, or + <c>e</c> to control how wrongly encoded filenames are to be + reported:</p> + <list type="bulleted"> + <item> + <p><c>w</c> means that a warning is sent to the <c>error_logger</c> + whenever a wrongly encoded filename is "skipped" in directory + listings. This is the default.</p> + </item> + <item> + <p><c>i</c> means that those wrongly encoded filenames are silently + ignored.</p> + </item> + <item> + <p><c>e</c> means that the API function returns an error whenever a + wrongly encoded filename (or directory name) is encountered.</p> + </item> + </list> + <p>Notice that <seealso marker="kernel:file#read_link/1"> + <c>file:read_link/1</c></seealso> always returns an error if the link + points to an invalid filename.</p> + <p>For more information about Unicode filenames, see section + <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode + Filenames</seealso> in the STDLIB User's Guide. Notice that + this value also applies to command-line parameters and environment + variables (see section <seealso + marker="stdlib:unicode_usage#unicode_in_environment_and_parameters"> + Unicode in Enviroment and Parameters</seealso> in the STDLIB + User's Guide).</p> </item> <tag><c><![CDATA[+fna[{w|i|e}]]]></c></tag> <item> <p>Selection between <c>+fnl</c> and <c>+fnu</c> is done based - on the current locale settings in the OS, meaning that if you - have set your terminal for UTF-8 encoding, the filesystem is - expected to use the same encoding for file names. This is - default on all operating systems except MacOS X and - Windows.</p> - <p>The <c>+fna</c> switch can be followed by <c>w</c>, - <c>i</c>, or <c>e</c>. This will have effect if the locale - settings cause the behavior of <c>+fnu</c> to be selected. - See the description of <c>+fnu</c> above. If the locale - settings cause the behavior of <c>+fnl</c> to be selected, - then <c>w</c>, <c>i</c>, or <c>e</c> will not have any - effect.</p> - <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p> + on the current locale settings in the OS. This means that if you + have set your terminal for UTF-8 encoding, the filesystem is + expected to use the same encoding for filenames. This is + default on all operating systems, except MacOS X and Windows.</p> + <p>The <c>+fna</c> switch can be followed by <c>w</c>, <c>i</c>, or + <c>e</c>. This has effect if the locale settings cause the behavior + of <c>+fnu</c> to be selected; see the description of <c>+fnu</c> + above. If the locale settings cause the behavior of <c>+fnl</c> to be + selected, then <c>w</c>, <c>i</c>, or <c>e</c> have no effect.</p> + <p>For more information about Unicode filenames, see section + <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode + Filenames</seealso> in the STDLIB User's Guide. Notice that + this value also applies to command-line parameters and environment + variables (see section <seealso + marker="stdlib:unicode_usage#unicode_in_environment_and_parameters"> + Unicode in Enviroment and Parameters</seealso> in the STDLIB + User's Guide).</p> </item> <tag><c><![CDATA[+hms Size]]></c></tag> <item> @@ -646,26 +704,24 @@ <tag><marker id="+hmax"/><c><![CDATA[+hmax Size]]></c></tag> <item> <p>Sets the default maximum heap size of processes to the size - <c><![CDATA[Size]]></c>. If <c>+hmax</c> is not given, the default is <c>0</c> - which means that no maximum heap size is used. - For more information, see the documentation of + <c><![CDATA[Size]]></c>. Defaults to <c>0</c>, which means that no + maximum heap size is used. For more information, see <seealso marker="erlang#process_flag_max_heap_size"> - <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> + <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> <tag><marker id="+hmaxel"/><c><![CDATA[+hmaxel true|false]]></c></tag> <item> - <p>Sets whether to send an error logger message for processes that reach - the maximum heap size or not. If <c>+hmaxel</c> is not given, the default is <c>true</c>. - For more information, see the documentation of - <seealso marker="erlang#process_flag_max_heap_size"> + <p>Sets whether to send an error logger message or not for processes + reaching the maximum heap size. Defaults to <c>true</c>. + For more information, see + <seealso marker="erlang#process_flag_max_heap_size"> <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> <tag><marker id="+hmaxk"/><c><![CDATA[+hmaxk true|false]]></c></tag> <item> - <p>Sets whether to kill processes that reach the maximum heap size or not. If - <c>+hmaxk</c> is not given, the default is <c>true</c>. For more information, - see the documentation of - <seealso marker="erlang#process_flag_max_heap_size"> + <p>Sets whether to kill processes reaching the maximum heap size or not. + Default to <c>true</c>. For more information, see + <seealso marker="erlang#process_flag_max_heap_size"> <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> <tag><c><![CDATA[+hpds Size]]></c></tag> @@ -674,95 +730,62 @@ <c><![CDATA[Size]]></c>.</p> </item> <tag><marker id="+hmqd"/><c>+hmqd off_heap|on_heap</c></tag> - <item><p> - Sets the default value for the process flag - <c>message_queue_data</c>. If <c>+hmqd</c> is not - passed, <c>on_heap</c> will be the default. For more information, - see the documentation of - <seealso marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data, - MQD)</c></seealso>. - </p></item> + <item> + <p>Sets the default value for process flag <c>message_queue_data</c>. + Defaults to <c>on_heap</c>. If <c>+hmqd</c> is not + passed, <c>on_heap</c> will be the default. For more information, see + <seealso marker="erlang#process_flag_message_queue_data"> + <c>process_flag(message_queue_data, MQD)</c></seealso>.</p> + </item> <tag><c><![CDATA[+K true | false]]></c></tag> <item> - <p>Enables or disables the kernel poll functionality if - the emulator supports it. Default is <c><![CDATA[false]]></c> (disabled). - If the emulator does not support kernel poll, and - the <c><![CDATA[+K]]></c> flag is passed to the emulator, a warning is + <p>Enables or disables the kernel poll functionality if supported by + the emulator. Defaults to <c><![CDATA[false]]></c> (disabled). + If the emulator does not support kernel poll, and flag + <c><![CDATA[+K]]></c> is passed to the emulator, a warning is issued at startup.</p> </item> <tag><c><![CDATA[+l]]></c></tag> <item> - <p>Enables auto load tracing, displaying info while loading + <p>Enables autoload tracing, displaying information while loading code.</p> </item> <tag><c><![CDATA[+L]]></c></tag> <item> - <p>Don't load information about source file names and line numbers. - This will save some memory, but exceptions will not contain - information about the file names and line numbers. - </p> + <p>Prevents loading information about source filenames and line + numbers. This saves some memory, but exceptions do not contain + information about the filenames and line numbers.</p> </item> <tag><marker id="erts_alloc"/><c><![CDATA[+MFlag Value]]></c></tag> <item> - <p>Memory allocator specific flags, see - <seealso marker="erts_alloc">erts_alloc(3)</seealso> for - further information.</p> - </item> - <tag><marker id="+n"/><c><![CDATA[+n Behavior]]></c></tag> - <item> - <p>Control behavior of signals to ports.</p> - <p>As of OTP-R16 signals to ports are truly asynchronously - delivered. Note that signals always have been documented as - asynchronous. The underlying implementation has, however, - previously delivered these signals synchronously. Correctly - written Erlang programs should be able to handle this without - any issues. Bugs in existing Erlang programs that make false - assumptions about signals to ports may, however, be tricky to - find. This switch has been introduced in order to at least - make it easier to compare behaviors during a transition period. - Note that <em>this flag is deprecated</em> as of its - introduction, and is scheduled for removal in OTP-R17. - <c>Behavior</c> should be one of the following characters:</p> - <taglist> - <tag><c>d</c></tag> - <item>The default. Asynchronous signals. A process that sends - a signal to a port may continue execution before the signal - has been delivered to the port.</item> - <tag><c>s</c></tag> - <item>Synchronous signals. A processes that sends a signal - to a port will not continue execution until the signal has - been delivered. Should <em>only</em> be used for testing and - debugging.</item> - <tag><c>a</c></tag> - <item>Asynchronous signals. As the default, but a processes - that sends a signal will even more frequently continue - execution before the signal has been delivered to the - port. Should <em>only</em> be used for testing and - debugging.</item> - </taglist> - </item> - <tag><marker id="+pc"/><marker id="printable_character_range"/><c><![CDATA[+pc Range]]></c></tag> - <item> - <p>Sets the range of characters that the system will consider printable in heuristic detection of strings. This typically affects the shell, debugger and io:format functions (when ~tp is used in the format string).</p> - <p>Currently two values for the <c>Range</c> are supported:</p> - <taglist> - <tag><c>latin1</c></tag> <item>The default. Only characters - in the ISO-latin-1 range can be considered printable, which means - that a character with a code point > 255 will never be - considered printable and that lists containing such - characters will be displayed as lists of integers rather - than text strings by tools.</item> - <tag><c>unicode</c></tag> - <item>All printable Unicode characters are considered when - determining if a list of integers is to be displayed in - string syntax. This may give unexpected results if for - example your font does not cover all Unicode - characters.</item> - </taglist> - <p>Se also <seealso marker="stdlib:io#printable_range/0"> - io:printable_range/0</seealso>.</p> - </item> - <tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number|legacy]]></c></tag> + <p>Memory allocator-specific flags. For more information, see + <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p> + </item> + <tag><marker id="+pc"/><marker id="printable_character_range"/> + <c><![CDATA[+pc Range]]></c></tag> + <item> + <p>Sets the range of characters that the system considers printable in + heuristic detection of strings. This typically affects the shell, + debugger, and <c>io:format</c> functions (when <c>~tp</c> is used in + the format string).</p> + <p>Two values are supported for <c>Range</c>:</p> + <taglist> + <tag><c>latin1</c></tag> + <item>The default. Only characters in the ISO Latin-1 range can be + considered printable. This means that a character with a code point + > 255 is never considered printable and that lists containing + such characters are displayed as lists of integers rather than text + strings by tools.</item> + <tag><c>unicode</c></tag> + <item>All printable Unicode characters are considered when + determining if a list of integers is to be displayed in + string syntax. This can give unexpected results if, for + example, your font does not cover all Unicode characters.</item> + </taglist> + <p>See also <seealso marker="stdlib:io#printable_range/0"> + <c>io:printable_range/0</c></seealso> in STDLIB.</p> + </item> + <tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number]]></c></tag> <item> <p>Sets the maximum number of simultaneously existing processes for this system if a <c>Number</c> is passed as value. Valid range for @@ -774,15 +797,8 @@ checked by calling <seealso marker="erlang#system_info_process_limit">erlang:system_info(process_limit)</seealso>.</p> <p>The default value is <c>262144</c></p> - <p>If <c>legacy</c> is passed as value, the legacy algorithm for - allocation of process identifiers will be used. Using the legacy - algorithm, identifiers will be allocated in a strictly increasing - fashion until largest possible identifier has been reached. Note that - this algorithm suffers from performance issues and can under certain - circumstances be extremely expensive. The legacy algoritm is deprecated, - and the <c>legacy</c> option is scheduled for removal in OTP-R18.</p> </item> - <tag><marker id="+Q"/><marker id="max_ports"/><c><![CDATA[+Q Number|legacy]]></c></tag> + <tag><marker id="+Q"/><marker id="max_ports"/><c><![CDATA[+Q Number]]></c></tag> <item> <p>Sets the maximum number of simultaneously existing ports for this system if a Number is passed as value. Valid range for <c>Number</c> @@ -799,179 +815,179 @@ than <c>65536</c>, the chosen value will increased to a value larger or equal to the maximum amount of file descriptors that can be opened.</p> - <p>On Windows the default value is set to <c>8196</c> because the + <p>On Windows the default value is set to <c>8196</c> because the normal OS limitations are set higher than most machines can handle.</p> - <p>Previously the environment variable <c>ERL_MAX_PORTS</c> was used - for setting the maximum number of simultaneously existing ports. This - environment variable is deprecated, and scheduled for removal in - OTP-R17, but can still be used.</p> - <p>If <c>legacy</c> is passed as value, the legacy algorithm for - allocation of port identifiers will be used. Using the legacy - algorithm, identifiers will be allocated in a strictly increasing - fashion until largest possible identifier has been reached. Note that - this algorithm suffers from performance issues and can under certain - circumstances be extremely expensive. The legacy algoritm is deprecated, - and the <c>legacy</c> option is scheduled for removal in OTP-R18.</p> </item> <tag><marker id="compat_rel"/><c><![CDATA[+R ReleaseNumber]]></c></tag> <item> <p>Sets the compatibility mode.</p> - <p>The distribution mechanism is not backwards compatible by - default. This flags sets the emulator in compatibility mode + <p>The distribution mechanism is not backward compatible by + default. This flag sets the emulator in compatibility mode with an earlier Erlang/OTP release <c><![CDATA[ReleaseNumber]]></c>. The release number must be in the range <c><![CDATA[<current release>-2..<current release>]]></c>. This - limits the emulator, making it possible for it to communicate - with Erlang nodes (as well as C- and Java nodes) running that - earlier release.</p> - <p>Note: Make sure all nodes (Erlang-, C-, and Java nodes) of - a distributed Erlang system is of the same Erlang/OTP release, - or from two different Erlang/OTP releases X and Y, where - <em>all</em> Y nodes have compatibility mode X.</p> + limits the emulator, making it possible for it to communicate + with Erlang nodes (as well as C- and Java nodes) running that + earlier release.</p> + <note> + <p>Ensure that all nodes (Erlang-, C-, and Java nodes) of + a distributed Erlang system is of the same Erlang/OTP release, + or from two different Erlang/OTP releases X and Y, where + <em>all</em> Y nodes have compatibility mode X.</p> + </note> </item> <tag><c><![CDATA[+r]]></c></tag> <item> - <p>Force ets memory block to be moved on realloc.</p> + <p>Forces ETS memory block to be moved on realloc.</p> </item> <tag><marker id="+rg"/><c><![CDATA[+rg ReaderGroupsLimit]]></c></tag> <item> - <p>Limits the amount of reader groups used by read/write locks - optimized for read operations in the Erlang runtime system. By - default the reader groups limit equals 64.</p> - <p>When the amount of schedulers is less than or equal to the reader - groups limit, each scheduler has its own reader group. When the - amount of schedulers is larger than the reader groups limit, - schedulers share reader groups. Shared reader groups degrades - read lock and read unlock performance while a large amount of - reader groups degrades write lock performance, so the limit is a - tradeoff between performance for read operations and performance - for write operations. Each reader group currently consumes 64 byte - in each read/write lock. Also note that a runtime system using - shared reader groups benefits from <seealso marker="#+sbt">binding - schedulers to logical processors</seealso>, since the reader groups - are distributed better between schedulers.</p> - </item> - <tag><marker id="+S"/><c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag> - <item> - <p>Sets the number of scheduler threads to create and scheduler - threads to set online when SMP support has been enabled. The maximum for - both values is 1024. If the Erlang runtime system is able to determine the - amount of logical processors configured and logical processors available, - <c>Schedulers</c> will default to logical processors configured, and - <c>SchedulersOnline</c> will default to logical processors available; - otherwise, the default values will be 1. <c>Schedulers</c> may be omitted - if <c>:SchedulerOnline</c> is not and vice versa. The number of schedulers - online can be changed at run time via - <seealso marker="erlang#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>. - </p> - <p>If <c>Schedulers</c> or <c>SchedulersOnline</c> is specified as a - negative number, the value is subtracted from the default number of - logical processors configured or logical processors available, respectively. - </p> - <p>Specifying the value 0 for <c>Schedulers</c> or <c>SchedulersOnline</c> - resets the number of scheduler threads or scheduler threads online respectively - to its default value. - </p> - <p>This option is ignored if the emulator doesn't have - SMP support enabled (see the <seealso marker="#smp">-smp</seealso> - flag).</p> - </item> - <tag><marker id="+SP"/><c><![CDATA[+SP SchedulersPercentage:SchedulersOnlinePercentage]]></c></tag> - <item> - <p>Similar to <seealso marker="#+S">+S</seealso> but uses percentages to set the - number of scheduler threads to create, based on logical processors configured, - and scheduler threads to set online, based on logical processors available, when - SMP support has been enabled. Specified values must be greater than 0. For example, - <c>+SP 50:25</c> sets the number of scheduler threads to 50% of the logical processors - configured and the number of scheduler threads online to 25% of the logical processors available. - <c>SchedulersPercentage</c> may be omitted if <c>:SchedulersOnlinePercentage</c> is - not and vice versa. The number of schedulers online can be changed at run time via - <seealso marker="erlang#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>. - </p> - <p>This option interacts with <seealso marker="#+S">+S</seealso> settings. - For example, on a system with 8 logical cores configured and 8 logical cores - available, the combination of the options <c>+S 4:4 +SP 50:25</c> (in either order) - results in 2 scheduler threads (50% of 4) and 1 scheduler thread online (25% of 4). - </p> - <p>This option is ignored if the emulator doesn't have - SMP support enabled (see the <seealso marker="#smp">-smp</seealso> - flag).</p> - </item> - <tag><marker id="+SDcpu"/><c><![CDATA[+SDcpu DirtyCPUSchedulers:DirtyCPUSchedulersOnline]]></c></tag> - <item> - <p>Sets the number of dirty CPU scheduler threads to create and dirty - CPU scheduler threads to set online when threading support has been - enabled. The maximum for both values is 1024, and each value is further - limited by the settings for normal schedulers: the number of dirty CPU - scheduler threads created cannot exceed the number of normal scheduler - threads created, and the number of dirty CPU scheduler threads online - cannot exceed the number of normal scheduler threads online (see the - <seealso marker="#+S">+S</seealso> and <seealso marker="#+SP">+SP</seealso> - flags for more details). By default, the number of dirty CPU scheduler - threads created equals the number of normal scheduler threads created, - and the number of dirty CPU scheduler threads online equals the number - of normal scheduler threads online. <c>DirtyCPUSchedulers</c> may be - omitted if <c>:DirtyCPUSchedulersOnline</c> is not and vice versa. The - number of dirty CPU schedulers online can be changed at run time via - <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>. - </p> - <p> - The amount of dirty CPU schedulers is limited by the amount of - normal schedulers in order to limit the effect on processes - executing on ordinary schedulers. If the amount of dirty CPU - schedulers was allowed to be unlimited, dirty CPU bound jobs would - potentially starve normal jobs. - </p> - <p>This option is ignored if the emulator doesn't have threading support - enabled. Currently, <em>this option is experimental</em> and is supported only - if the emulator was configured and built with support for dirty schedulers - enabled (it's disabled by default). - </p> - </item> - <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag> - <item> - <p>Similar to <seealso marker="#+SDcpu">+SDcpu</seealso> but uses percentages to set the - number of dirty CPU scheduler threads to create and number of dirty CPU scheduler threads - to set online when threading support has been enabled. Specified values must be greater - than 0. For example, <c>+SDPcpu 50:25</c> sets the number of dirty CPU scheduler threads - to 50% of the logical processors configured and the number of dirty CPU scheduler threads - online to 25% of the logical processors available. <c>DirtyCPUSchedulersPercentage</c> may - be omitted if <c>:DirtyCPUSchedulersOnlinePercentage</c> is not and vice versa. The - number of dirty CPU schedulers online can be changed at run time via - <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>. - </p> - <p>This option interacts with <seealso marker="#+SDcpu">+SDcpu</seealso> settings. - For example, on a system with 8 logical cores configured and 8 logical cores available, - the combination of the options <c>+SDcpu 4:4 +SDPcpu 50:25</c> (in either order) results - in 2 dirty CPU scheduler threads (50% of 4) and 1 dirty CPU scheduler thread online (25% of 4). - </p> - <p>This option is ignored if the emulator doesn't have threading support - enabled. Currently, <em>this option is experimental</em> and is supported only - if the emulator was configured and built with support for dirty schedulers - enabled (it's disabled by default). - </p> + <p>Limits the number of reader groups used by read/write locks + optimized for read operations in the Erlang runtime system. By + default the reader groups limit is 64.</p> + <p>When the number of schedulers is less than or equal to the reader + groups limit, each scheduler has its own reader group. When the + number of schedulers is larger than the reader groups limit, + schedulers share reader groups. Shared reader groups degrade + read lock and read unlock performance while many + reader groups degrade write lock performance. So, the limit is a + tradeoff between performance for read operations and performance + for write operations. Each reader group consumes 64 byte + in each read/write lock.</p> + <p>Notice that a runtime system using shared reader groups benefits from + <seealso marker="#+sbt">binding schedulers to logical + processors</seealso>, as the reader groups are distributed better + between schedulers.</p> + </item> + <tag><marker id="+S"/> + <c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag> + <item> + <p>Sets the number of scheduler threads to create and scheduler threads + to set online when SMP support has been enabled. The maximum for both + values is 1024. If the Erlang runtime system is able to determine the + number of logical processors configured and logical processors + available, <c>Schedulers</c> defaults to logical processors + configured, and <c>SchedulersOnline</c> defaults to logical processors + available; otherwise the default values are 1. <c>Schedulers</c> can + be omitted if <c>:SchedulerOnline</c> is not and conversely. The + number of schedulers online can be changed at runtime through + <seealso marker="erlang#system_flag_schedulers_online"> + <c>erlang:system_flag(schedulers_online, + SchedulersOnline)</c></seealso>.</p> + <p>If <c>Schedulers</c> or <c>SchedulersOnline</c> is specified as a + negative number, the value is subtracted from the default number of + logical processors configured or logical processors available, + respectively.</p> + <p>Specifying value <c>0</c> for <c>Schedulers</c> or + <c>SchedulersOnline</c> resets the number of scheduler threads or + scheduler threads online, respectively, to its default value.</p> + <p>This option is ignored if the emulator does not have SMP support + enabled (see flag <seealso marker="#smp"><c>-smp</c></seealso>).</p> + </item> + <tag><marker id="+SP"/><c><![CDATA[+SP + SchedulersPercentage:SchedulersOnlinePercentage]]></c></tag> + <item> + <p>Similar to <seealso marker="#+S"><c>+S</c></seealso> but uses + percentages to set the number of scheduler threads to create, based + on logical processors configured, and scheduler threads to set online, + based on logical processors available, when SMP support has been + enabled. Specified values must be > 0. For example, + <c>+SP 50:25</c> sets the number of scheduler threads to 50% of the + logical processors configured, and the number of scheduler threads + online to 25% of the logical processors available. + <c>SchedulersPercentage</c> can be omitted if + <c>:SchedulersOnlinePercentage</c> is not and conversely. The number + of schedulers online can be changed at runtime through + <seealso marker="erlang#system_flag_schedulers_online"> + <c>erlang:system_flag(schedulers_online, + SchedulersOnline)</c></seealso>.</p> + <p>This option interacts with <seealso marker="#+S"><c>+S</c></seealso> + settings. For example, on a system with 8 logical cores configured + and 8 logical cores available, the combination of the options + <c>+S 4:4 +SP 50:25</c> (in either order) results in 2 scheduler + threads (50% of 4) and 1 scheduler thread online (25% of 4).</p> + <p>This option is ignored if the emulator does not have SMP support + enabled (see flag <seealso marker="#smp"><c>-smp</c></seealso>).</p> + </item> + <tag><marker id="+SDcpu"/><c><![CDATA[+SDcpu + DirtyCPUSchedulers:DirtyCPUSchedulersOnline]]></c></tag> + <item> + <p>Sets the number of dirty CPU scheduler threads to create and dirty + CPU scheduler threads to set online when threading support has been + enabled. The maximum for both values is 1024, and each value is + further limited by the settings for normal schedulers:</p> + <list type="bulleted"> + <item>The number of dirty CPU scheduler threads created cannot exceed + the number of normal scheduler threads created.</item> + <item>The number of dirty CPU scheduler threads online cannot exceed + the number of normal scheduler threads online.</item> + </list> + <p>For details, see the <seealso marker="#+S"><c>+S</c></seealso> and + <seealso marker="#+SP"><c>+SP</c></seealso>. By default, the number + of dirty CPU scheduler threads created equals the number of normal + scheduler threads created, and the number of dirty CPU scheduler + threads online equals the number of normal scheduler threads online. + <c>DirtyCPUSchedulers</c> can be omitted if + <c>:DirtyCPUSchedulersOnline</c> is not and conversely. The number of + dirty CPU schedulers online can be changed at runtime through + <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online"> + <c>erlang:system_flag(dirty_cpu_schedulers_online, + DirtyCPUSchedulersOnline)</c></seealso>.</p> + <p>The amount of dirty CPU schedulers is limited by the amount of + normal schedulers in order to limit the effect on processes + executing on ordinary schedulers. If the amount of dirty CPU + schedulers was allowed to be unlimited, dirty CPU bound jobs would + potentially starve normal jobs.</p> + <p>This option is ignored if the emulator does not have threading + support enabled. <em>This option is experimental</em> and + is supported only if the emulator was configured and built with + support for dirty schedulers enabled (it is disabled by default).</p> + </item> + <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu + DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag> + <item> + <p>Similar to <seealso marker="#+SDcpu"><c>+SDcpu</c></seealso> but + uses percentages to set the number of dirty CPU scheduler threads to + create and the number of dirty CPU scheduler threads to set online + when threading support has been enabled. Specified values must be + > 0. For example, <c>+SDPcpu 50:25</c> sets the number of dirty + CPU scheduler threads to 50% of the logical processors configured + and the number of dirty CPU scheduler threads online to 25% of the + logical processors available. <c>DirtyCPUSchedulersPercentage</c> can + be omitted if <c>:DirtyCPUSchedulersOnlinePercentage</c> is not and + conversely. The number of dirty CPU schedulers online can be changed + at runtime through + <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online"> + <c>erlang:system_flag(dirty_cpu_schedulers_online, + DirtyCPUSchedulersOnline)</c></seealso>.</p> + <p>This option interacts with <seealso + marker="#+SDcpu"><c>+SDcpu</c></seealso> settings. For example, on a + system with 8 logical cores configured and 8 logical cores available, + the combination of the options <c>+SDcpu 4:4 +SDPcpu 50:25</c> (in + either order) results in 2 dirty CPU scheduler threads (50% of 4) and + 1 dirty CPU scheduler thread online (25% of 4).</p> + <p>This option is ignored if the emulator does not have threading + support enabled. <em>This option is experimental</em> and + is supported only if the emulator was configured and built with + support for dirty schedulers enabled (it is disabled by default).</p> </item> <tag><marker id="+SDio"/><c><![CDATA[+SDio DirtyIOSchedulers]]></c></tag> <item> - <p>Sets the number of dirty I/O scheduler threads to create when threading - support has been enabled. The valid range is 0-1024. By default, the number - of dirty I/O scheduler threads created is 10, same as the default number of - threads in the <seealso marker="#async_thread_pool_size">async thread pool - </seealso>. - </p> - <p> - The amount of dirty IO schedulers is not limited by the amount of - normal schedulers <seealso marker="#+SDcpu">like the amount of - dirty CPU schedulers</seealso>. This since only I/O bound work is - expected to execute on dirty I/O schedulers. If the user should schedule CPU - bound jobs on dirty I/O schedulers, these jobs might starve ordinary - jobs executing on ordinary schedulers. - </p> - <p>This option is ignored if the emulator doesn't have threading support - enabled. Currently, <em>this option is experimental</em> and is supported only - if the emulator was configured and built with support for dirty schedulers - enabled (it's disabled by default). - </p> + <p>Sets the number of dirty I/O scheduler threads to create when + threading support has been enabled. Valid range is 0-1024. By + default, the number of dirty I/O scheduler threads created is 10, + same as the default number of threads in the <seealso + marker="#async_thread_pool_size">async thread pool</seealso>.</p> + <p>The amount of dirty IO schedulers is not limited by the amount of + normal schedulers <seealso marker="#+SDcpu">like the amount of + dirty CPU schedulers</seealso>. This since only I/O bound work is + expected to execute on dirty I/O schedulers. If the user should schedule CPU + bound jobs on dirty I/O schedulers, these jobs might starve ordinary + jobs executing on ordinary schedulers.</p> + <p>This option is ignored if the emulator does not have threading + support enabled. <em>This option is experimental</em> and + is supported only if the emulator was configured and built with + support for dirty schedulers enabled (it is disabled by default).</p> </item> <tag><c><![CDATA[+sFlag Value]]></c></tag> <item> @@ -979,238 +995,237 @@ <taglist> <tag><marker id="+sbt"/><c>+sbt BindType</c></tag> <item> - <p>Set scheduler bind type.</p> - <p>Schedulers can also be bound using the - <seealso marker="#+stbt">+stbt</seealso> flag. The only difference - between these two flags is how the following errors are handled:</p> - <list> - <item>Binding of schedulers is not supported on the specific - platform.</item> - <item>No available CPU topology. That is the runtime system - was not able to automatically detected the CPU topology, and - no <seealso marker="#+sct">user defined CPU topology</seealso> - was set.</item> - </list> - <p>If any of these errors occur when <c>+sbt</c> has been passed, - the runtime system will print an error message, and refuse to - start. If any of these errors occur when <c>+stbt</c> has been - passed, the runtime system will silently ignore the error, and - start up using unbound schedulers.</p> - <p>Currently valid <c>BindType</c>s: - </p> - <taglist> - <tag><c>u</c></tag> - <item> - <p><c>unbound</c> - Schedulers will not be bound to logical - processors, i.e., the operating system decides where the - scheduler threads execute, and when to migrate them. This is - the default.</p> + <p>Sets scheduler bind type.</p> + <p>Schedulers can also be bound using flag + <seealso marker="#+stbt"><c>+stbt</c></seealso>. The only + difference between these two flags is how the following errors + are handled:</p> + <list type="bulleted"> + <item>Binding of schedulers is not supported on the specific + platform.</item> + <item>No available CPU topology. That is, the runtime system was + not able to detect the CPU topology automatically, and no + <seealso marker="#+sct">user-defined CPU topology</seealso> + was set.</item> + </list> + <p>If any of these errors occur when <c>+sbt</c> has been passed, + the runtime system prints an error message, and refuses to + start. If any of these errors occur when <c>+stbt</c> has been + passed, the runtime system silently ignores the error, and + start up using unbound schedulers.</p> + <p>Valid <c>BindType</c>s:</p> + <taglist> + <tag><c>u</c></tag> + <item><c>unbound</c> - Schedulers are not bound to logical + processors, that is, the operating system decides where the + scheduler threads execute, and when to migrate them. This is + the default. </item> - <tag><c>ns</c></tag> - <item> - <p><c>no_spread</c> - Schedulers with close scheduler - identifiers will be bound as close as possible in hardware.</p> + <tag><c>ns</c></tag> + <item><c>no_spread</c> - Schedulers with close scheduler + identifiers are bound as close as possible in hardware. </item> - <tag><c>ts</c></tag> - <item> - <p><c>thread_spread</c> - Thread refers to hardware threads - (e.g. Intel's hyper-threads). Schedulers with low scheduler - identifiers, will be bound to the first hardware thread of - each core, then schedulers with higher scheduler identifiers - will be bound to the second hardware thread of each core, - etc.</p> + <tag><c>ts</c></tag> + <item><c>thread_spread</c> - Thread refers to hardware threads + (such as Intel's hyper-threads). Schedulers with low scheduler + identifiers, are bound to the first hardware thread of + each core, then schedulers with higher scheduler identifiers + are bound to the second hardware thread of each core,and so on. </item> - <tag><c>ps</c></tag> - <item> - <p><c>processor_spread</c> - Schedulers will be spread like - <c>thread_spread</c>, but also over physical processor chips.</p> + <tag><c>ps</c></tag> + <item><c>processor_spread</c> - Schedulers are spread like + <c>thread_spread</c>, but also over physical processor chips. </item> - <tag><c>s</c></tag> - <item> - <p><c>spread</c> - Schedulers will be spread as much as - possible.</p> + <tag><c>s</c></tag> + <item><c>spread</c> - Schedulers are spread as much as possible. </item> - <tag><c>nnts</c></tag> - <item> - <p><c>no_node_thread_spread</c> - Like <c>thread_spread</c>, - but if multiple NUMA (Non-Uniform Memory Access) nodes exists, - schedulers will be spread over one NUMA node at a time, - i.e., all logical processors of one NUMA node will be bound - to schedulers in sequence.</p> + <tag><c>nnts</c></tag> + <item><c>no_node_thread_spread</c> - Like <c>thread_spread</c>, + but if multiple Non-Uniform Memory Access (NUMA) nodes exist, + schedulers are spread over one NUMA node at a time, + that is, all logical processors of one NUMA node are bound + to schedulers in sequence. </item> - <tag><c>nnps</c></tag> - <item> - <p><c>no_node_processor_spread</c> - Like - <c>processor_spread</c>, but if multiple NUMA nodes exists, - schedulers will be spread over one NUMA node at a time, i.e., - all logical processors of one NUMA node will be bound to - schedulers in sequence.</p> + <tag><c>nnps</c></tag> + <item><c>no_node_processor_spread</c> - Like + <c>processor_spread</c>, but if multiple NUMA nodes exist, + schedulers are spread over one NUMA node at a time, that is, + all logical processors of one NUMA node are bound to + schedulers in sequence. </item> - <tag><c>tnnps</c></tag> - <item> - <p><c>thread_no_node_processor_spread</c> - A combination of - <c>thread_spread</c>, and <c>no_node_processor_spread</c>. - Schedulers will be spread over hardware threads across NUMA - nodes, but schedulers will only be spread over processors - internally in one NUMA node at a time.</p> + <tag><c>tnnps</c></tag> + <item><c>thread_no_node_processor_spread</c> - A combination of + <c>thread_spread</c>, and <c>no_node_processor_spread</c>. + Schedulers are spread over hardware threads across NUMA + nodes, but schedulers are only spread over processors + internally in one NUMA node at a time. </item> - <tag><c>db</c></tag> - <item> - <p><c>default_bind</c> - Binds schedulers the default way. - Currently the default is <c>thread_no_node_processor_spread</c> - (which might change in the future).</p> + <tag><c>db</c></tag> + <item><c>default_bind</c> - Binds schedulers the default way. + Defaults to <c>thread_no_node_processor_spread</c> + (which can change in the future). </item> - </taglist> - <p>Binding of schedulers is currently only supported on newer - Linux, Solaris, FreeBSD, and Windows systems.</p> - <p>If no CPU topology is available when the <c>+sbt</c> flag - is processed and <c>BindType</c> is any other type than - <c>u</c>, the runtime system will fail to start. CPU - topology can be defined using the - <seealso marker="#+sct">+sct</seealso> flag. Note - that the <c>+sct</c> flag may have to be passed before the - <c>+sbt</c> flag on the command line (in case no CPU topology - has been automatically detected).</p> - <p>The runtime system will by default <em>not</em> bind schedulers - to logical processors. - </p> - <p><em>NOTE:</em> If the Erlang runtime system is the only operating system - process that binds threads to logical processors, this - improves the performance of the runtime system. However, - if other operating system processes (as for example - another Erlang runtime system) also bind threads to - logical processors, there might be a performance penalty - instead. In some cases this performance penalty might be - severe. If this is the case, you are advised to not - bind the schedulers.</p> + </taglist> + <p>Binding of schedulers is only supported on newer + Linux, Solaris, FreeBSD, and Windows systems.</p> + <p>If no CPU topology is available when flag <c>+sbt</c> + is processed and <c>BindType</c> is any other type than + <c>u</c>, the runtime system fails to start. CPU + topology can be defined using flag + <seealso marker="#+sct"><c>+sct</c></seealso>. Notice + that flag <c>+sct</c> can have to be passed before flag + <c>+sbt</c> on the command line (if no CPU topology + has been automatically detected).</p> + <p>The runtime system does by default <em>not</em> bind schedulers + to logical processors.</p> + <note> + <p>If the Erlang runtime system is the only operating system + process that binds threads to logical processors, this + improves the performance of the runtime system. However, + if other operating system processes (for example + another Erlang runtime system) also bind threads to + logical processors, there can be a performance penalty + instead. This performance penalty can sometimes be + severe. If so, you are advised not to + bind the schedulers.</p> + </note> <p>How schedulers are bound matters. For example, in - situations when there are fewer running processes than - schedulers online, the runtime system tries to migrate - processes to schedulers with low scheduler identifiers. - The more the schedulers are spread over the hardware, - the more resources will be available to the runtime - system in such situations. - </p> - <p> - <em>NOTE:</em> If a scheduler fails to bind, this - will often be silently ignored. This since it isn't always - possible to verify valid logical processor identifiers. If - an error is reported, it will be reported to the - <c>error_logger</c>. If you want to verify that the - schedulers actually have bound as requested, call - <seealso marker="erlang#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>. - </p> + situations when there are fewer running processes than + schedulers online, the runtime system tries to migrate + processes to schedulers with low scheduler identifiers. + The more the schedulers are spread over the hardware, + the more resources are available to the runtime + system in such situations.</p> + <note> + <p>If a scheduler fails to bind, this is + often silently ignored, as it is not always + possible to verify valid logical processor identifiers. If + an error is reported, it is reported to the + <c>error_logger</c>. If you want to verify that the + schedulers have bound as requested, call + <seealso marker="erlang#system_info_scheduler_bindings"> + <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p> + </note> </item> - <tag><marker id="+sbwt"/><c>+sbwt none|very_short|short|medium|long|very_long</c></tag> - <item> - <p>Set scheduler busy wait threshold. Default is <c>medium</c>. - The threshold determines how long schedulers should busy - wait when running out of work before going to sleep. - </p> - <p><em>NOTE:</em> This flag may be removed or changed at any time - without prior notice. - </p> - </item> - <tag><marker id="+scl"/><c>+scl true|false</c></tag> + <tag><marker id="+sbwt"/> + <c>+sbwt none|very_short|short|medium|long|very_long</c></tag> + <item> + <p>Sets scheduler busy wait threshold. Defaults to <c>medium</c>. + The threshold determines how long schedulers are to busy + wait when running out of work before going to sleep.</p> + <note> + <p>This flag can be removed or changed at any time + without prior notice.</p> + </note> + </item> +<tag><marker id="+scl"/><c>+scl true|false</c></tag> <item> - <p>Enable or disable scheduler compaction of load. By default - scheduler compaction of load is enabled. When enabled, load - balancing will strive for a load distribution which causes - as many scheduler threads as possible to be fully loaded (i.e., - not run out of work). This is accomplished by migrating load - (e.g. runnable processes) into a smaller set of schedulers - when schedulers frequently run out of work. When disabled, - the frequency with which schedulers run out of work will - not be taken into account by the load balancing logic. - <br/> <c>+scl false</c> is similar to - <seealso marker="#+sub">+sub true</seealso> with the difference - that <c>+sub true</c> also will balance scheduler utilization - between schedulers. - </p> + <p>Enables or disables scheduler compaction of load. By default + scheduler compaction of load is enabled. When enabled, load + balancing strives for a load distribution, which causes + as many scheduler threads as possible to be fully loaded (that is, + not run out of work). This is accomplished by migrating load + (for example, runnable processes) into a smaller set of schedulers + when schedulers frequently run out of work. When disabled, + the frequency with which schedulers run out of work is + not taken into account by the load balancing logic.</p> + <p><c>+scl false</c> is similar to + <seealso marker="#+sub"><c>+sub true</c></seealso>, but + <c>+sub true</c> also balances scheduler utilization + between schedulers.</p> </item> <tag><marker id="+sct"/><c>+sct CpuTopology</c></tag> <item> <list type="bulleted"> - <item><c><![CDATA[<Id> = integer(); when 0 =< <Id> =< 65535]]></c></item> + <item><c><![CDATA[<Id> = integer(); when 0 =< <Id> =< 65535]]></c> + </item> <item><c><![CDATA[<IdRange> = <Id>-<Id>]]></c></item> <item><c><![CDATA[<IdOrIdRange> = <Id> | <IdRange>]]></c></item> - <item><c><![CDATA[<IdList> = <IdOrIdRange>,<IdOrIdRange> | <IdOrIdRange>]]></c></item> + <item><c><![CDATA[<IdList> = <IdOrIdRange>,<IdOrIdRange> | + <IdOrIdRange>]]></c></item> <item><c><![CDATA[<LogicalIds> = L<IdList>]]></c></item> - <item><c><![CDATA[<ThreadIds> = T<IdList> | t<IdList>]]></c></item> + <item><c><![CDATA[<ThreadIds> = T<IdList> | t<IdList>]]></c> + </item> <item><c><![CDATA[<CoreIds> = C<IdList> | c<IdList>]]></c></item> - <item><c><![CDATA[<ProcessorIds> = P<IdList> | p<IdList>]]></c></item> + <item><c><![CDATA[<ProcessorIds> = P<IdList> | p<IdList>]]></c> + </item> <item><c><![CDATA[<NodeIds> = N<IdList> | n<IdList>]]></c></item> - <item><c><![CDATA[<IdDefs> = <LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds> | <LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c></item> - <item><c><![CDATA[CpuTopology = <IdDefs>:<IdDefs> | <IdDefs>]]></c></item> + <item><c><![CDATA[<IdDefs> = + <LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds> | + <LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c> + </item> + <item><c><![CDATA[CpuTopology = <IdDefs>:<IdDefs> | + <IdDefs>]]></c></item> </list> - <p>Set a user defined CPU topology. The user defined - CPU topology will override any automatically detected - CPU topology. The CPU topology is used when - <seealso marker="#+sbt">binding schedulers to logical - processors</seealso>. - </p> - <p>Upper-case letters signify real identifiers and lower-case - letters signify fake identifiers only used for description - of the topology. Identifiers passed as real identifiers may - be used by the runtime system when trying to access specific - hardware and if they are not correct the behavior is - undefined. Faked logical CPU identifiers are not accepted - since there is no point in defining the CPU topology without - real logical CPU identifiers. Thread, core, processor, and - node identifiers may be left out. If left out, thread id - defaults to <c>t0</c>, core id defaults to <c>c0</c>, - processor id defaults to <c>p0</c>, and node id will - be left undefined. Either each logical processor must - belong to one and only one NUMA node, or no logical - processors must belong to any NUMA nodes. - </p> - <p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s - are allowed.</p> - <p>NUMA node identifiers are system wide. That is, each NUMA - node on the system have to have a unique identifier. Processor - identifiers are also system wide. Core identifiers are - processor wide. Thread identifiers are core wide.</p> - <p>The order of the identifier types imply the hierarchy of the - CPU topology. Valid orders are either - <c><![CDATA[<LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds>]]></c>, - or - <c><![CDATA[<LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>. - That is, thread is part of a core which is part of a processor - which is part of a NUMA node, or thread is part of a core which - is part of a NUMA node which is part of a processor. A cpu - topology can consist of both processor external, and processor - internal NUMA nodes as long as each logical processor belongs - to one and only one NUMA node. If <c><![CDATA[<ProcessorIds>]]></c> - is left out, its default position will be before - <c><![CDATA[<NodeIds>]]></c>. That is, the default is - processor external NUMA nodes. - </p> - <p>If a list of identifiers is used in an - <c><![CDATA[<IdDefs>]]></c>:</p> - <list type="bulleted"> - <item><c><![CDATA[<LogicalIds>]]></c> have to be a list - of identifiers.</item> - <item>At least one other identifier type apart from - <c><![CDATA[<LogicalIds>]]></c> also have to have a - list of identifiers.</item> - <item>All lists of identifiers have to produce the - same amount of identifiers.</item> - </list> - <p>A simple example. A single quad core processor may be - described this way:</p> + <p>Sets a user-defined CPU topology. The user-defined + CPU topology overrides any automatically detected + CPU topology. The CPU topology is used when + <seealso marker="#+sbt">binding schedulers to logical + processors</seealso>.</p> + <p>Uppercase letters signify real identifiers and lowercase + letters signify fake identifiers only used for description + of the topology. Identifiers passed as real identifiers can + be used by the runtime system when trying to access specific + hardware; if they are incorrect the behavior is + undefined. Faked logical CPU identifiers are not accepted, + as there is no point in defining the CPU topology without + real logical CPU identifiers. Thread, core, processor, and + node identifiers can be omitted. If omitted, the thread ID + defaults to <c>t0</c>, the core ID defaults to <c>c0</c>, + the processor ID defaults to <c>p0</c>, and the node ID is + left undefined. Either each logical processor must + belong to only one NUMA node, or no logical + processors must belong to any NUMA nodes.</p> + <p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s + are allowed.</p> + <p>NUMA node identifiers are system wide. That is, each NUMA + node on the system must have a unique identifier. Processor + identifiers are also system wide. Core identifiers are + processor wide. Thread identifiers are core wide.</p> + <p>The order of the identifier types implies the hierarchy of the + CPU topology. The valid orders are as follows:</p> + <list type="bulleted"> + <item> + <p><c><![CDATA[<LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds>]]></c>, + that is, thread is part of a core that is part of a processor, + which is part of a NUMA node.</p> + </item> + <item> + <p><c><![CDATA[<LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>, + that is, thread is part of a core that is part of a NUMA node, + which is part of a processor.</p> + </item> + </list> + <p>A CPU topology can consist of both processor external, and + processor internal NUMA nodes as long as each logical processor + belongs to only one NUMA node. If + <c><![CDATA[<ProcessorIds>]]></c> is omitted, its default position + is before <c><![CDATA[<NodeIds>]]></c>. That is, the default is + processor external NUMA nodes.</p> + <p>If a list of identifiers is used in an + <c><![CDATA[<IdDefs>]]></c>:</p> + <list type="bulleted"> + <item><c><![CDATA[<LogicalIds>]]></c> must be a list + of identifiers.</item> + <item>At least one other identifier type besides + <c><![CDATA[<LogicalIds>]]></c> must also have a + list of identifiers.</item> + <item>All lists of identifiers must produce the + same number of identifiers.</item> + </list> + <p>A simple example. A single quad core processor can be + described as follows:</p> <pre> % <input>erl +sct L0-3c0-3</input> 1> <input>erlang:system_info(cpu_topology).</input> [{processor,[{core,{logical,0}}, {core,{logical,1}}, {core,{logical,2}}, - {core,{logical,3}}]}] -</pre> - <p>A little more complicated example. Two quad core - processors. Each processor in its own NUMA node. - The ordering of logical processors is a little weird. - This in order to give a better example of identifier - lists:</p> + {core,{logical,3}}]}]</pre> + <p>A more complicated example with two quad core + processors, each processor in its own NUMA node. + The ordering of logical processors is a bit weird. + This to give a better example of identifier lists:</p> <pre> % <input>erl +sct L0-1,3-2c0-3p0N0:L7,4,6-5c0-3p1N1</input> 1> <input>erlang:system_info(cpu_topology).</input> @@ -1221,239 +1236,247 @@ {node,[{processor,[{core,{logical,7}}, {core,{logical,4}}, {core,{logical,6}}, - {core,{logical,5}}]}]}] -</pre> - <p>As long as real identifiers are correct it is okay - to pass a CPU topology that is not a correct - description of the CPU topology. When used with - care this can actually be very useful. This in - order to trick the emulator to bind its schedulers - as you want. For example, if you want to run multiple - Erlang runtime systems on the same machine, you - want to reduce the amount of schedulers used and - manipulate the CPU topology so that they bind to - different logical CPUs. An example, with two Erlang - runtime systems on a quad core machine:</p> + {core,{logical,5}}]}]}]</pre> + <p>As long as real identifiers are correct, it is OK + to pass a CPU topology that is not a correct + description of the CPU topology. When used with + care this can be very useful. This + to trick the emulator to bind its schedulers + as you want. For example, if you want to run multiple + Erlang runtime systems on the same machine, you + want to reduce the number of schedulers used and + manipulate the CPU topology so that they bind to + different logical CPUs. An example, with two Erlang + runtime systems on a quad core machine:</p> <pre> % <input>erl +sct L0-3c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname one</input> -% <input>erl +sct L3-0c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname two</input> -</pre> - <p>In this example each runtime system have two - schedulers each online, and all schedulers online - will run on different cores. If we change to one - scheduler online on one runtime system, and three - schedulers online on the other, all schedulers - online will still run on different cores.</p> - <p>Note that a faked CPU topology that does not reflect - how the real CPU topology looks like is likely to - decrease the performance of the runtime system.</p> - <p>For more information, see - <seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p> +% <input>erl +sct L3-0c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname two</input></pre> + <p>In this example, each runtime system have two + schedulers each online, and all schedulers online + will run on different cores. If we change to one + scheduler online on one runtime system, and three + schedulers online on the other, all schedulers + online will still run on different cores.</p> + <p>Notice that a faked CPU topology that does not reflect + how the real CPU topology looks like is likely to + decrease the performance of the runtime system.</p> + <p>For more information, see + <seealso marker="erlang#system_info_cpu_topology"> + <c>erlang:system_info(cpu_topology)</c></seealso>.</p> </item> <tag><marker id="+secio"/><c>+secio true|false</c></tag> <item> - <p>Enable or disable eager check I/O scheduling. The default - is currently <c>true</c>. The default was changed from <c>false</c> - to <c>true</c> as of erts version 7.0. The behaviour before this - flag was introduced corresponds to <c>+secio false</c>.</p> - <p>The flag effects when schedulers will check for I/O - operations possible to execute, and when such I/O operations - will execute. As the name of the parameter implies, - schedulers will be more eager to check for I/O when - <c>true</c> is passed. This however also implies that - execution of outstanding I/O operation will not be - prioritized to the same extent as when <c>false</c> is - passed.</p> - <p><seealso marker="erlang#system_info_eager_check_io"><c>erlang:system_info(eager_check_io)</c></seealso> - returns the value of this parameter used when starting the VM.</p> + <p>Enables or disables eager check I/O scheduling. Defaults + to <c>true</c>. The default was changed from <c>false</c> + as from ERTS 7.0. The behavior before this + flag was introduced corresponds to <c>+secio false</c>.</p> + <p>The flag effects when schedulers will check for I/O + operations possible to execute, and when such I/O operations + will execute. As the parameter name implies, + schedulers are more eager to check for I/O when + <c>true</c> is passed. This, however, also implies that + execution of outstanding I/O operation is not + prioritized to the same extent as when <c>false</c> is + passed.</p> + <p><seealso marker="erlang#system_info_eager_check_io"> + <c>erlang:system_info(eager_check_io)</c></seealso> + returns the value of this parameter used when starting + the virtual machine.</p> </item> <tag><marker id="+sfwi"/><c>+sfwi Interval</c></tag> <item> - <p>Set scheduler forced wakeup interval. All run queues will - be scanned each <c>Interval</c> milliseconds. While there are - sleeping schedulers in the system, one scheduler will be woken - for each non-empty run queue found. An <c>Interval</c> of zero - disables this feature, which also is the default. - </p> - <p>This feature has been introduced as a temporary workaround - for long-executing native code, and native code that does not - bump reductions properly in OTP. When these bugs have be fixed - the <c>+sfwi</c> flag will be removed. - </p> - </item> + <p>Sets scheduler-forced wakeup interval. All run queues are + scanned each <c>Interval</c> milliseconds. While there are + sleeping schedulers in the system, one scheduler is woken + for each non-empty run queue found. <c>Interval</c> default + to <c>0</c>, meaning this feature is disabled.</p> + <note> + <p>This feature has been introduced as a temporary workaround + for long-executing native code, and native code that does not + bump reductions properly in OTP. When these bugs have be fixed, + this flag will be removed.</p> + </note> + </item> + <tag><marker id="+spp"/><c>+spp Bool</c></tag> + <item> + <p>Sets default scheduler hint for port parallelism. If set to + <c>true</c>, the virtual machine schedules port tasks when it + improves parallelism in the system. If set to <c>false</c>, the + virtual machine tries to perform port tasks immediately, + improving latency at the expense of parallelism. Default to + <c>false</c>. The default used can be inspected in runtime by + calling <seealso marker="erlang#system_info_port_parallelism"> + <c>erlang:system_info(port_parallelism)</c></seealso>. + The default can be overridden on port creation by passing option + <seealso marker="erlang#open_port_parallelism"> + <c>parallelism</c></seealso> to + <seealso marker="erlang#open_port/2"> + <c>erlang:open_port/2</c></seealso></p>. + </item> + <tag><marker id="sched_thread_stack_size"/> + <c><![CDATA[+sss size]]></c></tag> + <item> + <p>Suggested stack size, in kilowords, for scheduler threads. + Valid range is 4-8192 kilowords. The default stack size is + OS-dependent.</p> + </item> <tag><marker id="+stbt"/><c>+stbt BindType</c></tag> <item> - <p>Try to set scheduler bind type. The same as the - <seealso marker="#+sbt">+sbt</seealso> flag with the exception of - how some errors are handled. For more information, see the - documentation of the <seealso marker="#+sbt">+sbt</seealso> flag. - </p> - </item> + <p>Tries to set the scheduler bind type. The same as flag + <seealso marker="#+sbt"><c>+sbt</c></seealso> except + how some errors are handled. For more information, see + <seealso marker="#+sbt"><c>+sbt</c></seealso>.</p> + </item> <tag><marker id="+sub"/><c>+sub true|false</c></tag> <item> - <p>Enable or disable - <seealso marker="erts:erlang#statistics_scheduler_wall_time">scheduler - utilization</seealso> balancing of load. By default scheduler - utilization balancing is disabled and instead scheduler - compaction of load is enabled which will strive for a load - distribution which causes as many scheduler threads as possible - to be fully loaded (i.e., not run out of work). When scheduler - utilization balancing is enabled the system will instead try to - balance scheduler utilization between schedulers. That is, - strive for equal scheduler utilization on all schedulers. - <br/> <c>+sub true</c> is only supported on - systems where the runtime system detects and uses a monotonically - increasing high resolution clock. On other systems, the runtime - system will fail to start. - <br/> <c>+sub true</c> implies - <seealso marker="#+scl">+scl false</seealso>. The difference - between <c>+sub true</c> and <c>+scl false</c> is that - <c>+scl false</c> will not try to balance the scheduler - utilization. - </p> + <p>Enables or disables + <seealso marker="erts:erlang#statistics_scheduler_wall_time"> + scheduler utilization</seealso> balancing of load. By default + scheduler utilization balancing is disabled and instead scheduler + compaction of load is enabled, which strives for a load + distribution that causes as many scheduler threads as possible + to be fully loaded (that is, not run out of work). When scheduler + utilization balancing is enabled, the system instead tries to + balance scheduler utilization between schedulers. That is, + strive for equal scheduler utilization on all schedulers.</p> + <p><c>+sub true</c> is only supported on systems where the runtime + system detects and uses a monotonically increasing high-resolution + clock. On other systems, the runtime system fails to start.</p> + <p><c>+sub true</c> implies <seealso marker="#+scl"> + <c>+scl false</c></seealso>. The difference between + <c>+sub true</c> and <c>+scl false</c> is that <c>+scl false</c> + does not try to balance the scheduler utilization.</p> </item> - <tag><marker id="+swct"/><c>+swct very_eager|eager|medium|lazy|very_lazy</c></tag> - <item> - <p> - Set scheduler wake cleanup threshold. Default is <c>medium</c>. - This flag controls how eager schedulers should be requesting - wake up due to certain cleanup operations. When a lazy setting - is used, more outstanding cleanup operations can be left undone - while a scheduler is idling. When an eager setting is used, - schedulers will more frequently be woken, potentially increasing - CPU-utilization. - </p> - <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice. - </p> - </item> - <tag><marker id="+sws"/><c>+sws default|legacy</c></tag> - <item> - <p> - Set scheduler wakeup strategy. Default strategy changed in erts-5.10/OTP-R16A. This strategy was previously known as <c>proposal</c> in OTP-R15. The <c>legacy</c> strategy was used as default from R13 up to and including R15. - </p> - <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice. - </p> - </item> - <tag><marker id="+swt"/><c>+swt very_low|low|medium|high|very_high</c></tag> - <item> - <p>Set scheduler wakeup threshold. Default is <c>medium</c>. - The threshold determines when to wake up sleeping schedulers - when more work than can be handled by currently awake schedulers - exist. A low threshold will cause earlier wakeups, and a high - threshold will cause later wakeups. Early wakeups will - distribute work over multiple schedulers faster, but work will - more easily bounce between schedulers. - </p> - <p><em>NOTE:</em> This flag may be removed or changed at any time - without prior notice. - </p> - </item> - <tag><marker id="+spp"/><c>+spp Bool</c></tag> + <tag><marker id="+swct"/> + <c>+swct very_eager|eager|medium|lazy|very_lazy</c></tag> <item> - <p>Set default scheduler hint for port parallelism. If set to - <c>true</c>, the VM will schedule port tasks when doing so will - improve parallelism in the system. If set to <c>false</c>, the VM - will try to perform port tasks immediately, improving latency at the - expense of parallelism. If this flag has not been passed, the - default scheduler hint for port parallelism is currently - <c>false</c>. The default used can be inspected in runtime by - calling <seealso - marker="erlang#system_info_port_parallelism">erlang:system_info(port_parallelism)</seealso>. - The default can be overriden on port creation by passing the - <seealso marker="erlang#open_port_parallelism">parallelism</seealso> - option to <seealso - marker="erlang#open_port/2">open_port/2</seealso></p>. - </item> - <tag><marker id="sched_thread_stack_size"/><c><![CDATA[+sss size]]></c></tag> - <item> - <p>Suggested stack size, in kilowords, for scheduler threads. - Valid range is 4-8192 kilowords. The default stack size - is OS dependent.</p> - </item> + <p>Sets scheduler wake cleanup threshold. Defaults to <c>medium</c>. + Controls how eager schedulers are to be requesting + wakeup because of certain cleanup operations. When a lazy setting + is used, more outstanding cleanup operations can be left undone + while a scheduler is idling. When an eager setting is used, + schedulers are more frequently woken, potentially increasing + CPU-utilization.</p> + <note> + <p>This flag can be removed or changed at any time without prior + notice.</p> + </note> + </item> + <tag><marker id="+sws"/><c>+sws default|legacy</c></tag> + <item> + <p>Sets scheduler wakeup strategy. Default strategy changed in + ERTS 5.10 (Erlang/OTP R16A). This strategy was known as + <c>proposal</c> in Erlang/OTP R15. The <c>legacy</c> strategy + was used as default from R13 up to and including R15.</p> + <note> + <p>This flag can be removed or changed at any time without prior + notice.</p> + </note> + </item> + <tag><marker id="+swt"/> + <c>+swt very_low|low|medium|high|very_high</c></tag> + <item> + <p>Sets scheduler wakeup threshold. Defaults to <c>medium</c>. + The threshold determines when to wake up sleeping schedulers + when more work than can be handled by currently awake schedulers + exists. A low threshold causes earlier wakeups, and a high + threshold causes later wakeups. Early wakeups distribute work + over multiple schedulers faster, but work does more easily bounce + between schedulers.</p> + <note> + <p>This flag can be removed or changed at any time without prior + notice.</p> + </note> + </item> </taglist> </item> <tag><marker id="+t"/><c><![CDATA[+t size]]></c></tag> <item> - <p>Set the maximum number of atoms the VM can handle. Default is 1048576.</p> + <p>Sets the maximum number of atoms the virtual machine can handle. + Defaults to 1,048,576.</p> </item> <tag><marker id="+T"/><c><![CDATA[+T Level]]></c></tag> <item> - <p>Enables modified timing and sets the modified timing level. - Currently valid range is 0-9. The timing of the runtime system - will change. A high level usually means a greater change than - a low level. Changing the timing can be very useful for finding - timing related bugs.</p> - <p>Currently, modified timing affects the following:</p> + <p>Enables modified timing and sets the modified timing level. Valid + range is 0-9. The timing of the runtime system is changed. A high + level usually means a greater change than a low level. Changing the + timing can be very useful for finding timing-related bugs.</p> + <p>Modified timing affects the following:</p> <taglist> <tag>Process spawning</tag> - <item> - <p>A process calling <c><![CDATA[spawn]]></c>, <c><![CDATA[spawn_link]]></c>, - <c><![CDATA[spawn_monitor]]></c>, or <c><![CDATA[spawn_opt]]></c> will be scheduled - out immediately after completing the call. When higher modified - timing levels are used, the caller will also sleep for a while - after being scheduled out.</p> + <item>A process calling <c><![CDATA[spawn]]></c>, + <c><![CDATA[spawn_link]]></c>, <c><![CDATA[spawn_monitor]]></c>, + or <c><![CDATA[spawn_opt]]></c> is scheduled out immediately + after completing the call. When higher modified timing levels are + used, the caller also sleeps for a while after it is scheduled out. </item> <tag>Context reductions</tag> - <item>The amount of reductions a process is a allowed to - use before being scheduled out is increased or reduced.</item> + <item>The number of reductions a process is allowed to use before it + is scheduled out is increased or reduced. + </item> <tag>Input reductions</tag> - <item>The amount of reductions performed before checking I/O - is increased or reduced.</item> + <item>The number of reductions performed before checking I/O is + increased or reduced. + </item> </taglist> - <p><em>NOTE:</em> Performance will suffer when modified timing - is enabled. This flag is <em>only</em> intended for testing and - debugging. Also note that <c><![CDATA[return_to]]></c> and <c><![CDATA[return_from]]></c> - trace messages will be lost when tracing on the spawn BIFs. This - flag may be removed or changed at any time without prior notice.</p> - </item> - <tag><c><![CDATA[+V]]></c></tag> - <item> - <p>Makes the emulator print out its version number.</p> + <note> + <p>Performance suffers when modified timing is enabled. This flag is + <em>only</em> intended for testing and debugging.</p> + <p><c><![CDATA[return_to]]></c> and <c><![CDATA[return_from]]></c> + trace messages are lost when tracing on the spawn BIFs.</p> + <p>This flag can be removed or changed at any time without prior + notice.</p> + </note> </item> <tag><c><![CDATA[+v]]></c></tag> <item> <p>Verbose.</p> </item> + <tag><c><![CDATA[+V]]></c></tag> + <item> + <p>Makes the emulator print its version number.</p> + </item> <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 (<c><![CDATA[+W e]]></c>), - warnings (<c><![CDATA[+W w]]></c>), or info reports - (<c><![CDATA[+W i]]></c>). The default is warnings. + <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 to errors + (<c><![CDATA[+W e]]></c>), warnings (<c><![CDATA[+W w]]></c>), or + information reports (<c><![CDATA[+W i]]></c>). Defaults to 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> + <c><![CDATA[error_logger:warning_map/0]]></c>. For more information, + see <seealso marker="kernel:error_logger#warning_map/0"> + <c>error_logger:warning_map/0</c></seealso> in Kernel.</p> </item> <tag><c><![CDATA[+zFlag Value]]></c></tag> <item> - <p>Miscellaneous flags.</p> + <p>Miscellaneous flags:</p> <taglist> <tag><marker id="+zdbbl"/><c>+zdbbl size</c></tag> <item> - <p>Set the distribution buffer busy limit - (<seealso marker="erlang#system_info_dist_buf_busy_limit">dist_buf_busy_limit</seealso>) - in kilobytes. Valid range is 1-2097151. Default is 1024.</p> - <p>A larger buffer limit will allow processes to buffer - more outgoing messages over the distribution. When the - buffer limit has been reached, sending processes will be - suspended until the buffer size has shrunk. The buffer - limit is per distribution channel. A higher limit will - give lower latency and higher throughput at the expense - of higher memory usage.</p> + <p>Sets the distribution buffer busy limit + (<seealso marker="erlang#system_info_dist_buf_busy_limit"> + <c>dist_buf_busy_limit</c></seealso>) + in kilobytes. Valid range is 1-2097151. Defaults to 1024.</p> + <p>A larger buffer limit allows processes to buffer + more outgoing messages over the distribution. When the + buffer limit has been reached, sending processes will be + suspended until the buffer size has shrunk. The buffer + limit is per distribution channel. A higher limit + gives lower latency and higher throughput at the expense + of higher memory use.</p> </item> <tag><marker id="+zdntgc"/><c>+zdntgc time</c></tag> <item> - <p>Set the delayed node table garbage collection time - (<seealso marker="erlang#system_info_delayed_node_table_gc">delayed_node_table_gc</seealso>) - in seconds. Valid values are either <c>infinity</c> or - an integer in the range [0-100000000]. Default is 60.</p> - <p>Node table entries that are not referred will linger - in the table for at least the amount of time that this - parameter determines. The lingering prevents repeated - deletions and insertions in the tables from occurring. - </p> + <p>Sets the delayed node table garbage collection time + (<seealso marker="erlang#system_info_delayed_node_table_gc"> + <c>delayed_node_table_gc</c></seealso>) + in seconds. Valid values are either <c>infinity</c> or + an integer in the range 0-100000000. Defaults to 60.</p> + <p>Node table entries that are not referred linger + in the table for at least the amount of time that this + parameter determines. The lingering prevents repeated + deletions and insertions in the tables from occurring.</p> </item> </taglist> </item> @@ -1462,104 +1485,95 @@ <section> <marker id="environment_variables"></marker> - <title>Environment variables</title> + <title>Environment Variables</title> <taglist> <tag><c><![CDATA[ERL_CRASH_DUMP]]></c></tag> <item> <p>If the emulator needs to write a crash dump, the value of this - variable will be the file name of the crash dump file. - If the variable is not set, the name of the crash dump file will - be <c><![CDATA[erl_crash.dump]]></c> in the current directory.</p> + variable is the filename of the crash dump file. + If the variable is not set, the name of the crash dump file is + <c><![CDATA[erl_crash.dump]]></c> in the current directory.</p> </item> <tag><c><![CDATA[ERL_CRASH_DUMP_NICE]]></c></tag> <item> - <p><em>Unix systems</em>: If the emulator needs to write a crash dump, - it will use the value of this variable to set the nice value - for the process, thus lowering its priority. The allowable range is - 1 through 39 (higher values will be replaced with 39). The highest - value, 39, will give the process the lowest priority.</p> + <p><em>Unix systems</em>: If the emulator needs to write a crash dump, + it uses the value of this variable to set the nice value + for the process, thus lowering its priority. Valid range is + 1-39 (higher values are replaced with 39). The highest + value, 39, gives the process the lowest priority.</p> </item> <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c></tag> <item> - <p><em>Unix systems</em>: This variable gives the number of seconds that - the emulator will be allowed to spend writing a crash dump. When - the given number of seconds have elapsed, the emulator will be - terminated by a SIGALRM signal.</p> - - <p> If the environment variable is <em>not</em> set or it is set to zero seconds, <c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>, - the runtime system will not even attempt to write the crash dump file. It will just terminate. - </p> - <p> If the environment variable is set to negative valie, e.g. <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>, - the runtime system will wait indefinitely for the crash dump file to be written. - </p> - <p> This environment variable is used in conjuction with - <seealso marker="kernel:heart"><c>heart</c></seealso> if <c>heart</c> is running: - </p> - <taglist> - <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag> - <item><p> - Suppresses the writing a crash dump file entirely, - thus rebooting the runtime system immediately. - This is the same as not setting the environment variable. - </p> - </item> - <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag> - <item><p>Setting the environment variable to a negative value will cause the - termination of the runtime system to wait until the crash dump file - has been completly written. - </p> - </item> - <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag> - <item><p> - Will wait for <c>S</c> seconds to complete the crash dump file and - then terminate the runtime system. - </p> - </item> - </taglist> + <p><em>Unix systems</em>: This variable gives the number of seconds + that the emulator is allowed to spend writing a crash dump. When the + given number of seconds have elapsed, the emulator is terminated by a + <c>SIGALRM</c> signal.</p> + <p>If the variable is <em>not</em> set or set to <c>0</c> seconds + (<c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>), the runtime system does + not even attempt to write the crash dump file. It only terminates.</p> + <p>If the variable is set to negative value, such as + <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>, the runtime system + waits indefinitely for the crash dump file to be written.</p> + <p>This variable is used with <seealso marker="kernel:heart"> + <c>heart(3)</c></seealso> if <c>heart</c> is running:</p> + <taglist> + <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag> + <item>Suppresses the writing a crash dump file entirely, thus + rebooting the runtime system immediately. This is the same as not + setting the environment variable. + </item> + <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag> + <item>Setting the environment variable to a negative value causes the + termination of the runtime system to wait until the crash dump file + has been completly written. + </item> + <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag> + <item>Waits for <c>S</c> seconds to complete the crash dump file and + then terminates the runtime system. + </item> + </taglist> </item> <tag><marker id="ERL_AFLAGS"/><c><![CDATA[ERL_AFLAGS]]></c></tag> <item> - <p>The content of this environment variable will be added to the - beginning of the command line for <c><![CDATA[erl]]></c>.</p> - <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends - at the end of the environment variable content. Arguments - following an <c><![CDATA[-extra]]></c> flag are moved on the command line into - the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line - following after an <c><![CDATA[-extra]]></c> flag.</p> - </item> - <tag><marker id="ERL_ZFLAGS"/><c><![CDATA[ERL_ZFLAGS]]></c> and <marker id="ERL_FLAGS"/><c><![CDATA[ERL_FLAGS]]></c></tag> - <item> - <p>The content of these environment variables will be added to the - end of the command line for <c><![CDATA[erl]]></c>.</p> - <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends - at the end of the environment variable content. Arguments - following an <c><![CDATA[-extra]]></c> flag are moved on the command line into - the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line - following after an <c><![CDATA[-extra]]></c> flag.</p> + <p>The content of this variable is added to the beginning of the + command line for <c><![CDATA[erl]]></c>.</p> + <p>Flag <c><![CDATA[-extra]]></c> is treated in a special way. Its + scope ends at the end of the environment variable content. Arguments + following an <c><![CDATA[-extra]]></c> flag are moved on the command + line into section <c><![CDATA[-extra]]></c>, that is, the end of the + command line following an <c><![CDATA[-extra]]></c> flag.</p> + </item> + <tag><marker id="ERL_ZFLAGS"/><c><![CDATA[ERL_ZFLAGS]]></c> and + <marker id="ERL_FLAGS"/><c><![CDATA[ERL_FLAGS]]></c></tag> + <item> + <p>The content of these variables are added to the end of the command + line for <c><![CDATA[erl]]></c>.</p> + <p>Flag <c><![CDATA[-extra]]></c> is treated in a special way. Its + scope ends at the end of the environment variable content. Arguments + following an <c><![CDATA[-extra]]></c> flag are moved on the command + line into section <c><![CDATA[-extra]]></c>, that is, the end of the + command line following an <c><![CDATA[-extra]]></c> flag.</p> </item> <tag><c><![CDATA[ERL_LIBS]]></c></tag> <item> - <p>This environment variable contains a list of additional library - directories that the code server will search for applications and - add to the code path. - See <seealso marker="kernel:code">code(3)</seealso>.</p> + <p>Contains a list of additional library directories that the code + server searches for applications and adds to the code path; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </item> <tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag> <item> - <p>This environment variable may be set to a comma-separated - list of IP addresses, in which case the - <seealso marker="epmd">epmd</seealso> daemon - will listen only on the specified address(es) and on the - loopback address (which is implicitly added to the list if it - has not been specified).</p> + <p>Can be set to a comma-separated list of IP addresses, in which case + the <seealso marker="epmd"><c>epmd</c></seealso> daemon listens only + on the specified address(es) and on the loopback address (which is + implicitly added to the list if it has not been specified).</p> </item> <tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag> <item> - <p>This environment variable can contain the port number to use when - communicating with <seealso marker="epmd">epmd</seealso>. The default - port will work fine in most cases. A different port can be specified + <p>Can contain the port number to use when communicating with + <seealso marker="epmd"><c>epmd</c></seealso>. The default port works + fine in most cases. A different port can be specified to allow nodes of independent clusters to co-exist on the same host. - All nodes in a cluster must use the same epmd port number.</p> + All nodes in a cluster must use the same <c>epmd</c> port number.</p> </item> </taglist> </section> @@ -1567,59 +1581,64 @@ <section> <marker id="configuration"></marker> <title>Configuration</title> - <p>The standard Erlang/OTP system can be re-configured to change the default - behavior on start-up.</p> + <p>The standard Erlang/OTP system can be reconfigured to change the default + behavior on startup.</p> <taglist> - <tag>The .erlang Start-up File</tag> - <item> - <p>When Erlang/OTP is started, the system searches for a file named .erlang - in the directory where Erlang/OTP is started. If not found, the user's home - directory is searched for an .erlang file.</p> - <p>If an .erlang file is found, it is assumed to contain valid Erlang expressions. - These expressions are evaluated as if they were input to the shell.</p> - <p>A typical .erlang file contains a set of search paths, for example:</p> - <code type="none"><![CDATA[ - io:format("executing user profile in HOME/.erlang\n",[]). - code:add_path("/home/calvin/test/ebin"). - code:add_path("/home/hobbes/bigappl-1.2/ebin"). - io:format(".erlang rc finished\n",[]). - ]]></code> - </item> - <tag>user_default and shell_default</tag> - <item> - <p>Functions in the shell which are not prefixed by a module name are assumed - to be functional objects (Funs), built-in functions (BIFs), or belong to the - module user_default or shell_default.</p> - <p>To include private shell commands, define them in a module user_default and - add the following argument as the first line in the .erlang file.</p> + <tag>The <c>.erlang</c> startup file</tag> + <item> + <p>When Erlang/OTP is started, the system searches for a file named + <c>.erlang</c> in the directory where Erlang/OTP is started. If not + found, the user's home directory is searched for an <c>.erlang</c> + file.</p> + <p>If an <c>.erlang</c> file is found, it is assumed to contain valid + Erlang expressions. These expressions are evaluated as if they were + input to the shell.</p> + <p>A typical <c>.erlang</c> file contains a set of search paths, for + example:</p> <code type="none"><![CDATA[ - code:load_abs("..../user_default"). - ]]></code> - </item> - <tag>erl</tag> - <item> - <p>If the contents of .erlang are changed and a private version of - user_default is defined, it is possible to customize the Erlang/OTP environment. - More powerful changes can be made by supplying command line arguments in the - start-up script erl. Refer to erl(1) and <seealso marker="init">init(3)</seealso> - for further information.</p> - </item> +io:format("executing user profile in HOME/.erlang\n",[]). +code:add_path("/home/calvin/test/ebin"). +code:add_path("/home/hobbes/bigappl-1.2/ebin"). +io:format(".erlang rc finished\n",[]). ]]></code> + </item> + <tag>user_default and shell_default</tag> + <item> + <p>Functions in the shell that are not prefixed by a module name are + assumed to be functional objects (funs), built-in functions (BIFs), + or belong to the module <c>user_default</c> or + <c>shell_default</c>.</p> + <p>To include private shell commands, define them in a module + <c>user_default</c> and add the following argument as the first line + in the <c>.erlang</c> file:</p> + <code type="none"><![CDATA[ +code:load_abs("..../user_default"). ]]></code> + </item> + <tag>erl</tag> + <item> + <p>If the contents of <c>.erlang</c> are changed and a private version + of <c>user_default</c> is defined, the Erlang/OTP environment can be + customized. More powerful changes can be made by supplying + command-line arguments in the startup script <c>erl</c>. For more + information, see <seealso marker="init"><c>init(3)</c></seealso>.</p> + </item> </taglist> </section> - <section> - <title>SEE ALSO</title> - <p><seealso marker="init">init(3)</seealso>, - <seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>, - <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>, - <seealso marker="kernel:code">code(3)</seealso>, - <seealso marker="kernel:application">application(3)</seealso>, - <seealso marker="kernel:heart">heart(3)</seealso>, - <seealso marker="kernel:net_kernel">net_kernel(3)</seealso>, - <seealso marker="kernel:auth">auth(3)</seealso>, - <seealso marker="tools:make">make(3)</seealso>, - <seealso marker="epmd">epmd(1)</seealso>, - <seealso marker="erts_alloc">erts_alloc(3)</seealso></p> + <section> + <title>See Also</title> + <p><seealso marker="epmd"><c>epmd(1)</c></seealso>, + <seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>, + <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>, + <seealso marker="init"><c>init(3)</c></seealso>, + <seealso marker="kernel:application"> + <c>application(3)</c></seealso>, + <seealso marker="kernel:auth"><c>auth(3)</c></seealso>, + <seealso marker="kernel:code"><c>code(3)</c></seealso>, + <seealso marker="kernel:erl_boot_server"> + <c>erl_boot_server(3)</c></seealso>, + <seealso marker="kernel:heart"><c>heart(3)</c></seealso>, + <seealso marker="kernel:net_kernel"><c>net_kernel(3)</c></seealso>, + <seealso marker="tools:make"><c>make(3)</c></seealso></p> </section> </comref> diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index 25f601a235..ee74983730 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2007</year> - <year>2015</year> + <year>2016</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -32,1061 +32,989 @@ <file>erl_dist_protocol.xml</file> </header> -<p> -The description here is far from complete and will therefore be further -refined in upcoming releases. - -The protocols both from Erlang nodes towards -EPMD (Erlang Port Mapper Daemon) and between Erlang nodes, however, are -stable since many years. -</p> - -<p>The distribution protocol can be divided into four (4) parts:</p> -<list> - <item> - <p> - 1. Low level socket connection. - </p> - </item> - <item> - 2. Handshake, interchange node name and authenticate. - </item> - <item> - 3. Authentication (done by net_kernel). - </item> - <item> - 4. Connected. - </item> -</list> -<p> - A node fetches the Port number of another node through the EPMD (at the - other host) in order to initiate a connection request. -</p> -<p> -For each host where a distributed Erlang node is running there should also -be an EPMD running. The EPMD can be started explicitly or automatically -as a result of the Erlang node startup. -</p> -<p> -By default EPMD listens on port 4369. -</p> -<p> - 3 and 4 are performed at the same level but the net_kernel disconnects the - other node if it communicates using an invalid cookie (after one (1) second). -</p> - -<p>The integers in all multi-byte fields are in big-endian order.</p> + <p>This description is far from complete. It will be updated if the + protocol is updated. However, the protocols, both from Erlang + nodes to the Erlang Port Mapper Daemon (EPMD) and between Erlang nodes + are stable since many years.</p> + + <p>The distribution protocol can be divided into four parts:</p> + + <list type="bulleted"> + <item> + <p>Low-level socket connection (1)</p> + </item> + <item> + <p>Handshake, interchange node name, and authenticate (2)</p> + </item> + <item> + <p>Authentication (done by <seealso marker="kernel:net_kernel"> + <c>net_kernel(3)</c></seealso>) (3)</p> + </item> + <item> + <p>Connected (4)</p> + </item> + </list> + + <p>A node fetches the port number of another node through the EPMD (at the + other host) to initiate a connection request.</p> + + <p>For each host, where a distributed Erlang node is running, also an EPMD + is to be running. The EPMD can be started explicitly or automatically + as a result of the Erlang node startup.</p> + + <p>By default the EPMD listens on port 4369.</p> + + <p>(3) and (4) above are performed at the same level but the <c>net_kernel</c> + disconnects the other node if it communicates using an invalid cookie (after + 1 second).</p> + + <p>The integers in all multibyte fields are in big-endian order.</p> <section> <title>EPMD Protocol</title> - <p> - The requests served by the EPMD (Erlang Port Mapper Daemon) are - summarized in the figure below. - </p> - - <image file="erl_ext_fig.gif"> - <icaption> - Summary of EPMD requests. - </icaption> - </image> - <p> - Each request <c>*_REQ</c> is preceded by a two-byte length field. - Thus, the overall request format is: - </p> + <p>The requests served by the EPMD are summarized in the following + figure.</p> + + <image file="erl_ext_fig.gif"> + <icaption>Summary of EPMD Requests</icaption> + </image> + + <p>Each request <c>*_REQ</c> is preceded by a 2 byte length field. + Thus, the overall request format is as follows:</p> + + <table align="left"> + <row> + <cell align="center">2</cell> + <cell align="center">n</cell> + </row> + <row> + <cell align="center"><c>Length</c></cell> + <cell align="center"><c>Request</c></cell> + </row> + <tcaption>Request Format</tcaption> + </table> + + <section> + <title>Register a Node in EPMD</title> + <p>When a distributed node is started it registers itself in the EPMD. + The message <c>ALIVE2_REQ</c> described below is sent from the node to + the EPMD. The response from the EPMD is <c>ALIVE2_RESP</c>.</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">2</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">2</cell> + <cell align="center">2</cell> + <cell align="center">2</cell> + <cell align="center">Nlen</cell> + <cell align="center">2</cell> + <cell align="center">Elen</cell> + </row> + <row> + <cell align="center"><c>120</c></cell> + <cell align="center"><c>PortNo</c></cell> + <cell align="center"><c>NodeType</c></cell> + <cell align="center"><c>Protocol</c></cell> + <cell align="center"><c>HighestVersion</c></cell> + <cell align="center"><c>LowestVersion</c></cell> + <cell align="center"><c>Nlen</c></cell> + <cell align="center"><c>NodeName</c></cell> + <cell align="center"><c>Elen</c></cell> + <cell align="center"><c>Extra</c></cell> + </row> + <tcaption>ALIVE2_REQ (120)</tcaption> + </table> + + <taglist> + <tag><c>PortNo</c></tag> + <item> + <p>The port number on which the node accept connection requests.</p> + </item> + <tag><c>NodeType</c></tag> + <item> + <p>77 = normal Erlang node, 72 = hidden node (C-node), ...</p> + </item> + <tag><c>Protocol</c></tag> + <item> + <p>0 = TCP/IPv4, ...</p> + </item> + <tag><c>HighestVersion</c></tag> + <item> + <p>The highest distribution version that this node can handle. + The value in Erlang/OTP R6B and later is 5.</p> + </item> + <tag><c>LowestVersion</c></tag> + <item> + <p>The lowest distribution version that this node can handle. + The value in Erlang/OTP R6B and later is 5.</p> + </item> + <tag><c>Nlen</c></tag> + <item> + <p>The length (in bytes) of field <c>NodeName</c>.</p> + </item> + <tag><c>NodeName</c></tag> + <item> + <p>The node name as an UTF-8 encoded string of <c>Nlen</c> bytes.</p> + </item> + <tag><c>Elen</c></tag> + <item> + <p>The length of field <c>Extra</c>.</p> + </item> + <tag><c>Extra</c></tag> + <item> + <p>Extra field of <c>Elen</c> bytes.</p> + </item> + </taglist> + + <p>The connection created to the EPMD must be kept as long as the + node is a distributed node. When the connection is closed, + the node is automatically unregistered from the EPMD.</p> + + <p>The response message <c>ALIVE2_RESP</c> is as follows:</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">2</cell> + </row> + <row> + <cell align="center"><c>121</c></cell> + <cell align="center"><c>Result</c></cell> + <cell align="center"><c>Creation</c></cell> + </row> + <tcaption>ALIVE2_RESP (121)</tcaption> + </table> + + <p>Result = 0 -> ok, result > 0 -> error.</p> + </section> + + <section> + <title>Unregister a Node from EPMD</title> + <p>A node unregisters itself from the EPMD by closing the TCP + connection to EPMD established when the node was registered.</p> + </section> + + <section> + <title>Get the Distribution Port of Another Node</title> + <p>When one node wants to connect to another node it starts with + a <c>PORT_PLEASE2_REQ</c> request to the EPMD on the host where the + node resides to get the distribution port that the node listens to.</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">N</cell> + </row> + <row> + <cell align="center"><c>122</c></cell> + <cell align="center"><c>NodeName</c></cell> + </row> + <tcaption>PORT_PLEASE2_REQ (122)</tcaption> + </table> + + <p>where N = <c>Length</c> - 1.</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">1</cell> + </row> + <row> + <cell align="center"><c>119</c></cell> + <cell align="center"><c>Result</c></cell> + </row> + <tcaption>PORT2_RESP (119) Response Indicating Error, Result > 0 + </tcaption> + </table> + + <p>or</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">2</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">2</cell> + <cell align="center">2</cell> + <cell align="center">2</cell> + <cell align="center">Nlen</cell> + <cell align="center">2</cell> + <cell align="center">Elen</cell> + </row> + <row> + <cell align="center"><c>119</c></cell> + <cell align="center"><c>Result</c></cell> + <cell align="center"><c>PortNo</c></cell> + <cell align="center"><c>NodeType</c></cell> + <cell align="center"><c>Protocol</c></cell> + <cell align="center"><c>HighestVersion</c></cell> + <cell align="center"><c>LowestVersion</c></cell> + <cell align="center"><c>Nlen</c></cell> + <cell align="center"><c>NodeName</c></cell> + <cell align="center"><c>Elen</c></cell> + <cell align="center">><c>Extra</c></cell> + </row> + <tcaption>PORT2_RESP, Result = 0</tcaption> + </table> + + <p>If <c>Result</c> > 0, the packet only consists of + <c>[119, Result]</c>.</p> + + <p>The EPMD closes the socket when it has sent the information.</p> + </section> + + <section> + <title>Get All Registered Names from EPMD</title> + <p>This request is used through the Erlang function + <seealso marker="kernel:net_adm#names/1,2"> + <c>net_adm:names/1,2</c></seealso>. A TCP connection is opened + to the EPMD and this request is sent.</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + </row> + <row> + <cell align="center"><c>110</c></cell> + </row> + <tcaption>NAMES_REQ (110)</tcaption> + </table> + + <p>The response for a <c>NAMES_REQ</c> is as follows:</p> + + <table align="left"> + <row> + <cell align="center">4</cell> + <cell align="center"> </cell> + </row> + <row> + <cell align="center"><c>EPMDPortNo</c></cell> + <cell align="center"><c>NodeInfo*</c></cell> + </row> + <tcaption>NAMES_RESP</tcaption> + </table> + + <p><c>NodeInfo</c> is a string written for each active node. + When all <c>NodeInfo</c> has been written the connection is + closed by the EPMD.</p> + + <p><c>NodeInfo</c> is, as expressed in Erlang:</p> + + <code> +io:format("name ~ts at port ~p~n", [NodeName, Port]).</code> + </section> + + <section> + <title>Dump All Data from EPMD</title> + <p>This request is not really used, it is to be regarded as a debug + feature.</p> + + <table align="left"> + <row> + <cell align="center">1</cell> + </row> + <row> + <cell align="center"><c>100</c></cell> + </row> + <tcaption>DUMP_REQ</tcaption> + </table> + + <p>The response for a <c>DUMP_REQ</c> is as follows:</p> + + <table align="left"> + <row> + <cell align="center">4</cell> + <cell align="center"> </cell> + </row> + <row> + <cell align="center"><c>EPMDPortNo</c></cell> + <cell align="center"><c>NodeInfo*</c></cell> + </row> + <tcaption>DUMP_RESP</tcaption> + </table> + + <p><c>NodeInfo</c> is a string written for each node kept in the EPMD. + When all <c>NodeInfo</c> has been written the connection is + closed by the EPMD.</p> + + <p><c>NodeInfo</c> is, as expressed in Erlang:</p> + + <code> +io:format("active name ~ts at port ~p, fd = ~p~n", + [NodeName, Port, Fd]).</code> + + <p>or</p> + + <code> +io:format("old/unused name ~ts at port ~p, fd = ~p ~n", + [NodeName, Port, Fd]).</code> + </section> + + <section> + <title>Kill EPMD</title> + <p>This request kills the running EPMD. It is almost never used.</p> <table align="left"> - <row> - <cell align="center">2</cell> - <cell align="center">n</cell> - </row> - <row> - <cell align="center">Length</cell> - <cell align="center">Request</cell> - </row> - <tcaption></tcaption></table> - - <section> - <title>Register a node in the EPMD</title> - <p> - When a distributed node is started it registers itself in EPMD. - The message ALIVE2_REQ described below is sent from the node towards - EPMD. The response from EPMD is ALIVE2_RESP. - </p> - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">2</cell> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">2</cell> - <cell align="center">2</cell> - <cell align="center">2</cell> - <cell align="center">Nlen</cell> - <cell align="center">2</cell> - <cell align="center">Elen</cell> - </row> - <row> - <cell align="center">120</cell> - <cell align="center">PortNo</cell> - <cell align="center">NodeType</cell> - <cell align="center">Protocol</cell> - <cell align="center">HighestVersion</cell> - <cell align="center">LowestVersion</cell> - <cell align="center">Nlen</cell> - <cell align="center">NodeName</cell> - <cell align="center">Elen</cell> - <cell align="center">Extra</cell> - </row> - <tcaption>ALIVE2_REQ (120)</tcaption></table> - <taglist> - <tag><c>PortNo</c></tag> - <item> - The port number on which the node accept connection requests. - </item> - <tag><c>NodeType</c></tag> - <item> - 77 = normal Erlang node, 72 = hidden node (C-node),... - </item> - <tag><c>Protocol</c></tag> - <item> - 0 = tcp/ip-v4, ... - </item> - <tag><c>HighestVersion</c></tag> - <item> - The highest distribution version that this node can handle. - The value in R6B and later is 5. - </item> - <tag><c>LowestVersion</c></tag> - <item> - The lowest distribution version that this node can handle. - The value in R6B and later is 5. - </item> - <tag><c>Nlen</c></tag> - <item> - The length (in bytes) of the <c>NodeName</c> field. - </item> - <tag><c>NodeName</c></tag> - <item> - The NodeName as an UTF-8 encoded string of - <c>Nlen</c> bytes. - </item> - <tag><c>Elen</c></tag> - <item> - The length of the <c>Extra</c> field. - </item> - <tag><c>Extra</c></tag> - <item> - Extra field of <c>Elen</c> bytes. - </item> - </taglist> - <p> - The connection created to the EPMD must be kept as long as the - node is a distributed node. When the connection is closed - the node is automatically unregistered from the EPMD. - </p> - <p> - The response message ALIVE2_RESP is described below. - </p> - - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">2</cell> - </row> - <row> - <cell align="center">121</cell> - <cell align="center">Result</cell> - <cell align="center">Creation</cell> - </row> - <tcaption>ALIVE2_RESP (121)</tcaption></table> - <p> - Result = 0 -> ok, Result > 0 -> error - </p> - </section> - - <section> - <title>Unregister a node from the EPMD</title> - <p> - A node unregisters itself from the EPMD by simply closing the - TCP connection towards EPMD established when the node was registered. - </p> - </section> - - <section> - <title>Get the distribution port of another node</title> - <p> - When one node wants to connect to another node it starts with - a PORT_PLEASE2_REQ request towards EPMD on the host where the - node resides in order to get the distribution port that the node - listens to. - </p> - - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">N</cell> - </row> - <row> - <cell align="center">122</cell> - <cell align="center">NodeName</cell> - </row> - <tcaption>PORT_PLEASE2_REQ (122)</tcaption></table> - <p> - where N = Length - 1 - </p> - - <p> - </p> + <row> + <cell align="center">1</cell> + </row> + <row> + <cell align="center"><c>107</c></cell> + </row> + <tcaption>KILL_REQ</tcaption> + </table> + + <p>The response for a <c>KILL_REQ</c> is as follows:</p> + <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - </row> - <row> - <cell align="center">119</cell> - <cell align="center">Result</cell> - </row> - <tcaption> - PORT2_RESP (119) response indicating error, Result > 0. - </tcaption> + <row> + <cell align="center">2</cell> + </row> + <row> + <cell align="center"><c>OKString</c></cell> + </row> + <tcaption>KILL_RESP</tcaption> </table> - <p>Or</p> + + <p>where <c>OKString</c> is "OK".</p> + </section> + + <section> + <title>STOP_REQ (Not Used)</title> <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">2</cell> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">2</cell> - <cell align="center">2</cell> - <cell align="center">2</cell> - <cell align="center">Nlen</cell> - <cell align="center">2</cell> - <cell align="center">Elen</cell> - </row> - <row> - <cell align="center">119</cell> - <cell align="center">Result</cell> - <cell align="center">PortNo</cell> - <cell align="center">NodeType</cell> - <cell align="center">Protocol</cell> - <cell align="center">HighestVersion</cell> - <cell align="center">LowestVersion</cell> - <cell align="center">Nlen</cell> - <cell align="center">NodeName</cell> - <cell align="center">Elen</cell> - <cell align="center">Extra</cell> - </row> - <tcaption>PORT2_RESP when Result = 0.</tcaption></table> -<p> -If Result > 0, the packet only consists of [119, Result]. -</p> - - <p>EPMD will close the socket as soon as it has sent the information.</p> - </section> - - <section> - <title>Get all registered names from EPMD</title> - <p> - This request is used via the Erlang function - <c>net_adm:names/1,2</c>. A TCP connection is opened - towards EPMD and this request is sent. - </p> - <table align="left"> - <row> - <cell align="center">1</cell> - </row> - <row> - <cell align="center">110</cell> - </row> - <tcaption>NAMES_REQ (110)</tcaption></table> - - - <p>The response for a <c>NAMES_REQ</c> looks like this:</p> - <table align="left"> - <row> - <cell align="center">4</cell> - <cell align="center"> </cell> - </row> - <row> - <cell align="center">EPMDPortNo</cell> - <cell align="center">NodeInfo*</cell> - </row> - <tcaption>NAMES_RESP</tcaption></table> - <p> - NodeInfo is a string written for each active node. - When all NodeInfo has been written the connection is - closed by EPMD. - </p> - <p> - NodeInfo is, as expressed in Erlang: - </p> - <code> - io:format("name ~ts at port ~p~n", [NodeName, Port]). - </code> - </section> - - - <section> - <title>Dump all data from EPMD</title> - <p> - This request is not really used, it should be regarded as a debug - feature. - </p> - <table align="left"> - <row> - <cell align="center">1</cell> - </row> - <row> - <cell align="center">100</cell> - </row> - <tcaption>DUMP_REQ</tcaption></table> - - <p>The response for a <c>DUMP_REQ</c> looks like this:</p> - <table align="left"> - <row> - <cell align="center">4</cell> - <cell align="center"> </cell> - </row> - <row> - <cell align="center">EPMDPortNo</cell> - <cell align="center">NodeInfo*</cell> - </row> - <tcaption>DUMP_RESP</tcaption></table> - <p> - NodeInfo is a string written for each node kept in EPMD. - When all NodeInfo has been written the connection is - closed by EPMD. - </p> - <p> - NodeInfo is, as expressed in Erlang: - </p> - <code> - io:format("active name ~ts at port ~p, fd = ~p~n", - [NodeName, Port, Fd]). - </code> - <p> - or - </p> - <code> - io:format("old/unused name ~ts at port ~p, fd = ~p ~n", - [NodeName, Port, Fd]). - </code> - - </section> - - <section> - <title>Kill the EPMD</title> - <p> - This request will kill the running EPMD. It is almost never used. - </p> - <table align="left"> - <row> - <cell align="center">1</cell> - </row> - <row> - <cell align="center">107</cell> - </row> - <tcaption>KILL_REQ</tcaption></table> - - <p>The response fo a <c>KILL_REQ</c> looks like this:</p> - <table align="left"> - <row> - <cell align="center">2</cell> - </row> - <row> - <cell align="center">OKString</cell> - </row> - <tcaption>KILL_RESP</tcaption></table> - <p> - where <c>OKString</c> is "OK". - </p> - </section> - - <section> - <title>STOP_REQ (Not Used)</title> - <p></p> - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">n</cell> - </row> - <row> - <cell align="center">115</cell> - <cell align="center">NodeName</cell> - </row> - <tcaption>STOP_REQ</tcaption></table> - <p> - where n = Length - 1 - </p> - <p> - The current implementation of Erlang does not care if the connection - to the EPMD is broken. - </p> - <p>The response for a <c>STOP_REQ</c> looks like this.</p> - <table align="left"> - <row> - <cell align="center">7</cell> - </row> - <row> - <cell align="center">OKString</cell> - </row> - <tcaption>STOP_RESP</tcaption></table> - <p> - where OKString is "STOPPED". - </p> - <p>A negative response can look like this.</p> - <table align="left"> - <row> - <cell align="center">7</cell> - </row> - <row> - <cell align="center">NOKString</cell> - </row> - <tcaption>STOP_NOTOK_RESP</tcaption></table> - <p> - where NOKString is "NOEXIST". - </p> - </section> + <row> + <cell align="center">1</cell> + <cell align="center">n</cell> + </row> + <row> + <cell align="center"><c>115</c></cell> + <cell align="center"><c>NodeName</c></cell> + </row> + <tcaption>STOP_REQ</tcaption> + </table> + + <p>where n = <c>Length</c> - 1.</p> + + <p>The current implementation of Erlang does not care if the connection + to the EPMD is broken.</p> + + <p>The response for a <c>STOP_REQ</c> is as follows:</p> + + <table align="left"> + <row> + <cell align="center">7</cell> + </row> + <row> + <cell align="center"><c>OKString</c></cell> + </row> + <tcaption>STOP_RESP</tcaption> + </table> + + <p>where <c>OKString</c> is "STOPPED".</p> + + <p>A negative response can look as follows:</p> + + <table align="left"> + <row> + <cell align="center">7</cell> + </row> + <row> + <cell align="center"><c>NOKString</c></cell> + </row> + <tcaption>STOP_NOTOK_RESP</tcaption> + </table> + + <p>where <c>NOKString</c> is "NOEXIST".</p> + </section> <!-- - <section> - <title>ALIVE_REQ (97)</title> - <p></p> - - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">2</cell> - <cell align="center">n</cell> - </row> - <row> - <cell align="center">97</cell> - <cell align="center">PortNo</cell> - <cell align="center">NodeName</cell> - </row> - <tcaption></tcaption></table> - - <p> - where n = Length - 3 - </p> - <p> - The connection created to the EPMD must be kept until the node is - not a distributed node any longer. - </p> - </section> - - <section> - <title>ALIVE_OK_RESP (89)</title> - <p></p> - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">2</cell> - </row> - <row> - <cell align="center">89</cell> - <cell align="center">Creation</cell> - </row> - <tcaption></tcaption></table> - </section> - - - <section> - <title>ALIVE_NOTOK_RESP ()</title> - <p> - EPMD closed the connection. - </p> - </section> - - <section> - <title>PORT_PLEASE_REQ (112)</title> - <p></p> + <section> + <title>ALIVE_REQ (97)</title> + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">2</cell> + <cell align="center">n</cell> + </row> + <row> + <cell align="center"><c>97</c></cell> + <cell align="center"><c>PortNo</c></cell> + <cell align="center"><c>NodeName</c></cell> + </row> + <tcaption></tcaption> + </table> + + <p>where n = <c>Length</c> - 3.</p> + + <p>The connection created to the EPMD must be kept until the node is + not a distributed node any longer.</p> + </section> + + <section> + <title>ALIVE_OK_RESP (89)</title> + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">2</cell> + </row> + <row> + <cell align="center"><c>89</c></cell> + <cell align="center"><c>Creation</c></cell> + </row> + <tcaption></tcaption> + </table> + </section> + + <section> + <title>ALIVE_NOTOK_RESP ()</title> + <p>The EPMD closed the connection.</p> + </section> + + <section> + <title>PORT_PLEASE_REQ (112)</title> + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">n</cell> + </row> + <row> + <cell align="center"><c>112</c></cell> + <cell align="center"><c>NodeName</c></cell> + </row> + <tcaption></tcaption> + </table> + + <p>where n = <c>Length</c> - 1.</p> + </section> + + <section> + <title>PORT_OK_RESP ()</title> <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">n</cell> - </row> - <row> - <cell align="center">112</cell> - <cell align="center">NodeName</cell> - </row> - <tcaption></tcaption></table> - <p> - where n = Length - 1 - </p> - </section> - - <section> - <title>PORT_OK_RESP ()</title> - <p></p> - <table align="left"> - <row> - <cell align="center">2</cell> - </row> - <row> - <cell align="center">PortNo</cell> - </row> - <tcaption></tcaption></table> - - </section> - - <section> - <title>PORT_NOTOK_RESP ()</title> - <p> - EPMD closed the connection. - </p> - </section> - - - <section> - <title>PORT_NOTOK_RESP ()</title> - <p> - EPMD closed the connection. - </p> - </section> + <row> + <cell align="center">2</cell> + </row> + <row> + <cell align="center"><c>PortNo</c></cell> + </row> + <tcaption></tcaption> + </table> + </section> + + <section> + <title>PORT_NOTOK_RESP ()</title> + <p>The EPMD closed the connection.</p> + </section> + + <section> + <title>PORT_NOTOK_RESP ()</title> + <p>The EPMD closed the connection.</p> + </section> --> + </section> + + <section> + <marker id="distribution_handshake"/> + <title>Distribution Handshake</title> + <p>This section describes the distribution handshake protocol introduced + in Erlang/OTP R6. This description was previously located in + <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c> and + has more or less been copied and "formatted" here. It has been almost + unchanged since 1999, but the handshake has not changed much since then + either.</p> + + <section> + <title>General</title> + <p>The TCP/IP distribution uses a handshake that expects a + connection-based protocol, that is, the protocol does not include any + authentication after the handshake procedure.</p> + + <p>This is not entirely safe, as it is vulnerable against takeover + attacks, but it is a tradeoff between fair safety and performance.</p> + + <p>The cookies are never sent in cleartext and the handshake procedure + expects the client (called <c>A</c>) to be the first one to prove that + it can generate a sufficient digest. The digest is generated with the + MD5 message digest algorithm and the challenges are expected to be + random numbers.</p> + </section> + <section> + <title>Definitions</title> + <p>A challenge is a 32-bit integer in big-endian order. Below the function + <c>gen_challenge()</c> returns a random 32-bit integer used as a + challenge.</p> + + <p>A digest is a (16 bytes) MD5 hash of the challenge (as text) + concatenated with the cookie (as text). Below, the function + <c>gen_digest(Challenge, Cookie)</c> generates a digest as described + above.</p> + + <p>An <c>out_cookie</c> is the cookie used in outgoing communication to a + certain node, so that <c>A</c>'s <c>out_cookie</c> for <c>B</c> is to + correspond with <c>B</c>'s <c>in_cookie</c> for <c>A</c> and conversely. + <c>A</c>'s <c>out_cookie</c> for <c>B</c> and <c>A</c>'s + <c>in_cookie</c> for <c>B</c> need <em>not</em> be the same. Below the + function <c>out_cookie(Node)</c> returns the current node's + <c>out_cookie</c> for <c>Node</c>.</p> + + <p>An <c>in_cookie</c> is the cookie expected to be used by another node + when communicating with us, so that <c>A</c>'s <c>in_cookie</c> for + <c>B</c> corresponds with <c>B</c>'s <c>out_cookie</c> for <c>A</c>. + Below the function <c>in_cookie(Node)</c> returns the current node's + <c>in_cookie</c> for <c>Node</c>.</p> + + <p>The cookies are text strings that can be viewed as passwords.</p> + + <p>Every message in the handshake starts with a 16-bit big-endian integer, + which contains the message length (not counting the two initial bytes). + In Erlang this corresponds to option <c>{packet, 2}</c> in + <seealso marker="kernel:gen_tcp"><c>gen_tcp(3)</c></seealso>. + Notice that after the handshake, the distribution switches to 4 byte + packet headers.</p> </section> - <section> - <title>Distribution Handshake</title> - <p> - <marker id="distribution_handshake"/> - This section describes the distribution handshake protocol - introduced in the OTP-R6 release of Erlang/OTP. This - description was previously located in - <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c>, - and has more or less been copied and "formatted" here. It has been - more or less unchanged since the year 1999, but the handshake - should not have changed much since then either. - </p> - <section> - <title>General</title> - <p> - The TCP/IP distribution uses a handshake which expects a - connection based protocol, i.e. the protocol does not include - any authentication after the handshake procedure. - </p> - <p> - This is not entirely safe, as it is vulnerable against takeover - attacks, but it is a tradeoff between fair safety and performance. - </p> - <p> - The cookies are never sent in cleartext and the handshake procedure - expects the client (called A) to be the first one to prove that it can - generate a sufficient digest. The digest is generated with the - MD5 message digest algorithm and the challenges are expected to be very - random numbers. - </p> - </section> - <section> - <title>Definitions</title> - <p> - A challenge is a 32 bit integer number in big endian order. Below the function - <c>gen_challenge()</c> returns a random 32 bit integer used as a challenge. - </p> - <p> - A digest is a (16 bytes) MD5 hash of the Challenge (as text) concatenated - with the cookie (as text). Below, the function <c>gen_digest(Challenge, Cookie)</c> - generates a digest as described above. - </p> - <p> - An out_cookie is the cookie used in outgoing communication to a certain node, - so that A's out_cookie for B should correspond with B's in_cookie for A and - the other way around. A's out_cookie for B and A's in_cookie for B need <em>NOT</em> - be the same. Below the function <c>out_cookie(Node)</c> returns the current - node's out_cookie for <c>Node</c>. - </p> - <p> - An in_cookie is the cookie expected to be used by another node when - communicating with us, so that A's in_cookie for B corresponds with B's - out_cookie for A. Below the function <c>in_cookie(Node)</c> returns the current - node's <c>in_cookie</c> for <c>Node</c>. - </p> - <p> - The cookies are text strings that can be viewed as passwords. - </p> - <p> - Every message in the handshake starts with a 16 bit big endian integer - which contains the length of the message (not counting the two initial bytes). - In erlang this corresponds to the <c>gen_tcp</c> option <c>{packet, 2}</c>. Note that after - the handshake, the distribution switches to 4 byte packet headers. - </p> - - </section> - <section> - <title>The Handshake in Detail</title> - <p> - Imagine two nodes, node A, which initiates the handshake and node B, which - accepts the connection. - </p> - <taglist> - <tag>1) connect/accept</tag> - <item><p>A connects to B via TCP/IP and B accepts the connection.</p></item> - <tag>2) send_name/receive_name</tag> - <item><p>A sends an initial identification to B. B receives the message. - The message looks like this (every "square" being one byte and the packet - header removed): - </p> -<pre> + + <section> + <title>The Handshake in Detail</title> + <p>Imagine two nodes, <c>A</c> that initiates the handshake and <c>B</c> + that accepts the connection.</p> + + <taglist> + <tag>1) connect/accept</tag> + <item> + <p><c>A</c> connects to <c>B</c> through TCP/IP and <c>B</c> accepts + the connection.</p> + </item> + <tag>2) <c>send_name</c>/<c>receive_name</c></tag> + <item> + <p><c>A</c> sends an initial identification to <c>B</c>, which + receives the message. The message looks as follows (every "square" + is one byte and the packet header is removed):</p> + <pre> +---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+ |'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN| -+---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+ -</pre> - <p> - The 'n' is just a message tag. - Version0 and Version1 is the distribution version selected by node A, - based on information from EPMD. (16 bit big endian) - Flag0 ... Flag3 are capability flags, the capabilities defined in - <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>. - (32 bit big endian) - Name0 ... NameN is the full nodename of A, as a string of bytes (the - packet length denotes how long it is). - </p></item> - <tag>3) recv_status/send_status</tag> - <item><p>B sends a status message to A, which indicates - if the connection is allowed. The following status codes are defined:</p> - <taglist> - <tag><c>ok</c></tag> - <item>The handshake will continue.</item> - <tag><c>ok_simultaneous</c></tag> - <item>The handshake will continue, but A is informed that B - has another ongoing connection attempt that will be - shut down (simultaneous connect where A's name is - greater than B's name, compared literally).</item> - <tag><c>nok</c></tag> - <item>The handshake will not continue, as B already has an ongoing handshake - which it itself has initiated. (simultaneous connect where B's name is - greater than A's).</item> - <tag><c>not_allowed</c></tag> - <item>The connection is disallowed for some (unspecified) security - reason.</item> - <tag><c>alive</c></tag> - <item>A connection to the node is already active, which either means - that node A is confused or that the TCP connection breakdown - of a previous node with this name has not yet reached node B. - See 3B below.</item> - </taglist> - <p>This is the format of the status message:</p> -<pre> ++---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+</pre> + <p>'n' is the message tag. 'Version0' and 'Version1' is the + distribution version selected by <c>A</c>, based on information + from the EPMD. (16-bit big-endian) 'Flag0' ... 'Flag3' are + capability flags, the capabilities are defined in + <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>. (32-bit big-endian) + 'Name0' ... 'NameN' is the full node name of <c>A</c>, as a string + of bytes (the packet length denotes how long it is).</p> + </item> + <tag>3) <c>recv_status</c>/<c>send_status</c></tag> + <item> + <p><c>B</c> sends a status message to <c>A</c>, which indicates if the + connection is allowed. The following status codes are defined:</p> + <taglist> + <tag><c>ok</c></tag> + <item> + <p>The handshake will continue.</p> + </item> + <tag><c>ok_simultaneous</c></tag> + <item> + <p>The handshake will continue, but <c>A</c> is informed that + <c>B</c> has another ongoing connection attempt that will be + shut down (simultaneous connect where <c>A</c>'s name is + greater than <c>B</c>'s name, compared literally).</p> + </item> + <tag><c>nok</c></tag> + <item> + <p>The handshake will not continue, as <c>B</c> already has an + ongoing handshake, which it itself has initiated (simultaneous + connect where <c>B</c>'s name is greater than <c>A</c>'s).</p> + </item> + <tag><c>not_allowed</c></tag> + <item> + <p>The connection is disallowed for some (unspecified) security + reason.</p> + </item> + <tag><c>alive</c></tag> + <item> + <p>A connection to the node is already active, which either means + that node <c>A</c> is confused or that the TCP connection + breakdown of a previous node with this name has not yet reached + node <c>B</c>. See step 3B below.</p> + </item> + </taglist> + <p>The format of the status message is as follows:</p> + <pre> +---+-------+-------+-...-+-------+ |'s'|Status0|Status1| ... |StatusN| -+---+-------+-------+-...-+-------+ -</pre> - <p> - 's' is the message tag Status0 ... StatusN is the status as a string (not terminated) - </p> - </item> - <tag>3B) send_status/recv_status</tag> - <item><p>If status was 'alive', node A will answer with - another status message containing either 'true' which means that the - connection should continue (The old connection from this node is broken), or - <c>'false'</c>, which simply means that the connection should be closed, the - connection attempt was a mistake.</p></item> - <tag>4) recv_challenge/send_challenge</tag> - <item><p>If the status was <c>ok</c> or <c>ok_simultaneous</c>, - The handshake continues with B sending A another message, the challenge. - The challenge contains the same type of information as the "name" message - initially sent from A to B, with the addition of a 32 bit challenge:</p> -<pre> ++---+-------+-------+-...-+-------+</pre> + <p>'s' is the message tag. 'Status0' ... 'StatusN' is the status as a + string (not terminated).</p> + </item> + <tag>3B) <c>send_status</c>/<c>recv_status</c></tag> + <item> + <p>If status was <c>alive</c>, node <c>A</c> answers with another + status message containing either <c>true</c>, which means that the + connection is to continue (the old connection from this node is + broken), or <c>false</c>, which means that the connection is to be + closed (the connection attempt was a mistake.</p> + </item> + <tag>4) <c>recv_challenge</c>/<c>send_challenge</c></tag> + <item> + <p>If the status was <c>ok</c> or <c>ok_simultaneous</c>, the + handshake continues with <c>B</c> sending <c>A</c> another message, + the challenge. The challenge contains the same type of information + as the "name" message initially sent from <c>A</c> to <c>B</c>, plus + a 32-bit challenge:</p> + <pre> +---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-...-+-----+ |'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|Name0|Name1| ... |NameN| -+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+ -</pre> - <p> - Where Chal0 ... Chal3 is the challenge as a 32 bit big endian integer - and the other fields are B's version, flags and full nodename. - </p></item> - <tag>5) send_challenge_reply/recv_challenge_reply</tag> - <item><p>Now A has generated a digest and its own challenge. Those are - sent together in a package to B:</p> -<pre> ++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+</pre> + <p>'Chal0' ... 'Chal3' is the challenge as a 32-bit big-endian integer + and the other fields are <c>B</c>'s version, flags, and full node + name.</p> + </item> + <tag>5) <c>send_challenge_reply</c>/<c>recv_challenge_reply</c></tag> + <item> + <p>Now <c>A</c> has generated a digest and its own challenge. Those + are sent together in a package to <c>B</c>:</p> + <pre> +---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+ |'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15| -+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+ -</pre> - <p> - Where 'r' is the tag, Chal0 ... Chal3 is A's challenge for B to handle and - Dige0 ... Dige15 is the digest that A constructed from the challenge B sent - in the previous step. - </p></item> - <tag>6) recv_challenge_ack/send_challenge_ack</tag> - <item><p>B checks that the digest received from A is correct and generates a - digest from the challenge received from A. The digest is then sent to A. The - message looks like this:</p> -<pre> ++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+</pre> + <p>'r' is the tag. 'Chal0' ... 'Chal3' is <c>A</c>'s challenge for + <c>B</c> to handle. 'Dige0' ... 'Dige15' is the digest that <c>A</c> + constructed from the challenge <c>B</c> sent in the previous + step.</p> + </item> + <tag>6) <c>recv_challenge_ack</c>/<c>send_challenge_ack</c></tag> + <item> + <p><c>B</c> checks that the digest received from <c>A</c> is correct + and generates a digest from the challenge received from <c>A</c>. + The digest is then sent to <c>A</c>. The message is as follows:</p> + <pre> +---+-----+-----+-----+-----+-...-+------+ |'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15| -+---+-----+-----+-----+-----+-...-+------+ -</pre> - <p> - Where 'a' is the tag and Dige0 ... Dige15 is the digest calculated by B - for A's challenge.</p></item> - <tag>7)</tag> - <item><p>A checks the digest from B and the connection is up.</p></item> - </taglist> - </section> - <section> - <title>Semigraphic View</title> -<pre> -A (initiator) B (acceptor) - -TCP connect -----------------------------------------> - TCP accept - -send_name -----------------------------------------> - recv_name - - <---------------------------------------- send_status ++---+-----+-----+-----+-----+-...-+------+</pre> + <p>'a' is the tag. 'Dige0' ... 'Dige15' is the digest calculated by + <c>B</c> for <c>A</c>'s challenge.</p> + </item> + <tag>7) check</tag> + <item> + <p><c>A</c> checks the digest from <c>B</c> and the connection is + up.</p> + </item> + </taglist> + </section> + + <section> + <title>Semigraphic View</title> + <pre> +A (initiator) B (acceptor) + +TCP connect ------------------------------------> + TCP accept + +send_name --------------------------------------> + recv_name + + <---------------------------------------------- send_status recv_status (if status was 'alive' - send_status - - - - - - - - - - - - - - - - - - - -> - recv_status) - ChB = gen_challenge() - (ChB) - <---------------------------------------- send_challenge + send_status - - - - - - - - - - - - - - - - - -> + recv_status) + ChB = gen_challenge() + (ChB) + <---------------------------------------------- send_challenge recv_challenge - ChA = gen_challenge(), OCA = out_cookie(B), -DiA = gen_digest(ChB,OCA) - (ChA, DiA) -send_challenge_reply --------------------------------> - recv_challenge_reply - ICB = in_cookie(A), - check: - DiA == gen_digest - (ChB, ICB) ? - - if OK: - OCB = out_cookie(A), - DiB = gen_digest - (DiB) (ChA, OCB) - <----------------------------------------- send_challenge_ack -recv_challenge_ack DONE -ICA = in_cookie(B), - else -check: CLOSE -DiB == gen_digest(ChA,ICA) ? -- if OK +DiA = gen_digest(ChB, OCA) + (ChA, DiA) +send_challenge_reply ---------------------------> + recv_challenge_reply + ICB = in_cookie(A), + check: + DiA == gen_digest (ChB, ICB)? + - if OK: + OCB = out_cookie(A), + DiB = gen_digest (ChA, OCB) + (DiB) + <----------------------------------------------- send_challenge_ack +recv_challenge_ack DONE +ICA = in_cookie(B), - else: +check: CLOSE +DiB == gen_digest(ChA, ICA)? +- if OK: DONE -- else - CLOSE -</pre> - </section> - <marker id="dflags"/> - <section> - <title>The Currently Defined Distribution Flags</title> - <p> - Currently (OTP-R16) the following capability flags are defined: - </p> -<pre> -%% The node should be published and part of the global namespace --define(DFLAG_PUBLISHED,1). - -%% The node implements an atom cache (obsolete) --define(DFLAG_ATOM_CACHE,2). - -%% The node implements extended (3 * 32 bits) references. This is -%% required today. If not present connection will be refused. --define(DFLAG_EXTENDED_REFERENCES,4). - -%% The node implements distributed process monitoring. --define(DFLAG_DIST_MONITOR,8). - -%% The node uses separate tag for fun's (lambdas) in the distribution protocol. --define(DFLAG_FUN_TAGS,16#10). - -%% The node implements distributed named process monitoring. --define(DFLAG_DIST_MONITOR_NAME,16#20). - -%% The (hidden) node implements atom cache (obsolete) --define(DFLAG_HIDDEN_ATOM_CACHE,16#40). - -%% The node understand new fun-tags --define(DFLAG_NEW_FUN_TAGS,16#80). - -%% The node is capable of handling extended pids and ports. This is -%% required today. If not present connection will be refused. --define(DFLAG_EXTENDED_PIDS_PORTS,16#100). - -%% --define(DFLAG_EXPORT_PTR_TAG,16#200). - -%% --define(DFLAG_BIT_BINARIES,16#400). - -%% The node understands new float format --define(DFLAG_NEW_FLOATS,16#800). - -%% --define(DFLAG_UNICODE_IO,16#1000). - -%% The node implements atom cache in distribution header. --define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000). - -%% The node understand the SMALL_ATOM_EXT tag --define(DFLAG_SMALL_ATOM_TAGS, 16#4000). - -%% The node understand UTF-8 encoded atoms --define(DFLAG_UTF8_ATOMS, 16#10000). - -</pre> - </section> - </section> - - <section> - <marker id="connected_nodes"/> - <title>Protocol between connected nodes</title> - <p> - As of erts version 5.7.2 the runtime system passes a distribution - flag in the handshake stage that enables the use of a - <seealso marker="erl_ext_dist#distribution_header">distribution - header</seealso> on all messages passed. Messages passed between - nodes are in this case on the following format: - </p> - <table align="left"> - <row> - <cell align="center">4</cell> - <cell align="center">d</cell> - <cell align="center">n</cell> - <cell align="center">m</cell> - </row> - <row> - <cell align="center"><c>Length</c></cell> - <cell align="center"><c>DistributionHeader</c></cell> - <cell align="center"><c>ControlMessage</c></cell> - <cell align="center"><c>Message</c></cell> - </row> - <tcaption></tcaption></table> - <p> - where: - </p> - <p> - <c>Length</c> is equal to d + n + m - </p> - <p> - <c>ControlMessage</c> is a tuple passed using the external format of - Erlang. - </p> - <p> - <c>Message</c> is the message sent to another node using the '!' - (in external format). Note that <c>Message</c> is only passed in - combination with a <c>ControlMessage</c> encoding a send ('!'). - </p> - <p> - Also note that <seealso marker="erl_ext_dist#overall_format">the - version number is omitted from the terms that follow a - distribution header</seealso>. - </p> - <p> - Nodes with an erts version less than 5.7.2 does not pass the - distribution flag that enables the distribution header. Messages - passed between nodes are in this case on the following format: - </p> - <table align="left"> - <row> - <cell align="center">4</cell> - <cell align="center">1</cell> - <cell align="center">n</cell> - <cell align="center">m</cell> - </row> - <row> - <cell align="center"><c>Length</c></cell> - <cell align="center"><c>Type</c></cell> - <cell align="center"><c>ControlMessage</c></cell> - <cell align="center"><c>Message</c></cell> - </row> - <tcaption></tcaption></table> - <p> - where: - </p> - <p> - <c>Length</c> is equal to 1 + n + m - </p> - <p> - Type is: 112 (pass through) - </p> - <p> - <c>ControlMessage</c> is a tuple passed using the external format of - Erlang. - </p> - <p> - <c>Message</c> is the message sent to another node using the '!' - (in external format). Note that <c>Message</c> is only passed in - combination with a <c>ControlMessage</c> encoding a send ('!'). - </p> - <p> - The <c>ControlMessage</c> is a tuple, where the first element - indicates which distributed operation it encodes. - </p> - <taglist> - <tag><c>LINK</c></tag> - <item> - <p> - <c>{1, FromPid, ToPid}</c> - </p> - </item> - - <tag><c>SEND</c></tag> - <item> - <p> - <c>{2, Unused, ToPid}</c> - </p> - <p> - <em>Note</em> followed by <c>Message</c> - </p> - <p> - <c>Unused</c> is kept for backward compatibility - </p> - </item> - - <tag><c>EXIT</c></tag> - <item> - <p> - <c>{3, FromPid, ToPid, Reason}</c> - </p> - </item> - - <tag><c>UNLINK</c></tag> - <item> - <p> - <c>{4, FromPid, ToPid}</c> - </p> - </item> - - <tag><c>NODE_LINK</c></tag> - <item> - <p> - <c>{5}</c> - </p> - </item> - - <tag><c>REG_SEND</c></tag> - <item> - <p> - <c>{6, FromPid, Unused, ToName}</c> - </p> - <p> - <em>Note</em> followed by <c>Message</c> - </p> - <p> - <c>Unused</c> is kept for backward compatibility - </p> - </item> - - <tag><c>GROUP_LEADER</c></tag> - <item> - <p> - <c>{7, FromPid, ToPid}</c> - </p> - </item> - - <tag><c>EXIT2</c></tag> - <item> - <p> - <c>{8, FromPid, ToPid, Reason}</c> - </p> - </item> - </taglist> - </section> - - - <section> - <title>New Ctrlmessages for distrvsn = 1 (OTP R4)</title> - <taglist> - <tag><c>SEND_TT</c></tag> - <item> - <p> - <c>{12, Unused, ToPid, TraceToken}</c> - </p> - <p> - <em>Note</em> followed by <c>Message</c> - </p> - <p> - <c>Unused</c> is kept for backward compatibility - </p> - </item> - - <tag><c>EXIT_TT</c></tag> - <item> - <p> - <c>{13, FromPid, ToPid, TraceToken, Reason}</c> - </p> - </item> - - <tag><c>REG_SEND_TT</c></tag> - <item> - <p> - <c>{16, FromPid, Unused, ToName, TraceToken}</c> - </p> - <p> - <em>Note</em> followed by <c>Message</c> - </p> - <p> - <c>Unused</c> is kept for backward compatibility - </p> - </item> - - <tag><c>EXIT2_TT</c></tag> - <item> - <p> - <c>{18, FromPid, ToPid, TraceToken, Reason}</c> - </p> - </item> - </taglist> - </section> - - <section> - <title>New Ctrlmessages for distrvsn = 2</title> - <p> - distrvsn 2 was never used. - </p> - </section> - - <section> - <title>New Ctrlmessages for distrvsn = 3 (OTP R5C)</title> - <p> - None, but the version number was increased anyway. - </p> +- else: + CLOSE</pre> </section> <section> - <title>New Ctrlmessages for distrvsn = 4 (OTP R6)</title> - <p> - These are only recognized by Erlang nodes, not by hidden nodes. - </p> + <marker id="dflags"/> + <title>Distribution Flags</title> + <p>The following capability flags are defined:</p> <taglist> - <tag><c>MONITOR_P</c></tag> - <item> - <p> - <c>{19, FromPid, ToProc, Ref}</c> - - <c>FromPid</c> = monitoring process - <c>ToProc</c> = monitored process pid or name (atom) - </p> - </item> - - <tag><c>DEMONITOR_P</c></tag> - <item> - <p> - <c>{20, FromPid, ToProc, Ref}</c> - We include the FromPid just in case we want to trace this. - - <c>FromPid</c> = monitoring process - <c>ToProc</c> = monitored process pid or name (atom) - </p> - </item> - - <tag><c>MONITOR_P_EXIT</c></tag> - <item> - <p> - <c>{21, FromProc, ToPid, Ref, Reason}</c> - - <c>FromProc</c> = monitored process pid or name (atom) - <c>ToPid</c> = monitoring process - <c>Reason</c> = exit reason for the monitored process - </p> - </item> + <tag><c>-define(DFLAG_PUBLISHED,16#1).</c></tag> + <item> + <p>The node is to be published and part of the global namespace.</p> + </item> + <tag><c>-define(DFLAG_ATOM_CACHE,16#2).</c></tag> + <item> + <p>The node implements an atom cache (obsolete).</p> + </item> + <tag><c>-define(DFLAG_EXTENDED_REFERENCES,16#4).</c></tag> + <item> + <p>The node implements extended (3 × 32 bits) references. This + is required today. If not present, the connection is refused.</p> + </item> + <tag><c>-define(DFLAG_DIST_MONITOR,16#8).</c></tag> + <item> + <p>The node implements distributed process monitoring.</p> + </item> + <tag><c>-define(DFLAG_FUN_TAGS,16#10).</c></tag> + <item> + <p>The node uses separate tag for funs (lambdas) in the distribution + protocol.</p> + </item> + <tag><c>-define(DFLAG_DIST_MONITOR_NAME,16#20).</c></tag> + <item> + <p>The node implements distributed named process monitoring.</p> + </item> + <tag><c>-define(DFLAG_HIDDEN_ATOM_CACHE,16#40).</c></tag> + <item> + <p>The (hidden) node implements atom cache (obsolete).</p> + </item> + <tag><c>-define(DFLAG_NEW_FUN_TAGS,16#80).</c></tag> + <item> + <p>The node understand new fun tags.</p> + </item> + <tag><c>-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).</c></tag> + <item> + <p>The node can handle extended pids and ports. This is required + today. If not present, the connection is refused.</p> + </item> + <tag><c>-define(DFLAG_EXPORT_PTR_TAG,16#200).</c></tag> + <item> + </item> + <tag><c>-define(DFLAG_BIT_BINARIES,16#400).</c></tag> + <item> + </item> + <tag><c>-define(DFLAG_NEW_FLOATS,16#800).</c></tag> + <item> + <p>The node understands new float format.</p> + </item> + <tag><c>-define(DFLAG_UNICODE_IO,16#1000).</c></tag> + <item> + </item> + <tag><c>-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).</c></tag> + <item> + <p>The node implements atom cache in distribution header.</p> + </item> + <tag><c>-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).</c></tag> + <item> + <p>The node understand the <c>SMALL_ATOM_EXT</c> tag.</p> + </item> + <tag><c>-define(DFLAG_UTF8_ATOMS, 16#10000).</c></tag> + <item> + <p>The node understand UTF-8 encoded atoms.</p> + </item> </taglist> </section> - </chapter> + </section> + + <section> + <marker id="connected_nodes"/> + <title>Protocol between Connected Nodes</title> + <p>As from ERTS 5.7.2 the runtime system passes a distribution flag + in the handshake stage that enables the use of a + <seealso marker="erl_ext_dist#distribution_header">distribution header + </seealso> on all messages passed. Messages passed between nodes have in + this case the following format:</p> + + <table align="left"> + <row> + <cell align="center">4</cell> + <cell align="center">d</cell> + <cell align="center">n</cell> + <cell align="center">m</cell> + </row> + <row> + <cell align="center"><c>Length</c></cell> + <cell align="center"><c>DistributionHeader</c></cell> + <cell align="center"><c>ControlMessage</c></cell> + <cell align="center"><c>Message</c></cell> + </row> + <tcaption>Format of Messages Passed between Nodes (as from ERTS 5.7.2) + </tcaption> + </table> + + <taglist> + <tag><c>Length</c></tag> + <item> + <p>Equal to d + n + m.</p> + </item> + <tag><c>ControlMessage</c></tag> + <item> + <p>A tuple passed using the external format of Erlang.</p> + </item> + <tag><c>Message</c></tag> + <item> + <p>The message sent to another node using the '!' (in external format). + Notice that <c>Message</c> is only passed in combination with a + <c>ControlMessage</c> encoding a send ('!').</p> + </item> + </taglist> + + <p>Notice that <seealso marker="erl_ext_dist#overall_format">the version + number is omitted from the terms that follow a distribution header + </seealso>.</p> + + <p>Nodes with an ERTS version earlier than 5.7.2 does not pass the + distribution flag that enables the distribution header. Messages passed + between nodes have in this case the following format:</p> + + <table align="left"> + <row> + <cell align="center">4</cell> + <cell align="center">1</cell> + <cell align="center">n</cell> + <cell align="center">m</cell> + </row> + <row> + <cell align="center"><c>Length</c></cell> + <cell align="center"><c>Type</c></cell> + <cell align="center"><c>ControlMessage</c></cell> + <cell align="center"><c>Message</c></cell> + </row> + <tcaption>Format of Messages Passed between Nodes (before ERTS 5.7.2) + </tcaption> + </table> + + <taglist> + <tag><c>Length</c></tag> + <item> + <p>Equal to 1 + n + m.</p> + </item> + <tag><c>Type</c></tag> + <item> + <p>Equal to <c>112</c> (pass through).</p> + </item> + <tag><c>ControlMessage</c></tag> + <item> + <p>A tuple passed using the external format of Erlang.</p> + </item> + <tag><c>Message</c></tag> + <item> + <p>The message sent to another node using the '!' (in external format). + Notice that <c>Message</c> is only passed in combination with a + <c>ControlMessage</c> encoding a send ('!').</p> + </item> + </taglist> + + <p>The <c>ControlMessage</c> is a tuple, where the first element indicates + which distributed operation it encodes:</p> + + <taglist> + <tag><c>LINK</c></tag> + <item> + <p><c>{1, FromPid, ToPid}</c></p> + </item> + <tag><c>SEND</c></tag> + <item> + <p><c>{2, Unused, ToPid}</c></p> + <p>Followed by <c>Message</c>.</p> + <p><c>Unused</c> is kept for backward compatibility.</p> + </item> + <tag><c>EXIT</c></tag> + <item> + <p><c>{3, FromPid, ToPid, Reason}</c></p> + </item> + <tag><c>UNLINK</c></tag> + <item> + <p><c>{4, FromPid, ToPid}</c></p> + </item> + <tag><c>NODE_LINK</c></tag> + <item> + <p><c>{5}</c></p> + </item> + <tag><c>REG_SEND</c></tag> + <item> + <p><c>{6, FromPid, Unused, ToName}</c></p> + <p>Followed by <c>Message</c>.</p> + <p><c>Unused</c> is kept for backward compatibility.</p> + </item> + <tag><c>GROUP_LEADER</c></tag> + <item> + <p><c>{7, FromPid, ToPid}</c></p> + </item> + <tag><c>EXIT2</c></tag> + <item> + <p><c>{8, FromPid, ToPid, Reason}</c></p> + </item> + </taglist> + </section> + + <section> + <title>New Ctrlmessages for distrvsn = 1 (Erlang/OTP R4)</title> + <taglist> + <tag><c>SEND_TT</c></tag> + <item> + <p><c>{12, Unused, ToPid, TraceToken}</c></p> + <p>Followed by <c>Message</c>.</p> + <p><c>Unused</c> is kept for backward compatibility.</p> + </item> + <tag><c>EXIT_TT</c></tag> + <item> + <p><c>{13, FromPid, ToPid, TraceToken, Reason}</c></p> + </item> + <tag><c>REG_SEND_TT</c></tag> + <item> + <p><c>{16, FromPid, Unused, ToName, TraceToken}</c></p> + <p>Followed by <c>Message</c>.</p> + <p><c>Unused</c> is kept for backward compatibility.</p> + </item> + <tag><c>EXIT2_TT</c></tag> + <item> + <p><c>{18, FromPid, ToPid, TraceToken, Reason}</c></p> + </item> + </taglist> + </section> + + <section> + <title>New Ctrlmessages for distrvsn = 2</title> + <p><c>distrvsn</c> 2 was never used.</p> + </section> + + <section> + <title>New Ctrlmessages for distrvsn = 3 (Erlang/OTP R5C)</title> + <p>None, but the version number was increased anyway.</p> + </section> + + <section> + <title>New Ctrlmessages for distrvsn = 4 (Erlang/OTP R6)</title> + <p>These are only recognized by Erlang nodes, not by hidden nodes.</p> + + <taglist> + <tag><c>MONITOR_P</c></tag> + <item> + <p><c>{19, FromPid, ToProc, Ref}</c>, where + <c>FromPid</c> = monitoring process and + <c>ToProc</c> = monitored process pid or name (atom)</p> + </item> + <tag><c>DEMONITOR_P</c></tag> + <item> + <p><c>{20, FromPid, ToProc, Ref}</c>, where + <c>FromPid</c> = monitoring process and + <c>ToProc</c> = monitored process pid or name (atom)</p> + <p>We include <c>FromPid</c> just in case we want to trace this.</p> + </item> + <tag><c>MONITOR_P_EXIT</c></tag> + <item> + <p><c>{21, FromProc, ToPid, Ref, Reason}</c>, where + <c>FromProc</c> = monitored process pid or name (atom), + <c>ToPid</c> = monitoring process, and + <c>Reason</c> = exit reason for the monitored process</p> + </item> + </taglist> + </section> +</chapter> diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 82215ead46..836a58a676 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -21,7 +21,6 @@ limitations under the License. </legalnotice> - <title>erl_driver</title> <prepared>Jakob Cederlund</prepared> <responsible>Jakob Cederlund</responsible> @@ -33,519 +32,545 @@ <file>erl_driver.xml</file> </header> <lib>erl_driver</lib> - <libsummary>API functions for an Erlang driver</libsummary> + <libsummary>API functions for an Erlang driver.</libsummary> <description> <p>An Erlang driver is a library containing a set of native driver - callback functions that the Erlang VM calls when certain - events occur. There may be multiple instances of a driver, each + callback functions that the Erlang Virtual Machine calls when certain + events occur. There can be multiple instances of a driver, each instance is associated with an Erlang port.</p> + <marker id="WARNING"/> - <warning><p><em>Use this functionality with extreme care!</em></p> + <warning> + <p><em>Use this functionality with extreme care.</em></p> <p>A driver callback is executed as a direct extension of the - native code of the VM. Execution is not made in a safe environment. - The VM can <em>not</em> provide the same services as provided when - executing Erlang code, such as preemptive scheduling or memory - protection. If the driver callback function doesn't behave well, - the whole VM will misbehave.</p> - <list> - <item><p>A driver callback that crash will crash the whole VM.</p></item> - <item><p>An erroneously implemented driver callback might cause - a VM internal state inconsistency which may cause a crash of the VM, - or miscellaneous misbehaviors of the VM at any point after the call - to the driver callback.</p></item> - <item><p>A driver callback that do <seealso marker="#lengthy_work">lengthy - work</seealso> before returning will degrade responsiveness of the VM, - and may cause miscellaneous strange behaviors. Such strange behaviors - include, but are not limited to, extreme memory usage, and bad load - balancing between schedulers. Strange behaviors that might occur due - to lengthy work may also vary between OTP releases.</p></item> + native code of the VM. Execution is not made in a safe environment. + The VM <em>cannot</em> provide the same services as provided when + executing Erlang code, such as pre-emptive scheduling or memory + protection. If the driver callback function does not behave well, + the whole VM will misbehave.</p> + <list type="bulleted"> + <item> + <p>A driver callback that crash will crash the whole VM.</p> + </item> + <item> + <p>An erroneously implemented driver callback can cause a VM + internal state inconsistency, which can cause a crash of the VM, + or miscellaneous misbehaviors of the VM at any point after the + call to the driver callback.</p> + </item> + <item> + <p>A driver callback doing + <seealso marker="#lengthy_work">lengthy work</seealso> before + returning degrades responsiveness of the VM and can cause + miscellaneous strange behaviors. Such strange behaviors + include, but are not limited to, extreme memory usage and bad + load balancing between schedulers. Strange behaviors that can + occur because of lengthy work can also vary between Erlang/OTP + releases.</p> + </item> </list> - </warning> - <p>As of erts version 5.5.3 the driver interface has been extended - (see <seealso marker="driver_entry#extended_marker">extended marker</seealso>). - The extended interface introduce + </warning> + + <p>As from ERTS 5.5.3 the driver interface has been extended + (see <seealso marker="driver_entry#extended_marker"> + <c>extended marker</c></seealso>). The extended interface introduces <seealso marker="#version_management">version management</seealso>, - the possibility to pass capability flags - (see <seealso marker="driver_entry#driver_flags">driver flags</seealso>) - to the runtime system at driver initialization, and some new - driver API functions. </p> + the possibility to pass capability flags (see + <seealso marker="driver_entry#driver_flags"> + <c>driver_flags</c></seealso>) to the runtime system at driver + initialization, and some new driver API functions.</p> + <note> - <p>As of erts version 5.9 old drivers have to be recompiled - and have to use the extended interface. They also have to be - adjusted to the - <seealso marker="#rewrites_for_64_bits">64-bit capable driver interface. - </seealso> - </p> + <p>As from ERTS 5.9 old drivers must be recompiled + and use the extended interface. They must also be adjusted to the + <seealso marker="#rewrites_for_64_bits"> + 64-bit capable driver interface</seealso>.</p> </note> + <p>The driver calls back to the emulator, using the API functions declared in <c>erl_driver.h</c>. They are used for - outputting data from the driver, using timers, etc.</p> + outputting data from the driver, using timers, and so on.</p> + <p>Each driver instance is associated with a port. Every port has a port owner process. Communication with the port is normally done through the port owner process. Most of the functions take the <c>port</c> handle as an argument. This identifies the driver - instance. Note that this port handle must be stored by the driver, + instance. Notice that this port handle must be stored by the driver, it is not given when the driver is called from the emulator (see - <seealso marker="driver_entry#emulator">driver_entry</seealso>).</p> + <seealso marker="driver_entry#emulator"> + <c>driver_entry</c></seealso>).</p> + <p>Some of the functions take a parameter of type - <c>ErlDrvBinary</c>, a driver binary. It should be both + <c>ErlDrvBinary</c>, a driver binary. It is to be both allocated and freed by the caller. Using a binary directly avoids one extra copying of data.</p> + <p>Many of the output functions have a "header buffer", with <c>hbuf</c> and <c>hlen</c> parameters. This buffer is sent as a list before the binary (or list, depending on port mode) that is sent. This is convenient when matching on messages received from - the port. (Although in the latest versions of Erlang, there is - the binary syntax, that enables you to match on the beginning of - a binary.) - <marker id="smp_support"></marker> -</p> - <p>In the runtime system with SMP support, drivers are locked either - on driver level or port level (driver instance level). By default - driver level locking will be used, i.e., only one emulator thread + the port. (Although in the latest Erlang versions there is + the binary syntax, which enables you to match on the beginning of + a binary.)</p> + <p><marker id="smp_support"></marker>In the runtime system with + SMP support, drivers are locked either on driver level + or port level (driver instance level). By default + driver level locking will be used, that is, only one emulator thread will execute code in the driver at a time. If port level locking - is used, multiple emulator threads may execute code in the driver - at the same time. There will only be one thread at a time calling - driver call-backs corresponding to the same port, though. In order - to enable port level locking set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c> + is used, multiple emulator threads can execute code in the driver + at the same time. Only one thread at a time will call + driver callbacks corresponding to the same port, though. + To enable port level locking, set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c> <seealso marker="driver_entry#driver_flags">driver flag</seealso> in - the <seealso marker="driver_entry">driver_entry</seealso> - used by the driver. When port level locking is used it is the - responsibility of the driver writer to synchronize all accesses + the <seealso marker="driver_entry"><c>driver_entry</c></seealso> + used by the driver. When port level locking is used, + the driver writer is responsible for synchronizing all accesses to data shared by the ports (driver instances).</p> + <p>Most drivers written before the runtime system with SMP - support existed will be able to run in the runtime system - with SMP support without being rewritten if driver + support existed can run in the runtime system + with SMP support, without being rewritten, if driver level locking is used.</p> + <note> <p>It is assumed that drivers do not access other drivers. If - drivers should access each other they have to provide their own - mechanism for thread safe synchronization. Such "inter driver + drivers access each other, they must provide their own + mechanism for thread-safe synchronization. Such "inter-driver communication" is strongly discouraged.</p> </note> + <p>Previously, in the runtime system without SMP support, - specific driver call-backs were always called from the same + specific driver callbacks were always called from the same thread. This is <em>not</em> the case in the runtime system with SMP support. Regardless of locking scheme used, calls - to driver call-backs may be made from different threads, e.g., - two consecutive calls to exactly the same call-back for exactly - the same port may be made from two different threads. This - will for <em>most</em> drivers not be a problem, but it might. - Drivers that depend on all call-backs being called in the - same thread, <em>have</em> to be rewritten before being used + to driver callbacks can be made from different threads. For example, + two consecutive calls to exactly the same callback for exactly + the same port can be made from two different threads. This + is for <em>most</em> drivers not a problem, but it can be. + Drivers that depend on all callbacks that are called in the + same thread, <em>must</em> be rewritten before they are used in the runtime system with SMP support.</p> + <note> <p>Regardless of locking scheme used, calls to driver - call-backs may be made from different threads.</p> + callbacks can be made from different threads.</p> </note> - <p>Most functions in this API are <em>not</em> thread-safe, i.e., - they may <em>not</em> be called from an arbitrary thread. Functions - that are not documented as thread-safe may only be called from - driver call-backs or function calls descending from a driver - call-back call. Note that driver call-backs may be called from + + <p>Most functions in this API are <em>not</em> thread-safe, that is, + they <em>cannot</em> be called from any thread. Functions + that are not documented as thread-safe can only be called from + driver callbacks or function calls descending from a driver + callback call. Notice that driver callbacks can be called from different threads. This, however, is not a problem for any - function in this API, since the emulator has control over + function in this API, as the emulator has control over these threads.</p> + <warning> - <p>Functions not explicitly documented as thread safe are - <em>not</em> thread safe. Also note that some functions - are <em>only</em> thread safe when used in a runtime + <p>Functions not explicitly documented as thread-safe are + <em>not</em> thread safe. Also notice that some functions + are <em>only</em> thread-safe when used in a runtime system with SMP support.</p> - <p>A function not explicitly documented as thread safe may at - some point in time have a thread safe implementation in the - runtime system. Such an implementation may however change to - a thread <em>unsafe</em> implementation at any time <em>without - any notice</em> at all. - </p> - <p><em>Only use functions explicitly documented as thread safe - from arbitrary threads.</em></p> + <p>A function not explicitly documented as thread-safe can, at + some point in time, have a thread-safe implementation in the + runtime system. Such an implementation can however change to + a thread <em>unsafe</em> implementation at any time <em>without + any notice</em>.</p> + <p><em>Only use functions explicitly documented as thread-safe + from arbitrary threads.</em></p> </warning> - <p><marker id="lengthy_work"/> - As mentioned in the <seealso marker="#WARNING">warning</seealso> text at - the beginning of this document it is of vital importance that a driver callback - does return relatively fast. It is hard to give an exact maximum amount - of time that a driver callback is allowed to work, but as a rule of thumb - a well behaving driver callback should return before a millisecond has - passed. This can be achieved using different approaches. - If you have full control over the code that are to execute in the driver - callback, the best approach is to divide the work into multiple chunks of - work and trigger multiple calls to the - <seealso marker="driver_entry#timeout">timeout callback</seealso> using - zero timeouts. The - <seealso marker="#erl_drv_consume_timeslice"><c>erl_drv_consume_timeslice()</c></seealso> - function can be useful in order to determine when to trigger such - timeout callback calls. It might, however, not always be possible to - implement it this way, e.g. when calling third party libraries. In this - case you typically want to dispatch the work to another thread. - Information about thread primitives can be found below.</p> + + <p><marker id="lengthy_work"/> + As mentioned in the <seealso marker="#WARNING">warning</seealso> text at + the beginning of this section, it is of vital importance that a driver + callback returns relatively fast. It is difficult to give an exact + maximum amount of time that a driver callback is allowed to work, but + usually a well-behaving driver callback is to return within 1 millisecond. + This can be achieved using different approaches. + If you have full control over the code to execute in the driver + callback, the best approach is to divide the work into multiple chunks of + work, and trigger multiple calls to the + <seealso marker="driver_entry#timeout">time-out callback</seealso> using + zero time-outs. Function <seealso marker="#erl_drv_consume_timeslice"> + <c>erl_drv_consume_timeslice</c></seealso> can be useful to + determine when to trigger such time-out callback calls. However, sometimes + it cannot be implemented this way, for example when calling + third-party libraries. In this case, you typically want to dispatch the + work to another thread. Information about thread primitives is provided + below.</p> </description> <section> - <title>FUNCTIONALITY</title> + <title>Functionality</title> <p>All functions that a driver needs to do with Erlang are - performed through driver API functions. There are functions + performed through driver API functions. Functions exist for the following functionality:</p> + <taglist> <tag>Timer functions</tag> - <item>Timer functions are used to control the timer that a driver - may use. The timer will have the emulator call the - <seealso marker="driver_entry#timeout">timeout</seealso> entry - function after a specified time. Only one timer is available - for each driver instance.</item> + <item> + <p>Control the timer that a driver can use. The timer has the + emulator call the <seealso marker="driver_entry#timeout"> + <c>timeout</c></seealso> entry function after a specified time. + Only one timer is available for each driver instance.</p> + </item> <tag>Queue handling</tag> <item> <p>Every driver instance has an associated queue. This queue is a - <c>SysIOVec</c> that works as a buffer. It's mostly used for - the driver to buffer data that should be written to a device, + <c>SysIOVec</c>, which works as a buffer. It is mostly used for + the driver to buffer data that is to be written to a device, it is a byte stream. If the port owner process closes the - driver, and the queue is not empty, the driver will not be + driver, and the queue is not empty, the driver is not closed. This enables the driver to flush its buffers before closing.</p> - <p>The queue can be manipulated from arbitrary threads if - a port data lock is used. See documentation of the - <seealso marker="#ErlDrvPDL">ErlDrvPDL</seealso> type for - more information.</p> + <p>The queue can be manipulated from any threads if + a port data lock is used. For more information, see + <seealso marker="#ErlDrvPDL"><c>ErlDrvPDL</c></seealso>.</p> </item> <tag>Output functions</tag> - <item>With the output functions, the driver sends data back to - the emulator. They will be received as messages by the port owner - process, see <c>open_port/2</c>. The vector function and the - function taking a driver binary are faster, because they avoid - copying the data buffer. There is also a fast way of sending - terms from the driver, without going through the binary term - format.</item> + <item> + <p>With these functions, the driver sends data back to the emulator. + The data is received as messages by the port owner process, see + <seealso marker="erlang:open_port/2"> + <c>erlang:open_port/2</c></seealso>. The vector function and the + function taking a driver binary are faster, as they avoid + copying the data buffer. There is also a fast way of sending + terms from the driver, without going through the binary term + format.</p></item> <tag>Failure</tag> - <item>The driver can exit and signal errors up to Erlang. This is - only for severe errors, when the driver can't possibly keep - open.</item> + <item> + <p>The driver can exit and signal errors up to Erlang. This is + only for severe errors, when the driver cannot possibly keep + open.</p> + </item> <tag>Asynchronous calls</tag> - <item>The latest Erlang versions (R7B and later) has provision for - asynchronous function calls, using a thread pool provided by - Erlang. There is also a select call, that can be used for - asynchronous drivers.</item> + <item> + <p>Erlang/OTP R7B and later versions have provision for + asynchronous function calls, using a thread pool provided by + Erlang. There is also a select call, which can be used for + asynchronous drivers.</p> + </item> <tag><marker id="multi_threading"/>Multi-threading</tag> <item> - <p>A POSIX thread like API for multi-threading is provided. The - Erlang driver thread API only provide a subset of the functionality - provided by the POSIX thread API. The subset provided is - more or less the basic functionality needed for multi-threaded - programming: - </p> - <list> - <item><seealso marker="#ErlDrvTid">Threads</seealso></item> - <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item> - <item><seealso marker="#ErlDrvCond">Condition variables</seealso></item> - <item><seealso marker="#ErlDrvRWLock">Read/Write locks</seealso></item> - <item><seealso marker="#ErlDrvTSDKey">Thread specific data</seealso></item> - </list> - <p>The Erlang driver thread API can be used in conjunction with - the POSIX thread API on UN-ices and with the Windows native thread - API on Windows. The Erlang driver thread API has the advantage of - being portable, but there might exist situations where you want to - use functionality from the POSIX thread API or the Windows - native thread API. - </p> - <p>The Erlang driver thread API only returns error codes when it is - reasonable to recover from an error condition. If it isn't reasonable - to recover from an error condition, the whole runtime system is - terminated. For example, if a create mutex operation fails, an error - code is returned, but if a lock operation on a mutex fails, the - whole runtime system is terminated. - </p> - <p>Note that there exists no "condition variable wait with timeout" in - the Erlang driver thread API. This is due to issues with - <c>pthread_cond_timedwait()</c>. When the system clock suddenly - is changed, it isn't always guaranteed that you will wake up from - the call as expected. An Erlang runtime system has to be able to - cope with sudden changes of the system clock. Therefore, we have - omitted it from the Erlang driver thread API. In the Erlang driver - case, timeouts can and should be handled with the timer functionality - of the Erlang driver API. - </p> - <p>In order for the Erlang driver thread API to function, thread - support has to be enabled in the runtime system. An Erlang driver - can check if thread support is enabled by use of - <seealso marker="#driver_system_info">driver_system_info()</seealso>. - Note that some functions in the Erlang driver API are thread-safe - only when the runtime system has SMP support, also this - information can be retrieved via - <seealso marker="#driver_system_info">driver_system_info()</seealso>. - Also note that a lot of functions in the Erlang driver API are - <em>not</em> thread-safe regardless of whether SMP support is - enabled or not. If a function isn't documented as thread-safe it - is <em>not</em> thread-safe. - </p> - <p><em>NOTE</em>: When executing in an emulator thread, it is - <em>very important</em> that you unlock <em>all</em> locks you - have locked before letting the thread out of your control; - otherwise, you are <em>very likely</em> to deadlock the whole - emulator. If you need to use thread specific data in an emulator - thread, only have the thread specific data set while the thread is - under your control, and clear the thread specific data before - you let the thread out of your control. - </p> - <p>In the future there will probably be debug functionality - integrated with the Erlang driver thread API. All functions - that create entities take a <c>name</c> argument. Currently - the <c>name</c> argument is unused, but it will be used when - the debug functionality has been implemented. If you name all - entities created well, the debug functionality will be able - to give you better error reports. - </p> + <p>A POSIX thread like API for multi-threading is provided. The + Erlang driver thread API only provides a subset of the functionality + provided by the POSIX thread API. The subset provided is + more or less the basic functionality needed for multi-threaded + programming:</p> + <list> + <item><seealso marker="#ErlDrvTid">Threads</seealso></item> + <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item> + <item><seealso marker="#ErlDrvCond"> + Condition variables</seealso></item> + <item><seealso marker="#ErlDrvRWLock"> + Read/write locks</seealso></item> + <item><seealso marker="#ErlDrvTSDKey"> + Thread-specific data</seealso></item> + </list> + <p>The Erlang driver thread API can be used in conjunction with + the POSIX thread API on UN-ices and with the Windows native thread + API on Windows. The Erlang driver thread API has the advantage of + being portable, but there can exist situations where you want to + use functionality from the POSIX thread API or the Windows + native thread API.</p> + <p>The Erlang driver thread API only returns error codes when it is + reasonable to recover from an error condition. If it is not reasonable + to recover from an error condition, the whole runtime system is + terminated. For example, if a create mutex operation fails, an error + code is returned, but if a lock operation on a mutex fails, the + whole runtime system is terminated.</p> + <p>Notice that there is no "condition variable wait with time-out" in + the Erlang driver thread API. This because of issues with + <c>pthread_cond_timedwait</c>. When the system clock suddenly + is changed, it is not always guaranteed that you will wake up from + the call as expected. An Erlang runtime system must be able to + cope with sudden changes of the system clock. Therefore, we have + omitted it from the Erlang driver thread API. In the Erlang driver + case, time-outs can and are to be handled with the timer functionality + of the Erlang driver API.</p> + <p>In order for the Erlang driver thread API to function, thread + support must be enabled in the runtime system. An Erlang driver + can check if thread support is enabled by use of + <seealso marker="#driver_system_info"> + <c>driver_system_info</c></seealso>. + Notice that some functions in the Erlang driver API are thread-safe + only when the runtime system has SMP support, also this + information can be retrieved through + <seealso marker="#driver_system_info"> + <c>driver_system_info</c></seealso>. + Also notice that many functions in the Erlang driver API are + <em>not</em> thread-safe, regardless of whether SMP support is + enabled or not. If a function is not documented as thread-safe, it + is <em>not</em> thread-safe.</p> + <note> + <p>When executing in an emulator thread, it is + <em>very important</em> that you unlock <em>all</em> locks you + have locked before letting the thread out of your control; + otherwise you are <em>very likely</em> to deadlock the whole + emulator.</p> + <p>If you need to use thread-specific data in an emulator + thread, only have the thread-specific data set while the thread is + under your control, and clear the thread-specific data before + you let the thread out of your control.</p> + </note> + <p>In the future, debug functionality will probably be + integrated with the Erlang driver thread API. All functions + that create entities take a <c>name</c> argument. Currently + the <c>name</c> argument is unused, but it will be used when + the debug functionality is implemented. If you name all + entities created well, the debug functionality will be able + to give you better error reports.</p> + </item> + <tag>Adding/removing drivers</tag> + <item> + <p>A driver can add and later remove drivers.</p> </item> - <tag>Adding / removing drivers</tag> - <item><p>A driver can add and later remove drivers.</p></item> <tag>Monitoring processes</tag> - <item><p>A driver can monitor a process that does not own a port.</p></item> + <item> + <p>A driver can monitor a process that does not own a port.</p> + </item> <tag><marker id="version_management"/>Version management</tag> <item> <p>Version management is enabled for drivers that have set the - <seealso marker="driver_entry#extended_marker">extended_marker</seealso> - field of their - <seealso marker="driver_entry">driver_entry</seealso> - to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines - <c>ERL_DRV_EXTENDED_MARKER</c>, - <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, and - <c>ERL_DRV_EXTENDED_MINOR_VERSION</c>. - <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> will be incremented when - driver incompatible changes are made to the Erlang runtime - system. Normally it will suffice to recompile drivers when the - <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it - could, under rare circumstances, mean that drivers have to - be slightly modified. If so, this will of course be documented. - <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> will be incremented when - new features are added. The runtime system uses the minor version - of the driver to determine what features to use. - The runtime system will normally refuse to load a driver if the major + <seealso marker="driver_entry#extended_marker"> + <c>extended_marker</c></seealso> field of their + <seealso marker="driver_entry"><c>driver_entry</c></seealso> + to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines:</p> + <list type="bulleted"> + <item> + <p><c>ERL_DRV_EXTENDED_MARKER</c></p> + </item> + <item> + <p><c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, which is incremented when + driver incompatible changes are made to the Erlang runtime + system. Normally it suffices to recompile drivers when + <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it + can, under rare circumstances, mean that drivers must + be slightly modified. If so, this will of course be + documented.</p> + </item> + <item> + <p><c>ERL_DRV_EXTENDED_MINOR_VERSION</c>, which is incremented when + new features are added. The runtime system uses the minor version + of the driver to determine what features to use.</p> + </item> + </list> + <p>The runtime system normally refuses to load a driver if the major versions differ, or if the major versions are equal and the minor version used by the driver is greater than the one used by the runtime system. Old drivers with lower major versions - will however be allowed after a bump of the major version during - a transition period of two major releases. Such old drivers might - however fail if deprecated features are used.</p> - <p>The emulator will refuse to load a driver that does not use - the extended driver interface, - to allow for 64-bit capable drivers, - since incompatible type changes for the callbacks - <seealso marker="driver_entry#output">output</seealso>, - <seealso marker="driver_entry#control">control</seealso> and - <seealso marker="driver_entry#call">call</seealso> - were introduced in release R15B. A driver written - with the old types would compile with warnings and when - called return garbage sizes to the emulator causing it - to read random memory and create huge incorrect result blobs.</p> - <p>Therefore it is not enough to just recompile drivers written with - version management for pre-R15B types; the types have to be changed - in the driver suggesting other rewrites especially regarding - size variables. Investigate all warnings when recompiling!</p> - <p>Also, the API driver functions <c>driver_output*</c>, - <c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c> - and the <c>driver_*</c> queue functions were changed to have - larger length arguments and return values. This is a - lesser problem since code that passes smaller types - will get them auto converted in the calls and as long as - the driver does not handle sizes that overflow an <c>int</c> - all will work as before.</p> + are however allowed after a bump of the major version during + a transition period of two major releases. Such old drivers can, + however, fail if deprecated features are used.</p> + <p>The emulator refuses to load a driver that does not use + the extended driver interface, to allow for 64-bit capable drivers, + as incompatible type changes for the callbacks + <seealso marker="driver_entry#output"><c>output</c></seealso>, + <seealso marker="driver_entry#control"><c>control</c></seealso>, and + <seealso marker="driver_entry#call"><c>call</c></seealso> + were introduced in Erlang/OTP R15B. A driver written + with the old types would compile with warnings and when + called return garbage sizes to the emulator, causing it + to read random memory and create huge incorrect result blobs.</p> + <p>Therefore it is not enough to only recompile drivers written with + version management for pre R15B types; the types must be changed + in the driver suggesting other rewrites, especially regarding size + variables. <em>Investigate all warnings when recompiling.</em></p> + <p>Also, the API driver functions <c>driver_output*</c> and + <c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c>, and the + <c>driver_*</c> queue functions were changed to have + larger length arguments and return values. This is a + lesser problem, as code that passes smaller types + gets them auto-converted in the calls, and as long as + the driver does not handle sizes that overflow an <c>int</c>, + all will work as before.</p> </item> - <tag><marker id="time_measurement"/>Time Measurement</tag> - <item><p>Support for time measurement in drivers:</p> - <list> - <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item> - <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item> - <item><seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso></item> - <item><seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso></item> - <item><seealso marker="#erl_drv_convert_time_unit"><c>erl_drv_convert_time_unit()</c></seealso></item> - </list> + <tag><marker id="time_measurement"/>Time measurement</tag> + <item> + <p>Support for time measurement in drivers:</p> + <list type="bulleted"> + <item><seealso marker="#ErlDrvTime"> + <c>ErlDrvTime</c></seealso></item> + <item><seealso marker="#ErlDrvTimeUnit"> + <c>ErlDrvTimeUnit</c></seealso></item> + <item><seealso marker="#erl_drv_monotonic_time"> + <c>erl_drv_monotonic_time</c></seealso></item> + <item><seealso marker="#erl_drv_time_offset"> + <c>erl_drv_time_offset</c></seealso></item> + <item><seealso marker="#erl_drv_convert_time_unit"> + <c>erl_drv_convert_time_unit</c></seealso></item> + </list> </item> </taglist> </section> <section> <marker id="rewrites_for_64_bits"/> - <title> - REWRITES FOR 64-BIT DRIVER INTERFACE - </title> - <p> - For erts-5.9 two new integer types - <seealso marker="#ErlDrvSizeT">ErlDrvSizeT</seealso> and - <seealso marker="#ErlDrvSSizeT">ErlDrvSSizeT</seealso> - were introduced that can hold 64-bit sizes if necessary. - </p> - <p> - To not update a driver and just recompile it probably works + <title>Rewrites for 64-Bit Driver Interface</title> + <p>ERTS 5.9 introduced two new integer types, + <seealso marker="#ErlDrvSizeT"><c>ErlDrvSizeT</c></seealso> and + <seealso marker="#ErlDrvSSizeT"><c>ErlDrvSSizeT</c></seealso>, + which can hold 64-bit sizes if necessary.</p> + + <p>To not update a driver and only recompile, it probably works when building for a 32-bit machine creating a false sense of security. Hopefully that will generate many important warnings. - But when recompiling the same driver later on for a 64-bit machine + But when recompiling the same driver later on for a 64-bit machine, there <em>will</em> be warnings and almost certainly crashes. - So it is a BAD idea to postpone updating the driver and - not fixing the warnings! - </p> - <p> - When recompiling with <c>gcc</c> use the <c>-Wstrict-prototypes</c> - flag to get better warnings. Try to find a similar flag if you - are using some other compiler. - </p> - <p> - Here follows a checklist for rewriting a pre erts-5.9 driver, - most important first. - </p> + So it is a <em>bad</em> idea to postpone updating the driver and + not fixing the warnings.</p> + + <p>When recompiling with <c>gcc</c>, use flag <c>-Wstrict-prototypes</c> + to get better warnings. Try to find a similar flag if you use + another compiler.</p> + + <p>The following is a checklist for rewriting a pre ERTS 5.9 driver, + most important first:</p> + <taglist> <tag>Return types for driver callbacks</tag> <item> - <p> - Rewrite driver callback + <p>Rrewrite driver callback <seealso marker="driver_entry#control"><c>control</c></seealso> - to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>. - </p> - <p> - Rewrite driver callback + to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.</p> + <p>Rewrite driver callback <seealso marker="driver_entry#call"><c>call</c></seealso> - to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>. - </p> + to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.</p> <note> - <p> - These changes are essential to not crash the emulator + <p>These changes are essential not to crash the emulator or worse cause malfunction. - Without them a driver may return garbage in the high 32 bits - to the emulator causing it to build a huge result from random - bytes either crashing on memory allocation or succeeding with - a random result from the driver call. - </p> + Without them a driver can return garbage in the high 32 bits + to the emulator, causing it to build a huge result from random + bytes, either crashing on memory allocation or succeeding with + a random result from the driver call.</p> </note> </item> <tag>Arguments to driver callbacks</tag> <item> - <p> - Driver callback + <p>Driver callback <seealso marker="driver_entry#output"><c>output</c></seealso> now gets <c>ErlDrvSizeT</c> as 3rd argument instead - of previously <c>int</c>. - </p> - <p> - Driver callback + of previously <c>int</c>.</p> + <p>Driver callback <seealso marker="driver_entry#control"><c>control</c></seealso> now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead - of previously <c>int</c>. - </p> - <p> - Driver callback + of previously <c>int</c>.</p> + <p>Driver callback <seealso marker="driver_entry#call"><c>call</c></seealso> now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead - of previously <c>int</c>. - </p> - <p> - Sane compiler's calling conventions probably make these changes + of previously <c>int</c>.</p> + <p>Sane compiler's calling conventions probably make these changes necessary only for a driver to handle data chunks that require - 64-bit size fields (mostly larger than 2 GB since that is what + 64-bit size fields (mostly larger than 2 GB, as that is what an <c>int</c> of 32 bits can hold). But it is possible to think of non-sane calling conventions that would make the driver - callbacks mix up the arguments causing malfunction. - </p> + callbacks mix up the arguments causing malfunction.</p> <note> - <p> - The argument type change is from signed to unsigned which - may cause problems for e.g. loop termination conditions or - error conditions if you just change the types all over the place. - </p> + <p>The argument type change is from signed to unsigned. This + can cause problems for, for example, loop termination conditions or + error conditions if you only change the types all over the place. + </p> </note> </item> <tag>Larger <c>size</c> field in <c>ErlIOVec</c></tag> <item> - <p> - The <c>size</c> field in + <p>The <c>size</c> field in <seealso marker="#ErlIOVec"><c>ErlIOVec</c></seealso> has been changed to <c>ErlDrvSizeT</c> from <c>int</c>. - Check all code that use that field. - </p> - <p> - Automatic type casting probably makes these changes necessary only - for a driver that encounters sizes larger than 32 bits. - </p> + Check all code that use that field.</p> + <p>Automatic type-casting probably makes these changes necessary only + for a driver that encounters sizes > 32 bits.</p> <note> - <p> - The <c>size</c> field changed from signed to unsigned which - may cause problems for e.g. loop termination conditions or - error conditions if you just change the types all over the place. - </p> + <p>The <c>size</c> field changed from signed to unsigned. This + can cause problems for, for example, loop termination conditions or + error conditions if you only change the types all over the place. + </p> </note> </item> <tag>Arguments and return values in the driver API</tag> <item> - <p> - Many driver API functions have changed argument type + <p>Many driver API functions have changed argument type and/or return value to <c>ErlDrvSizeT</c> from mostly <c>int</c>. - Automatic type casting probably makes these changes necessary only - for a driver that encounters sizes larger than 32 bits. - </p> + Automatic type-casting probably makes these changes necessary only + for a driver that encounters sizes > 32 bits.</p> <taglist> - <tag><seealso marker="#driver_output">driver_output</seealso></tag> + <tag><seealso marker="#driver_output"> + <c>driver_output</c></seealso></tag> <item>3rd argument</item> - <tag><seealso marker="#driver_output2">driver_output2</seealso></tag> + <tag><seealso marker="#driver_output2"> + <c>driver_output2</c></seealso></tag> <item>3rd and 5th arguments</item> - <tag> - <seealso marker="#driver_output_binary">driver_output_binary</seealso> - </tag> - <item>3rd 5th and 6th arguments</item> - <tag><seealso marker="#driver_outputv">driver_outputv</seealso></tag> + <tag><seealso marker="#driver_output_binary"> + <c>driver_output_binary</c></seealso></tag> + <item>3rd, 5th, and 6th arguments</item> + <tag><seealso marker="#driver_outputv"> + <c>driver_outputv</c></seealso></tag> <item>3rd and 5th arguments</item> - <tag> - <seealso marker="#driver_vec_to_buf">driver_vec_to_buf</seealso> - </tag> + <tag><seealso marker="#driver_vec_to_buf"> + <c>driver_vec_to_buf</c></seealso></tag> <item>3rd argument and return value</item> - <tag><seealso marker="#driver_alloc">driver_alloc</seealso></tag> + <tag><seealso marker="#driver_alloc"> + <c>driver_alloc</c></seealso></tag> <item>1st argument</item> - <tag><seealso marker="#driver_realloc">driver_realloc</seealso></tag> + <tag><seealso marker="#driver_realloc"> + <c>driver_realloc</c></seealso></tag> <item>2nd argument</item> - <tag> - <seealso marker="#driver_alloc_binary">driver_alloc_binary</seealso> - </tag> + <tag><seealso marker="#driver_alloc_binary"> + <c>driver_alloc_binary</c></seealso></tag> <item>1st argument</item> - <tag> - <seealso marker="#driver_realloc_binary">driver_realloc_binary</seealso> - </tag> + <tag><seealso marker="#driver_realloc_binary"> + <c>driver_realloc_binary</c></seealso></tag> <item>2nd argument</item> - <tag><seealso marker="#driver_enq">driver_enq</seealso></tag> + <tag><seealso marker="#driver_enq"> + <c>driver_enq</c></seealso></tag> <item>3rd argument</item> - <tag><seealso marker="#driver_pushq">driver_pushq</seealso></tag> + <tag><seealso marker="#driver_pushq"> + <c>driver_pushq</c></seealso></tag> <item>3rd argument</item> - <tag><seealso marker="#driver_deq">driver_deq</seealso></tag> + <tag><seealso marker="#driver_deq"> + <c>driver_deq</c></seealso></tag> <item>2nd argument and return value</item> - <tag><seealso marker="#driver_sizeq">driver_sizeq</seealso></tag> - <item>return value</item> - <tag><seealso marker="#driver_enq_bin">driver_enq_bin</seealso></tag> - <item>3rd and 4th argument</item> - <tag><seealso marker="#driver_pushq_bin">driver_pushq_bin</seealso></tag> - <item>3rd and 4th argument</item> - <tag><seealso marker="#driver_enqv">driver_enqv</seealso></tag> + <tag><seealso marker="#driver_sizeq"> + <c>driver_sizeq</c></seealso></tag> + <item>Return value</item> + <tag><seealso marker="#driver_enq_bin"> + <c>driver_enq_bin</c></seealso></tag> + <item>3rd and 4th arguments</item> + <tag><seealso marker="#driver_pushq_bin"> + <c>driver_pushq_bin</c></seealso></tag> + <item>3rd and 4th arguments</item> + <tag><seealso marker="#driver_enqv"> + <c>driver_enqv</c></seealso></tag> <item>3rd argument</item> - <tag><seealso marker="#driver_pushqv">driver_pushqv</seealso></tag> + <tag><seealso marker="#driver_pushqv"> + <c>driver_pushqv</c></seealso></tag> <item>3rd argument</item> - <tag><seealso marker="#driver_peekqv">driver_peekqv</seealso></tag> - <item>return value</item> + <tag><seealso marker="#driver_peekqv"> + <c>driver_peekqv</c></seealso></tag> + <item>Return value</item> </taglist> <note> - <p> - This is a change from signed to unsigned which - may cause problems for e.g. loop termination conditions and - error conditions if you just change the types all over the place. - </p> + <p>This is a change from signed to unsigned. This can cause + problems for, for example, loop termination conditions and error + conditions if you only change the types all over the place.</p> </note> </item> </taglist> </section> <section> - <title>DATA TYPES</title> - + <title>Data Types</title> <taglist> - <tag><marker id="ErlDrvSizeT"/>ErlDrvSizeT</tag> - <item><p>An unsigned integer type to be used as <c>size_t</c></p></item> - <tag><marker id="ErlDrvSSizeT"/>ErlDrvSSizeT</tag> - <item><p>A signed integer type the size of <c>ErlDrvSizeT</c></p></item> - <tag><marker id="ErlDrvSysInfo"/>ErlDrvSysInfo</tag> + <tag><marker id="ErlDrvSizeT"/><c>ErlDrvSizeT</c></tag> <item> - <p/> - <code type="none"> + <p>An unsigned integer type to be used as <c>size_t</c>.</p> + </item> + <tag><marker id="ErlDrvSSizeT"/><c>ErlDrvSSizeT</c></tag> + <item> + <p>A signed integer type, the size of <c>ErlDrvSizeT</c>.</p> + </item> + <tag><marker id="ErlDrvSysInfo"/><c>ErlDrvSysInfo</c></tag> + <item> + <code type="none"> typedef struct ErlDrvSysInfo { int driver_major_version; int driver_minor_version; @@ -558,2610 +583,2638 @@ typedef struct ErlDrvSysInfo { int nif_major_version; int nif_minor_version; int dirty_scheduler_support; -} ErlDrvSysInfo; - </code> - - <p> - The <c>ErlDrvSysInfo</c> structure is used for storage of - information about the Erlang runtime system. - <seealso marker="#driver_system_info">driver_system_info()</seealso> - will write the system information when passed a reference to - a <c>ErlDrvSysInfo</c> structure. A description of the - fields in the structure follows: - </p> - <taglist> - <tag><c>driver_major_version</c></tag> - <item>The value of - <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso> - when the runtime system was compiled. This value is the same - as the value of - <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso> - used when compiling the driver; otherwise, the runtime system - would have refused to load the driver. - </item> - <tag><c>driver_minor_version</c></tag> - <item>The value of - <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso> - when the runtime system was compiled. This value might differ - from the value of - <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso> - used when compiling the driver. - </item> - <tag><c>erts_version</c></tag> - <item>A string containing the version number of the runtime system - (the same as returned by - <seealso marker="erlang#system_info_version">erlang:system_info(version)</seealso>). - </item> - <tag><c>otp_release</c></tag> - <item>A string containing the OTP release number - (the same as returned by - <seealso marker="erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>). - </item> - <tag><c>thread_support</c></tag> - <item>A value <c>!= 0</c> if the runtime system has thread support; - otherwise, <c>0</c>. - </item> - <tag><c>smp_support</c></tag> - <item>A value <c>!= 0</c> if the runtime system has SMP support; - otherwise, <c>0</c>. - </item> - <tag><c>async_threads</c></tag> - <item>The number of async threads in the async thread pool used - by <seealso marker="#driver_async">driver_async()</seealso> - (the same as returned by - <seealso marker="erlang#system_info_thread_pool_size">erlang:system_info(thread_pool_size)</seealso>). - </item> - <tag><c>scheduler_threads</c></tag> - <item>The number of scheduler threads used by the runtime system - (the same as returned by - <seealso marker="erlang#system_info_schedulers">erlang:system_info(schedulers)</seealso>). - </item> - <tag><c>nif_major_version</c></tag> - <item>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime system was compiled. - </item> - <tag><c>nif_minor_version</c></tag> - <item>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime system was compiled. - </item> - <tag><c>dirty_scheduler_support</c></tag> - <item>A value <c>!= 0</c> if the runtime system has support for dirty scheduler threads; - otherwise <c>0</c>. - </item> +} ErlDrvSysInfo;</code> + <p>The <c>ErlDrvSysInfo</c> structure is used for storage of + information about the Erlang runtime system. + <seealso marker="#driver_system_info"> + <c>driver_system_info</c></seealso> + writes the system information when passed a reference to + a <c>ErlDrvSysInfo</c> structure. The fields in the structure + are as follows:</p> + <taglist> + <tag><c>driver_major_version</c></tag> + <item> + <p>The value of <seealso marker="#version_management"> + <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c></seealso> + when the runtime system was compiled. This value is the same + as the value of <seealso marker="#version_management"> + <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c></seealso> + used when compiling the driver; otherwise the runtime system + would have refused to load the driver.</p> + </item> + <tag><c>driver_minor_version</c></tag> + <item> + <p>The value of <seealso marker="#version_management"> + <c>ERL_DRV_EXTENDED_MINOR_VERSION</c></seealso> + when the runtime system was compiled. This value can differ + from the value of <seealso marker="#version_management"> + <c>ERL_DRV_EXTENDED_MINOR_VERSION</c></seealso> + used when compiling the driver.</p> + </item> + <tag><c>erts_version</c></tag> + <item> + <p>A string containing the version number of the runtime system + (the same as returned by + <seealso marker="erlang#system_info_version"> + <c>erlang:system_info(version)</c></seealso>).</p> + </item> + <tag><c>otp_release</c></tag> + <item> + <p>A string containing the OTP release number + (the same as returned by + <seealso marker="erlang#system_info_otp_release"> + <c>erlang:system_info(otp_release)</c></seealso>).</p> + </item> + <tag><c>thread_support</c></tag> + <item> + <p>A value <c>!= 0</c> if the runtime system has thread support; + otherwise <c>0</c>.</p> + </item> + <tag><c>smp_support</c></tag> + <item> + <p>A value <c>!= 0</c> if the runtime system has SMP support; + otherwise <c>0</c>.</p> + </item> + <tag><c>async_threads</c></tag> + <item> + <p>The number of async threads in the async thread pool used by + <seealso marker="#driver_async"><c>driver_async</c></seealso> + (the same as returned by + <seealso marker="erlang#system_info_thread_pool_size"> + <c>erlang:system_info(thread_pool_size)</c></seealso>).</p> + </item> + <tag><c>scheduler_threads</c></tag> + <item> + <p>The number of scheduler threads used by the runtime system + (the same as returned by + <seealso marker="erlang#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>).</p> + </item> + <tag><c>nif_major_version</c></tag> + <item> + <p>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime + system was compiled.</p> + </item> + <tag><c>nif_minor_version</c></tag> + <item> + <p>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime + system was compiled.</p> + </item> + <tag><c>dirty_scheduler_support</c></tag> + <item> + <p>A value <c>!= 0</c> if the runtime system has support for dirty + scheduler threads; otherwise <c>0</c>.</p> + </item> </taglist> </item> - <tag><marker id="ErlDrvBinary"/>ErlDrvBinary</tag> + <tag><marker id="ErlDrvBinary"/><c>ErlDrvBinary</c></tag> <item> - <p/> - <code type="none"> + <code type="none"> typedef struct ErlDrvBinary { ErlDrvSint orig_size; char orig_bytes[]; -} ErlDrvBinary; -</code> +} ErlDrvBinary;</code> <p>The <c>ErlDrvBinary</c> structure is a binary, as sent between the emulator and the driver. All binaries are reference counted; when <c>driver_binary_free</c> is called, the reference count is decremented, when it reaches zero, - the binary is deallocated. The <c>orig_size</c> is the size - of the binary, and <c>orig_bytes</c> is the buffer. The - <c>ErlDrvBinary</c> does not have a fixed size, its size is + the binary is deallocated. <c>orig_size</c> is the binary size + and <c>orig_bytes</c> is the buffer. + <c>ErlDrvBinary</c> has not a fixed size, its size is <c>orig_size + 2 * sizeof(int)</c>.</p> <note> <p>The <c>refc</c> field has been removed. The reference count of an <c>ErlDrvBinary</c> is now stored elsewhere. The - reference count of an <c>ErlDrvBinary</c> can be accessed via - <seealso marker="#driver_binary_get_refc">driver_binary_get_refc()</seealso>, - <seealso marker="#driver_binary_inc_refc">driver_binary_inc_refc()</seealso>, - and - <seealso marker="#driver_binary_dec_refc">driver_binary_dec_refc()</seealso>.</p> + reference count of an <c>ErlDrvBinary</c> can be accessed through + <seealso marker="#driver_binary_get_refc"> + <c>driver_binary_get_refc</c></seealso>, + <seealso marker="#driver_binary_inc_refc"> + <c>driver_binary_inc_refc</c></seealso>, and + <seealso marker="#driver_binary_dec_refc"> + <c>driver_binary_dec_refc</c></seealso>.</p> </note> <p>Some driver calls, such as <c>driver_enq_binary</c>, increment the driver reference count, and others, such as <c>driver_deq</c> decrement it.</p> - <p>Using a driver binary instead of a normal buffer, is often - faster, since the emulator doesn't need to copy the data, + <p>Using a driver binary instead of a normal buffer is often + faster, as the emulator needs not to copy the data, only the pointer is used.</p> <p>A driver binary allocated in the driver, with - <c>driver_alloc_binary</c>, should be freed in the driver (unless otherwise stated), - with <c>driver_free_binary</c>. (Note that this doesn't + <c>driver_alloc_binary</c>, is to be freed in the driver + (unless otherwise stated) + with <c>driver_free_binary</c>. (Notice that this does not necessarily deallocate it, if the driver is still referred in the emulator, the ref-count will not go to zero.)</p> <p>Driver binaries are used in the <c>driver_output2</c> and <c>driver_outputv</c> calls, and in the queue. Also the - driver call-back <seealso marker="driver_entry#outputv">outputv</seealso> uses driver - binaries.</p> - <p>If the driver for some reason or another, wants to keep a - driver binary around, in a static variable for instance, the - reference count should be incremented, - and the binary can later be freed in the <seealso marker="driver_entry#stop">stop</seealso> call-back, with - <c>driver_free_binary</c>.</p> - <p>Note that since a driver binary is shared by the driver and - the emulator, a binary received from the emulator or sent to - the emulator, must not be changed by the driver.</p> - <p>Since erts version 5.5 (OTP release R11B), orig_bytes is + driver callback <seealso marker="driver_entry#outputv"> + <c>outputv</c></seealso> uses driver binaries.</p> + <p>If the driver for some reason wants to keep a + driver binary around, for example in a static variable, the + reference count is to be incremented, and the binary can later + be freed in the <seealso marker="driver_entry#stop"> + <c>stop</c></seealso> callback, with <c>driver_free_binary</c>.</p> + <p>Notice that as a driver binary is shared by the driver and + the emulator. A binary received from the emulator or sent to + the emulator must not be changed by the driver.</p> + <p>Since ERTS 5.5 (Erlang/OTP R11B), <c>orig_bytes</c> is guaranteed to be properly aligned for storage of an array of doubles (usually 8-byte aligned).</p> </item> - <tag>ErlDrvData</tag> + <tag><c>ErlDrvData</c></tag> <item> - <p>The <c>ErlDrvData</c> is a handle to driver-specific data, - passed to the driver call-backs. It is a pointer, and is + <p>A handle to driver-specific data, + passed to the driver callbacks. It is a pointer, and is most often type cast to a specific pointer in the driver.</p> </item> - <tag>SysIOVec</tag> + <tag><c>SysIOVec</c></tag> <item> - <p>This is a system I/O vector, as used by <c>writev</c> on - unix and <c>WSASend</c> on Win32. It is used in + <p>A system I/O vector, as used by <c>writev</c> on + Unix and <c>WSASend</c> on Win32. It is used in <c>ErlIOVec</c>.</p> </item> - <tag><marker id="ErlIOVec"/>ErlIOVec</tag> + <tag><marker id="ErlIOVec"/><c>ErlIOVec</c></tag> <item> - <p/> - <code type="none"> + <code type="none"> typedef struct ErlIOVec { int vsize; ErlDrvSizeT size; SysIOVec* iov; ErlDrvBinary** binv; -} ErlIOVec; -</code> - <p>The I/O vector used by the emulator and drivers, is a list +} ErlIOVec;</code> + <p>The I/O vector used by the emulator and drivers is a list of binaries, with a <c>SysIOVec</c> pointing to the buffers of the binaries. It is used in <c>driver_outputv</c> and the - <seealso marker="driver_entry#outputv">outputv</seealso> - driver call-back. Also, the driver queue is an + <seealso marker="driver_entry#outputv"><c>outputv</c></seealso> + driver callback. Also, the driver queue is an <c>ErlIOVec</c>.</p> </item> - - <tag>ErlDrvMonitor</tag> + <tag><c>ErlDrvMonitor</c></tag> <item> <p>When a driver creates a monitor for a process, a <c>ErlDrvMonitor</c> is filled in. This is an opaque - data-type which can be assigned to but not compared without - using the supplied compare function (i.e. it behaves like a struct).</p> - <p>The driver writer should provide the memory for storing the - monitor when calling <seealso marker="#driver_monitor_process">driver_monitor_process</seealso>. The + data type that can be assigned to, but not compared without + using the supplied compare function (that is, it behaves like + a struct).</p> + <p>The driver writer is to provide the memory for storing the + monitor when calling <seealso marker="#driver_monitor_process"> + <c>driver_monitor_process</c></seealso>. The address of the data is not stored outside of the driver, so - the <c>ErlDrvMonitor</c> can be used as any other datum, it - can be copied, moved in memory, forgotten etc.</p> + <c>ErlDrvMonitor</c> can be used as any other data, it + can be copied, moved in memory, forgotten, and so on.</p> </item> - <tag><marker id="ErlDrvNowData"/>ErlDrvNowData</tag> + <tag><marker id="ErlDrvNowData"/><c>ErlDrvNowData</c></tag> <item> - <p>The <c>ErlDrvNowData</c> structure holds a timestamp + <p>The <c>ErlDrvNowData</c> structure holds a time stamp consisting of three values measured from some arbitrary point in the past. The three structure members are:</p> <taglist> - <tag>megasecs</tag> + <tag><c>megasecs</c></tag> <item>The number of whole megaseconds elapsed since the arbitrary - point in time</item> - <tag>secs</tag> + point in time</item> + <tag><c>secs</c></tag> <item>The number of whole seconds elapsed since the arbitrary - point in time</item> - <tag>microsecs</tag> + point in time</item> + <tag><c>microsecs</c></tag> <item>The number of whole microseconds elapsed since the arbitrary - point in time</item> + point in time</item> </taglist> </item> - <tag><marker id="ErlDrvPDL"/>ErlDrvPDL</tag> + <tag><marker id="ErlDrvPDL"/><c>ErlDrvPDL</c></tag> <item> - <p>If certain port specific data have to be accessed from other - threads than those calling the driver call-backs, a port data lock - can be used in order to synchronize the operations on the data. - Currently, the only port specific data that the emulator + <p>If certain port-specific data must be accessed from other + threads than those calling the driver callbacks, a port data lock + can be used to synchronize the operations on the data. + Currently, the only port-specific data that the emulator associates with the port data lock is the driver queue.</p> - <p>Normally a driver instance does not have a port data lock. If - the driver instance wants to use a port data lock, it has to + <p>Normally a driver instance has no port data lock. If + the driver instance wants to use a port data lock, it must create the port data lock by calling - <seealso marker="#driver_pdl_create">driver_pdl_create()</seealso>. - <em>NOTE</em>: Once the port data lock has been created, every - access to data associated with the port data lock has to be done - while having the port data lock locked. The port data lock is - locked, and unlocked, respectively, by use of - <seealso marker="#driver_pdl_lock">driver_pdl_lock()</seealso>, and - <seealso marker="#driver_pdl_unlock">driver_pdl_unlock()</seealso>.</p> + <seealso marker="#driver_pdl_create"> + <c>driver_pdl_create</c></seealso>.</p> + <note> + <p>Once the port data lock has been created, every + access to data associated with the port data lock must be done + while the port data lock is locked. The port data lock is + locked and unlocked by + <seealso marker="#driver_pdl_lock"> + <c>driver_pdl_lock</c></seealso>, and + <seealso marker="#driver_pdl_unlock"> + <c>driver_pdl_unlock</c></seealso>, respectively.</p> + </note> <p>A port data lock is reference counted, and when the reference - count reaches zero, it will be destroyed. The emulator will at - least increment the reference count once when the lock is - created and decrement it once when the port associated with - the lock terminates. The emulator will also increment the - reference count when an async job is enqueued and decrement - it after an async job has been invoked. Besides - this, it is the responsibility of the driver to ensure that + count reaches zero, it is destroyed. The emulator at + least increments the reference count once when the lock is + created and decrements it once the port associated with + the lock terminates. The emulator also increments the + reference count when an async job is enqueued and decrements + it when an async job has been invoked. + Also, the driver is responsible for ensuring that the reference count does not reach zero before the last use of the lock by the driver has been made. The reference count - can be read, incremented, and decremented, respectively, by - use of - <seealso marker="#driver_pdl_get_refc">driver_pdl_get_refc()</seealso>, - <seealso marker="#driver_pdl_inc_refc">driver_pdl_inc_refc()</seealso>, and - <seealso marker="#driver_pdl_dec_refc">driver_pdl_dec_refc()</seealso>.</p> + can be read, incremented, and decremented by + <seealso marker="#driver_pdl_get_refc"> + <c>driver_pdl_get_refc</c></seealso>, + <seealso marker="#driver_pdl_inc_refc"> + <c>driver_pdl_inc_refc</c></seealso>, and + <seealso marker="#driver_pdl_dec_refc"> + <c>driver_pdl_dec_refc</c></seealso>, respectively.</p> </item> - - <tag><marker id="ErlDrvTid"/>ErlDrvTid</tag> + <tag><marker id="ErlDrvTid"/><c>ErlDrvTid</c></tag> <item> <p>Thread identifier.</p> - <p>See also: - <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>, - <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>, - <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>, - <seealso marker="#erl_drv_thread_self">erl_drv_thread_self()</seealso>, - and - <seealso marker="#erl_drv_equal_tids">erl_drv_equal_tids()</seealso>. - </p> + <p>See also <seealso marker="#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>, + <seealso marker="#erl_drv_thread_exit"> + <c>erl_drv_thread_exit</c></seealso>, + <seealso marker="#erl_drv_thread_join"> + <c>erl_drv_thread_join</c></seealso>, + <seealso marker="#erl_drv_thread_self"> + <c>erl_drv_thread_self</c></seealso>, and + <seealso marker="#erl_drv_equal_tids"> + <c>erl_drv_equal_tids</c></seealso>.</p> </item> - <tag><marker id="ErlDrvThreadOpts"/>ErlDrvThreadOpts</tag> + <tag><marker id="ErlDrvThreadOpts"/><c>ErlDrvThreadOpts</c></tag> <item> - <p/> - <code type="none"> - int suggested_stack_size; - </code> + <code type="none"> +int suggested_stack_size;</code> <p>Thread options structure passed to - <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>. - Currently the following fields exist: - </p> + <seealso marker="#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>. + The following fields exists:</p> <taglist> - <tag>suggested_stack_size</tag> - <item>A suggestion, in kilo-words, on how large a stack to use. A value less - than zero means default size. + <tag><c>suggested_stack_size</c></tag> + <item>A suggestion, in kilowords, on how large a stack to use. + A value < 0 means default size. </item> </taglist> - <p>See also: - <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>, - <seealso marker="#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy()</seealso>, - and - <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>. - </p> + <p>See also <seealso marker="#erl_drv_thread_opts_create"> + <c>erl_drv_thread_opts_create</c></seealso>, + <seealso marker="#erl_drv_thread_opts_destroy"> + <c>erl_drv_thread_opts_destroy</c></seealso>, and + <seealso marker="#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>.</p> </item> - - <tag><marker id="ErlDrvMutex"/>ErlDrvMutex</tag> + <tag><marker id="ErlDrvMutex"/><c>ErlDrvMutex</c></tag> <item> <p>Mutual exclusion lock. Used for synchronizing access to shared data. - Only one thread at a time can lock a mutex. - </p> - <p>See also: - <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>, - <seealso marker="#erl_drv_mutex_destroy">erl_drv_mutex_destroy()</seealso>, - <seealso marker="#erl_drv_mutex_lock">erl_drv_mutex_lock()</seealso>, - <seealso marker="#erl_drv_mutex_trylock">erl_drv_mutex_trylock()</seealso>, - and - <seealso marker="#erl_drv_mutex_unlock">erl_drv_mutex_unlock()</seealso>. - </p> + Only one thread at a time can lock a mutex.</p> + <p>See also <seealso marker="#erl_drv_mutex_create"> + <c>erl_drv_mutex_create</c></seealso>, + <seealso marker="#erl_drv_mutex_destroy"> + <c>erl_drv_mutex_destroy</c></seealso>, + <seealso marker="#erl_drv_mutex_lock"> + <c>erl_drv_mutex_lock</c></seealso>, + <seealso marker="#erl_drv_mutex_trylock"> + <c>erl_drv_mutex_trylock</c></seealso>, and + <seealso marker="#erl_drv_mutex_unlock"> + <c>erl_drv_mutex_unlock</c></seealso>.</p> </item> - <tag><marker id="ErlDrvCond"/>ErlDrvCond</tag> + <tag><marker id="ErlDrvCond"/><c>ErlDrvCond</c></tag> <item> - <p>Condition variable. Used when threads need to wait for a specific - condition to appear before continuing execution. Condition variables - need to be used with associated mutexes. - </p> - <p>See also: - <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>, - <seealso marker="#erl_drv_cond_destroy">erl_drv_cond_destroy()</seealso>, - <seealso marker="#erl_drv_cond_signal">erl_drv_cond_signal()</seealso>, - <seealso marker="#erl_drv_cond_broadcast">erl_drv_cond_broadcast()</seealso>, - and - <seealso marker="#erl_drv_cond_wait">erl_drv_cond_wait()</seealso>. - </p> + <p>Condition variable. Used when threads must wait for a specific + condition to appear before continuing execution. Condition variables + must be used with associated mutexes.</p> + <p>See also <seealso marker="#erl_drv_cond_create"> + <c>erl_drv_cond_create</c></seealso>, + <seealso marker="#erl_drv_cond_destroy"> + <c>erl_drv_cond_destroy</c></seealso>, + <seealso marker="#erl_drv_cond_signal"> + <c>erl_drv_cond_signal</c></seealso>, + <seealso marker="#erl_drv_cond_broadcast"> + <c>erl_drv_cond_broadcast</c></seealso>, and + <seealso marker="#erl_drv_cond_wait"> + <c>erl_drv_cond_wait</c></seealso>.</p> </item> - <tag><marker id="ErlDrvRWLock"/>ErlDrvRWLock</tag> + <tag><marker id="ErlDrvRWLock"/><c>ErlDrvRWLock</c></tag> <item> <p>Read/write lock. Used to allow multiple threads to read shared data - while only allowing one thread to write the same data. Multiple threads - can read lock an rwlock at the same time, while only one thread can - read/write lock an rwlock at a time. - </p> - <p>See also: - <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>, - <seealso marker="#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy()</seealso>, - <seealso marker="#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock()</seealso>, - <seealso marker="#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock()</seealso>, - <seealso marker="#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock()</seealso>, - <seealso marker="#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock()</seealso>, - <seealso marker="#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock()</seealso>, - and - <seealso marker="#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock()</seealso>. - </p> + while only allowing one thread to write the same data. Multiple + threads can read lock an rwlock at the same time, while only + one thread can read/write lock an rwlock at a time.</p> + <p>See also <seealso marker="#erl_drv_rwlock_create"> + <c>erl_drv_rwlock_create</c></seealso>, + <seealso marker="#erl_drv_rwlock_destroy"> + <c>erl_drv_rwlock_destroy</c></seealso>, + <seealso marker="#erl_drv_rwlock_rlock"> + <c>erl_drv_rwlock_rlock</c></seealso>, + <seealso marker="#erl_drv_rwlock_tryrlock"> + <c>erl_drv_rwlock_tryrlock</c></seealso>, + <seealso marker="#erl_drv_rwlock_runlock"> + <c>erl_drv_rwlock_runlock</c></seealso>, + <seealso marker="#erl_drv_rwlock_rwlock"> + <c>erl_drv_rwlock_rwlock</c></seealso>, + <seealso marker="#erl_drv_rwlock_tryrwlock"> + <c>erl_drv_rwlock_tryrwlock</c></seealso>, and + <seealso marker="#erl_drv_rwlock_rwunlock"> + <c>erl_drv_rwlock_rwunlock</c></seealso>.</p> </item> - <tag><marker id="ErlDrvTSDKey"/>ErlDrvTSDKey</tag> + <tag><marker id="ErlDrvTSDKey"/><c>ErlDrvTSDKey</c></tag> <item> - <p>Key which thread specific data can be associated with.</p> - <p>See also: - <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>, - <seealso marker="#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy()</seealso>, - <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>, - and - <seealso marker="#erl_drv_tsd_get">erl_drv_tsd_get()</seealso>. - </p> + <p>Key that thread-specific data can be associated with.</p> + <p>See also <seealso marker="#erl_drv_tsd_key_create"> + <c>erl_drv_tsd_key_create</c></seealso>, + <seealso marker="#erl_drv_tsd_key_destroy"> + <c>erl_drv_tsd_key_destroy</c></seealso>, + <seealso marker="#erl_drv_tsd_set"> + <c>erl_drv_tsd_set</c></seealso>, and + <seealso marker="#erl_drv_tsd_get"> + <c>erl_drv_tsd_get</c></seealso>.</p> </item> - <tag><marker id="ErlDrvTime"/>ErlDrvTime</tag> + <tag><marker id="ErlDrvTime"/><c>ErlDrvTime</c></tag> <item> - <p>A signed 64-bit integer type for representation of time.</p> + <p>A signed 64-bit integer type for time representation.</p> </item> - <tag><marker id="ErlDrvTimeUnit"/>ErlDrvTimeUnit</tag> + <tag><marker id="ErlDrvTimeUnit"/><c>ErlDrvTimeUnit</c></tag> <item> <p>An enumeration of time units supported by the driver API:</p> - <taglist> + <taglist> <tag><c>ERL_DRV_SEC</c></tag> - <item><p>Seconds</p></item> + <item>Seconds</item> <tag><c>ERL_DRV_MSEC</c></tag> - <item><p>Milliseconds</p></item> + <item>Milliseconds</item> <tag><c>ERL_DRV_USEC</c></tag> - <item><p>Microseconds</p></item> + <item>Microseconds</item> <tag><c>ERL_DRV_NSEC</c></tag> - <item><p>Nanoseconds</p></item> - </taglist> + <item>Nanoseconds</item> + </taglist> </item> - </taglist> - </section> + </taglist> + </section> <funcs> <func> - <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)</nametext></name> - <fsummary>Get information about the Erlang runtime system</fsummary> + <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry + *de)</nametext></name> + <fsummary>Add a driver entry.</fsummary> <desc> - <marker id="driver_system_info"></marker> - <p>This function will write information about the Erlang runtime - system into the - <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso> - structure referred to by the first argument. The second - argument should be the size of the - <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso> - structure, i.e., <c>sizeof(ErlDrvSysInfo)</c>.</p> - <p>See the documentation of the - <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso> - structure for information about specific fields.</p> + <marker id="add_driver_entry"></marker> + <p>Adds a driver entry to the list of drivers known by Erlang. + The <seealso marker="driver_entry#init"><c>init</c></seealso> + function of parameter <c>de</c> is called.</p> + <note> + <p>To use this function for adding drivers residing in + dynamically loaded code is dangerous. If the driver code + for the added driver resides in the same dynamically + loaded module (that is, <c>.so</c> file) as a normal + dynamically loaded driver (loaded with the <c>erl_ddll</c> + interface), the caller is to call + <seealso marker="#driver_lock_driver"> + <c>driver_lock_driver</c></seealso> before + adding driver entries.</p> + <p><em>Use of this function is generally deprecated.</em></p> + </note> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)</nametext></name> - <fsummary>Send data from driver to port owner</fsummary> + <name><ret>void *</ret> + <nametext>driver_alloc(ErlDrvSizeT size)</nametext></name> + <fsummary>Allocate memory.</fsummary> <desc> - <marker id="driver_output"></marker> - <p>The <c>driver_output</c> function is used to send data from - the driver up to the emulator. The data will be received as - terms or binary data, depending on how the driver port was - opened.</p> - <p>The data is queued in the port owner process' message - queue. Note that this does not yield to the emulator. (Since - the driver and the emulator run in the same thread.)</p> - <p>The parameter <c>buf</c> points to the data to send, and - <c>len</c> is the number of bytes.</p> - <p>The return value for all output functions is 0. (Unless the - driver is used for distribution, in which case it can fail - and return -1. For normal use, the output function always - returns 0.)</p> + <marker id="driver_alloc"></marker> + <p>Allocates a memory block of the size specified + in <c>size</c>, and returns it. This fails only on out of + memory, in which case <c>NULL</c> is returned. (This is most + often a wrapper for <c>malloc</c>).</p> + <p>Memory allocated must be explicitly freed with a corresponding + call to <seealso marker="#driver_free"><c>driver_free</c></seealso> + (unless otherwise stated).</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name> - <fsummary>Send data and binary data to port owner</fsummary> + <name><ret>ErlDrvBinary *</ret> + <nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name> + <fsummary>Allocate a driver binary.</fsummary> <desc> - <marker id="driver_output2"></marker> - <p>The <c>driver_output2</c> function first sends <c>hbuf</c> - (length in <c>hlen</c>) data as a list, regardless of port - settings. Then <c>buf</c> is sent as a binary or list. - E.g. if <c>hlen</c> is 3 then the port owner process will - receive <c>[H1, H2, H3 | T]</c>.</p> - <p>The point of sending data as a list header, is to facilitate - matching on the data received.</p> - <p>The return value is 0 for normal use.</p> + <marker id="driver_alloc_binary"></marker> + <p>Allocates a driver binary with a memory block + of at least <c>size</c> bytes, and returns a pointer to it, + or <c>NULL</c> on failure (out of memory). When a driver binary has + been sent to the emulator, it must not be changed. Every + allocated binary is to be freed by a corresponding call to + <seealso marker="#driver_free_binary"> + <c>driver_free_binary</c></seealso> (unless otherwise stated).</p> + <p>Notice that a driver binary has an internal reference counter. + This means that calling <c>driver_free_binary</c>, it may not + actually dispose of it. If it is sent to the emulator, it can + be referenced there.</p> + <p>The driver binary has a field, <c>orig_bytes</c>, which + marks the start of the data in the binary.</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name> - <fsummary>Send data from a driver binary to port owner</fsummary> + <name><ret>long</ret><nametext>driver_async(ErlDrvPort port, unsigned + int* key, void (*async_invoke)(void*), void* async_data, void + (*async_free)(void*))</nametext></name> + <fsummary>Perform an asynchronous call within a driver.</fsummary> <desc> - <marker id="driver_output_binary"></marker> - <p>This function sends data to port owner process from a - driver binary, it has a header buffer (<c>hbuf</c> - and <c>hlen</c>) just like <c>driver_output2</c>. The - <c>hbuf</c> parameter can be <c>NULL</c>.</p> - <p>The parameter <c>offset</c> is an offset into the binary and - <c>len</c> is the number of bytes to send.</p> - <p>Driver binaries are created with <c>driver_alloc_binary</c>.</p> - <p>The data in the header is sent as a list and the binary as - an Erlang binary in the tail of the list.</p> - <p>E.g. if <c>hlen</c> is 2, then the port owner process will - receive <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p> - <p>The return value is 0 for normal use.</p> - <p>Note that, using the binary syntax in Erlang, the driver - application can match the header directly from the binary, - so the header can be put in the binary, and hlen can be set - to 0.</p> + <marker id="driver_async"></marker> + <p>Performs an asynchronous call. The function + <c>async_invoke</c> is invoked in a thread separate from the + emulator thread. This enables the driver to perform + time-consuming, blocking operations without blocking the + emulator.</p> + <p>The async thread pool size can be set with command-line argument + <seealso marker="erl#async_thread_pool_size"><c>+A</c></seealso> + in <seealso marker="erl"><c>erl(1)</c></seealso>. + If an async thread pool is unavailable, the call is made + synchronously in the thread calling <c>driver_async</c>. The + current number of async threads in the async thread pool can be + retrieved through <seealso marker="#driver_system_info"> + <c>driver_system_info</c></seealso>.</p> + <p>If a thread pool is available, a thread is used. + If argument <c>key</c> is <c>NULL</c>, the threads from the + pool are used in a round-robin way, each call to + <c>driver_async</c> uses the next thread in the pool. With + argument <c>key</c> set, this behavior is changed. The two + same values of <c>*key</c> always get the same thread.</p> + <p>To ensure that a driver instance always uses the same + thread, the following call can be used:</p> + <code type="none"><![CDATA[ +unsigned int myKey = driver_async_port_key(myPort); + +r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> + <p>It is enough to initialize <c>myKey</c> once for each + driver instance.</p> + <p>If a thread is already working, the calls are + queued up and executed in order. Using the same thread for + each driver instance ensures that the calls are made in sequence.</p> + <p>The <c>async_data</c> is the argument to the functions + <c>async_invoke</c> and <c>async_free</c>. It is typically a + pointer to a structure containing a pipe or event that + can be used to signal that the async operation completed. + The data is to be freed in <c>async_free</c>.</p> + <p>When the async operation is done, + <seealso marker="driver_entry#ready_async"> + <c>ready_async</c></seealso> driver + entry function is called. If <c>ready_async</c> is <c>NULL</c> in + the driver entry, the <c>async_free</c> function is called + instead.</p> + <p>The return value is <c>-1</c> if the <c>driver_async</c> call + fails.</p> + <note> + <p>As from ERTS 5.5.4.3 the default stack size for + threads in the async-thread pool is 16 kilowords, + that is, 64 kilobyte on 32-bit architectures. + This small default size has been chosen because the + amount of async-threads can be quite large. The + default stack size is enough for drivers delivered + with Erlang/OTP, but is possibly not sufficiently large + for other dynamically linked-in drivers that use the + <c>driver_async</c> functionality. A suggested stack size + for threads in the async-thread pool can be configured + through command-line argument + <seealso marker="erl#async_thread_stack_size"><c>+a</c></seealso> + in <seealso marker="erl"><c>erl(1)</c></seealso>.</p> + </note> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> - <fsummary>Send vectorized data to port owner</fsummary> + <name><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort + port)</nametext></name> + <fsummary>Calculate an async key from an ErlDrvPort.</fsummary> <desc> - <marker id="driver_outputv"></marker> - <p>This function sends data from an IO vector, <c>ev</c>, to - the port owner process. It has a header buffer (<c>hbuf</c> - and <c>hlen</c>), just like <c>driver_output2</c>.</p> - <p>The <c>skip</c> parameter is a number of bytes to skip of - the <c>ev</c> vector from the head.</p> - <p>You get vectors of <c>ErlIOVec</c> type from the driver - queue (see below), and the <seealso marker="driver_entry#outputv">outputv</seealso> driver entry - function. You can also make them yourself, if you want to - send several <c>ErlDrvBinary</c> buffers at once. Often - it is faster to use <c>driver_output</c> or - <c>driver_output_binary</c>.</p> - <p>E.g. if <c>hlen</c> is 2 and <c>ev</c> points to an array of - three binaries, the port owner process will receive <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p> - <p>The return value is 0 for normal use.</p> - <p>The comment for <c>driver_output_binary</c> applies for - <c>driver_outputv</c> too.</p> + <marker id="driver_async_port_key"></marker> + <p>Calculates a key for later use in <seealso + marker="#driver_async"><c>driver_async</c></seealso>. The keys are + evenly distributed so that a fair mapping between port IDs + and async thread IDs is achieved.</p> + <note> + <p>Before Erlang/OTP R16, the port ID could be used as a key + with proper casting, but after the rewrite of the port + subsystem, this is no longer the case. With this function, you + can achieve the same distribution based on port IDs as before + Erlang/OTP R16.</p> + </note> </desc> </func> + <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)</nametext></name> - <fsummary>Collect data segments into a buffer</fsummary> + <name><ret>long</ret> + <nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name> + <fsummary>Decrement the reference count of a driver binary.</fsummary> <desc> - <marker id="driver_vec_to_buf"></marker> - <p>This function collects several segments of data, referenced - by <c>ev</c>, by copying them in order to the buffer - <c>buf</c>, of the size <c>len</c>.</p> - <p>If the data is to be sent from the driver to the port owner - process, it is faster to use <c>driver_outputv</c>.</p> - <p>The return value is the space left in the buffer, i.e. if - the <c>ev</c> contains less than <c>len</c> bytes it's the - difference, and if <c>ev</c> contains <c>len</c> bytes or - more, it's 0. This is faster if there is more than one header byte, - since the binary syntax can construct integers directly from - the binary.</p> + <marker id="driver_binary_dec_refc"></marker> + <p>Decrements the reference count on <c>bin</c> and returns + the reference count reached after the decrement.</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> + <note> + <p>The reference count of driver binary is normally to be decremented + by calling <seealso marker="#driver_free_binary"> + <c>driver_free_binary</c></seealso>.</p> + <p><c>driver_binary_dec_refc</c> does <em>not</em> free + the binary if the reference count reaches zero. <em>Only</em> + use <c>driver_binary_dec_refc</c> when you are sure + <em>not</em> to reach a reference count of zero.</p> + </note> + </desc> + </func> + + <func> + <name><ret>long</ret> + <nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name> + <fsummary>Get the reference count of a driver binary.</fsummary> + <desc> + <marker id="driver_binary_get_refc"></marker> + <p>Returns the current reference count on <c>bin</c>.</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name> - <fsummary>Set a timer to call the driver</fsummary> + <name><ret>long</ret> + <nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name> + <fsummary>Increment the reference count of a driver binary.</fsummary> <desc> - <marker id="driver_set_timer"></marker> - <p>This function sets a timer on the driver, which will count - down and call the driver when it is timed out. The - <c>time</c> parameter is the time in milliseconds before the - timer expires.</p> - <p>When the timer reaches 0 and expires, the driver entry - function <seealso marker="driver_entry#timeout">timeout</seealso> is called.</p> - <p>Note that there is only one timer on each driver instance; - setting a new timer will replace an older one.</p> - <p>Return value is 0 (-1 only when the <c>timeout</c> driver - function is <c>NULL</c>).</p> + <marker id="driver_binary_inc_refc"></marker> + <p>Increments the reference count on <c>bin</c> and returns + the reference count reached after the increment.</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> + </desc> + </func> + + <func> + <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort + port)</nametext></name> + <fsummary>Return the process making the driver call.</fsummary> + <desc> + <marker id="driver_caller"></marker> + <p>Returns the process ID of the process that + made the current call to the driver. The process ID can be used with + <seealso marker="#driver_send_term"><c>driver_send_term</c></seealso> + to send back data to the caller. + <c>driver_caller</c> only returns valid data + when currently executing in one of the following driver callbacks:</p> + <taglist> + <tag><seealso marker="driver_entry#start"> + <c>start</c></seealso></tag> + <item>Called from <seealso marker="erlang:open_port/2"> + <c>erlang:open_port/2</c></seealso>.</item> + <tag><seealso marker="driver_entry#output"> + <c>output</c></seealso></tag> + <item>Called from <seealso marker="erlang:send/2"> + <c>erlang:send/2</c></seealso> and + <seealso marker="erlang:port_command/2"> + <c>erlang:port_command/2</c></seealso>.</item> + <tag><seealso marker="driver_entry#outputv"> + <c>outputv</c></seealso></tag> + <item>Called from <seealso marker="erlang:send/2"> + <c>erlang:send/2</c></seealso> and + <seealso marker="erlang:port_command/2"> + <c>erlang:port_command/2</c></seealso>.</item> + <tag><seealso marker="driver_entry#control"> + <c>control</c></seealso></tag> + <item>Called from <seealso marker="erlang:port_control/3"> + <c>erlang:port_control/3</c></seealso>.</item> + <tag><seealso marker="driver_entry#call"> + <c>call</c></seealso></tag> + <item>Called from <seealso marker="erlang:port_call/3"> + <c>erlang:port_call/3</c></seealso>.</item> + </taglist> + <p>Notice that this function is <em>not</em> thread-safe, not + even when the emulator with SMP support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name> - <fsummary>Cancel a previously set timer</fsummary> + <name><ret>int</ret> + <nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name> + <fsummary>Cancel a previously set timer.</fsummary> <desc> <marker id="driver_cancel_timer"></marker> - <p>This function cancels a timer set with - <c>driver_set_timer</c>.</p> - <p>The return value is 0.</p> + <p>Cancels a timer set with + <seealso marker="#driver_set_timer"> + <c>driver_set_timer</c></seealso>.</p> + <p>The return value is <c>0</c>.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned long *time_left)</nametext></name> - <fsummary>Read the time left before timeout</fsummary> + <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor + *monitor1, const ErlDrvMonitor *monitor2)</nametext></name> + <fsummary>Compare two monitors.</fsummary> <desc> - <marker id="driver_read_timer"></marker> - <p>This function reads the current time of a timer, and places - the result in <c>time_left</c>. This is the time in - milliseconds, before the timeout will occur.</p> - <p>The return value is 0.</p> + <marker id="driver_compare_monitors"></marker> + <p>Compares two <c>ErlDrvMonitor</c>s. + Can also be used to imply some artificial order on monitors, + for whatever reason.</p> + <p>Returns <c>0</c> if <c>monitor1</c> and <c>monitor2</c> are equal, + < <c>0</c> if <c>monitor1</c> < <c>monitor2</c>, and + > <c>0</c> if <c>monitor1</c> > <c>monitor2</c>.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_get_now(ErlDrvNowData *now)</nametext></name> - <fsummary>Read a system timestamp</fsummary> + <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort + port)</nametext></name> + <fsummary>Return the port owner process.</fsummary> <desc> - <marker id="driver_get_now"></marker> - <warning><p><em>This function is deprecated! Do not use it!</em> - Use <seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso> - (perhaps in combination with - <seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso>) - instead.</p></warning> - <p>This function reads a timestamp into the memory pointed to by - the parameter <c>now</c>. See the description of <seealso marker="#ErlDrvNowData">ErlDrvNowData</seealso> for - specification of its fields. </p> - <p>The return value is 0 unless the <c>now</c> pointer is not - valid, in which case it is < 0. </p> + <marker id="driver_connected"></marker> + <p>Returns the port owner process.</p> + <p>Notice that this function is <em>not</em> thread-safe, not + even when the emulator with SMP support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)</nametext></name> - <fsummary>Provide an event for having the emulator call the driver</fsummary> + <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, + ErlDrvTermData owner_pid, char* name, + ErlDrvData drv_data)</nametext></name> + <fsummary>Create a new port (driver instance).</fsummary> <desc> - <marker id="driver_select"></marker> - <p>This function is used by drivers to provide the emulator with - events to check for. This enables the emulator to call the driver - when something has happened asynchronously.</p> - <p>The <c>event</c> argument identifies an OS-specific event object. - On Unix systems, the functions <c>select</c>/<c>poll</c> are used. The - event object must be a socket or pipe (or other object that - <c>select</c>/<c>poll</c> can use). - On windows, the Win32 API function <c>WaitForMultipleObjects</c> - is used. This places other restrictions on the event object. - Refer to the Win32 SDK documentation.</p> - <p>The <c>on</c> parameter should be <c>1</c> for setting events - and <c>0</c> for clearing them.</p> - <p>The <c>mode</c> argument is a bitwise-or combination of - <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c> and <c>ERL_DRV_USE</c>. - The first two specify whether to wait for read events and/or write - events. A fired read event will call - <seealso marker="driver_entry#ready_input">ready_input</seealso> - while a fired write event will call - <seealso marker="driver_entry#ready_output">ready_output</seealso>. - </p> - <note> - <p>Some OS (Windows) do not differentiate between read and write events. - The call-back for a fired event then only depends on the value of <c>mode</c>.</p> - </note> - <p><c>ERL_DRV_USE</c> specifies if we are using the event object or if we want to close it. - On an emulator with SMP support, it is not safe to clear all events - and then close the event object after <c>driver_select</c> has - returned. Another thread may still be using the event object - internally. To safely close an event object call - <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>. That - will clear all events and then call - <seealso marker="driver_entry#stop_select">stop_select</seealso> - when it is safe to close the event object. - <c>ERL_DRV_USE</c> should be set together with the first event - for an event object. It is harmless to set <c>ERL_DRV_USE</c> - even though it already has been done. Clearing all events but keeping - <c>ERL_DRV_USE</c> set will indicate that we are using the event - object and probably will set events for it again.</p> - <note> - <p>ERL_DRV_USE was added in OTP release R13. Old drivers will still work - as before. But it is recommended to update them to use <c>ERL_DRV_USE</c> and - <c>stop_select</c> to make sure that event objects are closed in a safe way.</p> - </note> - <p>The return value is 0 (failure, -1, only if the - <c>ready_input</c>/<c>ready_output</c> is - <c>NULL</c>).</p> + <p>Creates a new port executing the same driver + code as the port creating the new port.</p> + <taglist> + <tag><c>port</c></tag> + <item>The port handle of the port (driver instance) creating + the new port.</item> + <tag><c>owner_pid</c></tag> + <item>The process ID of the Erlang process to become + owner of the new port. This process will be linked + to the new port. You usually want to use + <c>driver_caller(port)</c> as <c>owner_pid</c>.</item> + <tag><c>name</c></tag> + <item>The port name of the new port. You usually want to + use the same port name as the driver name + (<seealso marker="driver_entry#driver_name"> + <c>driver_name</c></seealso> field of the + <seealso marker="driver_entry"><c>driver_entry</c></seealso>). + </item> + <tag><c>drv_data</c></tag> + <item>The driver-defined handle that is passed in later + calls to driver callbacks. Notice that the + <seealso marker="driver_entry#start">driver start + callback</seealso> is not called for this new driver instance. + The driver-defined handle is normally created in the + <seealso marker="driver_entry#start">driver start callback</seealso> + when a port is created through + <seealso marker="erlang#open_port/2"> + <c>erlang:open_port/2</c></seealso>. + </item> + </taglist> + <p>The caller of <c>driver_create_port</c> is allowed to + manipulate the newly created port when <c>driver_create_port</c> + has returned. When + <seealso marker="#smp_support">port level locking</seealso> + is used, the creating port is only allowed to + manipulate the newly created port until the current driver + callback, which was called by the emulator, returns.</p> </desc> </func> + <func> - <name><ret>void *</ret><nametext>driver_alloc(ErlDrvSizeT size)</nametext></name> - <fsummary>Allocate memory</fsummary> + <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, + const ErlDrvMonitor *monitor)</nametext></name> + <fsummary>Stop monitoring a process from a driver.</fsummary> <desc> - <marker id="driver_alloc"></marker> - <p>This function allocates a memory block of the size specified - in <c>size</c>, and returns it. This only fails on out of - memory, in that case <c>NULL</c> is returned. (This is most - often a wrapper for <c>malloc</c>).</p> - <p>Memory allocated must be explicitly freed with a corresponding - call to <c>driver_free</c> (unless otherwise stated).</p> - <p>This function is thread-safe.</p> + <marker id="driver_demonitor_process"></marker> + <p>Cancels a monitor created earlier.</p> + <p>Returns <c>0</c> if a monitor was removed and > 0 if the monitor + no longer exists.</p> </desc> </func> + <func> - <name><ret>void *</ret><nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name> - <fsummary>Resize an allocated memory block</fsummary> + <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, + ErlDrvSizeT size)</nametext></name> + <fsummary>Dequeue data from the head of the driver queue.</fsummary> <desc> - <marker id="driver_realloc"></marker> - <p>This function resizes a memory block, either in place, or by - allocating a new block, copying the data and freeing the old - block. A pointer is returned to the reallocated memory. On - failure (out of memory), <c>NULL</c> is returned. (This is - most often a wrapper for <c>realloc</c>.)</p> - <p>This function is thread-safe.</p> + <marker id="driver_deq"></marker> + <p>Dequeues data by moving the head pointer + forward in the driver queue by <c>size</c> bytes. The data + in the queue is deallocated.</p> + <p>Returns the number of bytes remaining in the queue on success, + otherwise <c>-1</c>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name> - <fsummary>Free an allocated memory block</fsummary> + <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, + ErlDrvSizeT len)</nametext></name> + <fsummary>Enqueue data in the driver queue.</fsummary> <desc> - <marker id="driver_free"></marker> - <p>This function frees the memory pointed to by <c>ptr</c>. The - memory should have been allocated with - <c>driver_alloc</c>. All allocated memory should be - deallocated, just once. There is no garbage collection in - drivers.</p> - <p>This function is thread-safe.</p> + <marker id="driver_enq"></marker> + <p>Enqueues data in the driver queue. The data in + <c>buf</c> is copied (<c>len</c> bytes) and placed at the + end of the driver queue. The driver queue is normally used + in a FIFO way.</p> + <p>The driver queue is available to queue output from the + emulator to the driver (data from the driver to the emulator + is queued by the emulator in normal Erlang message + queues). This can be useful if the driver must wait for + slow devices, and so on, and wants to yield back to the + emulator. The driver queue is implemented as an <c>ErlIOVec</c>.</p> + <p>When the queue contains data, the driver does not close until + the queue is empty.</p> + <p>The return value is <c>0</c>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name> - <fsummary>Allocate a driver binary</fsummary> + <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, + ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext> + </name> + <fsummary>Enqueue binary in the driver queue.</fsummary> <desc> - <marker id="driver_alloc_binary"></marker> - <p>This function allocates a driver binary with a memory block - of at least <c>size</c> bytes, and returns a pointer to it, - or NULL on failure (out of memory). When a driver binary has - been sent to the emulator, it must not be altered. Every - allocated binary should be freed by a corresponding call to - <c>driver_free_binary</c> (unless otherwise stated).</p> - <p>Note that a driver binary has an internal reference counter, - this means that calling <c>driver_free_binary</c> it may not - actually dispose of it. If it's sent to the emulator, it may - be referenced there.</p> - <p>The driver binary has a field, <c>orig_bytes</c>, which - marks the start of the data in the binary.</p> - <p>This function is thread-safe.</p> + <marker id="driver_enq_bin"></marker> + <p>Enqueues a driver binary in the driver + queue. The data in <c>bin</c> at <c>offset</c> with length + <c>len</c> is placed at the end of the queue. This function + is most often faster than + <seealso marker="#driver_enq"><c>driver_enq</c></seealso>, + because no data must be copied.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> + <p>The return value is <c>0</c>.</p> </desc> </func> + <func> - <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name> - <fsummary>Resize a driver binary</fsummary> + <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, + ErlDrvSizeT skip)</nametext></name> + <fsummary>Enqueue vector in the driver queue.</fsummary> <desc> - <marker id="driver_realloc_binary"></marker> - <p>This function resizes a driver binary, while keeping the - data. The resized driver binary is returned. On failure (out - of memory), <c>NULL</c> is returned.</p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="driver_enqv"></marker> + <p>Enqueues the data in <c>ev</c>, skipping the + first <c>skip</c> bytes of it, at the end of the driver + queue. It is faster than + <seealso marker="#driver_enq"><c>driver_enq</c></seealso>, + because no data must be copied.</p> + <p>The return value is <c>0</c>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>void</ret><nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name> - <fsummary>Free a driver binary</fsummary> + <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int + error)</nametext></name> + <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char + *string)</nametext></name> + <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int + error)</nametext></name> + <fsummary>Fail with error.</fsummary> <desc> - <marker id="driver_free_binary"></marker> - <p>This function frees a driver binary <c>bin</c>, allocated - previously with <c>driver_alloc_binary</c>. Since binaries - in Erlang are reference counted, the binary may still be - around.</p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="driver_failure_atom"></marker> + <marker id="driver_failure_posix"></marker> + <marker id="driver_failure"></marker> + <p>Signals to Erlang that the driver has + encountered an error and is to be closed. The port is + closed and the tuple <c>{'EXIT', error, Err}</c> is sent to + the port owner process, where error is an error atom + (<c>driver_failure_atom</c> and + <c>driver_failure_posix</c>) or an integer + (<c>driver_failure</c>).</p> + <p>The driver is to fail only when in severe error situations, + when the driver cannot possibly keep open, for example, + buffer allocation gets out of memory. For normal errors + it is more appropriate to send error codes with + <seealso marker="#driver_output"><c>driver_output</c></seealso>.</p> + <p>The return value is <c>0</c>.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name> - <fsummary>Get the reference count of a driver binary</fsummary> + <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort + port)</nametext></name> + <fsummary>Fail with EOF.</fsummary> <desc> - <marker id="driver_binary_get_refc"></marker> - <p>Returns current reference count on <c>bin</c>.</p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="driver_failure_eof"></marker> + <p>Signals to Erlang that the driver has + encountered an EOF and is to be closed, unless the port was + opened with option <c>eof</c>, in which case <c>eof</c> is sent + to the port. Otherwise the port is closed and an + <c>'EXIT'</c> message is sent to the port owner process.</p> + <p>The return value is <c>0</c>.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name> - <fsummary>Increment the reference count of a driver binary</fsummary> + <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name> + <fsummary>Free an allocated memory block.</fsummary> <desc> - <marker id="driver_binary_inc_refc"></marker> - <p>Increments the reference count on <c>bin</c> and returns - the reference count reached after the increment.</p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="driver_free"></marker> + <p>Frees the memory pointed to by <c>ptr</c>. The + memory is to have been allocated with + <c>driver_alloc</c>. All allocated memory is to be + deallocated, only once. There is no garbage collection in + drivers.</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name> - <fsummary>Decrement the reference count of a driver binary</fsummary> + <name><ret>void</ret> + <nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name> + <fsummary>Free a driver binary.</fsummary> <desc> - <marker id="driver_binary_dec_refc"></marker> - <p>Decrements the reference count on <c>bin</c> and returns - the reference count reached after the decrement.</p> + <marker id="driver_free_binary"></marker> + <p>Frees a driver binary <c>bin</c>, allocated previously with + <seealso marker="#driver_alloc_binary"> + <c>driver_alloc_binary</c></seealso>. As binaries + in Erlang are reference counted, the binary can still be around.</p> <p>This function is only thread-safe when the emulator with SMP support is used.</p> - <note> - <p>You should normally decrement the reference count of a - driver binary by calling - <seealso marker="#driver_free_binary">driver_free_binary()</seealso>. - <c>driver_binary_dec_refc()</c> does <em>not</em> free - the binary if the reference count reaches zero. <em>Only</em> - use <c>driver_binary_dec_refc()</c> when you are sure - <em>not</em> to reach a reference count of zero.</p> - </note> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name> - <fsummary>Enqueue data in the driver queue</fsummary> + <name><ret>ErlDrvTermData</ret> + <nametext>driver_get_monitored_process(ErlDrvPort port, const + ErlDrvMonitor *monitor)</nametext></name> + <fsummary>Retrieve the process ID from a monitor.</fsummary> <desc> - <marker id="driver_enq"></marker> - <p>This function enqueues data in the driver queue. The data in - <c>buf</c> is copied (<c>len</c> bytes) and placed at the - end of the driver queue. The driver queue is normally used - in a FIFO way.</p> - <p>The driver queue is available to queue output from the - emulator to the driver (data from the driver to the emulator - is queued by the emulator in normal erlang message - queues). This can be useful if the driver has to wait for - slow devices etc, and wants to yield back to the - emulator. The driver queue is implemented as an ErlIOVec.</p> - <p>When the queue contains data, the driver won't close, until - the queue is empty.</p> - <p>The return value is 0.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_get_monitored_process"></marker> + <p>Returns the process ID associated with a living + monitor. It can be used in the + <seealso marker="driver_entry#process_exit"> + <c>process_exit</c></seealso> callback to + get the process identification for the exiting process.</p> + <p>Returns <c>driver_term_nil</c> if the monitor no longer exists.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name> - <fsummary>Push data at the head of the driver queue</fsummary> + <name><ret>int</ret> + <nametext>driver_get_now(ErlDrvNowData *now)</nametext></name> + <fsummary>Read a system time stamp.</fsummary> <desc> - <marker id="driver_pushq"></marker> - <p>This function puts data at the head of the driver queue. The - data in <c>buf</c> is copied (<c>len</c> bytes) and placed - at the beginning of the queue.</p> - <p>The return value is 0.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_get_now"></marker> + <warning> + <p><em>This function is deprecated. Do not use it.</em> Use + <seealso marker="#erl_drv_monotonic_time"> + <c>erl_drv_monotonic_time</c></seealso> (perhaps in combination with + <seealso marker="#erl_drv_time_offset"> + <c>erl_drv_time_offset</c></seealso>) instead.</p> + </warning> + <p>Reads a time stamp into the memory pointed to by + parameter <c>now</c>. For information about specific fields, see + <seealso marker="#ErlDrvNowData"><c>ErlDrvNowData</c></seealso>.</p> + <p>The return value is <c>0</c>, unless the <c>now</c> pointer is + invalid, in which case it is < <c>0</c>.</p> </desc> </func> + <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, ErlDrvSizeT size)</nametext></name> - <fsummary>Dequeue data from the head of the driver queue</fsummary> + <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort + port)</nametext></name> + <fsummary>Ensure the driver is never unloaded.</fsummary> <desc> - <marker id="driver_deq"></marker> - <p>This function dequeues data by moving the head pointer - forward in the driver queue by <c>size</c> bytes. The data - in the queue will be deallocated.</p> - <p>The return value is the number of bytes remaining in the queue - or -1 on failure.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_lock_driver"></marker> + <p>Locks the driver used by the port <c>port</c> + in memory for the rest of the emulator process' + lifetime. After this call, the driver behaves as one of Erlang's + statically linked-in drivers.</p> </desc> </func> + <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_sizeq(ErlDrvPort port)</nametext></name> - <fsummary>Return the size of the driver queue</fsummary> + <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* + string)</nametext></name> + <fsummary>Make an atom from a name.</fsummary> <desc> - <marker id="driver_sizeq"></marker> - <p>This function returns the number of bytes currently in the - driver queue.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_mk_atom"></marker> + <p>Returns an atom given a name + <c>string</c>. The atom is created and does not change, so the + return value can be saved and reused, which is faster than + looking up the atom several times.</p> + <p>Notice that this function is <em>not</em> thread-safe, not + even when the emulator with SMP support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name> - <fsummary>Enqueue binary in the driver queue</fsummary> + <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort + port)</nametext></name> + <fsummary>Make an Erlang term port from a port.</fsummary> <desc> - <marker id="driver_enq_bin"></marker> - <p>This function enqueues a driver binary in the driver - queue. The data in <c>bin</c> at <c>offset</c> with length - <c>len</c> is placed at the end of the queue. This function - is most often faster than <c>driver_enq</c>, because the - data doesn't have to be copied.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> - <p>The return value is 0.</p> + <marker id="driver_mk_port"></marker> + <p>Converts a port handle to the Erlang term format, usable in + <seealso marker="#erl_drv_output_term"> + <c>erl_drv_output_term</c></seealso> and + <seealso marker="#erl_drv_send_term"> + <c>erl_drv_send_term</c></seealso>.</p> + <p>Notice that this function is <em>not</em> thread-safe, not + even when the emulator with SMP support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name> - <fsummary>Push binary at the head of the driver queue</fsummary> + <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, + ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name> + <fsummary>Monitor a process from a driver.</fsummary> <desc> - <marker id="driver_pushq_bin"></marker> - <p>This function puts data in the binary <c>bin</c>, at - <c>offset</c> with length <c>len</c> at the head of the - driver queue. It is most often faster than - <c>driver_pushq</c>, because the data doesn't have to be - copied.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> - <p>The return value is 0.</p> + <marker id="driver_monitor_process"></marker> + <p>Starts monitoring a process from a driver. When a process is + monitored, a process exit results in a call to the provided + <seealso marker="driver_entry#process_exit"> + <c>process_exit</c></seealso> callback + in the <seealso marker="driver_entry"><c>ErlDrvEntry</c></seealso> + structure. The <c>ErlDrvMonitor</c> structure is filled in, for later + removal or compare.</p> + <p>Parameter <c>process</c> is to be the return value of an + earlier call to <seealso marker="#driver_caller"> + <c>driver_caller</c></seealso> or + <seealso marker="#driver_connected"><c>driver_connected</c></seealso> + call.</p> + <p>Returns <c>0</c> on success, < 0 if no callback is + provided, and > 0 if the process is no longer alive.</p> </desc> </func> + <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, ErlIOVec *ev)</nametext></name> - <fsummary>Get the driver queue as an IO vector</fsummary> + <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, + ErlDrvSizeT len)</nametext></name> + <fsummary>Send data from driver to port owner.</fsummary> <desc> - <marker id="driver_peekqv"></marker> - <p> - This function retrieves the driver queue into a supplied - <c>ErlIOVec</c> <c>ev</c>. It also returns the queue size. - This is one of two ways to get data out of the queue. - </p> - <p> - If <c>ev</c> is <c>NULL</c> all ones i.e. <c>-1</c> type cast to - <c>ErlDrvSizeT</c> is returned. - </p> - <p>Nothing is removed from the queue by this function, that must be done - with <c>driver_deq</c>.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_output"></marker> + <p>Sends data from the driver up to the emulator. The data is received + as terms or binary data, depending on how the driver port was + opened.</p> + <p>The data is queued in the port owner process' message + queue. Notice that this does not yield to the emulator (as + the driver and the emulator run in the same thread).</p> + <p>Parameter <c>buf</c> points to the data to send, and + <c>len</c> is the number of bytes.</p> + <p>The return value for all output functions is <c>0</c> for normal use. + If the driver is used for distribution, it can fail and return + <c>-1</c>.</p> </desc> </func> + <func> - <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name> - <fsummary>Get the driver queue as a vector</fsummary> + <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char + *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, + ErlDrvSizeT len)</nametext></name> + <fsummary>Send data from a driver binary to port owner.</fsummary> <desc> - <marker id="driver_peekq"></marker> - <p>This function retrieves the driver queue as a pointer to an - array of <c>SysIOVec</c>s. It also returns the number of - elements in <c>vlen</c>. This is one of two ways to get data - out of the queue.</p> - <p>Nothing is removed from the queue by this function, that must be done - with <c>driver_deq</c>.</p> - <p>The returned array is suitable to use with the Unix system - call <c>writev</c>.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_output_binary"></marker> + <p>Sends data to a port owner process from a + driver binary. It has a header buffer (<c>hbuf</c> + and <c>hlen</c>) just like + <seealso marker="#driver_output2"><c>driver_output2</c></seealso>. + Parameter <c>hbuf</c> can be <c>NULL</c>.</p> + <p>Parameter <c>offset</c> is an offset into the binary and + <c>len</c> is the number of bytes to send.</p> + <p>Driver binaries are created with + <seealso marker="#driver_alloc_binary"> + <c>driver_alloc_binary</c></seealso>.</p> + <p>The data in the header is sent as a list and the binary as + an Erlang binary in the tail of the list.</p> + <p>For example, if <c>hlen</c> is <c>2</c>, the port owner process + receives <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p> + <p>The return value is <c>0</c> for normal use.</p> + <p>Notice that, using the binary syntax in Erlang, the driver + application can match the header directly from the binary, + so the header can be put in the binary, and <c>hlen</c> can be set + to <c>0</c>.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> - <fsummary>Enqueue vector in the driver queue</fsummary> + <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, + ErlDrvTermData* term, int n)</nametext></name> + <fsummary>Send term data from driver to port owner.</fsummary> <desc> - <marker id="driver_enqv"></marker> - <p>This function enqueues the data in <c>ev</c>, skipping the - first <c>skip</c> bytes of it, at the end of the driver - queue. It is faster than <c>driver_enq</c>, because the data - doesn't have to be copied.</p> - <p>The return value is 0.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_output_term"></marker> + <warning> + <p><em>This function is deprecated.</em> + Use <seealso marker="#erl_drv_send_term"> + <c>erl_drv_output_term</c></seealso>instead.</p> + </warning> + <p>Parameters <c>term</c> and <c>n</c> work as in + <seealso marker="#erl_drv_output_term"> + <c>erl_drv_output_term</c></seealso>.</p> + <p>Notice that this function is <em>not</em> thread-safe, not + even when the emulator with SMP support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> - <fsummary>Push vector at the head of the driver queue</fsummary> + <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, + ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name> + <fsummary>Send data and binary data to port owner.</fsummary> <desc> - <marker id="driver_pushqv"></marker> - <p>This function puts the data in <c>ev</c>, skipping the first - <c>skip</c> bytes of it, at the head of the driver queue. - It is faster than <c>driver_pushq</c>, because the data - doesn't have to be copied.</p> - <p>The return value is 0.</p> - <p>This function can be called from an arbitrary thread if a - <seealso marker="#ErlDrvPDL">port data lock</seealso> - associated with the <c>port</c> is locked by the calling - thread during the call.</p> + <marker id="driver_output2"></marker> + <p>First sends <c>hbuf</c> + (length in <c>hlen</c>) data as a list, regardless of port + settings. Then sends <c>buf</c> as a binary or list. + For example, if <c>hlen</c> is <c>3</c>, the port owner process + receives <c>[H1, H2, H3 | T]</c>.</p> + <p>The point of sending data as a list header, is to facilitate + matching on the data received.</p> + <p>The return value is <c>0</c> for normal use.</p> </desc> </func> + <func> - <name><ret>ErlDrvPDL</ret><nametext>driver_pdl_create(ErlDrvPort port)</nametext></name> - <fsummary>Create a port data lock</fsummary> + <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, + ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> + <fsummary>Send vectorized data to port owner.</fsummary> <desc> - <marker id="driver_pdl_create"></marker> - <p>This function creates a port data lock associated with - the <c>port</c>. <em>NOTE</em>: Once a port data lock has - been created, it has to be locked during all operations - on the driver queue of the <c>port</c>.</p> - <p>On success a newly created port data lock is returned. On - failure <c>NULL</c> is returned. <c>driver_pdl_create()</c> will - fail if <c>port</c> is invalid or if a port data lock already has - been associated with the <c>port</c>.</p> + <marker id="driver_outputv"></marker> + <p>Sends data from an I/O vector, <c>ev</c>, to + the port owner process. It has a header buffer (<c>hbuf</c> + and <c>hlen</c>), just like <seealso marker="#driver_output2"> + <c>driver_output2</c></seealso>.</p> + <p>Parameter <c>skip</c> is a number of bytes to skip of + the <c>ev</c> vector from the head.</p> + <p>You get vectors of <c>ErlIOVec</c> type from the driver + queue (see below), and the + <seealso marker="driver_entry#outputv"><c>outputv</c></seealso> + driver entry function. You can also make them yourself, if you want to + send several <c>ErlDrvBinary</c> buffers at once. Often + it is faster to use + <seealso marker="#driver_output"><c>driver_output</c></seealso> or + <seealso marker="#driver_output_binary"></seealso>.</p> + <p>For example, if <c>hlen</c> is <c>2</c> and <c>ev</c> points to an + array of three binaries, the port owner process receives + <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p> + <p>The return value is <c>0</c> for normal use.</p> + <p>The comment for <c>driver_output_binary</c> also applies for + <c>driver_outputv</c>.</p> </desc> </func> + <func> - <name><ret>void</ret><nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name> - <fsummary>Lock port data lock</fsummary> + <name><ret>ErlDrvPDL</ret> + <nametext>driver_pdl_create(ErlDrvPort port)</nametext></name> + <fsummary>Create a port data lock.</fsummary> <desc> - <marker id="driver_pdl_lock"></marker> - <p>This function locks the port data lock passed as argument - (<c>pdl</c>).</p> - <p>This function is thread-safe.</p> + <marker id="driver_pdl_create"></marker> + <p>Creates a port data lock associated with the <c>port</c>.</p> + <note> + <p>Once a port data lock has been created, it must be locked during + all operations on the driver queue of the <c>port</c>.</p> + </note> + <p>Returns a newly created port data lock on success, + otherwise <c>NULL</c>. The function fails + if <c>port</c> is invalid or if a port data lock already has + been associated with the <c>port</c>.</p> </desc> </func> + <func> - <name><ret>void</ret><nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name> - <fsummary>Unlock port data lock</fsummary> + <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL + pdl)</nametext></name> + <fsummary></fsummary> <desc> - <marker id="driver_pdl_unlock"></marker> - <p>This function unlocks the port data lock passed as argument - (<c>pdl</c>).</p> + <marker id="driver_pdl_dec_refc"></marker> + <p>Decrements the reference count of + the port data lock passed as argument (<c>pdl</c>).</p> + <p>The current reference count after the decrement has + been performed is returned.</p> <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name> + <name><ret>long</ret> + <nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> <marker id="driver_pdl_get_refc"></marker> - <p>This function returns the current reference count of + <p>Returns the current reference count of the port data lock passed as argument (<c>pdl</c>).</p> <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name> + <name><ret>long</ret> + <nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> <marker id="driver_pdl_inc_refc"></marker> - <p>This function increments the reference count of + <p>Increments the reference count of the port data lock passed as argument (<c>pdl</c>).</p> <p>The current reference count after the increment has been performed is returned.</p> <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL pdl)</nametext></name> - <fsummary></fsummary> + <name><ret>void</ret> + <nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name> + <fsummary>Lock port data lock.</fsummary> <desc> - <marker id="driver_pdl_dec_refc"></marker> - <p>This function decrements the reference count of - the port data lock passed as argument (<c>pdl</c>).</p> - <p>The current reference count after the decrement has - been performed is returned.</p> + <marker id="driver_pdl_lock"></marker> + <p>Locks the port data lock passed as argument (<c>pdl</c>).</p> <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name> - <fsummary>Monitor a process from a driver</fsummary> + <name><ret>void</ret> + <nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name> + <fsummary>Unlock port data lock.</fsummary> <desc> - <marker id="driver_monitor_process"></marker> - <p>Start monitoring a process from a driver. When a process is - monitored, a process exit will result in a call to the - provided <seealso marker="driver_entry#process_exit">process_exit</seealso> call-back - in the <seealso marker="driver_entry">ErlDrvEntry</seealso> - structure. The <c>ErlDrvMonitor</c> structure is filled in, for later - removal or compare.</p> - <p>The <c>process</c> parameter should be the return value of an - earlier call to <seealso marker="#driver_caller">driver_caller</seealso> or <seealso marker="#driver_connected">driver_connected</seealso> call.</p> - <p>The function returns 0 on success, < 0 if no call-back is - provided and > 0 if the process is no longer alive.</p> + <marker id="driver_pdl_unlock"></marker> + <p>Unlocks the port data lock passed as argument (<c>pdl</c>).</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name> - <fsummary>Stop monitoring a process from a driver</fsummary> + <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int + *vlen)</nametext></name> + <fsummary>Get the driver queue as a vector.</fsummary> <desc> - <marker id="driver_demonitor_process"></marker> - <p>This function cancels a monitor created earlier. </p> - <p>The function returns 0 if a monitor was removed and > 0 - if the monitor did no longer exist.</p> + <marker id="driver_peekq"></marker> + <p>Retrieves the driver queue as a pointer to an + array of <c>SysIOVec</c>s. It also returns the number of + elements in <c>vlen</c>. This is one of two ways to get data + out of the queue.</p> + <p>Nothing is removed from the queue by this function, that must be done + with <seealso marker="#driver_deq"><c>driver_deq</c></seealso>.</p> + <p>The returned array is suitable to use with the Unix system + call <c>writev</c>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name> - <fsummary>Retrieve the process id from a monitor</fsummary> + <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, + ErlIOVec *ev)</nametext></name> + <fsummary>Get the driver queue as an I/O vector.</fsummary> <desc> - <marker id="driver_get_monitored_process"></marker> - <p>The function returns the process id associated with a living - monitor. It can be used in the <c>process_exit</c> call-back to - get the process identification for the exiting process.</p> - <p>The function returns <c>driver_term_nil</c> if the monitor - no longer exists.</p> + <marker id="driver_peekqv"></marker> + <p>Retrieves the driver queue into a supplied + <c>ErlIOVec</c> <c>ev</c>. It also returns the queue size. + This is one of two ways to get data out of the queue.</p> + <p>If <c>ev</c> is <c>NULL</c>, all ones that is <c>-1</c> type cast to + <c>ErlDrvSizeT</c> are returned.</p> + <p>Nothing is removed from the queue by this function, that must be done + with <seealso marker="#driver_deq"><c>driver_deq</c></seealso>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)</nametext></name> - <fsummary>Compare two monitors</fsummary> + <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, + ErlDrvSizeT len)</nametext></name> + <fsummary>Push data at the head of the driver queue.</fsummary> <desc> - <marker id="driver_compare_monitors"></marker> - <p>This function is used to compare two <c>ErlDrvMonitor</c>s. It - can also be used to imply some artificial order on monitors, - for whatever reason.</p> - <p>The function returns 0 if <c>monitor1</c> and - <c>monitor2</c> are equal, < 0 if <c>monitor1</c> is less - than <c>monitor2</c> and > 0 if <c>monitor1</c> is greater - than <c>monitor2</c>.</p> + <marker id="driver_pushq"></marker> + <p>Puts data at the head of the driver queue. The + data in <c>buf</c> is copied (<c>len</c> bytes) and placed + at the beginning of the queue.</p> + <p>The return value is <c>0</c>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry *de)</nametext></name> - <fsummary>Add a driver entry</fsummary> + <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, + ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext> + </name> + <fsummary>Push binary at the head of the driver queue.</fsummary> <desc> - <marker id="add_driver_entry"></marker> - <p>This function adds a driver entry to the list of drivers - known by Erlang. The <seealso marker="driver_entry#init">init</seealso> function of the <c>de</c> - parameter is called.</p> - <note> - <p>To use this function for adding drivers residing in - dynamically loaded code is dangerous. If the driver code - for the added driver resides in the same dynamically - loaded module (i.e. <c>.so</c> file) as a normal - dynamically loaded driver (loaded with the <c>erl_ddll</c> - interface), the caller should call <seealso marker="#driver_lock_driver">driver_lock_driver</seealso> before - adding driver entries.</p> - <p>Use of this function is generally deprecated.</p> - </note> + <marker id="driver_pushq_bin"></marker> + <p>Puts data in the binary <c>bin</c>, at + <c>offset</c> with length <c>len</c> at the head of the + driver queue. It is most often faster than + <seealso marker="#driver_pushq"><c>driver_pushq</c></seealso>, + because no data must be copied.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> + <p>The return value is <c>0</c>.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry *de)</nametext></name> - <fsummary>Remove a driver entry</fsummary> + <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec + *ev, ErlDrvSizeT skip)</nametext></name> + <fsummary>Push vector at the head of the driver queue.</fsummary> <desc> - <marker id="remove_driver_entry"></marker> - <p>This function removes a driver entry <c>de</c> previously - added with <c>add_driver_entry</c>.</p> - <p>Driver entries added by the <c>erl_ddll</c> erlang interface can - not be removed by using this interface.</p> + <marker id="driver_pushqv"></marker> + <p>Puts the data in <c>ev</c>, skipping the first + <c>skip</c> bytes of it, at the head of the driver queue. + It is faster than + <seealso marker="#driver_pushq"><c>driver_pushq</c></seealso>, + because no data must be copied.</p> + <p>The return value is <c>0</c>.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name> - <fsummary>Get erlang error atom name from error number</fsummary> + <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned + long *time_left)</nametext></name> + <fsummary>Read the time left before time-out.</fsummary> <desc> - <marker id="erl_errno_id"></marker> - <p>This function returns the atom name of the erlang error, - given the error number in <c>error</c>. Error atoms are: - <c>einval</c>, <c>enoent</c>, etc. It can be used to make - error terms from the driver.</p> + <marker id="driver_read_timer"></marker> + <p>Reads the current time of a timer, and places + the result in <c>time_left</c>. This is the time in + milliseconds, before the time-out occurs.</p> + <p>The return value is <c>0</c>.</p> </desc> </func> + <func> - <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name> - <fsummary>Set and get limits for busy port message queue</fsummary> - <desc> - <marker id="erl_drv_busy_msgq_limits"></marker> - <p>Sets and gets limits that will be used for controling the - busy state of the port message queue.</p> - <p>The port message queue will be set into a busy - state when the amount of command data queued on the - message queue reaches the <c>high</c> limit. The port - message queue will be set into a not busy state when the - amount of command data queued on the message queue falls - below the <c>low</c> limit. Command data is in this - context data passed to the port using either - <c>Port ! {Owner, {command, Data}}</c>, or - <c>port_command/[2,3]</c>. Note that these limits - only concerns command data that have not yet reached the - port. The <seealso marker="#set_busy_port">busy port</seealso> - feature can be used for data that has reached the port.</p> - - <p>Valid limits are values in the range - <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>. - Limits will be automatically adjusted to be sane. That is, - the system will adjust values so that the low limit used is - lower than or equal to the high limit used. By default the high - limit will be 8 kB and the low limit will be 4 kB.</p> - - <p>By passing a pointer to an integer variable containing - the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, currently used - limit will be read and written back to the integer variable. - A new limit can be set by passing a pointer to an integer - variable containing a valid limit. The passed value will be - written to the internal limit. The internal limit will then - be adjusted. After this the adjusted limit will be written - back to the integer variable from which the new value was - read. Values are in bytes.</p> - - <p>The busy message queue feature can be disabled either - by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c> - <seealso marker="driver_entry#driver_flags">driver flag</seealso> - in the <seealso marker="driver_entry">driver_entry</seealso> - used by the driver, or by calling this function with - <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or - high). When this feature has been disabled it cannot be - enabled again. When reading the limits both of them - will be <c>ERL_DRV_BUSY_MSGQ_DISABLED</c>, if this - feature has been disabled.</p> - - <p>Processes sending command data to the port will be suspended - if either the port is busy or if the port message queue is - busy. Suspended processes will be resumed when neither the - port is busy, nor the port message queue is busy.</p> - - <p>For information about busy port functionality - see the documentation of the - <seealso marker="#set_busy_port">set_busy_port()</seealso> - function.</p> - </desc> - </func> - <func> - <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name> - <fsummary>Signal or unsignal port as busy</fsummary> - <desc> - <marker id="set_busy_port"></marker> - <p>This function set and unset the busy state of the port. If - <c>on</c> is non-zero, the port is set to busy, if it's zero the port - is set to not busy. You typically want to combine - this feature with the <seealso marker="#erl_drv_busy_msgq_limits">busy - port message queue</seealso> functionality.</p> - <p>Processes sending command data to the port will be suspended - if either the port is busy or if the port message queue - is busy. Suspended processes will be resumed when neither the - port is busy, nor the port message queue is busy. Command data - is in this context data passed to the port using either - <c>Port ! {Owner, {command, Data}}</c>, or - <c>port_command/[2,3]</c>.</p> - <p>If the - <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso> - has been set in the - <seealso marker="driver_entry">driver_entry</seealso>, - data can be forced into the driver via - <seealso marker="erlang#port_command/3">port_command(Port, Data, [force])</seealso> - even though the driver has signaled that it is busy. - </p> - <p>For information about busy port message queue functionality - see the documentation of the - <seealso marker="#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso> - function.</p> - </desc> - </func> - <func> - <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, int flags)</nametext></name> - <fsummary>Set flags on how to handle control entry function</fsummary> + <name><ret>void *</ret> + <nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name> + <fsummary>Resize an allocated memory block.</fsummary> <desc> - <marker id="set_port_control_flags"></marker> - <p>This function sets flags for how the <seealso marker="driver_entry#control">control</seealso> driver entry - function will return data to the port owner process. (The - <c>control</c> function is called from <c>port_control/3</c> - in erlang.)</p> - <p>Currently there are only two meaningful values for - <c>flags</c>: 0 means that data is returned in a list, and - <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as - a binary from <c>control</c>.</p> + <marker id="driver_realloc"></marker> + <p>Resizes a memory block, either in place, or by + allocating a new block, copying the data, and freeing the old + block. A pointer is returned to the reallocated memory. On + failure (out of memory), <c>NULL</c> is returned. (This is + most often a wrapper for <c>realloc</c>.)</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort port)</nametext></name> - <fsummary>Fail with EOF</fsummary> + <name><ret>ErlDrvBinary *</ret> + <nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size) + </nametext></name> + <fsummary>Resize a driver binary.</fsummary> <desc> - <marker id="driver_failure_eof"></marker> - <p>This function signals to erlang that the driver has - encountered an EOF and should be closed, unless the port was - opened with the <c>eof</c> option, in that case eof is sent - to the port. Otherwise, the port is closed and an - <c>'EXIT'</c> message is sent to the port owner process.</p> - <p>The return value is 0.</p> + <marker id="driver_realloc_binary"></marker> + <p>Resizes a driver binary, while keeping the data.</p> + <p>Returns the resized driver binary on success. Returns <c>NULL</c> + on failure (out of memory).</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char *string)</nametext></name> - <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int error)</nametext></name> - <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int error)</nametext></name> - <fsummary>Fail with error</fsummary> + <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent + event, int mode, int on)</nametext></name> + <fsummary>Provides an event for having the emulator call the driver. + </fsummary> <desc> - <marker id="driver_failure_atom"></marker> - <marker id="driver_failure_posix"></marker> - <marker id="driver_failure"></marker> - <p>These functions signal to Erlang that the driver has - encountered an error and should be closed. The port is - closed and the tuple <c>{'EXIT', error, Err}</c>, is sent to - the port owner process, where error is an error atom - (<c>driver_failure_atom</c> and - <c>driver_failure_posix</c>), or an integer - (<c>driver_failure</c>).</p> - <p>The driver should fail only when in severe error situations, - when the driver cannot possibly keep open, for instance - buffer allocation gets out of memory. For normal errors - it is more appropriate to send error codes with - <c>driver_output</c>.</p> - <p>The return value is 0.</p> + <marker id="driver_select"></marker> + <p>This function is used by drivers to provide the emulator with + events to check for. This enables the emulator to call the driver + when something has occurred asynchronously.</p> + <p>Parameter <c>event</c> identifies an OS-specific event object. + On Unix systems, the functions <c>select</c>/<c>poll</c> are used. + The event object must be a socket or pipe (or other object that + <c>select</c>/<c>poll</c> can use). + On Windows, the Win32 API function <c>WaitForMultipleObjects</c> + is used. This places other restrictions on the event object; + see the Win32 SDK documentation.</p> + <p>Parameter <c>on</c> is to be <c>1</c> for setting events + and <c>0</c> for clearing them.</p> + <p>Parameter <c>mode</c> is a bitwise OR combination of + <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c>, and <c>ERL_DRV_USE</c>. + The first two specify whether to wait for read events and/or write + events. A fired read event calls + <seealso marker="driver_entry#ready_input"> + <c>ready_input</c></seealso> and a fired write event calls + <seealso marker="driver_entry#ready_output"> + <c>ready_output</c></seealso>.</p> + <note> + <p>Some OS (Windows) do not differentiate between read and write + events. The callback for a fired event then only depends on the + value of <c>mode</c>.</p> + </note> + <p><c>ERL_DRV_USE</c> specifies if we are using the event object or + if we want to close it. + On an emulator with SMP support, it is not safe to clear all events + and then close the event object after <c>driver_select</c> has + returned. Another thread can still be using the event object + internally. To safely close an event object, call + <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>, which + clears all events. Then call + <seealso marker="driver_entry#stop_select"> + <c>stop_select</c></seealso> when it is safe to close the event + object. <c>ERL_DRV_USE</c> is to be set together with the first event + for an event object. It is harmless to set <c>ERL_DRV_USE</c> + even if it already has been done. Clearing all events but keeping + <c>ERL_DRV_USE</c> set indicates that we are using the event + object and probably will set events for it again.</p> + <note> + <p><c>ERL_DRV_USE</c> was added in Erlang/OTP R13. Old drivers still + work as before, but it is recommended to update them to use + <c>ERL_DRV_USE</c> and <c>stop_select</c> to ensure that event + objects are closed in a safe way.</p> + </note> + <p>The return value is <c>0</c>, unless + <c>ready_input</c>/<c>ready_output</c> is <c>NULL</c>, in which case + it is <c>-1</c>.</p> </desc> </func> + <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort port)</nametext></name> - <fsummary>Return the port owner process</fsummary> + <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, + ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> + <fsummary>Send term data to other process than port owner process. + </fsummary> <desc> - <marker id="driver_connected"></marker> - <p>This function returns the port owner process.</p> - <p>Note that this function is <em>not</em> thread-safe, not - even when the emulator with SMP support is used.</p> + <marker id="driver_send_term"></marker> + <warning> + <p><em>This function is deprecated.</em> + Use <seealso marker="#erl_drv_send_term"> + <c>erl_drv_send_term</c></seealso> instead.</p> + </warning> + <note> + <p>The parameters of this function + cannot be properly checked by the runtime system when + executed by arbitrary threads. This can cause the + function not to fail when it should.</p> + </note> + <p>Parameters <c>term</c> and <c>n</c> work as in + <seealso marker="#erl_drv_output_term"> + <c>erl_drv_output_term</c></seealso>.</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> </desc> </func> + <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort port)</nametext></name> - <fsummary>Return the process making the driver call</fsummary> + <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned + long time)</nametext></name> + <fsummary>Set a timer to call the driver.</fsummary> <desc> - <marker id="driver_caller"></marker> - <p>This function returns the process id of the process that - made the current call to the driver. The process id can be - used with <c>driver_send_term</c> to send back data to the - caller. <c>driver_caller()</c> only returns valid data - when currently executing in one of the following driver - callbacks:</p> - <taglist> - <tag><seealso marker="driver_entry#start">start</seealso></tag> - <item>Called from <c>open_port/2</c>.</item> - <tag><seealso marker="driver_entry#output">output</seealso></tag> - <item>Called from <c>erlang:send/2</c>, and - <c>erlang:port_command/2</c></item> - <tag><seealso marker="driver_entry#outputv">outputv</seealso></tag> - <item>Called from <c>erlang:send/2</c>, and - <c>erlang:port_command/2</c></item> - <tag><seealso marker="driver_entry#control">control</seealso></tag> - <item>Called from <c>erlang:port_control/3</c></item> - <tag><seealso marker="driver_entry#call">call</seealso></tag> - <item>Called from <c>erlang:port_call/3</c></item> - </taglist> - <p>Note that this function is <em>not</em> thread-safe, not - even when the emulator with SMP support is used.</p> + <marker id="driver_set_timer"></marker> + <p>Sets a timer on the driver, which will count + down and call the driver when it is timed out. Parameter + <c>time</c> is the time in milliseconds before the timer expires.</p> + <p>When the timer reaches <c>0</c> and expires, the driver entry + function <seealso marker="driver_entry#timeout"> + <c>timeout</c></seealso> is called.</p> + <p>Notice that only one timer exists on each driver instance; + setting a new timer replaces an older one.</p> + <p>Return value is <c>0</c>, unless the <c>timeout</c> + driver function is <c>NULL</c>, in which case it is <c>-1</c>.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)</nametext></name> - <fsummary>Send term data from driver to port owner</fsummary> + <name><ret>ErlDrvSizeT</ret> + <nametext>driver_sizeq(ErlDrvPort port)</nametext></name> + <fsummary>Return the size of the driver queue.</fsummary> <desc> - <marker id="erl_drv_output_term"></marker> - <p>This functions sends data in the special driver term - format to the port owner process. This is a fast way to - deliver term data from a driver. It also needs no binary - conversion, so the port owner process receives data as - normal Erlang terms. The - <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> - functions can be used for sending to any arbitrary process - on the local node.</p> - <note><p>Note that the <c>port</c> parameter is <em>not</em> - an ordinary port handle, but a port handle converted using - <c>driver_mk_port()</c>.</p></note> - <p>The <c>term</c> parameter points to an array of - <c>ErlDrvTermData</c>, with <c>n</c> elements. This array - contains terms described in the driver term format. Every - term consists of one to four elements in the array. The - term first has a term type, and then arguments. The - <c>port</c> parameter specifies the sending port.</p> - <p>Tuples, maps and lists (with the exception of strings, see below), - are built in reverse polish notation, so that to build a - tuple, the elements are given first, and then the tuple - term, with a count. Likewise for lists and maps.</p> - <p>A tuple must be specified with the number of elements. (The - elements precede the <c>ERL_DRV_TUPLE</c> term.)</p> - <p>A list must be specified with the number of elements, - including the tail, which is the last term preceding - <c>ERL_DRV_LIST</c>.</p> - <p>A map must be specified with the number of key-value pairs <c>N</c>. - The key-value pairs must precede the <c>ERL_DRV_MAP</c> in this order: - <c>key1,value1,key2,value2,...,keyN,valueN</c>. - Duplicate keys are not allowed.</p> - <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to - "splice" in a string in a list, a string given this way is - not a list per se, but the elements are elements of the - surrounding list.</p> - <pre> -Term type Argument(s) -=========================================== -ERL_DRV_NIL -ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string)) -ERL_DRV_INT ErlDrvSInt integer -ERL_DRV_UINT ErlDrvUInt integer -ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr -ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr -ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port)) -ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset -ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len -ERL_DRV_STRING char *str, int len -ERL_DRV_TUPLE int sz -ERL_DRV_LIST int sz -ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) or driver_caller(ErlDrvPort port)) -ERL_DRV_STRING_CONS char *str, int len -ERL_DRV_FLOAT double *dbl -ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len -ERL_DRV_MAP int sz - </pre> - <p>The unsigned integer data type <c>ErlDrvUInt</c> and the - signed integer data type <c>ErlDrvSInt</c> are 64 bits wide - on a 64 bit runtime system and 32 bits wide on a 32 bit - runtime system. They were introduced in erts version 5.6, - and replaced some of the <c>int</c> arguments in the list above. - </p> - <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the - signed integer data type <c>ErlDrvSInt64</c> are always 64 bits - wide. They were introduced in erts version 5.7.4. - </p> - - <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the - following call could be made.</p> - <code type="none"><![CDATA[ - ErlDrvBinary* bin = ... - ErlDrvPort port = ... - ErlDrvTermData spec[] = { - ERL_DRV_ATOM, driver_mk_atom("tcp"), - ERL_DRV_PORT, driver_mk_port(drvport), - ERL_DRV_INT, 100, - ERL_DRV_BINARY, bin, 50, 0, - ERL_DRV_LIST, 2, - ERL_DRV_TUPLE, 3, - }; - erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); - ]]> - </code> - <p>Where <c>bin</c> is a driver binary of length at least 50 - and <c>drvport</c> is a port handle. Note that the <c>ERL_DRV_LIST</c> - comes after the elements of the list, likewise the - <c>ERL_DRV_TUPLE</c>.</p> - <p>The term <c>ERL_DRV_STRING_CONS</c> is a way to construct - strings. It works differently from how <c>ERL_DRV_STRING</c> - works. <c>ERL_DRV_STRING_CONS</c> builds a string list in - reverse order, (as opposed to how <c>ERL_DRV_LIST</c> - works), concatenating the strings added to a list. The tail - must be given before <c>ERL_DRV_STRING_CONS</c>.</p> - <p>The <c>ERL_DRV_STRING</c> constructs a string, and ends - it. (So it's the same as <c>ERL_DRV_NIL</c> followed by - <c>ERL_DRV_STRING_CONS</c>.)</p> - <code type="none"><![CDATA[ - /* to send [x, "abc", y] to the port: */ - ErlDrvTermData spec[] = { - ERL_DRV_ATOM, driver_mk_atom("x"), - ERL_DRV_STRING, (ErlDrvTermData)"abc", 3, - ERL_DRV_ATOM, driver_mk_atom("y"), - ERL_DRV_NIL, - ERL_DRV_LIST, 4 - }; - erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); - ]]></code> - <p></p> - <code type="none"><![CDATA[ - /* to send "abc123" to the port: */ - ErlDrvTermData spec[] = { - ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */ - ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3, - ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3, - }; - erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); - ]]></code> - <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a - term encoded with the - <seealso marker="erl_ext_dist">external format</seealso>, - i.e., a term that has been encoded by - <seealso marker="erlang#term_to_binary/2">erlang:term_to_binary</seealso>, - <seealso marker="erl_interface:ei">erl_interface</seealso>, etc. - For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c> - that contains the term <c>{17, 4711}</c> encoded with the - <seealso marker="erl_ext_dist">external format</seealso> - and you want to wrap it in a two tuple with the tag <c>my_tag</c>, - i.e., <c>{my_tag, {17, 4711}}</c>, you can do as follows: - </p> - <code type="none"><![CDATA[ - ErlDrvTermData spec[] = { - ERL_DRV_ATOM, driver_mk_atom("my_tag"), - ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size - ERL_DRV_TUPLE, 2, - }; - erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); - ]]></code> - - <p>To build the map <c>#{key1 => 100, key2 => {200, 300}}</c>, the - following call could be made.</p> - <code type="none"><![CDATA[ - ErlDrvPort port = ... - ErlDrvTermData spec[] = { - ERL_DRV_ATOM, driver_mk_atom("key1"), - ERL_DRV_INT, 100, - ERL_DRV_ATOM, driver_mk_atom("key2"), - ERL_DRV_INT, 200, - ERL_DRV_INT, 300, - ERL_DRV_TUPLE, 2, - ERL_DRV_MAP, 2 - }; - erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); - ]]> - </code> - - <p>If you want to pass a binary and don't already have the content - of the binary in an <c>ErlDrvBinary</c>, you can benefit from using - <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c> - via <c>driver_alloc_binary()</c> and then pass the binary via - <c>ERL_DRV_BINARY</c>. The runtime system will often allocate - binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used. - However, if the content of the binary to pass already resides in - an <c>ErlDrvBinary</c>, it is normally better to pass the binary - using <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question. - </p> - <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and - <c>ERL_DRV_EXT2TERM</c> term types were introduced in the 5.6 - version of erts. - </p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="driver_sizeq"></marker> + <p>Returns the number of bytes currently in the driver queue.</p> + <p>This function can be called from any thread if a + <seealso marker="#ErlDrvPDL">port data lock</seealso> + associated with the <c>port</c> is locked by the calling + thread during the call.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name> - <fsummary>Send term data from driver to port owner</fsummary> + <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo + *sys_info_ptr, size_t size)</nametext></name> + <fsummary>Get information about the Erlang runtime system.</fsummary> <desc> - <marker id="driver_output_term"></marker> - <warning><p><c>driver_output_term()</c> is deprecated and will - be removed in the OTP-R17 release. Use - <seealso marker="#erl_drv_send_term">erl_drv_output_term()</seealso> - instead.</p> - </warning> - <p>The parameters <c>term</c> and <c>n</c> do the same thing - as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p> - <p>Note that this function is <em>not</em> thread-safe, not - even when the emulator with SMP support is used.</p> + <marker id="driver_system_info"></marker> + <p>Writes information about the Erlang runtime system into the + <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso> + structure referred to by the first argument. The second + argument is to be the size of the + <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso> + structure, that is, <c>sizeof(ErlDrvSysInfo)</c>.</p> + <p>For information about specific fields, see + <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>.</p> </desc> </func> + <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* string)</nametext></name> - <fsummary>Make an atom from a name</fsummary> + <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, + char *buf, ErlDrvSizeT len)</nametext></name> + <fsummary>Collect data segments into a buffer.</fsummary> <desc> - <marker id="driver_mk_atom"></marker> - <p>This function returns an atom given a name - <c>string</c>. The atom is created and won't change, so the - return value may be saved and reused, which is faster than - looking up the atom several times.</p> - <p>Note that this function is <em>not</em> thread-safe, not - even when the emulator with SMP support is used.</p> + <marker id="driver_vec_to_buf"></marker> + <p>Collects several segments of data, referenced + by <c>ev</c>, by copying them in order to the buffer + <c>buf</c>, of the size <c>len</c>.</p> + <p>If the data is to be sent from the driver to the port owner + process, it is faster to use + <seealso marker="#driver_outputv"><c>driver_outputv</c></seealso>.</p> + <p>The return value is the space left in the buffer, that is, if + <c>ev</c> contains less than <c>len</c> bytes it is the + difference, and if <c>ev</c> contains <c>len</c> bytes or more, + it is <c>0</c>. This is faster if there is more than one header byte, + as the binary syntax can construct integers directly from + the binary.</p> </desc> </func> + <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort port)</nametext></name> - <fsummary>Make a erlang term port from a port</fsummary> + <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, + ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name> + <fsummary>Set and get limits for busy port message queue.</fsummary> <desc> - <marker id="driver_mk_port"></marker> - <p>This function converts a port handle to the erlang term - format, usable in the <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>, and <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> functions.</p> - <p>Note that this function is <em>not</em> thread-safe, not - even when the emulator with SMP support is used.</p> + <marker id="erl_drv_busy_msgq_limits"></marker> + <p>Sets and gets limits that will be used for controling the + busy state of the port message queue.</p> + <p>The port message queue is set into a busy + state when the amount of command data queued on the + message queue reaches the <c>high</c> limit. The port + message queue is set into a not busy state when the + amount of command data queued on the message queue falls + below the <c>low</c> limit. Command data is in this + context data passed to the port using either + <c>Port ! {Owner, {command, Data}}</c> or + <c>port_command/[2,3]</c>. Notice that these limits + only concerns command data that have not yet reached the + port. The <seealso marker="#set_busy_port">busy port</seealso> + feature can be used for data that has reached the port.</p> + <p>Valid limits are values in the range + <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>. + Limits are automatically adjusted to be sane. That is, + the system adjusts values so that the low limit used is + lower than or equal to the high limit used. By default the high + limit is 8 kB and the low limit is 4 kB.</p> + <p>By passing a pointer to an integer variable containing + the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, the currently used + limit is read and written back to the integer variable. + A new limit can be set by passing a pointer to an integer + variable containing a valid limit. The passed value is + written to the internal limit. The internal limit is then + adjusted. After this the adjusted limit is written + back to the integer variable from which the new value was + read. Values are in bytes.</p> + <p>The busy message queue feature can be disabled either + by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c> + <seealso marker="driver_entry#driver_flags">driver flag</seealso> + in the <seealso marker="driver_entry"><c>driver_entry</c></seealso> + used by the driver, or by calling this function with + <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or + high). When this feature has been disabled, it cannot be + enabled again. When reading the limits, both are + <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> if this + feature has been disabled.</p> + <p>Processes sending command data to the port are suspended + if either the port is busy or if the port message queue is + busy. Suspended processes are resumed when neither the + port or the port message queue is busy.</p> + <p>For information about busy port functionality, see + <seealso marker="#set_busy_port"><c>set_busy_port</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond + *cnd)</nametext></name> + <fsummary>Broadcast on a condition variable.</fsummary> + <desc> + <marker id="erl_drv_cond_broadcast"></marker> + <p>Broadcasts on a condition variable. That is, if + other threads are waiting on the condition variable being + broadcast on, <em>all</em> of them are woken.</p> + <p><c>cnd</c> is a pointer to a condition variable to broadcast on.</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> - <fsummary>Send term data to other process than port owner process</fsummary> + <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char + *name)</nametext></name> + <fsummary>Create a condition variable.</fsummary> <desc> - <marker id="erl_drv_send_term"></marker> - <p>This function is the only way for a driver to send data to - <em>other</em> processes than the port owner process. The - <c>receiver</c> parameter specifies the process to receive - the data.</p> - <note><p>Note that the <c>port</c> parameter is <em>not</em> - an ordinary port handle, but a port handle converted using - <c>driver_mk_port()</c>.</p></note> - <p>The parameters <c>port</c>, <c>term</c> and <c>n</c> do the same thing - as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="erl_drv_cond_create"></marker> + <p>Creates a condition variable and returns a pointer to it.</p> + <p><c>name</c> is a string identifying the created condition variable. + It is used to identify the condition variable in planned + future debug functionality.</p> + <p>Returns <c>NULL</c> on failure. The driver + creating the condition variable is responsibile for + destroying it before the driver is unloaded.</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> - <fsummary>Send term data to other process than port owner process</fsummary> + <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond + *cnd)</nametext></name> + <fsummary>Destroy a condition variable.</fsummary> <desc> - <marker id="driver_send_term"></marker> - <warning><p><c>driver_send_term()</c> is deprecated and will - be removed in the OTP-R17 release. Use - <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> - instead.</p> - <p>Also note that parameters of <c>driver_send_term()</c> - cannot be properly checked by the runtime system when - executed by arbitrary threads. This may cause the - <c>driver_send_term()</c> function not to fail when - it should.</p> - </warning> - <p>The parameters <c>term</c> and <c>n</c> do the same thing - as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p> - <p>This function is only thread-safe when the emulator with SMP - support is used.</p> + <marker id="erl_drv_cond_destroy"></marker> + <p>Destroys a condition variable previously created by + <seealso marker="#erl_drv_cond_create"> + <c>erl_drv_cond_create</c></seealso>.</p> + <p><c>cnd</c> is a pointer to a condition variable to destroy.</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>long</ret><nametext>driver_async (ErlDrvPort port, unsigned int* key, void (*async_invoke)(void*), void* async_data, void (*async_free)(void*))</nametext></name> - <fsummary>Perform an asynchronous call within a driver</fsummary> + <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond + *cnd)</nametext></name> + <fsummary>Get name of driver mutex.</fsummary> <desc> - <marker id="driver_async"></marker> - <p>This function performs an asynchronous call. The function - <c>async_invoke</c> is invoked in a thread separate from the - emulator thread. This enables the driver to perform - time-consuming, blocking operations without blocking the - emulator.</p> - <p>The async thread pool size can be set with the - <seealso marker="erl#async_thread_pool_size">+A</seealso> - command line argument of <seealso marker="erl">erl(1)</seealso>. - If no async thread pool is available, the call is made - synchronously in the thread calling <c>driver_async()</c>. The - current number of async threads in the async thread pool can be - retrieved via - <seealso marker="#driver_system_info">driver_system_info()</seealso>.</p> - <p>If there is a thread pool available, a thread will be - used. If the <c>key</c> argument is null, the threads from the - pool are used in a round-robin way, each call to - <c>driver_async</c> uses the next thread in the pool. With the - <c>key</c> argument set, this behaviour is changed. The two - same values of <c>*key</c> always get the same thread.</p> - <p>To make sure that a driver instance always uses the same - thread, the following call can be used:</p> - <p></p> - <code type="none"><![CDATA[ - unsigned int myKey = driver_async_port_key(myPort); - - r = driver_async(myPort, &myKey, myData, myFunc); - ]]></code> - <p>It is enough to initialize <c>myKey</c> once for each - driver instance.</p> - <p>If a thread is already working, the calls will be - queued up and executed in order. Using the same thread for - each driver instance ensures that the calls will be made in - sequence.</p> - <p>The <c>async_data</c> is the argument to the functions - <c>async_invoke</c> and <c>async_free</c>. It's typically a - pointer to a structure that contains a pipe or event that - can be used to signal that the async operation completed. - The data should be freed in <c>async_free</c>.</p> - <p>When the async operation is done, <seealso marker="driver_entry#ready_async">ready_async</seealso> driver - entry function is called. If <c>ready_async</c> is null in - the driver entry, the <c>async_free</c> function is called - instead.</p> - <p>The return value is -1 if the <c>driver_async</c> call - fails.</p> + <marker id="erl_drv_cnd_name"></marker> + <p>Returns a pointer to the name of the condition.</p> + <p><c>cnd</c> is a pointer to an initialized condition.</p> <note> - <p>As of erts version 5.5.4.3 the default stack size for - threads in the async-thread pool is 16 kilowords, - i.e., 64 kilobyte on 32-bit architectures. - This small default size has been chosen since the - amount of async-threads might be quite large. The - default stack size is enough for drivers delivered - with Erlang/OTP, but might not be sufficiently large - for other dynamically linked in drivers that use the - driver_async() functionality. A suggested stack size - for threads in the async-thread pool can be configured - via the - <seealso marker="erl#async_thread_stack_size">+a</seealso> - command line argument of - <seealso marker="erl">erl(1)</seealso>.</p> + <p>This function is intended for debugging purposes only.</p> </note> </desc> </func> + <func> - <name><ret>unsigned int</ret><nametext>driver_async_port_key (ErlDrvPort port)</nametext></name> - <fsummary>Calculate an async key from an ErlDrvPort</fsummary> + <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond + *cnd)</nametext></name> + <fsummary>Signal on a condition variable.</fsummary> <desc> - <marker id="driver_async_port_key"></marker> - <p>This function calculates a key for later use in <seealso - marker="#driver_async">driver_async()</seealso>. The keys are - evenly distributed so that a fair mapping between port id's - and async thread id's is achieved.</p> - <note> - <p>Before OTP-R16, the actual port id could be used as a key - with proper casting, but after the rewrite of the port - subsystem, this is no longer the case. With this function, you - can achieve the same distribution based on port id's as before - OTP-R16.</p> - </note> + <marker id="erl_drv_cond_signal"></marker> + <p>Signals on a condition variable. That is, if + other threads are waiting on the condition variable being + signaled, <em>one</em> of them is woken.</p> + <p><c>cnd</c> is a pointer to a condition variable to signal on.</p> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort port)</nametext></name> - <fsummary>Make sure the driver is never unloaded</fsummary> + <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, + ErlDrvMutex *mtx)</nametext></name> + <fsummary>Wait on a condition variable.</fsummary> <desc> - <marker id="driver_lock_driver"></marker> - <p>This function locks the driver used by the port <c>port</c> - in memory for the rest of the emulator process' - lifetime. After this call, the driver behaves as one of Erlang's - statically linked in drivers.</p> + <marker id="erl_drv_cond_wait"></marker> + <p>Waits on a condition variable. The calling + thread is blocked until another thread wakes it by signaling + or broadcasting on the condition variable. Before the calling + thread is blocked, it unlocks the mutex passed as argument. + When the calling thread is woken, it locks the same mutex before + returning. That is, the mutex currently must be locked by + the calling thread when calling this function.</p> + <p><c>cnd</c> is a pointer to a condition variable to wait on. + <c>mtx</c> is a pointer to a mutex to unlock while waiting.</p> + <note> + <p><c>erl_drv_cond_wait</c> can return even if + no one has signaled or broadcast on the condition + variable. Code calling <c>erl_drv_cond_wait</c> is + always to be prepared for <c>erl_drv_cond_wait</c> + returning even if the condition that the thread was + waiting for has not occurred. That is, when returning from + <c>erl_drv_cond_wait</c>, always check if the condition + has occurred, and if not call <c>erl_drv_cond_wait</c> again.</p> + </note> + <p>This function is thread-safe.</p> </desc> </func> + <func> - <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, ErlDrvTermData owner_pid, char* name, ErlDrvData drv_data)</nametext></name> - <fsummary>Create a new port (driver instance)</fsummary> + <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, + int percent)</nametext></name> + <fsummary>Give the runtime system a hint about how much CPU time the + current driver callback call has consumed.</fsummary> <desc> - <p>This function creates a new port executing the same driver - code as the port creating the new port. - A short description of the arguments:</p> + <marker id="erl_drv_consume_timeslice"></marker> + <p>Gives the runtime system a hint about how much CPU time the current + driver callback call has consumed since the last hint, or since the + the start of the callback if no previous hint has been given.</p> <taglist> <tag><c>port</c></tag> - <item>The port handle of the port (driver instance) creating - the new port.</item> - <tag><c>owner_pid</c></tag> - <item>The process id of the Erlang process which will be - owner of the new port. This process will be linked - to the new port. You usually want to use - <c>driver_caller(port)</c> as <c>owner_pid</c>.</item> - <tag><c>name</c></tag> - <item>The port name of the new port. You usually want to - use the same port name as the driver name - (<seealso marker="driver_entry#driver_name">driver_name</seealso> - field of the - <seealso marker="driver_entry">driver_entry</seealso>).</item> - <tag><c>drv_data</c></tag> - <item>The driver defined handle that will be passed in subsequent - calls to driver call-backs. Note, that the - <seealso marker="driver_entry#start">driver start call-back</seealso> - will not be called for this new driver instance. - The driver defined handle is normally created in the - <seealso marker="driver_entry#start">driver start call-back</seealso> - when a port is created via - <seealso marker="erlang#open_port/2">erlang:open_port/2</seealso>. </item> + <item>Port handle of the executing port.</item> + <tag><c>percent</c></tag> + <item>Approximate consumed fraction of a full + time-slice in percent.</item> </taglist> - <p>The caller of <c>driver_create_port()</c> is allowed to - manipulate the newly created port when <c>driver_create_port()</c> - has returned. When - <seealso marker="#smp_support">port level locking</seealso> - is used, the creating port is, however, only allowed to - manipulate the newly created port until the current driver - call-back that was called by the emulator returns.</p> + <p>The time is specified as a fraction, in percent, of a full time-slice + that a port is allowed to execute before it is to surrender the + CPU to other runnable ports or processes. Valid range is + <c>[1, 100]</c>. The scheduling time-slice is not an exact entity, + but can usually be approximated to about 1 millisecond.</p> + <p>Notice that it is up to the runtime system to determine if and + how to use this information. Implementations on some platforms + can use other means to determine the consumed fraction + of the time-slice. Lengthy driver callbacks should, regardless of + this, frequently call this function to determine if it is allowed + to continue execution or not.</p> + <p>This function returns a non-zero value + if the time-slice has been exhausted, and zero if the callback is + allowed to continue execution. If a non-zero value is + returned, the driver callback is to return as soon as possible in + order for the port to be able to yield.</p> + <p>This function is provided to better support co-operative scheduling, + improve system responsiveness, and to make it easier to prevent + misbehaviors of the VM because of a port monopolizing a scheduler + thread. It can be used when dividing lengthy work into some repeated + driver callback calls, without the need to use threads.</p> + <p>See also the important <seealso marker="#WARNING">warning</seealso> + text at the beginning of this manual page.</p> + </desc> + </func> + + <func> + <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime + val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name> + <fsummary>Convert time unit of a time value.</fsummary> + <desc> + <marker id="erl_drv_convert_time_unit"></marker> + <p>Converts the <c>val</c> value of time unit <c>from</c> to + the corresponding value of time unit <c>to</c>. The result is + rounded using the floor function.</p> + <taglist> + <tag><c>val</c></tag> + <item>Value to convert time unit for.</item> + <tag><c>from</c></tag> + <item>Time unit of <c>val</c>.</item> + <tag><c>to</c></tag> + <item>Time unit of returned value.</item> + </taglist> + <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid + time unit argument.</p> + <p>See also <seealso marker="#ErlDrvTime"> + <c>ErlDrvTime</c></seealso> and + <seealso marker="#ErlDrvTimeUnit"> + <c>ErlDrvTimeUnit</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, + ErlDrvTid tid2)</nametext></name> + <fsummary>Compare thread identifiers for equality.</fsummary> + <desc> + <marker id="erl_drv_equal_tids"></marker> + <p>Compares two thread identifiers, <c>tid1</c> and <c>tid2</c>, + for equality.</p> + <p>Returns <c>0</c> it they are not equal, and a value not equal to + <c>0</c> if they are equal.</p> <note> - <p>When - <seealso marker="#smp_support">port level locking</seealso> - is used, the creating port is only allowed to manipulate - the newly created port until the current driver call-back - returns.</p> + <p>A thread identifier can be reused very quickly after + a thread has terminated. Therefore, if a thread + corresponding to one of the involved thread identifiers + has terminated since the thread identifier was saved, + the result of <c>erl_drv_equal_tids</c> does possibly not give + the expected result.</p> </note> + <p>This function is thread-safe.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char + *value, size_t *value_size)</nametext></name> + <fsummary>Get the value of an environment variable.</fsummary> + <desc> + <marker id="erl_drv_getenv"></marker> + <p>Retrieves the value of an environment variable.</p> + <taglist> + <tag><c>key</c></tag> + <item>A <c>NULL</c>-terminated string containing the + name of the environment variable.</item> + <tag><c>value</c></tag> + <item>A pointer to an output buffer.</item> + <tag><c>value_size</c></tag> + <item>A pointer to an integer. The integer is used both for + passing input and output sizes (see below).</item> + </taglist> + <p>When this function is called, <c>*value_size</c> is to contain the + size of the <c>value</c> buffer.</p> + <p>On success, <c>0</c> is returned, + the value of the environment variable has been written to + the <c>value</c> buffer, and <c>*value_size</c> contains the + string length (excluding the terminating <c>NULL</c> character) of + the value written to the <c>value</c> buffer.</p> + <p>On failure, that is, no such environment variable was found, + a value < <c>0</c> is returned. When the size of the <c>value</c> + buffer is too small, a value > <c>0</c> is returned and + <c>*value_size</c> has been set to the buffer size needed.</p> + <warning> + <p>Do <em>not</em> use libc's <c>getenv</c> or similar C library + interfaces from a driver.</p> + </warning> + <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, ErlDrvData res)</nametext></name> - <fsummary>Acknowledge the start of the port</fsummary> + <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, + ErlDrvData res)</nametext></name> + <fsummary>Acknowledge the start of the port.</fsummary> <desc> <marker id="erl_drv_init_ack"></marker> - <p>Arguments:</p> + <p>Acknowledges the start of the port.</p> <taglist> <tag><c>port</c></tag> - <item>The port handle of the port (driver instance) creating - doing the acknowledgment. + <item>The port handle of the port (driver instance) + doing the acknowledgment. </item> <tag><c>res</c></tag> - <item>The result of the port initialization. This can be the same values - as the return value of <seealso marker="driver_entry#start">start</seealso>, - i.e any of the error codes or the ErlDrvData that is to be used for this - port. + <item>The result of the port initialization. Can be the same + values as the return value of <seealso marker="driver_entry#start"> + <c>start</c></seealso>, that is, any of the error codes or the + <c>ErlDrvData</c> that is to be used for this port. </item> </taglist> - <p> - When this function is called the initiating erlang:open_port call is - returned as if the <seealso marker="driver_entry#start">start</seealso> - function had just been called. It can only be used when the - <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_USE_INIT_ACK</seealso> - flag has been set on the linked-in driver. - </p> + <p>When this function is called the initiating <c>erlang:open_port</c> + call is returned as if the <seealso marker="driver_entry#start"> + <c>start</c></seealso> function had just been called. It can only be + used when flag <seealso marker="driver_entry#driver_flags"> + <c>ERL_DRV_FLAG_USE_INIT_ACK</c></seealso> + has been set on the linked-in driver.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, ErlDrvSInt pid)</nametext></name> - <fsummary>Set the os_pid for the port</fsummary> + <name><ret>ErlDrvTime</ret> + <nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext> + </name> + <fsummary>Get Erlang monotonic time.</fsummary> <desc> - <marker id="erl_drv_set_os_pid"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>port</c></tag> - <item>The port handle of the port (driver instance) to set the pid on. - </item> - <tag><c>pid</c></tag> - <item>The pid to set.</item> - </taglist> - <p> - Set the os_pid seen when doing erlang:port_info/2 on this port. - </p> + <marker id="erl_drv_monotonic_time"></marker> + <p>Returns <seealso marker="time_correction#Erlang_Monotonic_Time"> + Erlang monotonic time</seealso>. Notice that negative values are + not uncommon.</p> + <p><c>time_unit</c> is time unit of returned value.</p> + <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.</p> + <p>See also <seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso> + and <seealso marker="#ErlDrvTimeUnit"> + <c>ErlDrvTimeUnit</c></seealso>.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_thread_create(char *name, - ErlDrvTid *tid, - void * (*func)(void *), - void *arg, - ErlDrvThreadOpts *opts)</nametext></name> - <fsummary>Create a thread</fsummary> + <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char + *name)</nametext></name> + <fsummary>Create a mutex.</fsummary> <desc> - <marker id="erl_drv_thread_create"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>name</c></tag> - <item>A string identifying the created thread. It will be used - to identify the thread in planned future debug - functionality. - </item> - <tag><c>tid</c></tag> - <item>A pointer to a thread identifier variable.</item> - <tag><c>func</c></tag> - <item>A pointer to a function to execute in the created thread.</item> - <tag><c>arg</c></tag> - <item>A pointer to argument to the <c>func</c> function.</item> - <tag><c>opts</c></tag> - <item>A pointer to thread options to use or <c>NULL</c>.</item> - </taglist> - <p>This function creates a new thread. On success <c>0</c> is returned; - otherwise, an <c>errno</c> value is returned to indicate the error. - The newly created thread will begin executing in the function pointed - to by <c>func</c>, and <c>func</c> will be passed <c>arg</c> as - argument. When <c>erl_drv_thread_create()</c> returns the thread - identifier of the newly created thread will be available in - <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a - pointer to an - <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso> - structure. If <c>opts</c> is a <c>NULL</c> pointer, default options - will be used; otherwise, the passed options will be used. - </p> - <warning><p>You are not allowed to allocate the - <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso> - structure by yourself. It has to be allocated and - initialized by - <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>. - </p></warning> - <p>The created thread will terminate either when <c>func</c> returns - or if - <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso> - is called by the thread. The exit value of the thread is either - returned from <c>func</c> or passed as argument to - <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>. - The driver creating the thread has the responsibility of joining the - thread, via - <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>, - before the driver is unloaded. It is not possible to create - "detached" threads, i.e., threads that don't need to be joined. - </p> - <warning><p>All created threads need to be joined by the driver before - it is unloaded. If the driver fails to join all threads - created before it is unloaded, the runtime system will - most likely crash when the code of the driver is unloaded. - </p></warning> + <marker id="erl_drv_mutex_create"></marker> + <p>Creates a mutex and returns a pointer to it.</p> + <p><c>name</c> is a string identifying the created mutex. It is used + to identify the mutex in planned future debug functionality.</p> + <p>Returns <c>NULL</c> on failure. The driver creating the mutex is + responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>ErlDrvThreadOpts *</ret><nametext>erl_drv_thread_opts_create(char *name)</nametext></name> - <fsummary>Create thread options</fsummary> + <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex + *mtx)</nametext></name> + <fsummary>Destroy a mutex.</fsummary> <desc> - <marker id="erl_drv_thread_opts_create"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>name</c></tag> - <item>A string identifying the created thread options. It will be used - to identify the thread options in planned future debug - functionality. - </item> - </taglist> - <p>This function allocates and initialize a thread option - structure. On failure <c>NULL</c> is returned. A thread option - structure is used for passing options to - <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>. - If the structure isn't modified before it is passed to - <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>, - the default values will be used. - </p> - <warning><p>You are not allowed to allocate the - <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso> - structure by yourself. It has to be allocated and - initialized by <c>erl_drv_thread_opts_create()</c>. - </p></warning> + <marker id="erl_drv_mutex_destroy"></marker> + <p>Destroys a mutex previously created by + <seealso marker="#erl_drv_mutex_create"> + <c>erl_drv_mutex_create</c></seealso>. + The mutex must be in an unlocked state before it is destroyed.</p> + <p><c>mtx</c> is a pointer to a mutex to destroy.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext></name> - <fsummary>Destroy thread options</fsummary> + <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex + *mtx)</nametext></name> + <fsummary>Lock a mutex.</fsummary> <desc> - <marker id="erl_drv_thread_opts_destroy"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>opts</c></tag> - <item>A pointer to thread options to destroy.</item> - </taglist> - <p>This function destroys thread options previously created by - <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>. - </p> + <marker id="erl_drv_mutex_lock"></marker> + <p>Locks a mutex. The calling thread is blocked until the mutex has + been locked. A thread that has currently locked the mutex + <em>cannot</em> lock the same mutex again.</p> + <p><c>mtx</c> is a pointer to a mutex to lock.</p> + <warning> + <p>If you leave a mutex locked in an emulator thread + when you let the thread out of your control, you will + <em>very likely</em> deadlock the whole emulator.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_thread_exit(void *exit_value)</nametext></name> - <fsummary>Terminate calling thread</fsummary> + <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex + *mtx)</nametext></name> + <fsummary>Get name of driver mutex.</fsummary> <desc> - <marker id="erl_drv_thread_exit"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>exit_value</c></tag> - <item>A pointer to an exit value or <c>NULL</c>.</item> - </taglist> - <p>This function terminates the calling thread with the exit - value passed as argument. You are only allowed to terminate - threads created with - <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>. - The exit value can later be retrieved by another thread via - <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>. - </p> - <p>This function is thread-safe.</p> + <marker id="erl_drv_mutex_name"></marker> + <p>Returns a pointer to the mutex name.</p> + <p><c>mtx</c> is a pointer to an initialized mutex.</p> + <note> + <p>This function is intended for debugging purposes only.</p> + </note> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void **exit_value)</nametext></name> - <fsummary>Join with another thread</fsummary> + <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex + *mtx)</nametext></name> + <fsummary>Try lock a mutex.</fsummary> <desc> - <marker id="erl_drv_thread_join"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>tid</c></tag> - <item>The thread identifier of the thread to join.</item> - <tag><c>exit_value</c></tag> - <item>A pointer to a pointer to an exit value, or <c>NULL</c>.</item> - </taglist> - <p>This function joins the calling thread with another thread, i.e., - the calling thread is blocked until the thread identified by - <c>tid</c> has terminated. On success <c>0</c> is returned; - otherwise, an <c>errno</c> value is returned to indicate the error. - A thread can only be joined once. The behavior of joining - more than once is undefined, an emulator crash is likely. If - <c>exit_value == NULL</c>, the exit value of the terminated thread - will be ignored; otherwise, the exit value of the terminated thread - will be stored at <c>*exit_value</c>. - </p> + <marker id="erl_drv_mutex_trylock"></marker> + <p>Tries to lock a mutex. A thread that has currently locked the mutex + <em>cannot</em> try to lock the same mutex again.</p> + <p><c>mtx</c> is a pointer to a mutex to try to lock.</p> + <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.</p> + <warning> + <p>If you leave a mutex locked in an emulator thread + when you let the thread out of your control, you will + <em>very likely</em> deadlock the whole emulator.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>ErlDrvTid</ret><nametext>erl_drv_thread_self(void)</nametext></name> - <fsummary>Get the thread identifier of the current thread</fsummary> + <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex + *mtx)</nametext></name> + <fsummary>Unlock a mutex.</fsummary> <desc> - <marker id="erl_drv_thread_self"></marker> - <p>This function returns the thread identifier of the - calling thread. - </p> + <marker id="erl_drv_mutex_unlock"></marker> + <p>Unlocks a mutex. The mutex currently must be + locked by the calling thread.</p> + <p><c>mtx</c> is a pointer to a mutex to unlock.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)</nametext></name> - <fsummary>Compare thread identifiers for equality</fsummary> + <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, + ErlDrvTermData* term, int n)</nametext></name> + <fsummary>Send term data from driver to port owner.</fsummary> <desc> - <marker id="erl_drv_equal_tids"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>tid1</c></tag> - <item>A thread identifier.</item> - <tag><c>tid2</c></tag> - <item>A thread identifier.</item> - </taglist> - <p>This function compares two thread identifiers for equality, - and returns <c>0</c> it they aren't equal, and - a value not equal to <c>0</c> if they are equal.</p> - <note><p>A Thread identifier may be reused very quickly after - a thread has terminated. Therefore, if a thread - corresponding to one of the involved thread identifiers - has terminated since the thread identifier was saved, - the result of <c>erl_drv_equal_tids()</c> might not give - the expected result. - </p></note> - <p>This function is thread-safe.</p> + <marker id="erl_drv_output_term"></marker> + <p>Sends data in the special driver term + format to the port owner process. This is a fast way to + deliver term data from a driver. It needs no binary + conversion, so the port owner process receives data as + normal Erlang terms. The <seealso marker="#erl_drv_send_term"> + <c>erl_drv_send_term</c></seealso> + functions can be used for sending to any process + on the local node.</p> + <note> + <p>Parameter <c>port</c> is <em>not</em> + an ordinary port handle, but a port handle converted using + <seealso marker="#driver_mk_port"> + <c>driver_mk_port</c></seealso>.</p> + </note> + <p>Parameter <c>term</c> points to an array of + <c>ErlDrvTermData</c> with <c>n</c> elements. This array + contains terms described in the driver term format. Every + term consists of 1-4 elements in the array. The + first term has a term type and then arguments. + Parameter <c>port</c> specifies the sending port.</p> + <p>Tuples, maps, and lists (except strings, see below) + are built in reverse polish notation, so that to build a + tuple, the elements are specified first, and then the tuple + term, with a count. Likewise for lists and maps.</p> + <list type="bulleted"> + <item> + <p>A tuple must be specified with the number of elements. (The + elements precede the <c>ERL_DRV_TUPLE</c> term.)</p> + </item> + <item> + <p>A map must be specified with the number of key-value pairs + <c>N</c>. The key-value pairs must precede the <c>ERL_DRV_MAP</c> + in this order: <c>key1,value1,key2,value2,...,keyN,valueN</c>. + Duplicate keys are not allowed.</p> + </item> + <item> + <p>A list must be specified with the number of elements, + including the tail, which is the last term preceding + <c>ERL_DRV_LIST</c>.</p> + </item> + </list> + <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to + "splice" in a string in a list, a string specified this way is + not a list in itself, but the elements are elements of the + surrounding list.</p> + <pre> +Term type Arguments +--------- --------- +ERL_DRV_NIL +ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string)) +ERL_DRV_INT ErlDrvSInt integer +ERL_DRV_UINT ErlDrvUInt integer +ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr +ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr +ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port)) +ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset +ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len +ERL_DRV_STRING char *str, int len +ERL_DRV_TUPLE int sz +ERL_DRV_LIST int sz +ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) + or driver_caller(ErlDrvPort port)) +ERL_DRV_STRING_CONS char *str, int len +ERL_DRV_FLOAT double *dbl +ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len +ERL_DRV_MAP int sz</pre> + <p>The unsigned integer data type <c>ErlDrvUInt</c> and the + signed integer data type <c>ErlDrvSInt</c> are 64 bits wide + on a 64-bit runtime system and 32 bits wide on a 32-bit + runtime system. They were introduced in ERTS 5.6 + and replaced some of the <c>int</c> arguments in the list above.</p> + <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the + signed integer data type <c>ErlDrvSInt64</c> are always 64 bits + wide. They were introduced in ERTS 5.7.4.</p> + <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the + following call can be made.</p> + <code type="none"><![CDATA[ +ErlDrvBinary* bin = ... +ErlDrvPort port = ... +ErlDrvTermData spec[] = { + ERL_DRV_ATOM, driver_mk_atom("tcp"), + ERL_DRV_PORT, driver_mk_port(drvport), + ERL_DRV_INT, 100, + ERL_DRV_BINARY, bin, 50, 0, + ERL_DRV_LIST, 2, + ERL_DRV_TUPLE, 3, +}; +erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code> + <p>Here <c>bin</c> is a driver binary of length at least 50 and + <c>drvport</c> is a port handle. Notice that <c>ERL_DRV_LIST</c> + comes after the elements of the list, likewise + <c>ERL_DRV_TUPLE</c>.</p> + <p>The <c>ERL_DRV_STRING_CONS</c> term is a way to construct + strings. It works differently from how <c>ERL_DRV_STRING</c> + works. <c>ERL_DRV_STRING_CONS</c> builds a string list in + reverse order (as opposed to how <c>ERL_DRV_LIST</c> + works), concatenating the strings added to a list. The tail + must be specified before <c>ERL_DRV_STRING_CONS</c>.</p> + <p><c>ERL_DRV_STRING</c> constructs a string, and ends + it. (So it is the same as <c>ERL_DRV_NIL</c> followed by + <c>ERL_DRV_STRING_CONS</c>.)</p> + <code type="none"><![CDATA[ +/* to send [x, "abc", y] to the port: */ +ErlDrvTermData spec[] = { + ERL_DRV_ATOM, driver_mk_atom("x"), + ERL_DRV_STRING, (ErlDrvTermData)"abc", 3, + ERL_DRV_ATOM, driver_mk_atom("y"), + ERL_DRV_NIL, + ERL_DRV_LIST, 4 +}; +erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code> + <code type="none"><![CDATA[ +/* to send "abc123" to the port: */ +ErlDrvTermData spec[] = { + ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */ + ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3, + ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3, +}; +erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code> + <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a + term encoded with the + <seealso marker="erl_ext_dist">external format</seealso>, + that is, a term that has been encoded by + <seealso marker="erlang#term_to_binary/2"> + <c>erlang:term_to_binary</c></seealso>, + <seealso marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seealso>, + and so on. + For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c> + that contains term <c>{17, 4711}</c> encoded with the + <seealso marker="erl_ext_dist">external format</seealso>, + and you want to wrap it in a two-tuple with the tag <c>my_tag</c>, + that is, <c>{my_tag, {17, 4711}}</c>, you can do as follows:</p> + <code type="none"><![CDATA[ +ErlDrvTermData spec[] = { + ERL_DRV_ATOM, driver_mk_atom("my_tag"), + ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size + ERL_DRV_TUPLE, 2, +}; +erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code> + <p>To build the map <c>#{key1 => 100, key2 => {200, 300}}</c>, the + following call can be made.</p> + <code type="none"><![CDATA[ +ErlDrvPort port = ... +ErlDrvTermData spec[] = { + ERL_DRV_ATOM, driver_mk_atom("key1"), + ERL_DRV_INT, 100, + ERL_DRV_ATOM, driver_mk_atom("key2"), + ERL_DRV_INT, 200, + ERL_DRV_INT, 300, + ERL_DRV_TUPLE, 2, + ERL_DRV_MAP, 2 +}; +erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code> + <p>If you want to pass a binary and do not already have the content + of the binary in an <c>ErlDrvBinary</c>, you can benefit from using + <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c> + through <seealso marker="#driver_alloc_binary"> + <c>driver_alloc_binary</c></seealso> and then pass the binary through + <c>ERL_DRV_BINARY</c>. The runtime system often allocates + binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used. + However, if the content of the binary to pass already resides in + an <c>ErlDrvBinary</c>, it is normally better to pass the binary using + <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.</p> + <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and + <c>ERL_DRV_EXT2TERM</c> term types were introduced in + ERTS 5.6.</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> </desc> </func> <func> - <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char *name)</nametext></name> - <fsummary>Create a mutex</fsummary> + <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char + *value)</nametext></name> + <fsummary>Set the value of an environment variable.</fsummary> <desc> - <marker id="erl_drv_mutex_create"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>name</c></tag> - <item>A string identifying the created mutex. It will be used - to identify the mutex in planned future debug functionality. - </item> - </taglist> - <p>This function creates a mutex and returns a pointer to it. On - failure <c>NULL</c> is returned. The driver creating the mutex - has the responsibility of destroying it before the driver is - unloaded. - </p> + <marker id="erl_drv_putenv"></marker> + <p>Sets the value of an environment variable.</p> + <p><c>key</c> is a <c>NULL</c>-terminated string containing the + name of the environment variable.</p> + <p><c>value</c> is a <c>NULL</c>-terminated string containing the + new value of the environment variable.</p> + <p>Returns <c>0</c> on success, otherwise a value <c>!= 0</c>.</p> + <note> + <p>The result of passing the empty string (<c>""</c>) as a value + is platform-dependent. On some platforms the variable value + is set to the empty string, on others the + environment variable is removed.</p> + </note> + <warning> + <p>Do <em>not</em> use libc's <c>putenv</c> or similar C library + interfaces from a driver.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex *mtx)</nametext></name> - <fsummary>Destroy a mutex</fsummary> + <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char + *name)</nametext></name> + <fsummary>Create an rwlock.</fsummary> <desc> - <marker id="erl_drv_mutex_destroy"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>mtx</c></tag> - <item>A pointer to a mutex to destroy.</item> - </taglist> - <p>This function destroys a mutex previously created by - <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>. - The mutex has to be in an unlocked state before being - destroyed. - </p> + <marker id="erl_drv_rwlock_create"></marker> + <p>Creates an rwlock and returns a pointer to it.</p> + <p><c>name</c> is a string identifying the created rwlock. + It is used to identify the rwlock in planned future + debug functionality.</p> + <p>Returns <c>NULL</c> on failure. The driver creating the rwlock + is responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex *mtx)</nametext></name> - <fsummary>Lock a mutex</fsummary> + <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Destroy an rwlock.</fsummary> <desc> - <marker id="erl_drv_mutex_lock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>mtx</c></tag> - <item>A pointer to a mutex to lock.</item> - </taglist> - <p>This function locks a mutex. The calling thread will be - blocked until the mutex has been locked. A thread - which currently has locked the mutex may <em>not</em> lock - the same mutex again. - </p> - <warning><p>If you leave a mutex locked in an emulator thread - when you let the thread out of your control, you will - <em>very likely</em> deadlock the whole emulator. - </p></warning> + <marker id="erl_drv_rwlock_destroy"></marker> + <p>Destroys an rwlock previously created by + <seealso marker="#erl_drv_rwlock_create"> + <c>erl_drv_rwlock_create</c></seealso>. + The rwlock must be in an unlocked state before it is destroyed.</p> + <p><c>rwlck</c> is a pointer to an rwlock to destroy.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex *mtx)</nametext></name> - <fsummary>Try lock a mutex</fsummary> + <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Get name of driver mutex.</fsummary> <desc> - <marker id="erl_drv_mutex_trylock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>mtx</c></tag> - <item>A pointer to a mutex to try to lock.</item> - </taglist> - <p>This function tries to lock a mutex. If successful <c>0</c>, - is returned; otherwise, <c>EBUSY</c> is returned. A thread - which currently has locked the mutex may <em>not</em> try to - lock the same mutex again. - </p> - <warning><p>If you leave a mutex locked in an emulator thread - when you let the thread out of your control, you will - <em>very likely</em> deadlock the whole emulator. - </p></warning> - <p>This function is thread-safe.</p> + <marker id="erl_drv_rwlock_name"></marker> + <p>Returns a pointer to the name of the rwlock.</p> + <p><c>rwlck</c> is a pointer to an initialized rwlock.</p> + <note> + <p>This function is intended for debugging purposes only.</p> + </note> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex *mtx)</nametext></name> - <fsummary>Unlock a mutex</fsummary> + <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Read lock an rwlock.</fsummary> <desc> - <marker id="erl_drv_mutex_unlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>mtx</c></tag> - <item>A pointer to a mutex to unlock.</item> - </taglist> - <p>This function unlocks a mutex. The mutex currently has to be - locked by the calling thread. - </p> + <marker id="erl_drv_rwlock_rlock"></marker> + <p>Read locks an rwlock. The calling thread is + blocked until the rwlock has been read locked. A thread + that currently has read or read/write locked the rwlock + <em>cannot</em> lock the same rwlock again.</p> + <p><c>rwlck</c> is a pointer to the rwlock to read lock.</p> + <warning> + <p>If you leave an rwlock locked in an emulator thread + when you let the thread out of your control, you will + <em>very likely</em> deadlock the whole emulator.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char *name)</nametext></name> - <fsummary>Create a condition variable</fsummary> + <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Read unlock an rwlock.</fsummary> <desc> - <marker id="erl_drv_cond_create"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>name</c></tag> - <item>A string identifying the created condition variable. It - will be used to identify the condition variable in planned - future debug functionality. - </item> - </taglist> - <p>This function creates a condition variable and returns a - pointer to it. On failure <c>NULL</c> is returned. The driver - creating the condition variable has the responsibility of - destroying it before the driver is unloaded.</p> + <marker id="erl_drv_rwlock_runlock"></marker> + <p>Read unlocks an rwlock. The rwlock currently must + be read locked by the calling thread.</p> + <p><c>rwlck</c> is a pointer to an rwlock to read unlock.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond *cnd)</nametext></name> - <fsummary>Destroy a condition variable</fsummary> + <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Read/write lock an rwlock.</fsummary> <desc> - <marker id="erl_drv_cond_destroy"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>cnd</c></tag> - <item>A pointer to a condition variable to destroy.</item> - </taglist> - <p>This function destroys a condition variable previously - created by - <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>. - </p> - <p>This function is thread-safe.</p> + <marker id="erl_drv_rwlock_rwlock"></marker> + <p>Read/write locks an rwlock. The calling thread + is blocked until the rwlock has been read/write locked. + A thread that currently has read or read/write locked the + rwlock <em>cannot</em> lock the same rwlock again.</p> + <p><c>rwlck</c> is a pointer to an rwlock to read/write lock.</p> + <warning> + <p>If you leave an rwlock locked in an emulator thread + when you let the thread out of your control, you will + <em>very likely</em> deadlock the whole emulator.</p> + </warning> + <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond *cnd)</nametext></name> - <fsummary>Signal on a condition variable</fsummary> + <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Read/write unlock an rwlock.</fsummary> <desc> - <marker id="erl_drv_cond_signal"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>cnd</c></tag> - <item>A pointer to a condition variable to signal on.</item> - </taglist> - <p>This function signals on a condition variable. That is, if - other threads are waiting on the condition variable being - signaled, <em>one</em> of them will be woken. - </p> + <marker id="erl_drv_rwlock_rwunlock"></marker> + <p>Read/write unlocks an rwlock. The rwlock currently must be + read/write locked by the calling thread.</p> + <p><c>rwlck</c> is a pointer to an rwlock to read/write unlock.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond *cnd)</nametext></name> - <fsummary>Broadcast on a condition variable</fsummary> + <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Try to read lock an rwlock.</fsummary> <desc> - <marker id="erl_drv_cond_broadcast"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>cnd</c></tag> - <item>A pointer to a condition variable to broadcast on.</item> - </taglist> - <p>This function broadcasts on a condition variable. That is, if - other threads are waiting on the condition variable being - broadcast on, <em>all</em> of them will be woken. - </p> + <marker id="erl_drv_rwlock_tryrlock"></marker> + <p>Tries to read lock an rwlock.</p> + <p><c>rwlck</c> is a pointer to an rwlock to try to read lock.</p> + <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>. + A thread that currently has read or read/write locked the + rwlock <em>cannot</em> try to lock the same rwlock again.</p> + <warning> + <p>If you leave an rwlock locked in an emulator thread + when you let the thread out of your control, you will + <em>very likely</em> deadlock the whole emulator.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)</nametext></name> - <fsummary>Wait on a condition variable</fsummary> + <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock + *rwlck)</nametext></name> + <fsummary>Try to read/write lock an rwlock.</fsummary> <desc> - <marker id="erl_drv_cond_wait"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>cnd</c></tag> - <item>A pointer to a condition variable to wait on.</item> - <tag><c>mtx</c></tag> - <item>A pointer to a mutex to unlock while waiting.</item> - <tag><c></c></tag> - <item></item> - </taglist> - <p>This function waits on a condition variable. The calling - thread is blocked until another thread wakes it by signaling - or broadcasting on the condition variable. Before the calling - thread is blocked it unlocks the mutex passed as argument, and - when the calling thread is woken it locks the same mutex before - returning. That is, the mutex currently has to be locked by - the calling thread when calling this function. - </p> - <note><p><c>erl_drv_cond_wait()</c> might return even though - no-one has signaled or broadcast on the condition - variable. Code calling <c>erl_drv_cond_wait()</c> should - always be prepared for <c>erl_drv_cond_wait()</c> - returning even though the condition that the thread was - waiting for hasn't occurred. That is, when returning from - <c>erl_drv_cond_wait()</c> always check if the condition - has occurred, and if not call <c>erl_drv_cond_wait()</c> - again. - </p></note> + <marker id="erl_drv_rwlock_tryrwlock"></marker> + <p>Tries to read/write lock an rwlock. + A thread that currently has read or read/write locked the + rwlock <em>cannot</em> try to lock the same rwlock again.</p> + <p><c>rwlck</c>is pointer to an rwlock to try to read/write lock.</p> + <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.</p> + <warning> + <p>If you leave an rwlock locked in an emulator thread + when you let the thread out of your control, you will + <em>very likely</em> deadlock the whole emulator.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char *name)</nametext></name> - <fsummary>Create an rwlock</fsummary> + <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, + ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> + <fsummary>Send term data to other process than port owner process. + </fsummary> <desc> - <marker id="erl_drv_rwlock_create"></marker> - <p>Arguments:</p> + <marker id="erl_drv_send_term"></marker> + <p>This function is the only way for a driver to send data to + <em>other</em> processes than the port owner process. Parameter + <c>receiver</c> specifies the process to receive the data.</p> + <note> + <p>Parameter <c>port</c> is <em>not</em> an ordinary port handle, but + a port handle converted using + <seealso marker="#driver_mk_port"> + <c>driver_mk_port</c></seealso>.</p> + </note> + <p>Parameters <c>port</c>, <c>term</c>, and <c>n</c> work as in + <seealso marker="#erl_drv_output_term"> + <c>erl_drv_output_term</c></seealso>.</p> + <p>This function is only thread-safe when the emulator with SMP + support is used.</p> + </desc> + </func> + + <func> + <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, + ErlDrvSInt pid)</nametext></name> + <fsummary>Set the os_pid for the port.</fsummary> + <desc> + <marker id="erl_drv_set_os_pid"></marker> + <p>Sets the <c>os_pid</c> seen when doing + <seealso marker="erlang:port_info/2"> + <c>erlang:port_info/2</c></seealso> on this port.</p> + <p><c>port</c> is the port handle of the port (driver instance) to set + the pid on. <c>pid</c>is the pid to set.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>erl_drv_thread_create(char *name, ErlDrvTid + *tid, void * (*func)(void *), void *arg, ErlDrvThreadOpts + *opts)</nametext></name> + <fsummary>Create a thread.</fsummary> + <desc> + <marker id="erl_drv_thread_create"></marker> + <p>Creates a new thread.</p> <taglist> <tag><c>name</c></tag> - <item>A string identifying the created rwlock. It will be used to - identify the rwlock in planned future debug functionality. - </item> + <item>A string identifying the created thread. It is used to + identify the thread in planned future debug functionality. + </item> + <tag><c>tid</c></tag> + <item>A pointer to a thread identifier variable.</item> + <tag><c>func</c></tag> + <item>A pointer to a function to execute in the created thread.</item> + <tag><c>arg</c></tag> + <item>A pointer to argument to the <c>func</c> function.</item> + <tag><c>opts</c></tag> + <item>A pointer to thread options to use or <c>NULL</c>.</item> </taglist> - <p>This function creates an rwlock and returns a pointer to it. On - failure <c>NULL</c> is returned. The driver creating the rwlock - has the responsibility of destroying it before the driver is - unloaded. - </p> + <p>Returns <c>0</c> on success, + otherwise an <c>errno</c> value is returned to indicate the error. + The newly created thread begins executing in the function pointed + to by <c>func</c>, and <c>func</c> is passed <c>arg</c> as + argument. When <c>erl_drv_thread_create</c> returns, the thread + identifier of the newly created thread is available in + <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a + pointer to an + <seealso marker="#ErlDrvThreadOpts"><c>ErlDrvThreadOpts</c></seealso> + structure. If <c>opts</c> is a <c>NULL</c> pointer, default options + are used, otherwise the passed options are used.</p> + <warning> + <p>You are not allowed to allocate the + <seealso marker="#ErlDrvThreadOpts"> + <c>ErlDrvThreadOpts</c></seealso> structure by yourself. + It must be allocated and initialized by + <seealso marker="#erl_drv_thread_opts_create"> + <c>erl_drv_thread_opts_create</c></seealso>.</p> + </warning> + <p>The created thread terminates either when <c>func</c> returns or if + <seealso marker="#erl_drv_thread_exit"> + <c>erl_drv_thread_exit</c></seealso> + is called by the thread. The exit value of the thread is either + returned from <c>func</c> or passed as argument to + <seealso marker="#erl_drv_thread_exit"> + <c>erl_drv_thread_exit</c></seealso>. + The driver creating the thread is responsible for joining the + thread, through <seealso marker="#erl_drv_thread_join"> + <c>erl_drv_thread_join</c></seealso>, + before the driver is unloaded. "Detached" threads cannot be created, + that is, threads that do not need to be joined.</p> + <warning> + <p>All created threads must be joined by the driver before + it is unloaded. If the driver fails to join all threads + created before it is unloaded, the runtime system + most likely crashes when the driver code is unloaded.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Destroy an rwlock</fsummary> + <name><ret>void</ret><nametext>erl_drv_thread_exit(void + *exit_value)</nametext></name> + <fsummary>Terminate calling thread.</fsummary> <desc> - <marker id="erl_drv_rwlock_destroy"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to destroy.</item> - </taglist> - <p>This function destroys an rwlock previously created by - <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>. - The rwlock has to be in an unlocked state before being destroyed. - </p> + <marker id="erl_drv_thread_exit"></marker> + <p>Terminates the calling thread with the exit value passed as + argument. <c>exit_value</c> is a pointer to an exit value or + <c>NULL</c>.</p> + <p>You are only allowed to terminate threads created with + <seealso marker="#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>.</p> + <p>The exit value can later be retrieved by another thread through + <seealso marker="#erl_drv_thread_join"> + <c>erl_drv_thread_join</c></seealso>.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Read lock an rwlock</fsummary> + <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void + **exit_value)</nametext></name> + <fsummary>Join with another thread.</fsummary> <desc> - <marker id="erl_drv_rwlock_rlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to read lock.</item> - </taglist> - <p>This function read locks an rwlock. The calling thread will be - blocked until the rwlock has been read locked. A thread - which currently has read or read/write locked the rwlock may - <em>not</em> lock the same rwlock again. - </p> - <warning><p>If you leave an rwlock locked in an emulator thread - when you let the thread out of your control, you will - <em>very likely</em> deadlock the whole emulator. - </p></warning> + <marker id="erl_drv_thread_join"></marker> + <p>Joins the calling thread with another thread, that is, + the calling thread is blocked until the thread identified by + <c>tid</c> has terminated.</p> + <p><c>tid</c> is the thread identifier of the thread to join. + <c>exit_value</c> is a pointer to a pointer to an exit value, + or <c>NULL</c>.</p> + <p>Returns <c>0</c> on success, otherwise an <c>errno</c> + value is returned to indicate the error.</p> + <p>A thread can only be joined once. The behavior of joining + more than once is undefined, an emulator crash is likely. If + <c>exit_value == NULL</c>, the exit value of the terminated thread + is ignored, otherwise the exit value of the terminated thread + is stored at <c>*exit_value</c>.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Try to read lock an rwlock</fsummary> + <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid + tid)</nametext></name> + <fsummary>Get name of driver mutex.</fsummary> <desc> - <marker id="erl_drv_rwlock_tryrlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to try to read lock.</item> - </taglist> - <p>This function tries to read lock an rwlock. If successful - <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned. - A thread which currently has read or read/write locked the - rwlock may <em>not</em> try to lock the same rwlock again. - </p> - <warning><p>If you leave an rwlock locked in an emulator thread - when you let the thread out of your control, you will - <em>very likely</em> deadlock the whole emulator. - </p></warning> - <p>This function is thread-safe.</p> + <marker id="erl_drv_rwlock_name"></marker> + <p>Returns a pointer to the name of the thread.</p> + <p><c>tid</c> is a thread identifier.</p> + <note> + <p>This function is intended for debugging purposes only.</p> + </note> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Read unlock an rwlock</fsummary> + <name><ret>ErlDrvThreadOpts *</ret> + <nametext>erl_drv_thread_opts_create(char *name)</nametext></name> + <fsummary>Create thread options.</fsummary> <desc> - <marker id="erl_drv_rwlock_runlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to read unlock.</item> - </taglist> - <p>This function read unlocks an rwlock. The rwlock currently - has to be read locked by the calling thread. - </p> + <marker id="erl_drv_thread_opts_create"></marker> + <p>Allocates and initializes a thread option structure.</p> + <p><c>name</c> is a string identifying the created thread options. + It is used to identify the thread options in planned future debug + functionality.</p> + <p>Returns <c>NULL</c> on failure. A thread option + structure is used for passing options to + <seealso marker="#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>. + If the structure is not modified before it is passed to + <seealso marker="#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>, + the default values are used.</p> + <warning> + <p>You are not allowed to allocate the + <seealso marker="#ErlDrvThreadOpts"> + <c>ErlDrvThreadOpts</c></seealso> + structure by yourself. It must be allocated and initialized by + <c>erl_drv_thread_opts_create</c>.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Read/Write lock an rwlock</fsummary> + <name><ret>void</ret> + <nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext> + </name> + <fsummary>Destroy thread options.</fsummary> <desc> - <marker id="erl_drv_rwlock_rwlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to read/write lock.</item> - </taglist> - <p>This function read/write locks an rwlock. The calling thread - will be blocked until the rwlock has been read/write locked. - A thread which currently has read or read/write locked the - rwlock may <em>not</em> lock the same rwlock again. - </p> - <warning><p>If you leave an rwlock locked in an emulator thread - when you let the thread out of your control, you will - <em>very likely</em> deadlock the whole emulator. - </p></warning> + <marker id="erl_drv_thread_opts_destroy"></marker> + <p>Destroys thread options previously created by + <seealso marker="#erl_drv_thread_opts_create"> + <c>erl_drv_thread_opts_create</c></seealso>.</p> + <p><c>opts</c> is a pointer to thread options to destroy.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Try to read/write lock an rwlock</fsummary> + <name><ret>ErlDrvTid</ret> + <nametext>erl_drv_thread_self(void)</nametext></name> + <fsummary>Get the thread identifier of the current thread.</fsummary> <desc> - <marker id="erl_drv_rwlock_tryrwlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to try to read/write lock.</item> - </taglist> - <p>This function tries to read/write lock an rwlock. If successful - <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned. - A thread which currently has read or read/write locked the - rwlock may <em>not</em> try to lock the same rwlock again. - </p> - <warning><p>If you leave an rwlock locked in an emulator thread - when you let the thread out of your control, you will - <em>very likely</em> deadlock the whole emulator. - </p></warning> + <marker id="erl_drv_thread_self"></marker> + <p>Returns the thread identifier of the calling thread.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Read/Write unlock an rwlock</fsummary> + <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit + time_unit)</nametext></name> + <fsummary>Get current time offset.</fsummary> <desc> - <marker id="erl_drv_rwlock_rwunlock"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an rwlock to read/write unlock.</item> - </taglist> - <p>This function read/write unlocks an rwlock. The rwlock - currently has to be read/write locked by the calling thread. - </p> + <marker id="erl_drv_time_offset"></marker> + <p>Returns the current time offset between + <seealso marker="time_correction#Erlang_Monotonic_Time"> + Erlang monotonic time</seealso> and + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> + converted into the <c>time_unit</c> passed as argument.</p> + <p><c>time_unit</c> is time unit of returned value.</p> + <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.</p> + <p>See also <seealso marker="#ErlDrvTime"> + <c>ErlDrvTime</c></seealso> and + <seealso marker="#ErlDrvTimeUnit"> + <c>ErlDrvTimeUnit</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey + key)</nametext></name> + <fsummary>Get thread-specific data.</fsummary> + <desc> + <marker id="erl_drv_tsd_get"></marker> + <p>Returns the thread-specific data + associated with <c>key</c> for the calling thread.</p> + <p><c>key</c> is a thread-specific data key.</p> + <p>Returns <c>NULL</c> if no data has been associated + with <c>key</c> for the calling thread.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)</nametext></name> - <fsummary>Create a thread specific data key</fsummary> + <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, + ErlDrvTSDKey *key)</nametext></name> + <fsummary>Create a thread-specific data key.</fsummary> <desc> <marker id="erl_drv_tsd_key_create"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>name</c></tag> - <item>A string identifying the created key. It will be used - to identify the key in planned future debug - functionality. - </item> - <tag><c>key</c></tag> - <item>A pointer to a thread specific data key variable.</item> - </taglist> - <p>This function creates a thread specific data key. On success - <c>0</c> is returned; otherwise, an <c>errno</c> value is returned - to indicate the error. The driver creating the key has the - responsibility of destroying it before the driver is unloaded. - </p> + <p>Creates a thread-specific data key.</p> + <p><c>name</c> is a string identifying the created key. It is used + to identify the key in planned future debug functionality.</p> + <p><c>key</c> is a pointer to a thread-specific data key variable.</p> + <p>Returns <c>0</c> on success, otherwise an <c>errno</c> value is + returned to indicate the error. The driver creating the key is + responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey key)</nametext></name> - <fsummary>Destroy a thread specific data key</fsummary> + <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey + key)</nametext></name> + <fsummary>Destroy a thread-specific data key.</fsummary> <desc> <marker id="erl_drv_tsd_key_destroy"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>key</c></tag> - <item>A thread specific data key to destroy.</item> - </taglist> - <p>This function destroys a thread specific data key - previously created by - <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>. - All thread specific data using this key in all threads - have to be cleared (see - <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>) - prior to the call to <c>erl_drv_tsd_key_destroy()</c>. - </p> - <warning><p>A destroyed key is very likely to be reused soon. - Therefore, if you fail to clear the thread specific - data using this key in a thread prior to destroying - the key, you will <em>very likely</em> get unexpected - errors in other parts of the system. - </p></warning> + <p>Destroys a thread-specific data key previously created by + <seealso marker="#erl_drv_tsd_key_create"> + <c>erl_drv_tsd_key_create</c></seealso>. + All thread-specific data using this key in all threads + must be cleared (see <seealso marker="#erl_drv_tsd_set"> + <c>erl_drv_tsd_set</c></seealso>) + before the call to <c>erl_drv_tsd_key_destroy</c>.</p> + <p><c>key</c> is a thread-specific data key to destroy.</p> + <warning> + <p>A destroyed key is very likely to be reused soon. + Therefore, if you fail to clear the thread-specific + data using this key in a thread before destroying + the key, you will <em>very likely</em> get unexpected + errors in other parts of the system.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void *data)</nametext></name> - <fsummary>Set thread specific data</fsummary> + <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void + *data)</nametext></name> + <fsummary>Set thread-specific data.</fsummary> <desc> <marker id="erl_drv_tsd_set"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>key</c></tag> - <item>A thread specific data key.</item> - <tag><c>data</c></tag> - <item>A pointer to data to associate with <c>key</c> - in calling thread. - </item> - </taglist> - <p>This function sets thread specific data associated with - <c>key</c> for the calling thread. You are only allowed to set - thread specific data for threads while they are fully under your - control. For example, if you set thread specific data in a thread - calling a driver call-back function, it has to be cleared, i.e. - set to <c>NULL</c>, before returning from the driver call-back - function. - </p> - <warning><p>If you fail to clear thread specific data in an - emulator thread before letting it out of your control, - you might not ever be able to clear this data with - later unexpected errors in other parts of the system as - a result. - </p></warning> + <p>Sets thread-specific data associated with + <c>key</c> for the calling thread. You are only allowed to set + thread-specific data for threads while they are fully under your + control. For example, if you set thread-specific data in a thread + calling a driver callback function, it must be cleared, that is, + set to <c>NULL</c>, before returning from the driver callback + function.</p> + <p><c>key</c> is a thread-specific data key.</p> + <p><c>data</c> is a pointer to data to associate with <c>key</c> + in the calling thread.</p> + <warning> + <p>If you fail to clear thread-specific data in an + emulator thread before letting it out of your control, + you might never be able to clear this data with + later unexpected errors in other parts of the system as + a result.</p> + </warning> <p>This function is thread-safe.</p> </desc> </func> <func> - <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey key)</nametext></name> - <fsummary>Get thread specific data</fsummary> + <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name> + <fsummary>Get Erlang error atom name from error number.</fsummary> <desc> - <marker id="erl_drv_tsd_get"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>key</c></tag> - <item>A thread specific data key.</item> - </taglist> - <p>This function returns the thread specific data - associated with <c>key</c> for the calling thread. - If no data has been associated with <c>key</c> for - the calling thread, <c>NULL</c> is returned. - </p> - <p>This function is thread-safe.</p> + <marker id="erl_errno_id"></marker> + <p>Returns the atom name of the Erlang error, + given the error number in <c>error</c>. The error atoms are + <c>einval</c>, <c>enoent</c>, and so on. It can be used to make + error terms from the driver.</p> </desc> </func> <func> - <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char *value)</nametext></name> - <fsummary>Set the value of an environment variable</fsummary> + <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry + *de)</nametext></name> + <fsummary>Remove a driver entry.</fsummary> <desc> - <marker id="erl_drv_putenv"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>key</c></tag> - <item>A null terminated string containing the - name of the environment variable.</item> - <tag><c>value</c></tag> - <item>A null terminated string containing the - new value of the environment variable.</item> - </taglist> - <p>This function sets the value of an environment variable. - It returns <c>0</c> on success, and a value <c>!= 0</c> on - failure. - </p> - <note><p>The result of passing the empty string ("") as a value - is platform dependent. On some platforms the value of the - variable is set to the empty string, on others, the - environment variable is removed.</p> - </note> - <warning><p>Do <em>not</em> use libc's <c>putenv</c> or similar - C library interfaces from a driver. - </p></warning> - <p>This function is thread-safe.</p> + <marker id="remove_driver_entry"></marker> + <p>Removes a driver entry <c>de</c> previously added with + <seealso marker="#add_driver_entry"> + <c>add_driver_entry</c></seealso>.</p> + <p>Driver entries added by the <c>erl_ddll</c> Erlang interface + cannot be removed by using this interface.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char *value, size_t *value_size)</nametext></name> - <fsummary>Get the value of an environment variable</fsummary> + <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int + on)</nametext></name> + <fsummary>Signal or unsignal port as busy.</fsummary> <desc> - <marker id="erl_drv_getenv"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>key</c></tag> - <item>A null terminated string containing the - name of the environment variable.</item> - <tag><c>value</c></tag> - <item>A pointer to an output buffer.</item> - <tag><c>value_size</c></tag> - <item>A pointer to an integer. The integer is both used for - passing input and output sizes (see below). - </item> - </taglist> - <p>This function retrieves the value of an environment variable. - When called, <c>*value_size</c> should contain the size of - the <c>value</c> buffer. On success <c>0</c> is returned, - the value of the environment variable has been written to - the <c>value</c> buffer, and <c>*value_size</c> contains the - string length (excluding the terminating null character) of - the value written to the <c>value</c> buffer. On failure, - i.e., no such environment variable was found, a value less than - <c>0</c> is returned. When the size of the <c>value</c> - buffer is too small, a value greater than <c>0</c> is returned - and <c>*value_size</c> has been set to the buffer size needed. - </p> - <warning><p>Do <em>not</em> use libc's <c>getenv</c> or similar - C library interfaces from a driver. - </p></warning> - <p>This function is thread-safe.</p> + <marker id="set_busy_port"></marker> + <p>Sets and unsets the busy state of the port. If + <c>on</c> is non-zero, the port is set to busy. If it is zero, + the port is set to not busy. You typically want to combine + this feature with the <seealso marker="#erl_drv_busy_msgq_limits"> + busy port message queue</seealso> functionality.</p> + <p>Processes sending command data to the port are suspended + if either the port or the port message queue + is busy. Suspended processes are resumed when neither the + port or the port message queue is busy. Command data + is in this context data passed to the port using either + <c>Port ! {Owner, {command, Data}}</c> or + <c>port_command/[2,3]</c>.</p> + <p>If the <seealso marker="driver_entry#driver_flags"> + <![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso> has been set in the + <seealso marker="driver_entry"><c>driver_entry</c></seealso>, + data can be forced into the driver through + <seealso marker="erlang#port_command/3"> + <c>erlang:port_command(Port, Data, [force])</c></seealso> + even if the driver has signaled that it is busy.</p> + <p>For information about busy port message queue functionality, see + <seealso marker="#erl_drv_busy_msgq_limits"> + <c>erl_drv_busy_msgq_limits</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, + int flags)</nametext></name> + <fsummary>Set flags on how to handle control entry function.</fsummary> + <desc> + <marker id="set_port_control_flags"></marker> + <p>Sets flags for how the <seealso marker="driver_entry#control"> + <c>control</c></seealso> driver entry + function will return data to the port owner process. + (The <c>control</c> function is called from + <seealso marker="erlang:port_control/3"> + <c>erlang:port_control/3</c></seealso>.)</p> + <p>Currently there are only two meaningful values for + <c>flags</c>: <c>0</c> means that data is returned in a list, + and <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as + a binary from <c>control</c>.</p> </desc> </func> - <func> - <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, int percent)</nametext></name> - <fsummary>Give the runtime system a hint about how much CPU time the - current driver callback call has consumed</fsummary> - <desc> - <marker id="erl_drv_consume_timeslice"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>port</c></tag> - <item>Port handle of the executing port.</item> - <tag><c>percent</c></tag> - <item>Approximate consumed fraction of a full - time-slice in percent.</item> - </taglist> - <p>Give the runtime system a hint about how much CPU time the - current driver callback call has consumed since last hint, or - since the start of the callback if no previous hint has been given. - The time is given as a fraction, in percent, of a full time-slice - that a port is allowed to execute before it should surrender the - CPU to other runnable ports or processes. Valid range is - <c>[1, 100]</c>. The scheduling time-slice is not an exact entity, - but can usually be approximated to about 1 millisecond.</p> - - <p>Note that it is up to the runtime system to determine if and - how to use this information. Implementations on some platforms - may use other means in order to determine the consumed fraction - of the time-slice. Lengthy driver callbacks should regardless of - this frequently call the <c>erl_drv_consume_timeslice()</c> - function in order to determine if it is allowed to continue - execution or not.</p> - - <p><c>erl_drv_consume_timeslice()</c> returns a non-zero value - if the time-slice has been exhausted, and zero if the callback is - allowed to continue execution. If a non-zero value is - returned the driver callback should return as soon as possible in - order for the port to be able to yield.</p> - - <p>This function is provided to better support co-operative scheduling, - improve system responsiveness, and to make it easier to prevent - misbehaviors of the VM due to a port monopolizing a scheduler thread. - It can be used when dividing length work into a number of repeated - driver callback calls without the need to use threads. Also see the - important <seealso marker="#WARNING">warning</seealso> text at the - beginning of this document.</p> - </desc> - </func> - - <func> - <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond *cnd)</nametext></name> - <fsummary>Get name of driver mutex.</fsummary> - <desc> - <marker id="erl_drv_cnd_name"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>cnd</c></tag> - <item>A pointer to an initialized condition.</item> - </taglist> - <p> - Returns a pointer to the name of the condition. - </p> - <note> - <p>This function is intended for debugging purposes only.</p> - </note> - </desc> - </func> - - <func> - <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex *mtx)</nametext></name> - <fsummary>Get name of driver mutex.</fsummary> - <desc> - <marker id="erl_drv_mutex_name"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>mtx</c></tag> - <item>A pointer to an initialized mutex.</item> - </taglist> - <p> - Returns a pointer to the name of the mutex. - </p> - <note> - <p>This function is intended for debugging purposes only.</p> - </note> - </desc> - </func> - - <func> - <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock *rwlck)</nametext></name> - <fsummary>Get name of driver mutex.</fsummary> - <desc> - <marker id="erl_drv_rwlock_name"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>rwlck</c></tag> - <item>A pointer to an initialized r/w-lock.</item> - </taglist> - <p> - Returns a pointer to the name of the r/w-lock. - </p> - <note> - <p>This function is intended for debugging purposes only.</p> - </note> - </desc> - </func> - - <func> - <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid tid)</nametext></name> - <fsummary>Get name of driver mutex.</fsummary> - <desc> - <marker id="erl_drv_rwlock_name"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>tid</c></tag> - <item>A thread identifier.</item> - </taglist> - <p> - Returns a pointer to the name of the thread. - </p> - <note> - <p>This function is intended for debugging purposes only.</p> - </note> - </desc> - </func> - - <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext></name> - <fsummary>Get Erlang Monotonic Time</fsummary> - <desc> - <marker id="erl_drv_monotonic_time"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>time_unit</c></tag> - <item>Time unit of returned value.</item> - </taglist> - <p> - Returns - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso>. Note that it is not uncommon with - negative values. - </p> - <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid - time unit argument, or if called from a thread that is not a - scheduler thread.</p> - <p>See also:</p> - <list> - <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item> - <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item> - </list> - </desc> - </func> - - <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit time_unit)</nametext></name> - <fsummary>Get current Time Offset</fsummary> - <desc> - <marker id="erl_drv_time_offset"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>time_unit</c></tag> - <item>Time unit of returned value.</item> - </taglist> - <p>Returns the current time offset between - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso> - and - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> - converted into the <c>time_unit</c> passed as argument.</p> - <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid - time unit argument, or if called from a thread that is not a - scheduler thread.</p> - <p>See also:</p> - <list> - <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item> - <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item> - </list> - </desc> - </func> - - <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name> - <fsummary>Convert time unit of a time value</fsummary> - <desc> - <marker id="erl_drv_convert_time_unit"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>val</c></tag> - <item>Value to convert time unit for.</item> - <tag><c>from</c></tag> - <item>Time unit of <c>val</c>.</item> - <tag><c>to</c></tag> - <item>Time unit of returned value.</item> - </taglist> - <p>Converts the <c>val</c> value of time unit <c>from</c> to - the corresponding value of time unit <c>to</c>. The result is - rounded using the floor function.</p> - <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid - time unit argument.</p> - <p>See also:</p> - <list> - <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item> - <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item> - </list> - </desc> - </func> - </funcs> + <section> - <title>SEE ALSO</title> - <p><seealso marker="driver_entry">driver_entry(3)</seealso>, - <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>, - <seealso marker="erlang">erlang(3)</seealso></p> - <p>An Alternative Distribution Driver (ERTS User's - Guide Ch. 3)</p> + <title>See Also</title> + <p><seealso marker="driver_entry"><c>driver_entry(3)</c></seealso>, + <seealso marker="erlang"><c>erlang(3)</c></seealso>, + <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso>, + section <seealso marker="alt_dist">How to Implement an Alternative + Carrier for the Erlang Distribution></seealso> in the User's Guide</p> </section> </cref> diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index 2ac974f497..4f799f8f34 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2007</year> - <year>2015</year> + <year>2016</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -35,135 +35,130 @@ <section> <title>Introduction</title> <p> - The external term format is mainly used in the distribution + The external term format is mainly used in the distribution mechanism of Erlang. </p> <p> - Since Erlang has a fixed number of types, there is no need for a - programmer to define a specification for the external format used + As Erlang has a fixed number of types, there is no need for a + programmer to define a specification for the external format used within some application. - All Erlang terms has an external representation and the interpretation - of the different terms are application specific. + All Erlang terms have an external representation and the interpretation + of the different terms is application-specific. </p> <p> - In Erlang the BIF <seealso marker="erts:erlang#term_to_binary/1">term_to_binary/1,2</seealso> is used to convert a - term into the external format. - To convert binary data encoding a term the BIF + In Erlang the BIF <seealso marker="erts:erlang#term_to_binary/1"> + <c>erlang:term_to_binary/1,2</c></seealso> is used to convert a + term into the external format. + To convert binary data encoding to a term, the BIF <seealso marker="erts:erlang#binary_to_term/1"> - binary_to_term/1 - </seealso> - is used. + <c>erlang:binary_to_term/1</c>c></seealso> is used. </p> <p> - The distribution does this implicitly when sending messages across + The distribution does this implicitly when sending messages across node boundaries. </p> <marker id="overall_format"/> <p> - The overall format of the term format is: + The overall format of the term format is as follows: </p> <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">N</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">N</cell> </row> - <row> - <cell align="center"><c>131</c></cell> - <cell align="center"><c>Tag</c></cell> - <cell align="center"><c>Data</c></cell> - </row> - <tcaption></tcaption></table> + <row> + <cell align="center"><c>131</c></cell> + <cell align="center"><c>Tag</c></cell> + <cell align="center"><c>Data</c></cell> + </row> + <tcaption>Term Format</tcaption></table> <note> - <p> - When messages are - <seealso marker="erl_dist_protocol#connected_nodes">passed between - connected nodes</seealso> and a - <seealso marker="#distribution_header">distribution - header</seealso> is used, the first byte containing the version - number (131) is omitted from the terms that follow the distribution - header. This since - the version number is implied by the version number in the - distribution header. - </p> + <p> + When messages are + <seealso marker="erl_dist_protocol#connected_nodes">passed between + connected nodes</seealso> and a + <seealso marker="#distribution_header">distribution + header</seealso> is used, the first byte containing the version + number (131) is omitted from the terms that follow the distribution + header. This is because the version number is implied by the version + number in the distribution header. + </p> </note> <p> - A compressed term looks like this: + The compressed term format is as follows: </p> <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">4</cell> - <cell align="center">N</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">4</cell> + <cell align="center">N</cell> </row> <row> - <cell align="center">131</cell> - <cell align="center">80</cell> - <cell align="center">UncompressedSize</cell> - <cell align="center">Zlib-compressedData</cell> + <cell align="center"><c>131</c></cell> + <cell align="center"><c>80</c></cell> + <cell align="center"><c>UncompressedSize</c></cell> + <cell align="center"><c>Zlib-compressedData</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>Compressed Term Format</tcaption></table> <p> - Uncompressed Size (unsigned 32 bit integer in big-endian byte order) + Uncompressed size (unsigned 32-bit integer in big-endian byte order) is the size of the data before it was compressed. - The compressed data has the following format when it has been - expanded: + The compressed data has the following format when it has been expanded: </p> <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">Uncompressed Size</cell> + <cell align="center">1</cell> + <cell align="center">Uncompressed Size</cell> </row> <row> - <cell align="center">Tag</cell> - <cell align="center">Data</cell> + <cell align="center"><c>Tag</c></cell> + <cell align="center"><c>Data</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>Compressed Data Format when Expanded</tcaption></table> <marker id="utf8_atoms"/> <note> - <p>As of ERTS version 5.10 (OTP-R16) support - for UTF-8 encoded atoms has been introduced in the external format. - However, only characters that can be encoded using Latin1 (ISO-8859-1) - are currently supported in atoms. The support for UTF-8 encoded atoms - in the external format has been implemented in order to be able to support - all Unicode characters in atoms in <em>some future release</em>. - Until full Unicode support for - atoms has been introduced, it is an <em>error</em> to pass atoms containing - characters that cannot be encoded in Latin1, and <em>the behavior is - undefined</em>.</p> - <p>When the - <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso> - distribution flag has been exchanged between both nodes in the - <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>, - all atoms in the distribution header will be encoded in UTF-8; otherwise, - all atoms in the distribution header will be encoded in Latin1. The two - new tags <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>, and - <seealso marker="#SMALL_ATOM_UTF8_EXT">SMALL_ATOM_UTF8_EXT</seealso> - will only be used if the <c>DFLAG_UTF8_ATOMS</c> distribution flag has - been exchanged between nodes, or if an atom containing characters - that cannot be encoded in Latin1 is encountered. - </p> - <p>The maximum number of allowed characters in an atom is 255. In the - UTF-8 case each character may need 4 bytes to be encoded. - </p> + <p>As from ERTS 5.10 (OTP R16) support + for UTF-8 encoded atoms has been introduced in the external format. + However, only characters that can be encoded using Latin-1 (ISO-8859-1) + are currently supported in atoms. The support for UTF-8 encoded atoms + in the external format has been implemented to be able to support + all Unicode characters in atoms in <em>some future release</em>. + Until full Unicode support for atoms has been introduced, + it is an <em>error</em> to pass atoms containing + characters that cannot be encoded in Latin-1, and <em>the behavior is + undefined</em>.</p> + <p>When distribution flag <seealso marker="erl_dist_protocol#dflags"> + <c>DFLAG_UTF8_ATOMS</c></seealso> has been exchanged between both nodes + in the <seealso marker="erl_dist_protocol#distribution_handshake"> + distribution handshake</seealso>, all atoms in the distribution header + are encoded in UTF-8, otherwise in Latin-1. The two + new tags <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso> + and <seealso marker="#SMALL_ATOM_UTF8_EXT"> + <c>SMALL_ATOM_UTF8_EXT</c></seealso> + are only used if the distribution flag <c>DFLAG_UTF8_ATOMS</c> has + been exchanged between nodes, or if an atom containing characters + that cannot be encoded in Latin-1 is encountered.</p> + <p>The maximum number of allowed characters in an atom is 255. In the + UTF-8 case, each character can need 4 bytes to be encoded.</p> </note> </section> <section> - <title>Distribution header</title> + <title>Distribution Header</title> <p> <marker id="distribution_header"/> - As of erts version 5.7.2 the old atom cache protocol was - dropped and a new one was introduced. This atom cache protocol - introduced the distribution header. Nodes with erts versions + As from ERTS 5.7.2 the old atom cache protocol was + dropped and a new one was introduced. This protocol + introduced the distribution header. Nodes with an ERTS version earlier than 5.7.2 can still communicate with new nodes, - but no distribution header and no atom cache will be used.</p> + but no distribution header and no atom cache are used.</p> <p> - The distribution header currently only contains an atom cache - reference section, but could in the future contain more + The distribution header only contains an atom cache + reference section, but can in the future contain more information. The distribution header precedes one or more Erlang - terms on the external format. For more information see the + terms on the external format. For more information, see the documentation of the <seealso marker="erl_dist_protocol#connected_nodes">protocol between connected nodes</seealso> in the @@ -173,37 +168,37 @@ <p> <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso> entries with corresponding <c>AtomCacheReferenceIndex</c> in terms - encoded on the external format following a distribution header refers + encoded on the external format following a distribution header refer to the atom cache references made in the distribution header. The range - is 0 <= <c>AtomCacheReferenceIndex</c> < 255, i.e., at most 255 + is 0 <= <c>AtomCacheReferenceIndex</c> < 255, that is, at most 255 different atom cache references from the following terms can be made. </p> <p> - The distribution header format is: + The distribution header format is as follows: </p> <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">NumberOfAtomCacheRefs/2+1 | 0</cell> - <cell align="center">N | 0</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">NumberOfAtomCacheRefs/2+1 | 0</cell> + <cell align="center">N | 0</cell> </row> <row> - <cell align="center"><c>131</c></cell> - <cell align="center"><c>68</c></cell> - <cell align="center"><c>NumberOfAtomCacheRefs</c></cell> - <cell align="center"><c>Flags</c></cell> - <cell align="center"><c>AtomCacheRefs</c></cell> + <cell align="center"><c>131</c></cell> + <cell align="center"><c>68</c></cell> + <cell align="center"><c>NumberOfAtomCacheRefs</c></cell> + <cell align="center"><c>Flags</c></cell> + <cell align="center"><c>AtomCacheRefs</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>Distribution Header Format</tcaption></table> <p> - <c>Flags</c> consists of <c>NumberOfAtomCacheRefs/2+1</c> bytes, + <c>Flags</c> consist of <c>NumberOfAtomCacheRefs/2+1</c> bytes, unless <c>NumberOfAtomCacheRefs</c> is <c>0</c>. If <c>NumberOfAtomCacheRefs</c> is <c>0</c>, <c>Flags</c> and - <c>AtomCacheRefs</c> are omitted. Each atom cache reference have + <c>AtomCacheRefs</c> are omitted. Each atom cache reference has a half byte flag field. Flags corresponding to a specific - <c>AtomCacheReferenceIndex</c>, are located in flag byte number + <c>AtomCacheReferenceIndex</c> are located in flag byte number <c>AtomCacheReferenceIndex/2</c>. Flag byte 0 is the first byte after the <c>NumberOfAtomCacheRefs</c> byte. Flags for an even <c>AtomCacheReferenceIndex</c> are located in the least significant @@ -216,95 +211,97 @@ </p> <table align="left"> <row> - <cell align="center">1 bit</cell> - <cell align="center">3 bits</cell> + <cell align="center">1 bit</cell> + <cell align="center">3 bits</cell> </row> <row> - <cell align="center"><c>NewCacheEntryFlag</c></cell> - <cell align="center"><c>SegmentIndex</c></cell> + <cell align="center"><c>NewCacheEntryFlag</c></cell> + <cell align="center"><c>SegmentIndex</c></cell> </row> <tcaption></tcaption></table> <p> The most significant bit is the <c>NewCacheEntryFlag</c>. If set, the corresponding cache reference is new. The three least significant bits are the <c>SegmentIndex</c> of the corresponding - atom cache entry. An atom cache consists of 8 segments each of size - 256, i.e., an atom cache can contain 2048 entries. + atom cache entry. An atom cache consists of 8 segments, each of size + 256, that is, an atom cache can contain 2048 entries. </p> <p> After flag fields for atom cache references, another half byte flag - field is located which has the following format: + field is located with the following format: </p> <table align="left"> <row> - <cell align="center">3 bits</cell> - <cell align="center">1 bit</cell> + <cell align="center">3 bits</cell> + <cell align="center">1 bit</cell> </row> <row> - <cell align="center"><c>CurrentlyUnused</c></cell> - <cell align="center"><c>LongAtoms</c></cell> + <cell align="center"><c>CurrentlyUnused</c></cell> + <cell align="center"><c>LongAtoms</c></cell> </row> <tcaption></tcaption></table> <p> - The least significant bit in that half byte is the <c>LongAtoms</c> - flag. If it is set, 2 bytes are used for atom lengths instead of + The least significant bit in that half byte is flag <c>LongAtoms</c>. + If it is set, 2 bytes are used for atom lengths instead of 1 byte in the distribution header. </p> <p> After the <c>Flags</c> field follow the <c>AtomCacheRefs</c>. The first <c>AtomCacheRef</c> is the one corresponding to - <c>AtomCacheReferenceIndex</c> 0. Higher indices follows + <c>AtomCacheReferenceIndex</c> 0. Higher indices follow in sequence up to index <c>NumberOfAtomCacheRefs - 1</c>. </p> <p> If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c> has - been set, a <c>NewAtomCacheRef</c> on the following format will follow: + been set, a <c>NewAtomCacheRef</c> on the following format follows: </p> <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">1 | 2</cell> - <cell align="center">Length</cell> + <cell align="center">1</cell> + <cell align="center">1 | 2</cell> + <cell align="center">Length</cell> </row> <row> - <cell align="center"><c>InternalSegmentIndex</c></cell> - <cell align="center"><c>Length</c></cell> - <cell align="center"><c>AtomText</c></cell> + <cell align="center"><c>InternalSegmentIndex</c></cell> + <cell align="center"><c>Length</c></cell> + <cell align="center"><c>AtomText</c></cell> </row> <tcaption></tcaption></table> <p> <c>InternalSegmentIndex</c> together with the <c>SegmentIndex</c> completely identify the location of an atom cache entry in the - atom cache. <c>Length</c> is number of bytes that <c>AtomText</c> - consists of. Length is a two byte big endian integer - if the <c>LongAtoms</c> flag has been set, otherwise a one byte - integer. When the - <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso> - distribution flag has been exchanged between both nodes in the - <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>, - characters in <c>AtomText</c> is encoded in UTF-8; otherwise, - encoded in Latin1. Subsequent <c>CachedAtomRef</c>s with the same + atom cache. <c>Length</c> is the number of bytes that <c>AtomText</c> + consists of. Length is a 2 byte big-endian integer + if flag <c>LongAtoms</c> has been set, otherwise a 1 byte + integer. When distribution flag + <seealso marker="erl_dist_protocol#dflags"> + <c>DFLAG_UTF8_ATOMS</c></seealso> + has been exchanged between both nodes in the + <seealso marker="erl_dist_protocol#distribution_handshake"> + distribution handshake</seealso>, + characters in <c>AtomText</c> are encoded in UTF-8, otherwise + in Latin-1. The following <c>CachedAtomRef</c>s with the same <c>SegmentIndex</c> and <c>InternalSegmentIndex</c> as this - <c>NewAtomCacheRef</c> will refer to this atom until a new + <c>NewAtomCacheRef</c> refer to this atom until a new <c>NewAtomCacheRef</c> with the same <c>SegmentIndex</c> and <c>InternalSegmentIndex</c> appear. </p> <p> - For more information on encoding of atoms, see + For more information on encoding of atoms, see the <seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso> - in the beginning of this document. + in the beginning of this section. </p> <p> If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c> has not been set, a <c>CachedAtomRef</c> on the following format - will follow: + follows: </p> <table align="left"> <row> - <cell align="center">1</cell> + <cell align="center">1</cell> </row> <row> - <cell align="center"><c>InternalSegmentIndex</c></cell> + <cell align="center"><c>InternalSegmentIndex</c></cell> </row> <tcaption></tcaption></table> <p> @@ -319,157 +316,147 @@ <section> <marker id="ATOM_CACHE_REF"/> <title>ATOM_CACHE_REF</title> - <table align="left"> <row> <cell align="center">1</cell> - <cell align="center">1</cell> + <cell align="center">1</cell> </row> <row> - <cell align="center"><c>82</c></cell> - <cell align="center"><c>AtomCacheReferenceIndex</c></cell> + <cell align="center"><c>82</c></cell> + <cell align="center"><c>AtomCacheReferenceIndex</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>ATOM_CACHE_REF</tcaption></table> <p> Refers to the atom with <c>AtomCacheReferenceIndex</c> in the - <seealso marker="#distribution_header">distribution header</seealso>. + <seealso marker="#distribution_header">distribution header</seealso>. </p> </section> <section> <marker id="SMALL_INTEGER_EXT"/> <title>SMALL_INTEGER_EXT</title> - <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">1</cell> + <cell align="center">1</cell> + <cell align="center">1</cell> </row> <row> - <cell align="center">97</cell> - <cell align="center">Int</cell> + <cell align="center"><c>97</c></cell> + <cell align="center"><c>Int</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>SMALL_INTEGER_EXT</tcaption></table> <p> - Unsigned 8 bit integer. + Unsigned 8-bit integer. </p> </section> <section> <marker id="INTEGER_EXT"/> <title>INTEGER_EXT</title> - <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">4</cell> + <cell align="center">1</cell> + <cell align="center">4</cell> </row> <row> - <cell align="center">98</cell> - <cell align="center">Int</cell> + <cell align="center"><c>98</c></cell> + <cell align="center"><c>Int</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>INTEGER_EXT</tcaption></table> <p> - Signed 32 bit integer in big-endian format (i.e. MSB first) + Signed 32-bit integer in big-endian format. </p> </section> <section> <marker id="FLOAT_EXT"/> <title>FLOAT_EXT</title> - <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">31</cell> + <cell align="center">1</cell> + <cell align="center">31</cell> </row> <row> - <cell align="center">99</cell> - <cell align="center">Float String</cell> + <cell align="center"><c>99</c></cell> + <cell align="center"><c>Float string</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>FLOAT_EXT</tcaption></table> <p> - A float is stored in string format. the format used in sprintf to - format the float is "%.20e" + A float is stored in string format. The format used in sprintf to + format the float is "%.20e" (there are more bytes allocated than necessary). - To unpack the float use sscanf with format "%lf". + To unpack the float, use sscanf with format "%lf". </p> <p> - This term is used in minor version 0 of the external format; - it has been superseded by - <seealso marker="#NEW_FLOAT_EXT"> - NEW_FLOAT_EXT - </seealso>. + This term is used in minor version 0 of the external format; + it has been superseded by + <seealso marker="#NEW_FLOAT_EXT"><c>NEW_FLOAT_EXT</c></seealso>. </p> </section> <section> <marker id="ATOM_EXT"/> <title>ATOM_EXT</title> - <table align="left"> <row> - <cell align="center">1</cell> - <cell align="center">2</cell> - <cell align="center">Len</cell> + <cell align="center">1</cell> + <cell align="center">2</cell> + <cell align="center">Len</cell> </row> <row> - <cell align="center"><c>100</c></cell> - <cell align="center"><c>Len</c></cell> - <cell align="center"><c>AtomName</c></cell> + <cell align="center"><c>100</c></cell> + <cell align="center"><c>Len</c></cell> + <cell align="center"><c>AtomName</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>ATOM_EXT</tcaption></table> <p> - An atom is stored with a 2 byte unsigned length in big-endian order, - followed by <c>Len</c> numbers of 8 bit Latin1 characters that forms - the <c>AtomName</c>. - <em>Note</em>: The maximum allowed value for <c>Len</c> is 255. + An atom is stored with a 2 byte unsigned length in big-endian order, + followed by <c>Len</c> numbers of 8-bit Latin-1 characters that forms + the <c>AtomName</c>. The maximum allowed value for <c>Len</c> is 255. </p> </section> <section> <marker id="REFERENCE_EXT"/> <title>REFERENCE_EXT</title> - - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">N</cell> - <cell align="center">4</cell> - <cell align="center">1</cell> - </row> - <row> - <cell align="center"><c>101</c></cell> - <cell align="center"><c>Node</c></cell> - <cell align="center"><c>ID</c></cell> - <cell align="center"><c>Creation</c></cell> - </row> - <tcaption></tcaption></table> + <table align="left"> + <row> + <cell align="center">1</cell> + <cell align="center">N</cell> + <cell align="center">4</cell> + <cell align="center">1</cell> + </row> + <row> + <cell align="center"><c>101</c></cell> + <cell align="center"><c>Node</c></cell> + <cell align="center"><c>ID</c></cell> + <cell align="center"><c>Creation</c></cell> + </row> + <tcaption>REFERENCE_EXT</tcaption></table> <p> - Encode a reference object (an object generated with <c>make_ref/0</c>). - The <c>Node</c> term is an encoded atom, i.e. - <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>, - <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or - <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>. - The <c>ID</c> field contains a big-endian - unsigned integer, - but <em>should be regarded as uninterpreted data</em> - since this field is node specific. - <c>Creation</c> is a byte containing a node serial number that - makes it possible to separate old (crashed) nodes from a new one. + Encodes a reference object (an object generated with + <seealso marker="erlang:make_ref/0">erlang:make_ref/0</seealso>). + The <c>Node</c> term is an encoded atom, that is, + <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>, + <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, or + <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>. + The <c>ID</c> field contains a big-endian unsigned integer, + but <em>is to be regarded as uninterpreted data</em>, + as this field is node-specific. + <c>Creation</c> is a byte containing a node serial number, which + makes it possible to separate old (crashed) nodes from a new one. </p> <p> - In <c>ID</c>, only 18 bits are significant; the rest should be 0. - In <c>Creation</c>, only 2 bits are significant; the rest should be 0. - - See <seealso marker="#NEW_REFERENCE_EXT">NEW_REFERENCE_EXT</seealso>. + In <c>ID</c>, only 18 bits are significant; the rest are to be 0. + In <c>Creation</c>, only two bits are significant; the rest are to be 0. + See <seealso marker="#NEW_REFERENCE_EXT"> + <c>NEW_REFERENCE_EXT</c></seealso>. </p> </section> <section> <marker id="PORT_EXT"/> <title>PORT_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -483,20 +470,21 @@ <cell align="center"><c>ID</c></cell> <cell align="center"><c>Creation</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>PORT_EXT</tcaption></table> <p> - Encode a port object (obtained form <c>open_port/2</c>). - The <c>ID</c> is a node specific identifier for a local port. + Encodes a port object (obtained from + <seealso marker="erlang:open_port/2"> + <c>erlang:open_port/2</c></seealso>). + The <c>ID</c> is a node-specific identifier for a local port. Port operations are not allowed across node boundaries. The <c>Creation</c> works just like in - <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>. + <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>. </p> </section> <section> <marker id="PID_EXT"/> <title>PID_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -512,23 +500,20 @@ <cell align="center"><c>Serial</c></cell> <cell align="center"><c>Creation</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>PID_EXT</tcaption></table> <p> - Encode a process identifier object (obtained from <c>spawn/3</c> or - friends). - The <c>ID</c> and <c>Creation</c> fields works just like in - <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>, while - the <c>Serial</c> field is used to improve safety. - - In <c>ID</c>, only 15 bits are significant; the rest should be 0. + Encodes a process identifier object (obtained from + <seealso marker="erlang:spawn/3"><c>erlang:spawn/3</c></seealso> or + friends). The <c>ID</c> and <c>Creation</c> fields works just like in + <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>, while + the <c>Serial</c> field is used to improve safety. + In <c>ID</c>, only 15 bits are significant; the rest are to be 0. </p> - </section> <section> <marker id="SMALL_TUPLE_EXT"/> <title>SMALL_TUPLE_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -536,22 +521,21 @@ <cell align="center">N</cell> </row> <row> - <cell align="center">104</cell> - <cell align="center">Arity</cell> - <cell align="center">Elements</cell> + <cell align="center"><c>104</c></cell> + <cell align="center"><c>Arity</c></cell> + <cell align="center"><c>Elements</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>SMALL_TUPLE_EXT</tcaption></table> <p> - <c>SMALL_TUPLE_EXT</c> encodes a tuple. The <c>Arity</c> - field is an unsigned byte that determines how many element - that follows in the <c>Elements</c> section. + Encodes a tuple. The <c>Arity</c> + field is an unsigned byte that determines how many elements + that follows in section <c>Elements</c>. </p> </section> <section> <marker id="LARGE_TUPLE_EXT"/> <title>LARGE_TUPLE_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -559,23 +543,22 @@ <cell align="center">N</cell> </row> <row> - <cell align="center">105</cell> - <cell align="center">Arity</cell> - <cell align="center">Elements</cell> + <cell align="center"><c>105</c></cell> + <cell align="center"><c>Arity</c></cell> + <cell align="center"><c>Elements</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>LARGE_TUPLE_EXT</tcaption></table> <p> - Same as - <seealso marker="#SMALL_TUPLE_EXT">SMALL_TUPLE_EXT</seealso> - with the exception that <c>Arity</c> is an - unsigned 4 byte integer in big endian format. + Same as + <seealso marker="#SMALL_TUPLE_EXT"><c>SMALL_TUPLE_EXT</c></seealso> + except that <c>Arity</c> is an + unsigned 4 byte integer in big-endian format. </p> </section> <section> <marker id="MAP_EXT"/> <title>MAP_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -583,43 +566,42 @@ <cell align="center">N</cell> </row> <row> - <cell align="center">116</cell> - <cell align="center">Arity</cell> - <cell align="center">Pairs</cell> + <cell align="center"><c>116</c></cell> + <cell align="center"><c>Arity</c></cell> + <cell align="center"><c>Pairs</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>MAP_EXT</tcaption></table> <p> - <c>MAP_EXT</c> encodes a map. The <c>Arity</c> field is an unsigned - 4 byte integer in big endian format that determines the number of + Encodes a map. The <c>Arity</c> field is an unsigned + 4 byte integer in big-endian format that determines the number of key-value pairs in the map. Key and value pairs (<c>Ki => Vi</c>) - are encoded in the <c>Pairs</c> section in the following order: + are encoded in section <c>Pairs</c> in the following order: <c>K1, V1, K2, V2,..., Kn, Vn</c>. Duplicate keys are <em>not allowed</em> within the same map. </p> - <p><em>Since: </em>OTP 17.0</p> + <p><em>As from </em>Erlang/OTP 17.0</p> </section> <section> <marker id="NIL_EXT"/> <title>NIL_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> </row> <row> - <cell align="center">106</cell> + <cell align="center"><c>106</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>NIL_EXT</tcaption></table> <p> - The representation for an empty list, i.e. the Erlang syntax <c>[]</c>. + The representation for an empty list, that is, the Erlang syntax + <c>[]</c>. </p> </section> <section> <marker id="STRING_EXT"/> <title>STRING_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -627,27 +609,25 @@ <cell align="center">Len</cell> </row> <row> - <cell align="center">107</cell> - <cell align="center">Length</cell> - <cell align="center">Characters</cell> + <cell align="center"><c>107</c></cell> + <cell align="center"><c>Length</c></cell> + <cell align="center"><c>Characters</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>STRING_EXT</tcaption></table> <p> - String does NOT have a corresponding Erlang representation, + String does <em>not</em> have a corresponding Erlang representation, but is an optimization for sending lists of bytes (integer in the range 0-255) more efficiently over the distribution. - Since the <c>Length</c> field is an unsigned 2 byte integer - (big endian), implementations must make sure that lists longer than - 65535 elements are encoded as - <seealso marker="#LIST_EXT">LIST_EXT</seealso>. + As field <c>Length</c> is an unsigned 2 byte integer + (big-endian), implementations must ensure that lists longer than + 65535 elements are encoded as + <seealso marker="#LIST_EXT"><c>LIST_EXT</c></seealso>. </p> - </section> <section> <marker id="LIST_EXT"/> <title>LIST_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -656,27 +636,24 @@ <cell align="center"> </cell> </row> <row> - <cell align="center">108</cell> - <cell align="center">Length</cell> - <cell align="center">Elements</cell> - <cell align="center">Tail</cell> + <cell align="center"><c>108</c></cell> + <cell align="center"><c>Length</c></cell> + <cell align="center"><c>Elements</c></cell> + <cell align="center"><c>Tail</c></cell> </row> - <tcaption></tcaption></table> - + <tcaption>LIST_EXT</tcaption></table> <p> - <c>Length</c> is the number of elements that follows in the - <c>Elements</c> section. <c>Tail</c> is the final tail of - the list; it is - <seealso marker="#NIL_EXT">NIL_EXT</seealso> - for a proper list, but may be anything type if the list is - improper (for instance <c>[a|b]</c>). + <c>Length</c> is the number of elements that follows in section + <c>Elements</c>. <c>Tail</c> is the final tail of the list; it is + <seealso marker="#NIL_EXT"><c>NIL_EXT</c></seealso> + for a proper list, but can be any type if the list is + improper (for example, <c>[a|b]</c>). </p> </section> <section> <marker id="BINARY_EXT"/> <title>BINARY_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -684,25 +661,26 @@ <cell align="center">Len</cell> </row> <row> - <cell align="center">109</cell> - <cell align="center">Len</cell> - <cell align="center">Data</cell> + <cell align="center"><c>109</c></cell> + <cell align="center"><c>Len</c></cell> + <cell align="center"><c>Data</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>BINARY_EXT</tcaption></table> <p> Binaries are generated with bit syntax expression or with - <seealso marker="erts:erlang#list_to_binary/1">list_to_binary/1</seealso>, - <seealso marker="erts:erlang#term_to_binary/1">term_to_binary/1</seealso>, + <seealso marker="erts:erlang#list_to_binary/1"> + <c>erlang:list_to_binary/1</c></seealso>, + <seealso marker="erts:erlang#term_to_binary/1"> + <c>erlang:term_to_binary/1</c></seealso>, or as input from binary ports. - The <c>Len</c> length field is an unsigned 4 byte integer - (big endian). + The <c>Len</c> length field is an unsigned 4 byte integer + (big-endian). </p> </section> <section> <marker id="SMALL_BIG_EXT"/> <title>SMALL_BIG_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -711,27 +689,26 @@ <cell align="center">n</cell> </row> <row> - <cell align="center">110</cell> - <cell align="center">n</cell> - <cell align="center">Sign</cell> - <cell align="center">d(0) ... d(n-1)</cell> + <cell align="center"><c>110</c></cell> + <cell align="center"><c>n</c></cell> + <cell align="center"><c>Sign</c></cell> + <cell align="center"><c>d(0)</c> ... <c>d(n-1)</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>SMALL_BIG_EXT</tcaption></table> <p> - Bignums are stored in unary form with a <c>Sign</c> byte - that is 0 if the binum is positive and 1 if is negative. The - digits are stored with the LSB byte stored first. To - calculate the integer the following formula can be used:<br/> - - B = 256<br/> - (d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1)) + Bignums are stored in unary form with a <c>Sign</c> byte, + that is, 0 if the binum is positive and 1 if it is negative. The + digits are stored with the least significant byte stored first. To + calculate the integer, the following formula can be used: + </p> + <p><c>B</c> = 256<br/> + <c>(d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))</c> </p> </section> <section> <marker id="LARGE_BIG_EXT"/> <title>LARGE_BIG_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -740,24 +717,22 @@ <cell align="center">n</cell> </row> <row> - <cell align="center">111</cell> - <cell align="center">n</cell> - <cell align="center">Sign</cell> - <cell align="center">d(0) ... d(n-1)</cell> + <cell align="center"><c>111</c></cell> + <cell align="center"><c>n</c></cell> + <cell align="center"><c>Sign</c></cell> + <cell align="center"><c>d(0)</c> ... <c>d(n-1)</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>LARGE_BIG_EXT</tcaption></table> <p> - Same as <seealso marker="#SMALL_BIG_EXT">SMALL_BIG_EXT</seealso> - with the difference that the length field - is an unsigned 4 byte integer. + Same as <seealso marker="#SMALL_BIG_EXT"> + <c>SMALL_BIG_EXT</c></seealso> + except that the length field is an unsigned 4 byte integer. </p> - </section> <section> <marker id="NEW_REFERENCE_EXT"/> <title>NEW_REFERENCE_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -767,44 +742,43 @@ <cell align="center">N'</cell> </row> <row> - <cell align="center">114</cell> - <cell align="center">Len</cell> - <cell align="center">Node</cell> - <cell align="center">Creation</cell> - <cell align="center">ID ...</cell> + <cell align="center"><c>114</c></cell> + <cell align="center"><c>Len</c></cell> + <cell align="center"><c>Node</c></cell> + <cell align="center"><c>Creation</c></cell> + <cell align="center"><c>ID ...</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>NEW_REFERENCE_EXT</tcaption></table> <p> - Node and Creation are as in - <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>. + <c>Node</c> and <c>Creation</c> are as in + <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>. </p> <p> - <c>ID</c> contains a sequence of big-endian unsigned integers - (4 bytes each, so <c>N'</c> is a multiple of 4), - but should be regarded as uninterpreted data. + <c>ID</c> contains a sequence of big-endian unsigned integers + (4 bytes each, so <c>N'</c> is a multiple of 4), + but is to be regarded as uninterpreted data. </p> <p> <c>N'</c> = 4 * <c>Len</c>. </p> <p> - In the first word (four bytes) of <c>ID</c>, only 18 bits are - significant, the rest should be 0. - In <c>Creation</c>, only 2 bits are significant, - the rest should be 0. + In the first word (4 bytes) of <c>ID</c>, only 18 bits are + significant, the rest are to be 0. + In <c>Creation</c>, only two bits are significant, + the rest are to be 0. </p> <p> - NEW_REFERENCE_EXT was introduced with distribution version 4. - In version 4, <c>N'</c> should be at most 12. + <c>NEW_REFERENCE_EXT</c> was introduced with distribution version 4. + In version 4, <c>N'</c> is to be at most 12. </p> <p> - See <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>). + See <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>. </p> </section> <section> <marker id="SMALL_ATOM_EXT"/> <title>SMALL_ATOM_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -816,24 +790,28 @@ <cell align="center"><c>Len</c></cell> <cell align="center"><c>AtomName</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>SMALL_ATOM_EXT</tcaption></table> <p> - An atom is stored with a 1 byte unsigned length, - followed by <c>Len</c> numbers of 8 bit Latin1 characters that + An atom is stored with a 1 byte unsigned length, + followed by <c>Len</c> numbers of 8-bit Latin-1 characters that forms the <c>AtomName</c>. Longer atoms can be represented - by <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>. <em>Note</em> - the <c>SMALL_ATOM_EXT</c> was introduced in erts version 5.7.2 and - require an exchange of the - <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_SMALL_ATOM_TAGS</c></seealso> - distribution flag in the - <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>. + by <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>. </p> + <note> + <p> + <c>SMALL_ATOM_EXT</c> was introduced in ERTS 5.7.2 and + require an exchange of distribution flag + <seealso marker="erl_dist_protocol#dflags"> + <c>DFLAG_SMALL_ATOM_TAGS</c></seealso> in the + <seealso marker="erl_dist_protocol#distribution_handshake"> + distribution handshake</seealso>. + </p> + </note> </section> <section> <marker id="FUN_EXT"/> <title>FUN_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -845,48 +823,56 @@ <cell align="center">N5</cell> </row> <row> - <cell align="center">117</cell> - <cell align="center">NumFree</cell> - <cell align="center">Pid</cell> - <cell align="center">Module</cell> - <cell align="center">Index</cell> - <cell align="center">Uniq</cell> - <cell align="center">Free vars ...</cell> + <cell align="center"><c>117</c></cell> + <cell align="center"><c>NumFree</c></cell> + <cell align="center"><c>Pid</c></cell> + <cell align="center"><c>Module</c></cell> + <cell align="center"><c>Index</c></cell> + <cell align="center"><c>Uniq</c></cell> + <cell align="center"><c>Free vars ...</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>FUN_EXT</tcaption></table> <taglist> - <tag><c>Pid</c></tag> + <tag><c>Pid</c></tag> <item> - is a process identifier as in - <seealso marker="#PID_EXT">PID_EXT</seealso>. - It represents the process in which the fun was created. + <p>A process identifier as in + <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso>. + Represents the process in which the fun was created. + </p> </item> <tag><c>Module</c></tag> <item> - is an encoded as an atom, using - <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>, - <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> - or <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>. - This is the module that the fun is implemented in. + <p>Encoded as an atom, using + <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>, + <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, + or <seealso marker="#ATOM_CACHE_REF"> + <c>ATOM_CACHE_REF</c></seealso>. + This is the module that the fun is implemented in. + </p> </item> <tag><c>Index</c></tag> <item> - is an integer encoded using - <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso> - or <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>. - It is typically a small index into the module's fun table. + <p>An integer encoded using + <seealso marker="#SMALL_INTEGER_EXT"> + <c>SMALL_INTEGER_EXT</c></seealso> + or <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>. + It is typically a small index into the module's fun table. + </p> </item> <tag><c>Uniq</c></tag> <item> - is an integer encoded using - <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso> or - <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>. - <c>Uniq</c> is the hash value of the parse for the fun. + <p>An integer encoded using + <seealso marker="#SMALL_INTEGER_EXT"> + <c>SMALL_INTEGER_EXT</c></seealso> or + <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>. + <c>Uniq</c> is the hash value of the parse for the fun. + </p> </item> <tag><c>Free vars</c></tag> <item> - is <c>NumFree</c> number of terms, each one encoded according - to its type. + <p><c>NumFree</c> number of terms, each one encoded according + to its type. + </p> </item> </taglist> </section> @@ -894,7 +880,6 @@ <section> <marker id="NEW_FUN_EXT"/> <title>NEW_FUN_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -910,19 +895,19 @@ <cell align="center">N5</cell> </row> <row> - <cell align="center">112</cell> - <cell align="center">Size</cell> - <cell align="center">Arity</cell> - <cell align="center">Uniq</cell> - <cell align="center">Index</cell> - <cell align="center">NumFree</cell> - <cell align="center">Module</cell> - <cell align="center">OldIndex</cell> - <cell align="center">OldUniq</cell> - <cell align="center">Pid</cell> - <cell align="center">Free Vars</cell> + <cell align="center"><c>112</c></cell> + <cell align="center"><c>Size</c></cell> + <cell align="center"><c>Arity</c></cell> + <cell align="center"><c>Uniq</c></cell> + <cell align="center"><c>Index</c></cell> + <cell align="center"><c>NumFree</c></cell> + <cell align="center"><c>Module</c></cell> + <cell align="center"><c>OldIndex</c></cell> + <cell align="center"><c>OldUniq</c></cell> + <cell align="center"><c>Pid</c></cell> + <cell align="center"><c>Free Vars</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>NEW_FUN_EXT</tcaption></table> <p> This is the new encoding of internal funs: <c>fun F/A</c> and <c>fun(Arg1,..) -> ... end</c>. @@ -930,68 +915,73 @@ <taglist> <tag><c>Size</c></tag> <item> - is the total number of bytes, including the <c>Size</c> field. + <p>The total number of bytes, including field <c>Size</c>.</p> </item> <tag><c>Arity</c></tag> <item> - is the arity of the function implementing the fun. + <p>The arity of the function implementing the fun.</p> </item> <tag><c>Uniq</c></tag> <item> - is the 16 bytes MD5 of the significant parts of the Beam file. + <p>The 16 bytes MD5 of the significant parts of the Beam file.</p> </item> <tag><c>Index</c></tag> <item> - is an index number. Each fun within a module has an unique - index. <c>Index</c> is stored in big-endian byte order. + <p>An index number. Each fun within a module has an unique + index. <c>Index</c> is stored in big-endian byte order. + </p> </item> <tag><c>NumFree</c></tag> <item> - is the number of free variables. + <p>The number of free variables.</p> </item> <tag><c>Module</c></tag> <item> - is an encoded as an atom, using - <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>, - <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or - <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>. - This is the module that the fun is implemented in. + <p>Encoded as an atom, using + <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>, + <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, + or <seealso marker="#ATOM_CACHE_REF"> + <c>ATOM_CACHE_REF</c></seealso>. + Is the module that the fun is implemented in. + </p> </item> <tag><c>OldIndex</c></tag> <item> - is an integer encoded using - <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso> - or <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>. - It is typically a small index into the module's fun table. + <p>An integer encoded using + <seealso marker="#SMALL_INTEGER_EXT"> + <c>SMALL_INTEGER_EXT</c></seealso> or + <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>. + Is typically a small index into the module's fun table. + </p> </item> <tag><c>OldUniq</c></tag> <item> - is an integer encoded using - <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso> - or - <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>. - <c>Uniq</c> is the hash value of the parse tree for the fun. + <p>An integer encoded using + <seealso marker="#SMALL_INTEGER_EXT"> + <c>SMALL_INTEGER_EXT</c></seealso> or + <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>. + <c>Uniq</c> is the hash value of the parse tree for the fun. + </p> </item> <tag><c>Pid</c></tag> <item> - is a process identifier as in - <seealso marker="#PID_EXT">PID_EXT</seealso>. - It represents the process in which - the fun was created. + <p>A process identifier as in + <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso>. + Represents the process in which the fun was created. + </p> </item> - <tag><c>Free vars</c></tag> <item> - is <c>NumFree</c> number of terms, each one encoded according - to its type. + <p><c>NumFree</c> number of terms, each one encoded according + to its type. + </p> </item> </taglist> </section> <section> <marker id="EXPORT_EXT"/> - <title>EXPORT_EXT</title> - + <title>EXPORT_EXT</title> <table align="left"> <row> <cell align="center">1</cell> @@ -1000,32 +990,31 @@ <cell align="center">N3</cell> </row> <row> - <cell align="center">113</cell> - <cell align="center">Module</cell> - <cell align="center">Function</cell> - <cell align="center">Arity</cell> + <cell align="center"><c>113</c></cell> + <cell align="center"><c>Module</c></cell> + <cell align="center"><c>Function</c></cell> + <cell align="center"><c>Arity</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>EXPORT_EXT</tcaption></table> <p> This term is the encoding for external funs: <c>fun M:F/A</c>. </p> <p> - <c>Module</c> and <c>Function</c> are atoms - (encoded using <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>, - <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or - <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>). + <c>Module</c> and <c>Function</c> are atoms + (encoded using <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>, + <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, or + <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>). </p> <p> - <c>Arity</c> is an integer encoded using - <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>. + <c>Arity</c> is an integer encoded using + <seealso marker="#SMALL_INTEGER_EXT"> + <c>SMALL_INTEGER_EXT</c></seealso>. </p> - </section> <section> <marker id="BIT_BINARY_EXT"/> <title>BIT_BINARY_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -1034,39 +1023,35 @@ <cell align="center">Len</cell> </row> <row> - <cell align="center">77</cell> - <cell align="center">Len</cell> - <cell align="center">Bits</cell> - <cell align="center">Data</cell> + <cell align="center"><c>77</c></cell> + <cell align="center"><c>Len</c></cell> + <cell align="center"><c>Bits</c></cell> + <cell align="center"><c>Data</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>BIT_BINARY_EXT</tcaption></table> <p> This term represents a bitstring whose length in bits does not have to be a multiple of 8. - The <c>Len</c> field is an unsigned 4 byte integer (big endian). + The <c>Len</c> field is an unsigned 4 byte integer (big-endian). The <c>Bits</c> field is the number of bits (1-8) that are used - in the last byte in the data field, - counting from the most significant bit towards the least - significant. + in the last byte in the data field, + counting from the most significant bit to the least significant. </p> - - </section> <section> <marker id="NEW_FLOAT_EXT"/> <title>NEW_FLOAT_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> <cell align="center">8</cell> </row> <row> - <cell align="center">70</cell> - <cell align="center">IEEE float</cell> + <cell align="center"><c>70</c></cell> + <cell align="center"><c>IEEE float</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>NEW_FLOAT_EXT</tcaption></table> <p> A float is stored as 8 bytes in big-endian IEEE format. </p> @@ -1074,10 +1059,10 @@ This term is used in minor version 1 of the external format. </p> </section> + <section> <marker id="ATOM_UTF8_EXT"/> <title>ATOM_UTF8_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -1089,23 +1074,22 @@ <cell align="center"><c>Len</c></cell> <cell align="center"><c>AtomName</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>ATOM_UTF8_EXT</tcaption></table> <p> An atom is stored with a 2 byte unsigned length in big-endian order, followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded in UTF-8. </p> <p> - For more information on encoding of atoms, see + For more information on encoding of atoms, see the <seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso> - in the beginning of this document. + in the beginning of this section. </p> </section> <section> <marker id="SMALL_ATOM_UTF8_EXT"/> <title>SMALL_ATOM_UTF8_EXT</title> - <table align="left"> <row> <cell align="center">1</cell> @@ -1117,20 +1101,19 @@ <cell align="center"><c>Len</c></cell> <cell align="center"><c>AtomName</c></cell> </row> - <tcaption></tcaption></table> + <tcaption>SMALL_ATOM_UTF8_EXT</tcaption></table> <p> - An atom is stored with a 1 byte unsigned length, + An atom is stored with a 1 byte unsigned length, followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded in UTF-8. Longer atoms encoded in UTF-8 can be represented using - <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>. + <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>. </p> <p> - For more information on encoding of atoms, see + For more information on encoding of atoms, see the <seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso> - in the beginning of this document. + in the beginning of this section. </p> </section> - </chapter> - + diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index f3921f1922..b5dc9037c4 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -33,43 +33,52 @@ <file>erl_nif.xml</file> </header> <lib>erl_nif</lib> - <libsummary>API functions for an Erlang NIF library</libsummary> + <libsummary>API functions for an Erlang NIF library.</libsummary> <description> <p>A NIF library contains native implementation of some functions - of an Erlang module. The native implemented functions (NIFs) are - called like any other functions without any difference to the - caller. Each NIF must also have an implementation in Erlang that - will be invoked if the function is called before the NIF library - has been successfully loaded. A typical such stub implementation - is to throw an exception. But it can also be used as a fallback - implementation if the NIF library is not implemented for some - architecture.</p> - <marker id="WARNING"/> - <warning><p><em>Use this functionality with extreme care!</em></p> + of an Erlang module. The native implemented functions (NIFs) are + called like any other functions without any difference to the + caller. Each NIF must have an implementation in Erlang that + is invoked if the function is called before the NIF library + is successfully loaded. A typical such stub implementation + is to throw an exception. But it can also be used as a fallback + implementation if the NIF library is not implemented for some + architecture.</p> + + <warning> + <marker id="WARNING"/> + <p><em>Use this functionality with extreme care.</em></p> <p>A native function is executed as a direct extension of the - native code of the VM. Execution is not made in a safe environment. - The VM can <em>not</em> provide the same services as provided when - executing Erlang code, such as preemptive scheduling or memory - protection. If the native function doesn't behave well, the whole - VM will misbehave.</p> - <list> - <item><p>A native function that crash will crash the whole VM.</p></item> - <item><p>An erroneously implemented native function might cause - a VM internal state inconsistency which may cause a crash of the VM, - or miscellaneous misbehaviors of the VM at any point after the call - to the native function.</p></item> - <item><p>A native function that do <seealso marker="#lengthy_work">lengthy - work</seealso> before returning will degrade responsiveness of the VM, - and may cause miscellaneous strange behaviors. Such strange behaviors - include, but are not limited to, extreme memory usage, and bad load - balancing between schedulers. Strange behaviors that might occur due - to lengthy work may also vary between OTP releases.</p></item> + native code of the VM. Execution is not made in a safe environment. + The VM <em>cannot</em> provide the same services as provided when + executing Erlang code, such as pre-emptive scheduling or memory + protection. If the native function does not behave well, the whole + VM will misbehave.</p> + <list type="bulleted"> + <item> + <p>A native function that crash will crash the whole VM.</p> + </item> + <item> + <p>An erroneously implemented native function can cause a VM + internal state inconsistency, which can cause a crash of the VM, + or miscellaneous misbehaviors of the VM at any point after the + call to the native function.</p> + </item> + <item> + <p>A native function doing <seealso marker="#lengthy_work">lengthy + work</seealso> before returning degrades responsiveness of the VM, + and can cause miscellaneous strange behaviors. Such strange + behaviors include, but are not limited to, extreme memory usage, + and bad load balancing between schedulers. Strange behaviors that + can occur because of lengthy work can also vary between Erlang/OTP + releases.</p> + </item> </list> - </warning> + </warning> - <p>A minimal example of a NIF library can look like this:</p> - <p/> - <code type="none"> + <p>A minimal example of a NIF library can look as follows:</p> + + <code type="none"> /* niftest.c */ #include "erl_nif.h" @@ -83,13 +92,11 @@ static ErlNifFunc nif_funcs[] = {"hello", 0, hello} }; -ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL) -</code> +ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)</code> + + <p>The Erlang module can look as follows:</p> - <p>and the Erlang module would have to look something like - this:</p> - <p/> - <code type="none"> + <code type="none"> -module(niftest). -export([init/0, hello/0]). @@ -98,11 +105,11 @@ init() -> erlang:load_nif("./niftest", 0). hello() -> - "NIF library not loaded". -</code> - <p>and compile and test something like this (on Linux):</p> - <p/> - <code type="none"> + "NIF library not loaded".</code> + + <p>Compile and test can look as follows (on Linux):</p> + + <code type="none"> $> gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/ $> erl @@ -113,1353 +120,1936 @@ $> erl 3> niftest:init(). ok 4> niftest:hello(). -"Hello world!" -</code> - - <p>A better solution for a real module is to take advantage of - the new directive <seealso - marker="doc/reference_manual:code_loading#on_load">on_load</seealso> to automatically - load the NIF library when the module is loaded.</p> - <note><p>A NIF does not have to be exported, it can be local to the module. - Note however that unused local stub functions will be optimized - away by the compiler causing loading of the NIF library to fail.</p> +"Hello world!"</code> + + <p>A better solution for a real module is to take advantage of the new + directive <c>on load</c> (see section + <seealso marker="doc/reference_manual:code_loading#on_load">Running a + Function When a Module is Loaded</seealso> in the Erlang Reference + Manual) to load the NIF library automatically when the module is + loaded.</p> + + <note> + <p>A NIF does not have to be exported, it can be local to the module. + However, unused local stub functions will be optimized + away by the compiler, causing loading of the NIF library to fail.</p> </note> - <p>A loaded NIF library is tied to the Erlang module code version - that loaded it. If the module is upgraded with a new version, the - new Erlang code will have to load its own NIF library (or maybe choose not - to). The new code version can however choose to load the exact - same NIF library as the old code if it wants to. Sharing the same - dynamic library will mean that static data defined by the library - will be shared as well. To avoid unintentionally shared static - data, each Erlang module code can keep its own private data. This - private data can be set when the NIF library is loaded and - then retrieved by calling <seealso marker="#enif_priv_data">enif_priv_data</seealso>.</p> - <p>There is no way to explicitly unload a NIF library. A library will be - automatically unloaded when the module code that it belongs to is purged - by the code server.</p> + <p>A loaded NIF library is tied to the Erlang module code version + that loaded it. If the module is upgraded with a new version, the + new Erlang code need to load its own NIF library (or maybe choose not + to). The new code version can, however, choose to load the + same NIF library as the old code if it wants to. Sharing the + dynamic library means that static data defined by the library + is shared as well. To avoid unintentionally shared static + data, each Erlang module code can keep its own private data. This + private data can be set when the NIF library is loaded and + then retrieved by calling <seealso marker="#enif_priv_data"> + <c>enif_priv_data</c></seealso>.</p> + + <p>A NIF library cannot be loaded explicitly. A library is + automatically unloaded when the module code that it belongs to is purged + by the code server.</p> </description> + <section> - <title>FUNCTIONALITY</title> - <p>All functions that a NIF library needs to do with Erlang are - performed through the NIF API functions. There are functions + <title>Functionality</title> + <p>All functions that a NIF library needs to do with Erlang are + performed through the NIF API functions. Functions exist for the following functionality:</p> + <taglist> <tag>Read and write Erlang terms</tag> - <item><p>Any Erlang terms can be passed to a NIF as function arguments and - be returned as function return values. The terms are of C-type - <seealso marker="#ERL_NIF_TERM">ERL_NIF_TERM</seealso> - and can only be read or written using API functions. Most functions to read - the content of a term are prefixed <c>enif_get_</c> and usually return - true (or false) if the term was of the expected type (or not). - The functions to write terms are all prefixed <c>enif_make_</c> and usually - return the created <c>ERL_NIF_TERM</c>. There are also some functions - to query terms, like <c>enif_is_atom</c>, <c>enif_is_identical</c> and - <c>enif_compare</c>.</p> - <p>All terms of type <c>ERL_NIF_TERM</c> belong to an environment of type - <seealso marker="#ErlNifEnv">ErlNifEnv</seealso>. The lifetime of a term is - controlled by the lifetime of its environment object. All API functions that read - or write terms has the environment, that the term belongs to, as the first - function argument.</p></item> + <item> + <p>Any Erlang terms can be passed to a NIF as function arguments and + be returned as function return values. The terms are of C-type + <seealso marker="#ERL_NIF_TERM"><c>ERL_NIF_TERM</c></seealso> and can + only be read or written using API functions. Most functions to read + the content of a term are prefixed <c>enif_get_</c> and usually return + <c>true</c> (or <c>false</c>) if the term is of the expected type (or + not). The functions to write terms are all prefixed <c>enif_make_</c> + and usually + return the created <c>ERL_NIF_TERM</c>. There are also some functions + to query terms, like <c>enif_is_atom</c>, <c>enif_is_identical</c>, + and <c>enif_compare</c>.</p> + <p>All terms of type <c>ERL_NIF_TERM</c> belong to an environment of + type <seealso marker="#ErlNifEnv"><c>ErlNifEnv</c></seealso>. The + lifetime of a term is controlled by the lifetime of its environment + object. All API functions that read or write terms has the + environment that the term belongs to as the first function + argument.</p> + </item> <tag>Binaries</tag> - <item><p>Terms of type binary are accessed with the help of the struct type - <seealso marker="#ErlNifBinary">ErlNifBinary</seealso> - that contains a pointer (<c>data</c>) to the raw binary data and the length - (<c>size</c>) of the data in bytes. Both <c>data</c> and <c>size</c> are - read-only and should only be written using calls to API functions. - Instances of <c>ErlNifBinary</c> are however always allocated by the user - (usually as local variables).</p> - <p>The raw data pointed to by <c>data</c> is only mutable after a call to - <seealso marker="#enif_alloc_binary">enif_alloc_binary</seealso> or - <seealso marker="#enif_realloc_binary">enif_realloc_binary</seealso>. - All other functions that operates on a binary will leave the data as read-only. - A mutable binary must in the end either be freed with - <seealso marker="#enif_release_binary">enif_release_binary</seealso> - or made read-only by transferring it to an Erlang term with - <seealso marker="#enif_make_binary">enif_make_binary</seealso>. - But it does not have to happen in the same NIF call. Read-only binaries - do not have to be released.</p> - <p><seealso marker="#enif_make_new_binary">enif_make_new_binary</seealso> - can be used as a shortcut to allocate and return a binary in the same NIF call.</p> - <p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary - bit length have no support yet.</p> - </item> + <item> + <p>Terms of type binary are accessed with the help of struct type + <seealso marker="#ErlNifBinary"><c>ErlNifBinary</c></seealso>, + which contains a pointer (<c>data</c>) to the raw binary data and the + length (<c>size</c>) of the data in bytes. Both <c>data</c> and + <c>size</c> are read-only and are only to be written using calls to + API functions. Instances of <c>ErlNifBinary</c> are, however, always + allocated by the user (usually as local variables).</p> + <p>The raw data pointed to by <c>data</c> is only mutable after a call + to <seealso marker="#enif_alloc_binary"> + <c>enif_alloc_binary</c></seealso> or + <seealso marker="#enif_realloc_binary"> + <c>enif_realloc_binary</c></seealso>. All other functions that + operate on a binary leave the data as read-only. + A mutable binary must in the end either be freed with + <seealso marker="#enif_release_binary"> + <c>enif_release_binary</c></seealso> + or made read-only by transferring it to an Erlang term with + <seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>. + However, it does not have to occur in the same NIF call. Read-only + binaries do not have to be released.</p> + <p><seealso marker="#enif_make_new_binary"> + <c>enif_make_new_binary</c></seealso> can be used as a shortcut to + allocate and return a binary in the same NIF call.</p> + <p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary + bit length have no support yet.</p> + </item> <tag>Resource objects</tag> - <item><p>The use of resource objects is a safe way to return pointers to - native data structures from a NIF. A resource object is - just a block of memory allocated with - <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>. - A handle ("safe pointer") to this memory block can then be returned to Erlang by the use of - <seealso marker="#enif_make_resource">enif_make_resource</seealso>. - The term returned by <c>enif_make_resource</c> - is totally opaque in nature. It can be stored and passed between processes - on the same node, but the only real end usage is to pass it back as an argument to a NIF. - The NIF can then call <seealso marker="#enif_get_resource">enif_get_resource</seealso> - and get back a pointer to the memory block that is guaranteed to still be - valid. A resource object will not be deallocated until the last handle term - has been garbage collected by the VM and the resource has been - released with <seealso marker="#enif_release_resource">enif_release_resource</seealso> - (not necessarily in that order).</p> - <p>All resource objects are created as instances of some <em>resource type</em>. - This makes resources from different modules to be distinguishable. - A resource type is created by calling - <seealso marker="#enif_open_resource_type">enif_open_resource_type</seealso> - when a library is loaded. Objects of that resource type can then later be allocated - and <c>enif_get_resource</c> verifies that the resource is of the expected type. - A resource type can have a user supplied destructor function that is - automatically called when resources of that type are released (by either - the garbage collector or <c>enif_release_resource</c>). Resource types - are uniquely identified by a supplied name string and the name of the - implementing module.</p> - <marker id="enif_resource_example"/><p>Here is a template example of how to create and return a resource object.</p> - <p/> - <code type="none"> - ERL_NIF_TERM term; - MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct)); - - /* initialize struct ... */ - - term = enif_make_resource(env, obj); - - if (keep_a_reference_of_our_own) { - /* store 'obj' in static variable, private data or other resource object */ - } - else { - enif_release_resource(obj); - /* resource now only owned by "Erlang" */ - } - return term; - </code> - <p>Note that once <c>enif_make_resource</c> creates the term to - return to Erlang, the code can choose to either keep its own - native pointer to the allocated struct and release it later, or - release it immediately and rely solely on the garbage collector - to eventually deallocate the resource object when it collects - the term.</p> - <p>Another usage of resource objects is to create binary terms with - user defined memory management. - <seealso marker="#enif_make_resource_binary">enif_make_resource_binary</seealso> - will create a binary term that is connected to a resource object. The - destructor of the resource will be called when the binary is garbage - collected, at which time the binary data can be released. An example of - this can be a binary term consisting of data from a <c>mmap</c>'ed file. - The destructor can then do <c>munmap</c> to release the memory - region.</p> - <p>Resource types support upgrade in runtime by allowing a loaded NIF - library to takeover an already existing resource type and thereby - "inherit" all existing objects of that type. The destructor of the new - library will thereafter be called for the inherited objects and the - library with the old destructor function can be safely unloaded. Existing - resource objects, of a module that is upgraded, must either be deleted - or taken over by the new NIF library. The unloading of a library will be - postponed as long as there exist resource objects with a destructor - function in the library. - </p> + <item> + <p>The use of resource objects is a safe way to return pointers to + native data structures from a NIF. A resource object is + only a block of memory allocated with + <seealso marker="#enif_alloc_resource"> + <c>enif_alloc_resource</c></seealso>. + A handle ("safe pointer") to this memory block can then be returned + to Erlang by the use of + <seealso marker="#enif_make_resource"> + <c>enif_make_resource</c></seealso>. + The term returned by <c>enif_make_resource</c> is opaque in nature. + It can be stored and passed between processes on the same node, but + the only real end usage is to pass it back as an argument to a NIF. + The NIF can then call <seealso marker="#enif_get_resource"> + <c>enif_get_resource</c></seealso> and get back a pointer to the + memory block, which is guaranteed to still be valid. A resource + object is not deallocated until the last handle term + is garbage collected by the VM and the resource is released with + <seealso marker="#enif_release_resource"> + <c>enif_release_resource</c></seealso> + (not necessarily in that order).</p> + <p>All resource objects are created as instances of some <em>resource + type</em>. This makes resources from different modules to be + distinguishable. A resource type is created by calling + <seealso marker="#enif_open_resource_type"> + <c>enif_open_resource_type</c></seealso> when a library is loaded. + Objects of that resource type can then later be allocated and + <c>enif_get_resource</c> verifies that the resource is of the + expected type. A resource type can have a user-supplied destructor + function, which is automatically called when resources of that type + are released (by either the garbage collector or + <c>enif_release_resource</c>). Resource types are uniquely identified + by a supplied name string and the name of the implementing module.</p> + <marker id="enif_resource_example"/> + <p>The following is a template example of how to create and return a + resource object.</p> + <code type="none"> +ERL_NIF_TERM term; +MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct)); + +/* initialize struct ... */ + +term = enif_make_resource(env, obj); + +if (keep_a_reference_of_our_own) { + /* store 'obj' in static variable, private data or other resource object */ +} +else { + enif_release_resource(obj); + /* resource now only owned by "Erlang" */ +} +return term;</code> + <p>Notice that once <c>enif_make_resource</c> creates the term to + return to Erlang, the code can choose to either keep its own + native pointer to the allocated struct and release it later, or + release it immediately and rely only on the garbage collector + to deallocate the resource object eventually when it collects + the term.</p> + <p>Another use of resource objects is to create binary terms with + user-defined memory management. + <seealso marker="#enif_make_resource_binary"> + <c>enif_make_resource_binary</c></seealso> + creates a binary term that is connected to a resource object. The + destructor of the resource is called when the binary is garbage + collected, at which time the binary data can be released. An example + of this can be a binary term consisting of data from a <c>mmap</c>'ed + file. The destructor can then do <c>munmap</c> to release the memory + region.</p> + <p>Resource types support upgrade in runtime by allowing a loaded NIF + library to take over an already existing resource type and by that + "inherit" all existing objects of that type. The destructor of the + new library is thereafter called for the inherited objects and the + library with the old destructor function can be safely unloaded. + Existing resource objects, of a module that is upgraded, must either + be deleted or taken over by the new NIF library. The unloading of a + library is postponed as long as there exist resource objects with a + destructor function in the library.</p> </item> <tag>Threads and concurrency</tag> - <item><p>A NIF is thread-safe without any explicit synchronization as - long as it acts as a pure function and only reads the supplied - arguments. As soon as you write towards a shared state either through - static variables or <seealso marker="#enif_priv_data">enif_priv_data</seealso> - you need to supply your own explicit synchronization. This includes terms - in process independent environments that are shared between threads. - Resource objects will also require synchronization if you treat them as - mutable.</p> - <p>The library initialization callbacks <c>load</c>, <c>reload</c> and - <c>upgrade</c> are all thread-safe even for shared state data.</p> + <item> + <p>A NIF is thread-safe without any explicit synchronization as + long as it acts as a pure function and only reads the supplied + arguments. When you write to a shared state either through + static variables or <seealso marker="#enif_priv_data"> + <c>enif_priv_data</c></seealso>, you need to supply your own explicit + synchronization. This includes terms in process-independent + environments that are shared between threads. Resource objects also + require synchronization if you treat them as mutable.</p> + <p>The library initialization callbacks <c>load</c>, <c>reload</c>, and + <c>upgrade</c> are all thread-safe even for shared state data.</p> </item> - <tag><marker id="version_management"/>Version Management</tag> - <item><p> - When a NIF library is built, information about NIF API version - is compiled into the library. When a NIF library is loaded the - runtime system verifies that the library is of a compatible version. - <c>erl_nif.h</c> defines <c>ERL_NIF_MAJOR_VERSION</c>, and - <c>ERL_NIF_MINOR_VERSION</c>. <c>ERL_NIF_MAJOR_VERSION</c> will be - incremented when NIF library incompatible changes are made to the - Erlang runtime system. Normally it will suffice to recompile the NIF - library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it - could, under rare circumstances, mean that NIF libraries have to - be slightly modified. If so, this will of course be documented. - <c>ERL_NIF_MINOR_VERSION</c> will be incremented when - new features are added. The runtime system uses the minor version - to determine what features to use. - </p><p> - The runtime system will normally refuse to load a NIF library if - the major versions differ, or if the major versions are equal and - the minor version used by the NIF library is greater than the one - used by the runtime system. Old NIF libraries with lower major - versions will however be allowed after a bump of the major version - during a transition period of two major releases. Such old NIF - libraries might however fail if deprecated features are used. - </p></item> - + <item> + <p>When a NIF library is built, information about the NIF API version + is compiled into the library. When a NIF library is loaded, the + runtime system verifies that the library is of a compatible version. + <c>erl_nif.h</c> defines the following:</p> + <taglist> + <tag><c>ERL_NIF_MAJOR_VERSION</c></tag> + <item> + <p>Incremented when NIF library incompatible changes are made to the + Erlang runtime system. Normally it suffices to recompile the NIF + library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it + can, under rare circumstances, mean that NIF libraries must be + slightly modified. If so, this will of course be documented.</p> + </item> + <tag><c>ERL_NIF_MINOR_VERSION</c></tag> + <item> + <p>Incremented when new features are added. The runtime system uses + the minor version to determine what features to use.</p> + </item> + </taglist> + <p>The runtime system normally refuses to load a NIF library if + the major versions differ, or if the major versions are equal and + the minor version used by the NIF library is greater than the one + used by the runtime system. Old NIF libraries with lower major + versions are, however, allowed after a bump of the major version + during a transition period of two major releases. Such old NIF + libraries can however fail if deprecated features are used.</p> + </item> <tag><marker id="time_measurement"/>Time Measurement</tag> - <item><p>Support for time measurement in NIF libraries:</p> - <list> - <item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item> - <item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item> - <item><seealso marker="#enif_monotonic_time"><c>enif_monotonic_time()</c></seealso></item> - <item><seealso marker="#enif_time_offset"><c>enif_time_offset()</c></seealso></item> - <item><seealso marker="#enif_convert_time_unit"><c>enif_convert_time_unit()</c></seealso></item> + <item> + <p>Support for time measurement in NIF libraries:</p> + <list type="bulleted"> + <item><seealso marker="#ErlNifTime"> + <c>ErlNifTime</c></seealso></item> + <item><seealso marker="#ErlNifTimeUnit"> + <c>ErlNifTimeUnit</c></seealso></item> + <item><seealso marker="#enif_monotonic_time"> + <c>enif_monotonic_time()</c></seealso></item> + <item><seealso marker="#enif_time_offset"> + <c>enif_time_offset()</c></seealso></item> + <item><seealso marker="#enif_convert_time_unit"> + <c>enif_convert_time_unit()</c></seealso></item> </list> </item> - <tag><marker id="lengthy_work"/>Long-running NIFs</tag> - - <item><p> - As mentioned in the <seealso marker="#WARNING">warning</seealso> text at - the beginning of this document it is of <em>vital importance</em> that a - native function return relatively quickly. It is hard to give an exact - maximum amount of time that a native function is allowed to work, but as a - rule of thumb a well-behaving native function should return to its caller - before a millisecond has passed. This can be achieved using different - approaches. If you have full control over the code to execute in the - native function, the best approach is to divide the work into multiple - chunks of work and call the native function multiple times. In some - cases this might however not always be possible, e.g. when calling - third-party libraries.</p> - - <p>The - <seealso marker="#enif_consume_timeslice">enif_consume_timeslice()</seealso> - function can be used to inform the runtime system about the length of the - NIF call. It should typically always be used unless the NIF executes very - quickly.</p> - - <p>If the NIF call is too lengthy one needs to handle this in one of the - following ways in order to avoid degraded responsiveness, scheduler load - balancing problems, and other strange behaviors:</p> - - <taglist> - <tag>Yielding NIF</tag> - <item> - <p> - If the functionality of a long-running NIF can be split so that - its work can be achieved through a series of shorter NIF calls, - the application can either make that series of NIF calls from the - Erlang level, or it can call a NIF that first performs a chunk of - the work, then invokes the - <seealso marker="#enif_schedule_nif">enif_schedule_nif</seealso> - function to schedule another NIF call to perform the next chunk. - The final call scheduled in this manner can then return the - overall result. Breaking up a long-running function in - this manner enables the VM to regain control between calls to the - NIFs. - </p> - <p> - This approach is always preferred over the other alternatives - described below. This both from a performance perspective and - a system characteristics perspective. - </p> - </item> - - <tag>Threaded NIF</tag> - <item> - <p> - This is accomplished by dispatching the work to another thread - managed by the NIF library, return from the NIF, and wait for the - result. The thread can send the result back to the Erlang - process using <seealso marker="#enif_send">enif_send</seealso>. - Information about thread primitives can be found below. - </p> - </item> - - <tag><marker id="dirty_nifs"/>Dirty NIF</tag> - <item> - - <note> - <p> - <em>The dirty NIF functionality described here - is experimental</em>. Dirty NIF support is available only when - the emulator is configured with dirty schedulers enabled. This - feature is currently disabled by default. The Erlang runtime - without SMP support do not support dirty schedulers even when - the dirty scheduler support has been enabled. To check at - runtime for the presence of dirty scheduler threads, code can - use the - <seealso marker="#enif_system_info"><c>enif_system_info()</c></seealso> - API function. - </p> - </note> - - <p> - A NIF that cannot be split and cannot execute in a millisecond or - less is called a "dirty NIF" because it performs work that the - ordinary schedulers of the Erlang runtime system cannot handle cleanly. - Applications that make use of such functions must indicate to the - runtime that the functions are dirty so they can be handled - specially. This is handled by executing dirty jobs on a separate + <item> + <p>As mentioned in the <seealso marker="#WARNING">warning</seealso> text + at the beginning of this manual page, it is of <em>vital + importance</em> that a native function returns relatively fast. It is + difficult to give an exact maximum amount of time that a native + function is allowed to work, but usually a well-behaving native + function is to return to its caller within 1 millisecond. This can be + achieved using different approaches. If you have full control over the + code to execute in the native function, the best approach is to + divide the work into multiple chunks of work and call the native + function multiple times. This is, however, not always possible, for + example when calling third-party libraries.</p> + <p>The <seealso marker="#enif_consume_timeslice"> + <c>enif_consume_timeslice()</c></seealso> function can be used to + inform the runtime system about the length of the NIF call. + It is typically always to be used unless the NIF executes very + fast.</p> + <p>If the NIF call is too lengthy, this must be handled in one of + the following ways to avoid degraded responsiveness, scheduler load + balancing problems, and other strange behaviors:</p> + <taglist> + <tag>Yielding NIF</tag> + <item> + <p>If the functionality of a long-running NIF can be split so that + its work can be achieved through a series of shorter NIF calls, + the application has two options:</p> + <list type="bulleted"> + <item> + <p>Make that series of NIF calls from the Erlang level.</p> + </item> + <item> + <p>Call a NIF that first performs a chunk of the work, then + invokes the <seealso marker="#enif_schedule_nif"> + <c>enif_schedule_nif</c></seealso> function to schedule + another NIF call to perform the next chunk. The final call + scheduled in this manner can then return the overall + result.</p> + </item> + </list> + <p>Breaking up a long-running function in this manner enables the + VM to regain control between calls to the NIFs.</p> + <p>This approach is always preferred over the other alternatives + described below. This both from a performance perspective and + a system characteristics perspective.</p> + </item> + <tag>Threaded NIF</tag> + <item> + <p>This is accomplished by dispatching the work to another thread + managed by the NIF library, return from the NIF, and wait for + the result. The thread can send the result back to the Erlang + process using <seealso marker="#enif_send"> + <c>enif_send</c></seealso>. + Information about thread primitives is provided below.</p> + </item> + <tag><marker id="dirty_nifs"/>Dirty NIF</tag> + <item> + <note> + <p><em>The dirty NIF functionality described here + is experimental</em>. Dirty NIF support is available only when + the emulator is configured with dirty schedulers enabled. This + feature is disabled by default. The Erlang runtime + without SMP support does not support dirty schedulers even when + the dirty scheduler support is enabled. To check at runtime for + the presence of dirty scheduler threads, code can use the + <seealso marker="#enif_system_info"> + <c>enif_system_info()</c></seealso> API function.</p> + </note> + <p>A NIF that cannot be split and cannot execute in a millisecond + or less is called a "dirty NIF", as it performs work that the + ordinary schedulers of the Erlang runtime system cannot handle cleanly. + Applications that make use of such functions must indicate to the + runtime that the functions are dirty so they can be handled + specially. This is handled by executing dirty jobs on a separate set of schedulers called dirty schedulers. A dirty NIF executing on a dirty scheduler does not have the same duration restriction as a normal NIF. - </p> + </p> - <p> - It is important to classify the dirty job correct. An I/O bound + <p> + It is important to classify the dirty job correct. An I/O bound job should be classified as such, and a CPU bound job should be classified as such. If you should classify CPU bound jobs as I/O bound jobs, dirty I/O schedulers might starve ordinary schedulers. I/O bound jobs are expected to either block waiting for I/O, and/or spend a limited amount of time moving data. - </p> - - <p> - To schedule a dirty NIF for execution, the appropriate - <c>flags</c> value can be set for the NIF in its - <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso> - entry, or the application can call - <seealso marker="#enif_schedule_nif"><c>enif_schedule_nif</c></seealso>, - passing to it a pointer to the dirty NIF to be executed and - indicating with the <c>flags</c> argument whether it expects the - operation to be CPU-bound or I/O-bound. A job that alternates - between I/O bound and CPU bound can be reclassified and - rescheduled using <c>enif_schedule_nif</c> so that it executes on - the correct type of dirty scheduler at all times. For more - information see the documentation of the <c>erl</c> command line - arguments <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>, - and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>. - </p> - - <p> - While a process is executing a dirty NIF some operations that - communicate with it may take a very long time to complete. - Suspend, or garbage collection of a process executing a dirty - NIF cannot be done until the dirty NIF has returned, so other - processes waiting for such operations to complete might have to - wait for a very long time. Blocking multi scheduling, i.e., - calling - <seealso marker="erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling, - block)</c></seealso>, might also take a very long time to - complete. This since all ongoing dirty operations on all - dirty schedulers need to complete before the block - operation can complete. - </p> - - <p> - A lot of operations communicating with a process executing a - dirty NIF can, however, complete while it is executing the - dirty NIF. For example, retrieving information about it via - <c>process_info()</c>, setting its group leader, - register/unregister its name, etc. - </p> - - <p> - Termination of a process executing a dirty NIF can only be - completed up to a certain point while it is executing the - dirty NIF. All Erlang resources such as its registered name, - its ETS tables, etc will be released. All links and monitors - will be triggered. The actual execution of the NIF will - however <em>not</em> be stopped. The NIF can safely continue - execution, allocate heap memory, etc, but it is of course better - to stop executing as soon as possible. The NIF can check - whether current process is alive or not using - <seealso marker="#enif_is_current_process_alive"><c>enif_is_current_process_alive</c></seealso>. - Communication using - <seealso marker="#enif_send"><c>enif_send</c></seealso>, - and <seealso marker="#enif_port_command"><c>enif_port_command</c></seealso> - will also be dropped when the sending process is not alive. - Deallocation of certain internal resources such as process - heap, and process control block will be delayed until the - dirty NIF has completed. - </p> - - <p>Currently known issues that are planned to be fixed:</p> - <list> - <item> - <p> - Since purging of a module currently might need to garbage - collect a process in order to determine if it has - references to the module, a process executing a dirty - NIF might delay purging for a very long time. Delaying - a purge operation implies delaying <em>all</em> code - loading operations which might cause severe problems for - the system as a whole. - </p> - </item> - </list> - - </item> - </taglist> + </p> + <p> + To schedule a dirty NIF for execution, the application has two options:</p> + <list type="bulleted"> + <item> + <p>Set the appropriate flags value for the dirty NIF in its + <seealso marker="#ErlNifFunc"> <c>ErlNifFunc</c></seealso> + entry.</p> + </item> + <item> + <p>Call <seealso marker="#enif_schedule_nif"> + <c>enif_schedule_nif</c></seealso>, pass to it a pointer + to the dirty NIF to be executed, and indicate with argument + <c>flags</c> whether it expects the operation to be CPU-bound + or I/O-bound.</p> + </item> + </list> + <p>A job that alternates between I/O bound and CPU bound can be + reclassified and rescheduled using <c>enif_schedule_nif</c> so + that it executes on the correct type of dirty scheduler at all + times. For more information see the documentation of the + <c>erl(1)</c> command line arguments + <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>, + and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>.</p> + <p>While a process executes a dirty NIF, some operations that + communicate with it can take a very long time to complete. + Suspend or garbage collection of a process executing a dirty + NIF cannot be done until the dirty NIF has returned. Thus, other + processes waiting for such operations to complete might + have to wait for a very long time. Blocking multi-scheduling, that + is, calling <seealso marker="erlang#system_flag_multi_scheduling"> + <c>erlang:system_flag(multi_scheduling, block)</c></seealso>, can + also take a very long time to complete. This becaue all ongoing + dirty operations on all dirty schedulers must complete before + the block operation can complete.</p> + <p>Many operations communicating with a process executing a + dirty NIF can, however, complete while it executes the + dirty NIF. For example, retrieving information about it through + <seealso marker="erlang:process_info/1"> + <c>erlang:process_info</c></seealso>, setting its group leader, + register/unregister its name, and so on.</p> + <p>Termination of a process executing a dirty NIF can only be + completed up to a certain point while it executes the dirty NIF. + All Erlang resources, such as its registered name and its ETS + tables, are released. All links and monitors are triggered. The + execution of the NIF is, however, <em>not</em> stopped. The NIF + can safely continue execution, allocate heap memory, and so on, + but it is of course better to stop executing as soon as possible. + The NIF can check whether a current process is alive using + <seealso marker="#enif_is_current_process_alive"> + <c>enif_is_current_process_alive</c></seealso>. Communication + using <seealso marker="#enif_send"><c>enif_send</c></seealso> and + <seealso marker="#enif_port_command"> + <c>enif_port_command</c></seealso> is also dropped when the + sending process is not alive. Deallocation of certain internal + resources, such as process heap and process control block, is + delayed until the dirty NIF has completed.</p> + </item> + </taglist> </item> </taglist> </section> + <section> - <title>INITIALIZATION</title> + <title>Initialization</title> <taglist> - <tag><marker id="ERL_NIF_INIT"/>ERL_NIF_INIT(MODULE, ErlNifFunc funcs[], load, reload, upgrade, unload)</tag> - <item><p>This is the magic macro to initialize a NIF library. It - should be evaluated in global file scope.</p> - <p><c>MODULE</c> is the name of the Erlang module as an - identifier without string quotations. It will be stringified by - the macro.</p> - <p><c>funcs</c> is a static array of function descriptors for - all the implemented NIFs in this library.</p> - <p><c>load</c>, <c>reload</c>, <c>upgrade</c> and <c>unload</c> - are pointers to functions. One of <c>load</c>, <c>reload</c> or - <c>upgrade</c> will be called to initialize the library. - <c>unload</c> is called to release the library. They are all - described individually below.</p> - <p>If compiling a nif for static inclusion via --enable-static-nifs you - have to define STATIC_ERLANG_NIF before the ERL_NIF_INIT declaration.</p> + <tag><marker id="ERL_NIF_INIT"/><c>ERL_NIF_INIT(MODULE, + ErlNifFunc funcs[], load, reload, upgrade, unload)</c></tag> + <item> + <p>This is the magic macro to initialize a NIF library. It + is to be evaluated in global file scope.</p> + <p><c>MODULE</c> is the name of the Erlang module as an + identifier without string quotations. It is stringified by + the macro.</p> + <p><c>funcs</c> is a static array of function descriptors for + all the implemented NIFs in this library.</p> + <p><c>load</c>, <c>reload</c>, <c>upgrade</c> and <c>unload</c> + are pointers to functions. One of <c>load</c>, <c>reload</c>, or + <c>upgrade</c> is called to initialize the library. + <c>unload</c> is called to release the library. All are + described individually below.</p> + <p>If compiling a NIF for static inclusion through + <c>--enable-static-nifs</c>, you must define <c>STATIC_ERLANG_NIF</c> + before the <c>ERL_NIF_INIT</c> declaration.</p> </item> - - <tag><marker id="load"/>int (*load)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag> - <item><p><c>load</c> is called when the NIF library is loaded - and there is no previously loaded library for this module.</p> + <tag><marker id="load"/><c>int (*load)(ErlNifEnv* env, void** priv_data, + ERL_NIF_TERM load_info)</c></tag> + <item> + <p><c>load</c> is called when the NIF library is loaded + and no previously loaded library exists for this module.</p> <p><c>*priv_data</c> can be set to point to some private data - that the library needs in order to keep a state between NIF - calls. <c>enif_priv_data</c> will return this pointer. - <c>*priv_data</c> will be initialized to NULL when <c>load</c> is - called.</p> + that the library needs to keep a state between NIF + calls. <c>enif_priv_data</c> returns this pointer. + <c>*priv_data</c> is initialized to <c>NULL</c> when <c>load</c> is + called.</p> <p><c>load_info</c> is the second argument to <seealso - marker="erlang#load_nif-2">erlang:load_nif/2</seealso>.</p> - <p>The library will fail to load if <c>load</c> returns - anything other than 0. <c>load</c> can be NULL in case no - initialization is needed.</p> - </item> - - <tag><marker id="upgrade"/>int (*upgrade)(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)</tag> - <item><p><c>upgrade</c> is called when the NIF library is loaded - and there is old code of this module with a loaded NIF library.</p> - <p>Works the same as <c>load</c>. The only difference is that - <c>*old_priv_data</c> already contains the value set by the - last call to <c>load</c> or <c>reload</c> for the old module - code. <c>*priv_data</c> will be initialized to NULL when <c>upgrade</c> - is called. It is allowed to write to both *priv_data and *old_priv_data.</p> - <p>The library will fail to load if <c>upgrade</c> returns - anything other than 0 or if <c>upgrade</c> is NULL.</p> + marker="erlang#load_nif-2"><c>erlang:load_nif/2</c></seealso>.</p> + <p>The library fails to load if <c>load</c> returns + anything other than <c>0</c>. <c>load</c> can be <c>NULL</c> if + initialization is not needed.</p> </item> - - <tag><marker id="unload"/>void (*unload)(ErlNifEnv* env, void* priv_data)</tag> - <item><p><c>unload</c> is called when the module code that - the NIF library belongs to is purged as old. New code - of the same module may or may not exist. Note that <c>unload</c> is not - called for a replaced library as a consequence of <c>reload</c>.</p> + <tag><marker id="upgrade"/><c>int (*upgrade)(ErlNifEnv* env, void** + priv_data, void** old_priv_data, ERL_NIF_TERM load_info)</c></tag> + <item> + <p><c>upgrade</c> is called when the NIF library is loaded + and there is old code of this module with a loaded NIF library.</p> + <p>Works as <c>load</c>, except that <c>*old_priv_data</c> already + contains the value set by the last call to <c>load</c> or + <c>reload</c> for the old module code. <c>*priv_data</c> is + initialized to <c>NULL</c> when <c>upgrade</c> is called. It is + allowed to write to both <c>*priv_data</c> and + <c>*old_priv_data.</c></p> + <p>The library fails to load if <c>upgrade</c> returns + anything other than <c>0</c> or if <c>upgrade</c> is <c>NULL</c>.</p> </item> - - <tag><marker id="reload"/>int (*reload)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag> - - <item> - <note><p>The reload mechanism is <em>deprecated</em>. It was only intended - as a development feature. Do not use it as an upgrade method for - live production systems. It might be removed in future releases. Be sure - to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso> - to disable it when not used.</p> - </note> - <p><c>reload</c> is called when the NIF library is loaded - and there is already a previously loaded library for this - module code.</p> - <p>Works the same as <c>load</c>. The only difference is that - <c>*priv_data</c> already contains the value set by the - previous call to <c>load</c> or <c>reload</c>.</p> - <p>The library will fail to load if <c>reload</c> returns - anything other than 0 or if <c>reload</c> is NULL.</p> + <tag><marker id="unload"/><c>void (*unload)(ErlNifEnv* env, void* + priv_data)</c></tag> + <item> + <p><c>unload</c> is called when the module code that + the NIF library belongs to is purged as old. New code of the same + module may or may not exist. Notice that <c>unload</c> is not + called for a replaced library as a consequence of <c>reload</c>.</p> + </item> + <tag><marker id="reload"/><c>int (*reload)(ErlNifEnv* env, void** + priv_data, ERL_NIF_TERM load_info)</c></tag> + <item> + <note> + <p><em>The reload mechanism is deprecated.</em> It was only intended + as a development feature. Do not use it as an upgrade method for + live production systems. It can be removed in future releases. + Ensure to pass <c>reload</c> as <c>NULL</c> to + <seealso marker="#ERL_NIF_INIT"><c>ERL_NIF_INIT</c></seealso> + to disable it when not used.</p> + </note> + <p><c>reload</c> is called when the NIF library is loaded and a + previously loaded library already exists for this module code.</p> + <p>Works as <c>load</c>, except that + <c>*priv_data</c> already contains the value set by the + previous call to <c>load</c> or <c>reload</c>.</p> + <p>The library fails to load if <c>reload</c> returns + anything other than <c>0</c> or if <c>reload</c> is <c>NULL</c>.</p> </item> - </taglist> </section> <section> - <title>DATA TYPES</title> - + <title>Data Types</title> <taglist> - <tag><marker id="ERL_NIF_TERM"/>ERL_NIF_TERM</tag> - <item> + <tag><marker id="ERL_NIF_TERM"/><c>ERL_NIF_TERM</c></tag> + <item> <p>Variables of type <c>ERL_NIF_TERM</c> can refer to any Erlang term. - This is an opaque type and values of it can only by used either as - arguments to API functions or as return values from NIFs. All - <c>ERL_NIF_TERM</c>'s belong to an environment - (<seealso marker="#ErlNifEnv">ErlNifEnv</seealso>). A term can not be - destructed individually, it is valid until its environment is destructed.</p> + This is an opaque type and values of it can only by used either as + arguments to API functions or as return values from NIFs. All + <c>ERL_NIF_TERM</c>s belong to an environment + (<seealso marker="#ErlNifEnv"><c>ErlNifEnv</c></seealso>). + A term cannot be destructed individually, it is valid until its + environment is destructed.</p> </item> - <tag><marker id="ErlNifEnv"/>ErlNifEnv</tag> + <tag><marker id="ErlNifEnv"/><c>ErlNifEnv</c></tag> <item> - <p><c>ErlNifEnv</c> represents an environment that can host Erlang terms. - All terms in an environment are valid as long as the environment is valid. - <c>ErlNifEnv</c> is an opaque type and pointers to it can only be passed - on to API functions. There are two types of environments; process - bound and process independent.</p> - <p>A <em>process bound environment</em> is passed as the first argument to all NIFs. - All function arguments passed to a NIF will belong to that environment. - The return value from a NIF must also be a term belonging to the same - environment. - In addition a process bound environment contains transient information - about the calling Erlang process. The environment is only valid in the - thread where it was supplied as argument until the NIF returns. It is - thus useless and dangerous to store pointers to process bound - environments between NIF calls. </p> - <p>A <em>process independent environment</em> is created by calling - <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>. It can be - used to store terms between NIF calls and to send terms with - <seealso marker="#enif_send">enif_send</seealso>. A process - 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><c>ErlNifEnv</c> represents an environment that can host Erlang + terms. All terms in an environment are valid as long as the + environment is valid. <c>ErlNifEnv</c> is an opaque type; pointers to + it can only be passed on to API functions. Two types of environments + exist:</p> + <taglist> + <tag>Process-bound environment</tag> + <item> + <p>Passed as the first argument to all NIFs. All function arguments + passed to a NIF belong to that environment. The return value from + a NIF must also be a term belonging to the same environment.</p> + <p>A process-bound environment contains transient information + about the calling Erlang process. The environment is only valid + in the thread where it was supplied as argument until the NIF + returns. It is thus useless and dangerous to store pointers to + process-bound environments between NIF calls.</p> + </item> + <tag>Process-independent environment</tag> + <item> + <p>Created by calling <seealso marker="#enif_alloc_env"> + <c>enif_alloc_env</c></seealso>. This environment can be + used to store terms between NIF calls and to send terms with + <seealso marker="#enif_send"><c>enif_send</c></seealso>. A + process-independent environment with all its terms is valid until + you explicitly invalidate it with + <seealso marker="#enif_free_env"><c>enif_free_env</c></seealso> + or <c>enif_send</c>.</p> + </item> + </taglist> <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> + environment as the list/tuple/map itself. Terms can be copied between + environments with + <seealso marker="#enif_make_copy"><c>enif_make_copy</c></seealso>.</p> </item> - <tag><marker id="ErlNifFunc"/>ErlNifFunc</tag> - <item> - <p/> - <code type="none"> + <tag><marker id="ErlNifFunc"/><c>ErlNifFunc</c></tag> + <item> + <code type="none"> typedef struct { const char* name; unsigned arity; ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); unsigned flags; -} ErlNifFunc; -</code> - <p>Describes a NIF by its name, arity and implementation. - <c>fptr</c> is a pointer to the function that implements the - NIF. The argument <c>argv</c> of a NIF will contain the - function arguments passed to the NIF and <c>argc</c> is the - length of the array, i.e. the function arity. <c>argv[N-1]</c> - will thus denote the Nth argument to the NIF. Note that the - <c>argc</c> argument allows for the same C function to - implement several Erlang functions with different arity (but - same name probably). For a regular NIF, <c>flags</c> is 0 (and - so its value can be omitted for statically initialized <c>ErlNifFunc</c> - instances), or it can be used to indicate that the NIF is a <seealso - marker="#dirty_nifs">dirty NIF</seealso> that should be executed - on a dirty scheduler thread (<em>note that the dirty NIF functionality - described here is experimental</em> and that you have to enable - support for dirty schedulers when building OTP in order to try the - functionality out). If the dirty NIF is expected to be - CPU-bound, its <c>flags</c> field should be set to - <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>, or for I/O-bound jobs, - <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p> - <note><p>If one of the - <c>ERL_NIF_DIRTY_JOB_*_BOUND</c> flags is set, and the runtime - system has no support for dirty schedulers, the runtime system - will refuse to load the NIF library.</p></note> +} ErlNifFunc;</code> + <p>Describes a NIF by its name, arity, and implementation.</p> + <taglist> + <tag><c>fptr</c></tag> + <item> + <p>A pointer to the function that implements the NIF.</p> + </item> + <tag><c>argv</c></tag> + <item> + <p>Contains the function arguments passed to the NIF.</p> + </item> + <tag><c>argc</c></tag> + <item> + <p>The array length, that is, the function arity. <c>argv[N-1]</c> + thus denotes the Nth argument to the NIF. Notice that the argument + <c>argc</c> allows for the same C function to implement several + Erlang functions with different arity (but probably with the same + name).</p> + </item> + <tag><c>flags</c></tag> + <item> + <p>Is <c>0</c> for a regular NIF (and so its value can be omitted + for statically initialized <c>ErlNifFunc</c> instances).</p> + <p><c>flags</c> can be used to indicate that the NIF is a + <seealso marker="#dirty_nifs">dirty NIF</seealso> that is to be + executed on a dirty scheduler thread.</p> + <p><em>The dirty NIF functionality described here is + experimental.</em> You have to enable support for dirty + schedulers when building OTP to try out the functionality.</p> + <p>If the dirty NIF is expected to be CPU-bound, its <c>flags</c> + field is to be set to <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> or + <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p> + <note> + <p>If one of the <c>ERL_NIF_DIRTY_JOB_*_BOUND</c> flags is set, + and the runtime system has no support for dirty schedulers, + the runtime system refuses to load the NIF library.</p> + </note> + </item> + </taglist> </item> - <tag><marker id="ErlNifBinary"/>ErlNifBinary</tag> - <item> - <p/> - <code type="none"> + <tag><marker id="ErlNifBinary"/><c>ErlNifBinary</c></tag> + <item> + <code type="none"> typedef struct { unsigned size; unsigned char* data; -} ErlNifBinary; -</code> +} ErlNifBinary;</code> <p><c>ErlNifBinary</c> contains transient information about an inspected binary term. <c>data</c> is a pointer to a buffer of <c>size</c> bytes with the raw content of the binary.</p> - <p>Note that <c>ErlNifBinary</c> is a semi-opaque type and you are + <p>Notice that <c>ErlNifBinary</c> is a semi-opaque type and you are only allowed to read fields <c>size</c> and <c>data</c>.</p> </item> - - <tag><marker id="ErlNifBinaryToTerm"/>ErlNifBinaryToTerm</tag> + <tag><marker id="ErlNifBinaryToTerm"/><c>ErlNifBinaryToTerm</c></tag> <item> - <p>An enumeration of the options that can be given to - <seealso marker="#enif_binary_to_term">enif_binary_to_term</seealso>. - For default behavior, use the value <c>0</c>.</p> - <taglist> - <tag><c>ERL_NIF_BIN2TERM_SAFE</c></tag> - <item><p>Use this option when receiving data from untrusted sources.</p></item> - </taglist> + <p>An enumeration of the options that can be specified to + <seealso marker="#enif_binary_to_term"> + <c>enif_binary_to_term</c></seealso>. + For default behavior, use value <c>0</c>.</p> + <p>When receiving data from untrusted sources, use option + <c>ERL_NIF_BIN2TERM_SAFE</c>.</p> </item> - - <tag><marker id="ErlNifPid"/>ErlNifPid</tag> - <item> - <p><c>ErlNifPid</c> is a process identifier (pid). In contrast to - pid terms (instances of <c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>'s are self - contained and not bound to any - <seealso marker="#ErlNifEnv">environment</seealso>. <c>ErlNifPid</c> - is an opaque type.</p> - </item> - <tag><marker id="ErlNifPort"/>ErlNifPort</tag> - <item> - <p><c>ErlNifPort</c> is a port identifier. In contrast to - port id terms (instances of <c>ERL_NIF_TERM</c>), <c>ErlNifPort</c>'s are self - contained and not bound to any - <seealso marker="#ErlNifEnv">environment</seealso>. <c>ErlNifPort</c> - is an opaque type.</p> - </item> - - <tag><marker id="ErlNifResourceType"/>ErlNifResourceType</tag> - <item> - <p>Each instance of <c>ErlNifResourceType</c> represent a class of - memory managed resource objects that can be garbage collected. + <tag><marker id="ErlNifPid"/><c>ErlNifPid</c></tag> + <item> + <p>A process identifier (pid). In contrast to pid terms (instances of + <c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>s are self-contained and not + bound to any <seealso marker="#ErlNifEnv">environment</seealso>. + <c>ErlNifPid</c> is an opaque type.</p> + </item> + <tag><marker id="ErlNifPort"/><c>ErlNifPort</c></tag> + <item> + <p>A port identifier. In contrast to port ID terms (instances of + <c>ERL_NIF_TERM</c>), <c>ErlNifPort</c>s are self-contained and not + bound to any <seealso marker="#ErlNifEnv">environment</seealso>. + <c>ErlNifPort</c> is an opaque type.</p> + </item> + <tag><marker id="ErlNifResourceType"/><c>ErlNifResourceType</c></tag> + <item> + <p>Each instance of <c>ErlNifResourceType</c> represents a class of + memory-managed resource objects that can be garbage collected. Each resource type has a unique name and a destructor function that is called when objects of its type are released.</p> - </item> - <tag><marker id="ErlNifResourceDtor"/>ErlNifResourceDtor</tag> - <item> - <p/> - <code type="none"> -typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj); -</code> - <p>The function prototype of a resource destructor function.</p> - </item> - <tag><marker id="ErlNifCharEncoding"/>ErlNifCharEncoding</tag> - <item> - <p/> - <code type="none"> + </item> + <tag><marker id="ErlNifResourceDtor"/><c>ErlNifResourceDtor</c></tag> + <item> + <code type="none"> +typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);</code> + <p>The function prototype of a resource destructor function.</p> + </item> + <tag><marker id="ErlNifCharEncoding"/><c>ErlNifCharEncoding</c></tag> + <item> + <code type="none"> typedef enum { ERL_NIF_LATIN1 -}ErlNifCharEncoding; -</code> - <p>The character encoding used in strings and atoms. The only - supported encoding is currently <c>ERL_NIF_LATIN1</c> for - iso-latin-1 (8-bit ascii).</p> - </item> - <tag><marker id="ErlNifSysInfo"/>ErlNifSysInfo</tag> - <item> - <p>Used by <seealso marker="#enif_system_info">enif_system_info</seealso> - to return information about the runtime system. Contains currently - the exact same content as <seealso marker="erl_driver#ErlDrvSysInfo">ErlDrvSysInfo</seealso>.</p> - </item> - <tag><marker id="ErlNifSInt64"/>ErlNifSInt64</tag> - <item><p>A native signed 64-bit integer type.</p></item> - <tag><marker id="ErlNifUInt64"/>ErlNifUInt64</tag> - <item><p>A native unsigned 64-bit integer type.</p></item> - - <tag><marker id="ErlNifTime"/>ErlNifTime</tag> +}ErlNifCharEncoding;</code> + <p>The character encoding used in strings and atoms. The only + supported encoding is <c>ERL_NIF_LATIN1</c> for + ISO Latin-1 (8-bit ASCII).</p> + </item> + <tag><marker id="ErlNifSysInfo"/><c>ErlNifSysInfo</c></tag> + <item> + <p>Used by <seealso marker="#enif_system_info"> + <c>enif_system_info</c></seealso> to return information about the + runtime system. Contains the same content as + <seealso marker="erl_driver#ErlDrvSysInfo"> + <c>ErlDrvSysInfo</c></seealso>.</p> + </item> + <tag><marker id="ErlNifSInt64"/><c>ErlNifSInt64</c></tag> + <item> + <p>A native signed 64-bit integer type.</p> + </item> + <tag><marker id="ErlNifUInt64"/><c>ErlNifUInt64</c></tag> + <item> + <p>A native unsigned 64-bit integer type.</p> + </item> + <tag><marker id="ErlNifTime"/><c>ErlNifTime</c></tag> <item> <p>A signed 64-bit integer type for representation of time.</p> </item> - <tag><marker id="ErlNifTimeUnit"/>ErlNifTimeUnit</tag> + <tag><marker id="ErlNifTimeUnit"/><c>ErlNifTimeUnit</c></tag> <item> <p>An enumeration of time units supported by the NIF API:</p> - <taglist> - <tag><c>ERL_NIF_SEC</c></tag> - <item><p>Seconds</p></item> - <tag><c>ERL_NIF_MSEC</c></tag> - <item><p>Milliseconds</p></item> - <tag><c>ERL_NIF_USEC</c></tag> - <item><p>Microseconds</p></item> - <tag><c>ERL_NIF_NSEC</c></tag> - <item><p>Nanoseconds</p></item> - </taglist> + <taglist> + <tag><c>ERL_NIF_SEC</c></tag> + <item>Seconds</item> + <tag><c>ERL_NIF_MSEC</c></tag> + <item>Milliseconds</item> + <tag><c>ERL_NIF_USEC</c></tag> + <item>Microseconds</item> + <tag><c>ERL_NIF_NSEC</c></tag> + <item>Nanoseconds</item> + </taglist> </item> - - <tag><marker id="ErlNifUniqueInteger"/>ErlNifUniqueInteger</tag> + <tag><marker id="ErlNifUniqueInteger"/><c>ErlNifUniqueInteger</c></tag> <item> <p>An enumeration of the properties that can be requested from - <seealso marker="#enif_make_unique_integer">enif_unique_integer</seealso>. - For default properties, use the value <c>0</c>.</p> + <seealso marker="#enif_make_unique_integer"> + <c>enif_unique_integer</c></seealso>. + For default properties, use value <c>0</c>.</p> <taglist> - <tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag> - <item><p>Return only positive integers</p></item> - <tag><c>ERL_NIF_UNIQUE_MONOTONIC</c></tag> - <item><p>Return only - <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly - monotonically increasing</seealso> integer corresponding to creation time</p></item> - </taglist> + <tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag> + <item> + <p>Return only positive integers.</p> + </item> + <tag><c>ERL_NIF_UNIQUE_MONOTONIC</c></tag> + <item> + <p>Return only <seealso + marker="time_correction#Strictly_Monotonically_Increasing"> + strictly monotonically increasing</seealso> integer corresponding + to creation time.</p> + </item> + </taglist> </item> - </taglist> </section> <funcs> - <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> - <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> - <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 - <seealso marker="#enif_release_binary">enif_release_binary</seealso> - or ownership transferred to an Erlang term with - <seealso marker="#enif_make_binary">enif_make_binary</seealso>. - An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF - calls.</p> - <p>Return true on success or false if allocation failed.</p> - </desc> - </func> - <func><name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> - <fsummary>Create a new environment</fsummary> - <desc><p>Allocate a new process independent environment. The environment can - be used to hold terms that is not bound to any process. Such terms can - later be copied to a process environment with - <seealso marker="#enif_make_copy">enif_make_copy</seealso> - or be sent to a process as a message with <seealso marker="#enif_send">enif_send</seealso>.</p> - <p>Return pointer to the new environment.</p> - </desc> - </func> - <func><name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name> - <fsummary>Allocate a memory managed resource object</fsummary> - <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> - <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> - </func> - <func><name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts)</nametext></name> - <fsummary>Create a term from the external format</fsummary> - <desc> - <p>Create a term that is the result of decoding the binary data - at <c>data</c>, which must be encoded according to the Erlang external term format. - No more than <c>size</c> bytes are read from <c>data</c>. Argument <c>opts</c> - correspond to the second argument to <seealso marker="erlang#binary_to_term-2"> - <c>erlang:binary_to_term/2</c></seealso>, and must be either <c>0</c> or - <c>ERL_NIF_BIN2TERM_SAFE</c>.</p> - <p>On success, store the resulting term at <c>*term</c> and return - the actual number of bytes read. Return zero if decoding fails or if <c>opts</c> - is invalid.</p> - <p>See also: - <seealso marker="#ErlNifBinaryToTerm"><c>ErlNifBinaryToTerm</c></seealso>, - <seealso marker="erlang#binary_to_term-2"><c>erlang:binary_to_term/2</c></seealso> and - <seealso marker="#enif_term_to_binary"><c>enif_term_to_binary</c></seealso>. - </p> - </desc> + <func> + <name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> + <fsummary>Allocate dynamic memory.</fsummary> + <desc> + <p>Allocates memory of <c>size</c> bytes.</p> + <p>Returns <c>NULL</c> if the allocation fails.</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> + <desc> + <p>Allocates a new binary of size <c>size</c> bytes. + Initializes the structure pointed to by <c>bin</c> to + refer to the allocated binary. The binary must either be released by + <seealso marker="#enif_release_binary"> + <c>enif_release_binary</c></seealso> + or ownership transferred to an Erlang term with + <seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>. + An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF + calls.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if allocation + fails.</p> + </desc> + </func> + + <func> + <name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> + <fsummary>Create a new environment.</fsummary> + <desc> + <p>Allocates a new process-independent environment. The environment can + be used to hold terms that are not bound to any process. Such terms + can later be copied to a process environment with + <seealso marker="#enif_make_copy"><c>enif_make_copy</c></seealso> or + be sent to a process as a message with <seealso marker="#enif_send"> + <c>enif_send</c></seealso>.</p> + <p>Returns pointer to the new environment.</p> + </desc> </func> - <func><name><ret>int</ret><nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name> - <fsummary>Compare two terms</fsummary> - <desc><p>Return an integer less than, equal to, or greater than - zero if <c>lhs</c> is found, respectively, to be less than, - equal, or greater than <c>rhs</c>. Corresponds to the Erlang - operators <c>==</c>, <c>/=</c>, <c>=<</c>, <c><</c>, - <c>>=</c> and <c>></c> (but <em>not</em> <c>=:=</c> or <c>=/=</c>).</p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">erl_drv_cond_broadcast</seealso>. - </p></desc> - </func> - <func><name><ret>ErlNifCond *</ret><nametext>enif_cond_create(char *name)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_create">erl_drv_cond_create</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_destroy">erl_drv_cond_destroy</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_signal">erl_drv_cond_signal</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">erl_drv_cond_wait</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext></name> - <fsummary></fsummary> - <desc><p>Give the runtime system a hint about how much CPU time the current NIF call has consumed - since last hint, or since the start of the NIF if no previous hint has been given. - The time is given as a <c>percent</c> of the timeslice that a process is allowed to execute Erlang - code until it may be suspended to give time for other runnable processes. - The scheduling timeslice is not an exact entity, but can usually be - approximated to about 1 millisecond.</p> - <p>Note that it is up to the runtime system to determine if and how to use this information. - Implementations on some platforms may use other means in order to determine consumed - CPU time. Lengthy NIFs should regardless of this frequently call <c>enif_consume_timeslice</c> - in order to determine if it is allowed to continue execution or not.</p> - - <p>Returns 1 if the timeslice is exhausted, or 0 otherwise. If 1 is returned the NIF should return - as soon as possible in order for the process to yield.</p> - <p>Argument <c>percent</c> must be an integer between 1 and 100. This function - must only be called from a NIF-calling thread and argument <c>env</c> must be - the environment of the calling process.</p> - <p>This function is provided to better support co-operative scheduling, improve system responsiveness, - and make it easier to prevent misbehaviors of the VM due to a NIF monopolizing a scheduler thread. - It can be used to divide <seealso marker="#lengthy_work">length work</seealso> into - a number of repeated NIF-calls without the need to create threads. - See also the <seealso marker="#WARNING">warning</seealso> text at the beginning of this document.</p> - </desc> + <func> + <name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* + type, unsigned size)</nametext></name> + <fsummary>Allocate a memory-managed resource object.</fsummary> + <desc> + <p>Allocates a memory-managed resource object of type <c>type</c> and + size <c>size</c> bytes.</p> + </desc> </func> <func> - <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name> - <fsummary>Convert time unit of a time value</fsummary> + <name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, + const unsigned char* data, size_t size, ERL_NIF_TERM *term, + ErlNifBinaryToTerm opts)</nametext></name> + <fsummary>Create a term from the external format.</fsummary> <desc> - <marker id="enif_convert_time_unit"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>val</c></tag> - <item>Value to convert time unit for.</item> - <tag><c>from</c></tag> - <item>Time unit of <c>val</c>.</item> - <tag><c>to</c></tag> - <item>Time unit of returned value.</item> - </taglist> - <p>Converts the <c>val</c> value of time unit <c>from</c> to - the corresponding value of time unit <c>to</c>. The result is - rounded using the floor function.</p> - <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid - time unit argument.</p> - <p>See also: - <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> and - <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>. - </p> + <p>Creates a term that is the result of decoding the binary data at + <c>data</c>, which must be encoded according to the Erlang external + term format. No more than <c>size</c> bytes are read from <c>data</c>. + Argument <c>opts</c> corresponds to the second argument to + <seealso marker="erlang#binary_to_term-2"> + <c>erlang:binary_to_term/2</c></seealso> and must be either <c>0</c> + or <c>ERL_NIF_BIN2TERM_SAFE</c>.</p> + <p>On success, stores the resulting term at <c>*term</c> and returns + the number of bytes read. Returns <c>0</c> if decoding fails or if + <c>opts</c> is invalid.</p> + <p>See also <seealso marker="#ErlNifBinaryToTerm"> + <c>ErlNifBinaryToTerm</c></seealso>, + <seealso marker="erlang#binary_to_term-2"> + <c>erlang:binary_to_term/2</c></seealso>, and + <seealso marker="#enif_term_to_binary"> + <c>enif_term_to_binary</c></seealso>.</p> </desc> </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_cpu_time(ErlNifEnv *)</nametext></name> + <name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext> + </name> + <fsummary>Clear an environment for reuse.</fsummary> + <desc> + <p>Frees all terms in an environment and clears it for reuse. + The environment must have been allocated with + <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext> + </name> + <fsummary>Compare two terms.</fsummary> + <desc> + <p>Returns an integer < <c>0</c> if <c>lhs</c> < <c>rhs</c>, + <c>0</c> if <c>lhs</c> = <c>rhs</c>, and > <c>0</c> if + <c>lhs</c> > <c>rhs</c>. Corresponds to the Erlang + operators <c>==</c>, <c>/=</c>, <c>=<</c>, <c><</c>, + <c>>=</c>, and <c>></c> (but <em>not</em> <c>=:=</c> or + <c>=/=</c>).</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> - <p>Returns the CPU time in the same format as <seealso marker="erlang#timestamp-0">erlang:timestamp()</seealso>. - The CPU time is the time the current logical cpu has spent executing since - some arbitrary point in the past. - If the OS does not support fetching of this value <c>enif_cpu_time</c> - invokes <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>. - </p> + <p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast"> + <c>erl_drv_cond_broadcast</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ErlNifCond *</ret> + <nametext>enif_cond_create(char *name)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_cond_create"> + <c>erl_drv_cond_create</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_cond_destroy"> + <c>erl_drv_cond_destroy</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_cond_signal"> + <c>erl_drv_cond_signal</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext> + </name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_cond_wait"> + <c>erl_drv_cond_wait</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext> + </name> + <fsummary></fsummary> + <desc> + <p>Gives the runtime system a hint about how much CPU time the current + NIF call has consumed since the last hint, or since the start of the + NIF if no previous hint has been specified. The time is specified as a + percent of the timeslice that a process is allowed to execute + Erlang code until it can be suspended to give time for other runnable + processes. The scheduling timeslice is not an exact entity, but can + usually be approximated to about 1 millisecond.</p> + <p>Notice that it is up to the runtime system to determine if and how + to use this information. Implementations on some platforms can use + other means to determine consumed CPU time. Lengthy NIFs should + regardless of this frequently call <c>enif_consume_timeslice</c> to + determine if it is allowed to continue execution.</p> + <p>Argument <c>percent</c> must be an integer between 1 and 100. This + function must only be called from a NIF-calling thread, and argument + <c>env</c> must be the environment of the calling process.</p> + <p>Returns <c>1</c> if the timeslice is exhausted, otherwise <c>0</c>. + If <c>1</c> is returned, the NIF is to return as soon as possible in + order for the process to yield.</p> + <p>This function is provided to better support co-operative scheduling, + improve system responsiveness, and make it easier to prevent + misbehaviors of the VM because of a NIF monopolizing a scheduler + thread. It can be used to divide <seealso marker="#lengthy_work"> + length work</seealso> into a number of repeated NIF calls without the + need to create threads.</p> + <p>See also the <seealso marker="#WARNING">warning</seealso> text at + the beginning of this manual page.</p> + </desc> + </func> + + <func> + <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime + val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name> + <fsummary>Convert time unit of a time value.</fsummary> + <desc> + <marker id="enif_convert_time_unit"></marker> + <p>Converts the <c>val</c> value of time unit <c>from</c> to + the corresponding value of time unit <c>to</c>. The result is + rounded using the floor function.</p> + <taglist> + <tag><c>val</c></tag> + <item>Value to convert time unit for.</item> + <tag><c>from</c></tag> + <item>Time unit of <c>val</c>.</item> + <tag><c>to</c></tag> + <item>Time unit of returned value.</item> + </taglist> + <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid + time unit argument.</p> + <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> + and + <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_cpu_time(ErlNifEnv *)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Returns the CPU time in the same format as + <seealso marker="erlang#timestamp-0"> + <c>erlang:timestamp()</c></seealso>. + The CPU time is the time the current logical CPU has spent executing + since some arbitrary point in the past. If the OS does not support + fetching this value, <c>enif_cpu_time</c> invokes + <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext> + </name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_equal_tids"> + <c>erl_drv_equal_tids</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> + <fsummary>Free dynamic memory.</fsummary> + <desc> + <p>Frees memory allocated by + <seealso marker="#enif_alloc"><c>enif_alloc</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_free_env(ErlNifEnv* env)</nametext></name> + <fsummary>Free an environment allocated with enif_alloc_env.</fsummary> + <desc> + <p>Frees an environment allocated with + <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>. + All terms created in the environment are freed as well.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM + term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext> + </name> + <fsummary>Get the text representation of an atom term.</fsummary> + <desc> + <p>Writes a <c>NULL</c>-terminated string in the buffer pointed to by + <c>buf</c> of size <c>size</c>, consisting of the string + representation of the atom <c>term</c> with encoding + <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p> + <p>Returns the number of bytes written (including terminating + <c>NULL</c> character) or <c>0</c> if <c>term</c> is not an atom with + maximum length of <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> + <desc> + <p>Sets <c>*len</c> to the length (number of bytes excluding + terminating <c>NULL</c> character) of the atom <c>term</c> with + encoding <c>encode</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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> + <desc> + <p>Sets <c>*dp</c> to the floating-point value of <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not + a float.</p> </desc> </func> - <func><name><ret>int</ret><nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">erl_drv_equal_tids</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> - <fsummary>Free dynamic memory</fsummary> - <desc><p>Free memory allocated by <c>enif_alloc</c>.</p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_free_env(ErlNifEnv* env)</nametext></name> - <fsummary>Free an environment allocated with enif_alloc_env</fsummary> - <desc><p>Free an environment allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>. - All terms created in the environment will be freed as well.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext></name> - <fsummary>Get the text representation of an atom term</fsummary> - <desc><p>Write a null-terminated string, in the buffer pointed to by - <c>buf</c> of size <c>size</c>, consisting of the string - representation of the atom <c>term</c> with encoding - <seealso marker="#ErlNifCharEncoding">encode</seealso>. Return - the number of bytes written (including terminating null character) or 0 if - <c>term</c> is not an atom with maximum length of - <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> - <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> - <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> - <func><name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM term, int* ip)</nametext></name> - <fsummary>Read an integer term</fsummary> - <desc><p>Set <c>*ip</c> to the integer value of - <c>term</c>. Return true on success or false if <c>term</c> is not an - integer or is outside the bounds of type <c>int</c>.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)</nametext></name> - <fsummary>Read a 64-bit integer term</fsummary> - <desc><p>Set <c>*ip</c> to the integer value of - <c>term</c>. Return true on success or false if <c>term</c> is not an - integer or is outside the bounds of a signed 64-bit integer.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name> - <fsummary>Read an local pid term</fsummary> - <desc><p>If <c>term</c> is the pid of a node local process, initialize the - pid variable <c>*pid</c> from it and return true. Otherwise return false. - No check if the process is alive is done.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name> - <fsummary>Read an local port term</fsummary> - <desc><p>If <c>term</c> identifies a node local port, initialize the - port variable <c>*port_id</c> from it and return true. Otherwise return false. - No check if the port is alive is done.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext></name> - <fsummary>Get head and tail from a list</fsummary> - <desc><p>Set <c>*head</c> and <c>*tail</c> from - <c>list</c> and return true, or return false if <c>list</c> is not a - 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> - <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 proper 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> - <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_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 - of type <c>type</c>.</p></desc> - </func> - <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> - <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 - <seealso marker="#ErlNifCharEncoding">encode</seealso>. - Return the number of bytes written (including terminating null - character), or <c>-size</c> if the string was truncated due to - buffer space, or 0 if <c>list</c> is not a string that can be - encoded with <c>encode</c> or if <c>size</c> was less than 1. - The written string is always null-terminated unless buffer - <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> - <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 - is read-only and <c>(*array)[N-1]</c> will be the Nth element of - the tuple. <c>*array</c> is undefined if the arity of the tuple - is zero.</p><p>Return true on success or false if <c>term</c> is not a - 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> - <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> - <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> - <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_getenv(const char* key, char* value, size_t *value_size)</nametext></name> - <fsummary>Get the value of an environment variable</fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_getenv">erl_drv_getenv</seealso>.</p></desc> - </func> - <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>. 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, &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> - <desc><p>Initialize the structure pointed to by <c>bin</c> with - information about the binary term - <c>bin_term</c>. Return true on success or false if <c>bin_term</c> is not a binary.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv* - env, ERL_NIF_TERM term, ErlNifBinary* bin) - </nametext></name> - <fsummary>Inspect the content of an iolist</fsummary> - <desc><p>Initialize the structure pointed to by <c>bin</c> with one - continuous buffer with the same byte content as <c>iolist</c>. As with - inspect_binary, the data pointed to by <c>bin</c> is transient and does - not need to be released. Return true on success or false if <c>iolist</c> is not an - iolist.</p> + <func> + <name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM + term, int* ip)</nametext></name> + <fsummary>Read an integer term.</fsummary> + <desc> + <p>Sets <c>*ip</c> to the integer value of <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not + an integer or is outside the bounds of type <c>int</c>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM + term, ErlNifSInt64* ip)</nametext></name> + <fsummary>Read a 64-bit integer term.</fsummary> + <desc> + <p>Sets <c>*ip</c> to the integer value of <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not + an integer or is outside the bounds of a signed 64-bit integer.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, + ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name> + <fsummary>Read a local pid term.</fsummary> + <desc> + <p>If <c>term</c> is the pid of a node local process, this function + initializes the pid variable <c>*pid</c> from it and returns + <c>true</c>. Otherwise returns <c>false</c>. No check is done to see + if the process is alive.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, + ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name> + <fsummary>Read a local port term.</fsummary> + <desc> + <p>If <c>term</c> identifies a node local port, this function + initializes the port variable <c>*port_id</c> from it and returns + <c>true</c>. Otherwise returns <c>false</c>. No check is done to see + if the port is alive.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, + ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext> + </name> + <fsummary>Get head and tail from a list.</fsummary> + <desc> + <p>Sets <c>*head</c> and <c>*tail</c> from list <c>list</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if it is + not a list or the list is empty.</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> + <desc> + <p>Sets <c>*len</c> to the length of list <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is + not a proper 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 a long integer term.</fsummary> + <desc> + <p>Sets <c>*ip</c> to the long integer value of <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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_map_size(ErlNifEnv* env, + ERL_NIF_TERM term, size_t *size)</nametext></name> + <fsummary>Read the size of a map term.</fsummary> + <desc> + <p>Sets <c>*size</c> to the number of key-value pairs in the map + <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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>Sets <c>*value</c> to the value associated with <c>key</c> in the + map <c>map</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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>Sets <c>*objp</c> to point to the resource object referred to by + <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is + not a handle to a resource object of type <c>type</c>.</p> + </desc> + </func> + + <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> + <desc> + <p>Writes a <c>NULL</c>-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 + <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p> + <p>Returns one of the following:</p> + <list type="bulleted"> + <item>The number of bytes written (including terminating <c>NULL</c> + character)</item> + <item><c>-size</c> if the string was truncated because of buffer + space</item> + <item><c>0</c> if <c>list</c> is not a string that can be encoded + with <c>encode</c> or if <c>size</c> was < <c>1</c>.</item> + </list> + <p>The written string is always <c>NULL</c>-terminated, unless buffer + <c>size</c> is < <c>1</c>.</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> + <desc> + <p>If <c>term</c> is a tuple, this function sets <c>*array</c> to point + to an array containing the elements of the tuple, and sets + <c>*arity</c> to the number of elements. Notice that the array + is read-only and <c>(*array)[N-1]</c> is the Nth element of + the tuple. <c>*array</c> is undefined if the arity of the tuple + is zero.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is + not a 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> + <desc> + <p>Sets <c>*ip</c> to the unsigned integer value of <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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> + <desc> + <p>Sets <c>*ip</c> to the unsigned integer value of <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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> + <desc> + <p>Sets <c>*ip</c> to the unsigned long integer value of + <c>term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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_getenv(const char* key, char* value, + size_t *value_size)</nametext></name> + <fsummary>Get the value of an environment variable.</fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_getenv"> + <c>erl_drv_getenv</c></seealso>.</p> + </desc> + </func> + + <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>Returns <c>true</c> if a pending exception is associated with the + environment <c>env</c>. If <c>reason</c> is a <c>NULL</c> pointer, + ignore it. Otherwise, if a pending exception associated with + <c>env</c> exists, set <c>ERL_NIF_TERM</c> to which <c>reason</c> + points to the value of the exception's term. For example, if + <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></seealso> is called to set a pending + <c>badarg</c> exception, a later call to + <c>enif_has_pending_exception(env, &reason)</c> sets + <c>reason</c> to the atom <c>badarg</c>, then return <c>true</c>.</p> + <p>See also <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></seealso> and + <seealso marker="#enif_raise_exception"> + <c>enif_raise_exception</c></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> + <desc> + <p>Initializes the structure pointed to by <c>bin</c> with information + about binary term <c>bin_term</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>bin_term</c> + is not a binary.</p> </desc> </func> - <func><name><ret>int</ret><nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is an atom</fsummary> - <desc><p>Return true if <c>term</c> is an atom.</p></desc> + + <func> + <name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv* + env, ERL_NIF_TERM term, ErlNifBinary* bin)</nametext></name> + <fsummary>Inspect the content of an iolist.</fsummary> + <desc> + <p>Initializes the structure pointed to by <c>bin</c> with a + continuous buffer with the same byte content as <c>iolist</c>. As + with <c>inspect_binary</c>, the data pointed to by <c>bin</c> is + transient and does not need to be released.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>iolist</c> is + not an iolist.</p> + </desc> </func> - <func><name><ret>int</ret><nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a binary</fsummary> - <desc><p>Return true if <c>term</c> is a binary</p></desc> + + <func> + <name><ret>int</ret> + <nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is an atom.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is an atom.</p> + </desc> </func> - <func><name><ret>int</ret><nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext></name> - <fsummary>Determine if currently executing process is alive or not.</fsummary> - <desc><p>Return true if currently executing process is currently alive; otherwise - false.</p> - <p>This function can only be used from a NIF-calling thread, and with an - environment corresponding to currently executing processes.</p></desc> + + <func> + <name><ret>int</ret> + <nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is a binary.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a binary.</p> + </desc> </func> - <func><name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is an empty list</fsummary> - <desc><p>Return true if <c>term</c> is an empty list.</p></desc> + + <func> + <name><ret>int</ret> + <nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext> + </name> + <fsummary>Determine if currently executing process is alive.</fsummary> + <desc> + <p>Returns <c>true</c> if the currently executing process is currently + alive, otherwise <c>false</c>.</p> + <p>This function can only be used from a NIF-calling thread, and with + an environment corresponding to currently executing processes.</p> + </desc> </func> - <func><name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is an exception</fsummary> + + <func> + <name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, + ERL_NIF_TERM term)</nametext></name> + <fsummary>Determine if a term is an empty list.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is an empty list.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, + ERL_NIF_TERM term)</nametext></name> + <fsummary>Determine if a term is an exception.</fsummary> <desc><marker id="enif_is_exception"/> - <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> - </func> - <func><name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a fun</fsummary> - <desc><p>Return true if <c>term</c> is a fun.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name> - <fsummary>Erlang operator =:=</fsummary> - <desc><p>Return true if the two terms are identical. Corresponds to the - Erlang operators <c>=:=</c> and - <c>=/=</c>.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a pid</fsummary> - <desc><p>Return true if <c>term</c> is a pid.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a port</fsummary> - <desc><p>Return true if <c>term</c> is a port.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id)</nametext></name> - <fsummary>Determine if a local port is alive or not.</fsummary> - <desc><p>Return true if <c>port_id</c> is currently alive.</p> - <p>This function is only thread-safe when the emulator with SMP support is used. - It can only be used in a non-SMP emulator from a NIF-calling thread.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid)</nametext></name> - <fsummary>Determine if a local process is alive or not.</fsummary> - <desc><p>Return true if <c>pid</c> is currently alive.</p> - <p>This function is only thread-safe when the emulator with SMP support is used. - It can only be used in a non-SMP emulator from a NIF-calling thread.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a reference</fsummary> - <desc><p>Return true if <c>term</c> is a reference.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a tuple</fsummary> - <desc><p>Return true if <c>term</c> is a tuple.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> - <fsummary>Determine if a term is a list</fsummary> - <desc><p>Return true if <c>term</c> is a list.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_keep_resource(void* obj)</nametext></name> - <fsummary>Add a reference to a resource object</fsummary> - <desc><p>Add a reference to resource object <c>obj</c> obtained from - <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>. - Each call to <c>enif_keep_resource</c> for an object must be balanced by - a call to <seealso marker="#enif_release_resource">enif_release_resource</seealso> - before the object will be destructed.</p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext></name> - <fsummary>Create an atom term</fsummary> - <desc><p>Create an atom term from the null-terminated C-string <c>name</c> - with iso-latin-1 encoding. If the length of <c>name</c> exceeds the maximum length - allowed for an atom (255 characters), <c>enif_make_atom</c> invokes - <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>. - </p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)</nametext></name> - <fsummary>Create an atom term</fsummary> - <desc><p>Create an atom term from the string <c>name</c> with length <c>len</c>. - Null-characters are treated as any other characters. If <c>len</c> is greater than the maximum length - allowed for an atom (255 characters), <c>enif_make_atom</c> invokes - <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>. - </p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name> - <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 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_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 - requirement is now lifted as the return value from the NIF is ignored - 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> - <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> - <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> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name> - <fsummary>Create a floating-point term</fsummary> - <desc><p>Create a floating-point term from a <c>double</c>. If the <c>double</c> argument is - not finite or is NaN, <c>enif_make_double</c> invokes - <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)</nametext></name> - <fsummary>Create an existing atom term</fsummary> - <desc><p>Try to create the term of an already existing atom from - the null-terminated C-string <c>name</c> with encoding - <seealso marker="#ErlNifCharEncoding">encode</seealso>. If the atom - already exists store the term in <c>*atom</c> and return true, otherwise - return false. If the length of <c>name</c> exceeds the maximum length - allowed for an atom (255 characters), <c>enif_make_existing_atom</c> - returns false.</p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)</nametext></name> - <fsummary>Create an existing atom term</fsummary> - <desc><p>Try to create the term of an already existing atom from the - string <c>name</c> with length <c>len</c> and encoding - <seealso marker="#ErlNifCharEncoding">encode</seealso>. Null-characters - are treated as any other characters. If the atom already exists store the term - in <c>*atom</c> and return true, otherwise return false. If <c>len</c> is greater - than the maximum length allowed for an atom (255 characters), - <c>enif_make_existing_atom_len</c> returns false.</p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name> - <fsummary>Create an integer term</fsummary> - <desc><p>Create an integer term.</p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext></name> - <fsummary>Create an integer term</fsummary> - <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> - <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> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> - <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> - <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> - <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> - <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>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> - </func> - <func><name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name> - <fsummary>Allocate and create a new binary term</fsummary> - <desc><p>Allocate a binary of size <c>size</c> bytes and create an owning - term. The binary data is mutable until the calling NIF returns. This is a - quick way to create a new binary without having to use - <seealso marker="#ErlNifBinary">ErlNifBinary</seealso>. The drawbacks are - that the binary can not be kept between NIF calls and it can not be - 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> - <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> - <fsummary>Create an opaque handle to a resource object</fsummary> - <desc><p>Create an opaque handle to a memory managed resource object - obtained by <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>. - No ownership transfer is done, as the resource object still needs to be released by - <seealso marker="#enif_release_resource">enif_release_resource</seealso>, - but note that the call to <c>enif_release_resource</c> can occur - immediately after obtaining the term from <c>enif_make_resource</c>, - in which case the resource object will be deallocated when the - term is garbage collected. See the - <seealso marker="#enif_resource_example">example of creating and - returning a resource object</seealso> for more details.</p> - <p>Note that the only defined behaviour of using a resource term in - an Erlang program is to store it and send it between processes on the - same node. Other operations such as matching or <c>term_to_binary</c> - will have unpredictable (but harmless) results.</p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const void* data, size_t size)</nametext></name> - <fsummary>Create a custom binary term</fsummary> - <desc><p>Create a binary term that is memory managed by a resource object - <c>obj</c> obtained by <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>. - The returned binary term will consist of <c>size</c> bytes pointed to - by <c>data</c>. This raw binary data must be kept readable and unchanged - until the destructor of the resource is called. The binary data may be - stored external to the resource object in which case it is the responsibility - of the destructor to release the data.</p> - <p>Several binary terms may be managed by the same resource object. The - destructor will not be called until the last binary is garbage collected. - This can be useful as a way to return different parts of a larger binary - buffer.</p> - <p>As with <seealso marker="#enif_make_resource">enif_make_resource</seealso>, - no ownership transfer is done. The resource still needs to be released with - <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> - <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> - <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> - <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 - <c>pos+size</c> must be less or equal to the number of whole - 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> - <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> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> - <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> - <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> - <desc><p>Create a tuple containing the elements of array <c>arr</c> - of length <c>cnt</c>.</p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext></name> - <fsummary>Create an unsigned integer term</fsummary> - <desc><p>Create an integer term from an <c>unsigned int</c>.</p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext></name> - <fsummary>Create an unsigned integer term</fsummary> - <desc><p>Create an integer term from an unsigned 64-bit integer.</p></desc> - </func> - <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties)</nametext></name> + <p>Return true if <c>term</c> is an exception.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM + term)</nametext></name> + <fsummary>Determine if a term is a fun.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a fun.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, + ERL_NIF_TERM rhs)</nametext></name> + <fsummary>Erlang operator =:=.</fsummary> + <desc> + <p>Returns <c>true</c> if the two terms are identical. Corresponds to + the Erlang operators <c>=:=</c> and <c>=/=</c>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is a list.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a list.</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>Returns <c>true</c> if <c>term</c> is a map, otherwise + <c>false</c>.</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>Returns <c>true</c> if <c>term</c> is a number.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is a pid.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a pid.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is a port.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a port.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, + ErlNifPort *port_id)</nametext></name> + <fsummary>Determine if a local port is alive.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>port_id</c> is alive.</p> + <p>This function is only thread-safe when the emulator with SMP support + is used. It can only be used in a non-SMP emulator from a NIF-calling + thread.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, + ErlNifPid *pid)</nametext></name> + <fsummary>Determine if a local process is alive.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>pid</c> is alive.</p> + <p>This function is only thread-safe when the emulator with SMP support + is used. It can only be used in a non-SMP emulator from a NIF-calling + thread.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is a reference.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a reference.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> + </name> + <fsummary>Determine if a term is a tuple.</fsummary> + <desc> + <p>Returns <c>true</c> if <c>term</c> is a tuple.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_keep_resource(void* obj)</nametext> + </name> + <fsummary>Add a reference to a resource object.</fsummary> + <desc> + <p>Adds a reference to resource object <c>obj</c> obtained from + <seealso marker="#enif_alloc_resource"> + <c>enif_alloc_resource</c></seealso>. Each call to + <c>enif_keep_resource</c> for an object must be balanced by a call to + <seealso marker="#enif_release_resource"> + <c>enif_release_resource</c></seealso> + before the object is destructed.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext> + </name> + <fsummary>Create an atom term.</fsummary> + <desc> + <p>Creates an atom term from the <c>NULL</c>-terminated C-string + <c>name</c> with ISO Latin-1 encoding. If the length of <c>name</c> + exceeds the maximum length allowed for an atom (255 characters), + <c>enif_make_atom</c> invokes <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, + const char* name, size_t len)</nametext></name> + <fsummary>Create an atom term.</fsummary> + <desc> + <p>Create an atom term from the string <c>name</c> with length + <c>len</c>. <c>NULL</c> characters are treated as any other + characters. If <c>len</c> exceeds the maximum length + allowed for an atom (255 characters), <c>enif_make_atom</c> invokes + <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name> + <fsummary>Make a badarg exception.</fsummary> + <desc> + <p>Makes a <c>badarg</c> exception to be returned from a NIF, and + associates it with 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.</p> + <p>The return value from <c>enif_make_badarg</c> can 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"> + <c>enif_is_exception</c></seealso>, but not to any other NIF API + function.</p> + <p>See also <seealso marker="#enif_has_pending_exception"> + <c>enif_has_pending_exception</c></seealso> and + <seealso marker="#enif_raise_exception"> + <c>enif_raise_exception</c></seealso>.</p> + <note> + <p>Before ERTS 7.0 (Erlang/OTP 18), the return value + from <c>enif_make_badarg</c> had to be returned from the NIF. This + requirement is now lifted as the return value from the NIF is + ignored 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> + <desc> + <p>Makes a binary term from <c>bin</c>. Any ownership of + the binary data is transferred to the created term and + <c>bin</c> is to 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> + <desc> + <p>Makes a copy of term <c>src_term</c>. The copy is created in + environment <c>dst_env</c>. The source term can be located in any + environment.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name> + <fsummary>Create a floating-point term.</fsummary> + <desc> + <p>Creates a floating-point term from a <c>double</c>. If argument + <c>double</c> is not finite or is NaN, <c>enif_make_double</c> + invokes <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, + const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding + encode)</nametext></name> + <fsummary>Create an existing atom term.</fsummary> + <desc> + <p>Tries to create the term of an already existing atom from + the <c>NULL</c>-terminated C-string <c>name</c> with encoding + <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p> + <p>If the atom already exists, this function stores the term in + <c>*atom</c> and returns <c>true</c>, otherwise <c>false</c>. + Also returns <c>false</c> if the length of <c>name</c> exceeds the + maximum length allowed for an atom (255 characters).</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, + const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding + encoding)</nametext></name> + <fsummary>Create an existing atom term.</fsummary> + <desc> + <p>Tries to create the term of an already existing atom from the + string <c>name</c> with length <c>len</c> and encoding + <seealso marker="#ErlNifCharEncoding">encode</seealso>. <c>NULL</c> + characters are treated as any other characters.</p> + <p>If the atom already exists, this function stores the term in + <c>*atom</c> and returns <c>true</c>, otherwise <c>false</c>. + Also returns <c>false</c> if <c>len</c> exceeds the maximum length + allowed for an atom (255 characters).</p> + </desc> + </func> + + <func><name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name> + <fsummary>Create an integer term.</fsummary> + <desc> + <p>Creates an integer term.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext> + </name> + <fsummary>Create an integer term.</fsummary> + <desc> + <p>Creates 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> + <desc> + <p>Creates an ordinary list term of length <c>cnt</c>. Expects + <c>cnt</c> number of arguments (after <c>cnt</c>) of type + <c>ERL_NIF_TERM</c> as the elements of the list.</p> + <p>Returns an empty list if <c>cnt</c> is 0.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext> + </name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, + ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, + ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, + ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, + ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, + ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> + <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> + <desc> + <p>Creates 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> + <desc> + <p>Creates 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> + <desc> + <p>Creates an ordinary list containing the elements of array <c>arr</c> + of length <c>cnt</c>.</p> + <p>Returns an empty list if <c>cnt</c> is 0.</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>Creates an integer term from a <c>long int</c>.</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>Makes a copy of map <c>map_in</c> and inserts <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>.</p> + <p>If successful, this function sets <c>*map_out</c> to the new map and + returns <c>true</c>. Returns <c>false</c> if <c>map_in</c> is not a + map.</p> + <p>The <c>map_in</c> term must belong to 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>, this function makes a copy + of <c>map_in</c> in <c>*map_out</c>, and removes <c>key</c> and the + associated value. If map <c>map_in</c> does not contain <c>key</c>, + <c>*map_out</c> is set to <c>map_in</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if <c>map_in</c> is + not a map.</p> + <p>The <c>map_in</c> term must belong to 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>Makes a copy of map <c>map_in</c> and replace the old associated + value for <c>key</c> with <c>new_value</c>.</p> + <p>If successful, this function sets <c>*map_out</c> to the new map and + returns <c>true</c>. Returns <c>false</c> if <c>map_in</c> is not a + map or if it does not contain <c>key</c>.</p> + <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p> + </desc> + </func> + + <func> + <name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* + env, size_t size, ERL_NIF_TERM* termp)</nametext></name> + <fsummary>Allocate and create a new binary term.</fsummary> + <desc> + <p>Allocates a binary of size <c>size</c> bytes and creates an owning + term. The binary data is mutable until the calling NIF returns. + This is a quick way to create a new binary without having to use + <seealso marker="#ErlNifBinary"><c>ErlNifBinary</c></seealso>. + The drawbacks are that the binary cannot be kept between NIF calls + and it cannot be reallocated.</p> + <p>Returns a pointer to the raw binary data and sets + <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>Makes an empty map term.</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>Makes 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> + <desc> + <p>Creates a reference like <seealso marker="erlang#make_ref-0"> + <c>erlang:make_ref/0</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext> + </name> + <fsummary>Create an opaque handle to a resource object.</fsummary> + <desc> + <p>Creates an opaque handle to a memory-managed resource object + obtained by <seealso marker="#enif_alloc_resource"> + <c>enif_alloc_resource</c></seealso>. No ownership transfer is done, + as the resource object still needs to be released by + <seealso marker="#enif_release_resource"> + <c>enif_release_resource</c></seealso>. However, notice that the call + to <c>enif_release_resource</c> can occur immediately after obtaining + the term from <c>enif_make_resource</c>, in which case the resource + object is deallocated when the term is garbage collected. For more + details, see the <seealso marker="#enif_resource_example">example of + creating and returning a resource object</seealso> in the User's + Guide.</p> + <p>Notice that the only defined behavior of using a resource term in + an Erlang program is to store it and send it between processes on the + same node. Other operations, such as matching or + <c>term_to_binary</c>, have unpredictable (but harmless) results.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const + void* data, size_t size)</nametext></name> + <fsummary>Create a custom binary term.</fsummary> + <desc> + <p>Creates a binary term that is memory-managed by a resource object + <c>obj</c> obtained by <seealso marker="#enif_alloc_resource"> + <c>enif_alloc_resource</c></seealso>. The returned binary term + consists of <c>size</c> bytes pointed to by <c>data</c>. This raw + binary data must be kept readable and unchanged until the destructor + of the resource is called. The binary data can be stored external to + the resource object, in which case the destructor is responsible + for releasing the data.</p> + <p>Several binary terms can be managed by the same resource object. The + destructor is not called until the last binary is garbage collected. + This can be useful to return different parts of a larger binary + buffer.</p> + <p>As with <seealso marker="#enif_make_resource"> + <c>enif_make_resource</c></seealso>, no ownership transfer is done. + The resource still needs to be released with + <seealso marker="#enif_release_resource"> + <c>enif_release_resource</c></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>Sets <c>*list_out</c> to the reverse list of the list <c>list_in</c> + and returns <c>true</c>, or returns <c>false</c> if <c>list_in</c> is + not a list.</p> + <p>This function is only to be used on short lists, as a copy is + created of the list, which is not released until after the NIF + returns.</p> + <p>The <c>list_in</c> term must belong to 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> + <desc> + <p>Creates a list containing the characters of the + <c>NULL</c>-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> + <desc> + <p>Creates a list containing the characters of the string <c>string</c> + with length <c>len</c> and encoding + <seealso marker="#ErlNifCharEncoding">encoding</seealso>. + <c>NULL</c> 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> + <desc> + <p>Makes 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. <c>pos+size</c> must + be less or equal to the number of whole 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>Creates a tuple term.</fsummary> + <desc> + <p>Creates a tuple term of arity <c>cnt</c>. Expects <c>cnt</c> number + of arguments (after <c>cnt</c>) of type <c>ERL_NIF_TERM</c> as the + elements of the tuple.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, + ERL_NIF_TERM e1)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, + ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, + ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, + ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, + ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, + ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> + <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> + <desc> + <p>Creates 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> + <desc> + <p>Creates a tuple containing the elements of array <c>arr</c> + of length <c>cnt</c>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext> + </name> + <fsummary>Create an unsigned integer term.</fsummary> + <desc> + <p>Creates an integer term from an <c>unsigned int</c>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext> + </name> + <fsummary>Create an unsigned integer term.</fsummary> + <desc> + <p>Creates an integer term from an unsigned 64-bit integer.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext> + </name> + <fsummary>Create an integer term from an unsigned long int.</fsummary> + <desc> + <p>Creates an integer term from an <c>unsigned long int</c>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv + *env, ErlNifUniqueInteger properties)</nametext></name> <fsummary></fsummary> <desc> - <p>Returns a unique integer with the same properties as given by <seealso marker="erlang#unique_integer-1">erlang:unique_integer/1</seealso>.</p> + <p>Returns a unique integer with the same properties as specified by + <seealso marker="erlang#unique_integer-1"> + <c>erlang:unique_integer/1</c></seealso>.</p> <p><c>env</c> is the environment to create the integer in.</p> - <p> - <c>ERL_NIF_UNIQUE_POSITIVE</c> and <c>ERL_NIF_UNIQUE_MONOTONIC</c> can - be passed as the second argument to change the properties of the - integer returned. It is possible to combine them by or:ing the - two values together. - </p> - <p>See also: - <seealso marker="#ErlNifUniqueInteger"><c>ErlNifUniqueInteger</c></seealso>. - </p> - </desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext></name> - <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"> + <p><c>ERL_NIF_UNIQUE_POSITIVE</c> and <c>ERL_NIF_UNIQUE_MONOTONIC</c> + can be passed as the second argument to change the properties of the + integer returned. They can be combined by OR:ing the two values + together.</p> + <p>See also <seealso marker="#ErlNifUniqueInteger"> + <c>ErlNifUniqueInteger</c></seealso>.</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>Creates an iterator for the map <c>map</c> by initializing the + structure pointed to by <c>iter</c>. Argument <c>entry</c> determines + the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c> + or <c>ERL_NIF_MAP_ITERATOR_LAST</c>.</p> + <p>Returns <c>true</c> on success, or false if <c>map</c> is not a + map.</p> + <p>A map iterator is only useful during the lifetime of environment + <c>env</c> that the <c>map</c> belongs to. The iterator must be + destroyed by calling <seealso marker="#enif_map_iterator_destroy"> + <c>enif_map_iterator_destroy</c></seealso>:</p> + <code type="none"> ERL_NIF_TERM key, value; ErlNifMapIterator iter; enif_map_iterator_create(env, my_map, &iter, ERL_NIF_MAP_ITERATOR_FIRST); @@ -1468,439 +2058,714 @@ while (enif_map_iterator_get_pair(env, &iter, &key, &value)) { do_something(key,value); enif_map_iterator_next(env, &iter); } -enif_map_iterator_destroy(env, &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> +enif_map_iterator_destroy(env, &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>Destroys a map iterator created by + <seealso marker="#enif_map_iterator_create"> + <c>enif_map_iterator_create</c></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>Gets key and value terms at the current map iterator position.</p> + <p>On success, sets <c>*key</c> and <c>*value</c> and returns + <c>true</c>. Returns <c>false</c> if the iterator is positioned at + head (before first entry) or tail (beyond last entry).</p> </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>ErlNifTime</ret><nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext></name> - <fsummary>Get Erlang Monotonic Time</fsummary> - <desc> - <marker id="enif_monotonic_time"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>time_unit</c></tag> - <item>Time unit of returned value.</item> - </taglist> - <p> - Returns the current - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso>. Note that it is not uncommon with - negative values. - </p> - <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid - time unit argument, or if called from a thread that is not a - scheduler thread.</p> - <p>See also: - <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> and - <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>. - </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>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_destroy">erl_drv_mutex_destroy</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_lock">erl_drv_mutex_lock</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_trylock">erl_drv_mutex_trylock</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">erl_drv_mutex_unlock</seealso>. - </p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_now_time(ErlNifEnv *env)</nametext></name> - <fsummary></fsummary> - <desc><p>Retuns an <seealso marker="erlang#now-0">erlang:now()</seealso> timestamp. - The enif_now_time function is <em>deprecated</em>.</p></desc> - </func> - <func><name><ret>ErlNifResourceType *</ret><nametext>enif_open_resource_type(ErlNifEnv* env, - const char* module_str, const char* name, - ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext></name> - <fsummary>Create or takeover a resource type</fsummary> - <desc><p>Create or takeover a resource type identified by the string - <c>name</c> and give it the destructor function pointed to by <seealso marker="#ErlNifResourceDtor">dtor</seealso>. - Argument <c>flags</c> can have the following values:</p> - <taglist> - <tag><c>ERL_NIF_RT_CREATE</c></tag> - <item>Create a new resource type that does not already exist.</item> - <tag><c>ERL_NIF_RT_TAKEOVER</c></tag> - <item>Open an existing resource type and take over ownership of all its instances. - The supplied destructor <c>dtor</c> will be called both for existing instances - as well as new instances not yet created by the calling NIF library.</item> - </taglist> - <p>The two flag values can be combined with bitwise-or. The name of the - resource type is local to the calling module. Argument <c>module_str</c> - is not (yet) used and must be NULL. The <c>dtor</c> may be <c>NULL</c> - in case no destructor is needed.</p> - <p>On success, return a pointer to the resource type and <c>*tried</c> - will be set to either <c>ERL_NIF_RT_CREATE</c> or - <c>ERL_NIF_RT_TAKEOVER</c> to indicate what was actually done. - On failure, return <c>NULL</c> and set <c>*tried</c> to <c>flags</c>. - It is allowed to set <c>tried</c> to <c>NULL</c>.</p> - <p>Note that <c>enif_open_resource_type</c> is only allowed to be called in the three callbacks - <seealso marker="#load">load</seealso>, <seealso marker="#reload">reload</seealso> - and <seealso marker="#upgrade">upgrade</seealso>.</p> - </desc> - </func> - <func><name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext></name> - <fsummary>Send a port_command to to_port</fsummary> - <desc> - <p>This function works the same as <seealso marker="erlang#port_command-2">erlang:port_command/2</seealso> - except that it is always completely asynchronous.</p> + + <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>Returns <c>true</c> if map iterator <c>iter</c> is positioned + before the 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>Returns <c>true</c> if map iterator <c>iter</c> is positioned + after the 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>Increments map iterator to point to the next key-value entry.</p> + <p>Returns <c>true</c> if the iterator is now positioned at a valid + key-value entry, or <c>false</c> 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>Decrements map iterator to point to the previous key-value entry.</p> + <p>Returns <c>true</c> if the iterator is now positioned at a valid + key-value entry, or <c>false</c> if the iterator is positioned at + the head (before the first entry).</p> + </desc> + </func> + + <func> + <name><ret>ErlNifTime</ret> + <nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext> + </name> + <fsummary>Get Erlang monotonic time.</fsummary> + <desc> + <marker id="enif_monotonic_time"></marker> + <p>Returns the current + <seealso marker="time_correction#Erlang_Monotonic_Time"> + Erlang monotonic time</seealso>. Notice that it is not uncommon with + negative values.</p> + <p><c>time_unit</c> is the time unit of the returned value.</p> + <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid time + unit argument, or if called from a thread that is not a scheduler + thread.</p> + <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> + and <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>. + </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"> + <c>erl_drv_mutex_create</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_mutex_destroy"> + <c>erl_drv_mutex_destroy</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_mutex_lock"> + <c>erl_drv_mutex_lock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_mutex_trylock"> + <c>erl_drv_mutex_trylock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock"> + <c>erl_drv_mutex_unlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret> + <nametext>enif_now_time(ErlNifEnv *env)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Returns an <seealso marker="erlang#now-0"> + <c>erlang:now()</c></seealso> time stamp.</p> + <p><em>This function is deprecated.</em></p> + </desc> + </func> + + <func> + <name><ret>ErlNifResourceType *</ret> + <nametext>enif_open_resource_type(ErlNifEnv* env, const char* + module_str, const char* name, ErlNifResourceDtor* dtor, + ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext> + </name> + <fsummary>Create or takeover a resource type.</fsummary> + <desc> + <p>Creates or takes over a resource type identified by the string + <c>name</c> and gives it the destructor function pointed to by + <seealso marker="#ErlNifResourceDtor"><c>dtor</c></seealso>. + Argument <c>flags</c> can have the following values:</p> + <taglist> + <tag><c>ERL_NIF_RT_CREATE</c></tag> + <item>Creates a new resource type that does not already exist.</item> + <tag><c>ERL_NIF_RT_TAKEOVER</c></tag> + <item>Opens an existing resource type and takes over ownership of all + its instances. The supplied destructor <c>dtor</c> is called both + for existing instances and new instances not yet created by the + calling NIF library.</item> + </taglist> + <p>The two flag values can be combined with bitwise OR. The resource + type name is local to the calling module. Argument <c>module_str</c> + is not (yet) used and must be <c>NULL</c>. <c>dtor</c> can be + <c>NULL</c> if no destructor is needed.</p> + <p>On success, the function returns a pointer to the resource type and + <c>*tried</c> is set to either <c>ERL_NIF_RT_CREATE</c> or + <c>ERL_NIF_RT_TAKEOVER</c> to indicate what was done. On failure, + returns <c>NULL</c> and sets <c>*tried</c> to <c>flags</c>. + It is allowed to set <c>tried</c> to <c>NULL</c>.</p> + <p>Notice that <c>enif_open_resource_type</c> is only allowed to be + called in the three callbacks + <seealso marker="#load"><c>load</c></seealso>, + <seealso marker="#reload"><c>reload</c></seealso>, and + <seealso marker="#upgrade"><c>upgrade</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const + ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext> + </name> + <fsummary>Send a port_command to to_port.</fsummary> + <desc> + <p>Works as <seealso marker="erlang#port_command-2"> + <c>erlang:port_command/2</c></seealso>, + except that it is always completely asynchronous.</p> + <taglist> + <tag><c>env</c></tag> + <item>The environment of the calling process. Must not be + <c>NULL</c>.</item> + <tag><c>*to_port</c></tag> + <item>The port ID of the receiving port. The port ID is to refer to a + port on the local node.</item> + <tag><c>msg_env</c></tag> + <item>The environment of the message term. Can be a process-independent + environment allocated with <seealso marker="#enif_alloc_env"> + <c>enif_alloc_env</c></seealso> or <c>NULL</c>.</item> + <tag><c>msg</c></tag> + <item>The message term to send. The same limitations apply as on the + payload to <seealso marker="erlang#port_command-2"> + <c>erlang:port_command/2</c></seealso>.</item> + </taglist> + <p>Using a <c>msg_env</c> of <c>NULL</c> is an optimization, which + groups together calls to <c>enif_alloc_env</c>, <c>enif_make_copy</c>, + <c>enif_port_command</c>, and <c>enif_free_env</c> into one call. + This optimization is only useful when a majority of the terms are to + be copied from <c>env</c> to <c>msg_env</c>.</p> + <p>Returns <c>true</c> if the command is successfully sent. Returns + <c>false</c> if the command fails, for example:</p> + <list type="bulleted"> + <item><c>*to_port</c> does not refer to a local port.</item> + <item>The currently executing process (that is, the sender) is not + alive.</item> + <item><c>msg</c> is invalid.</item> + </list> + <p>See also <seealso marker="#enif_get_local_port"> + <c>enif_get_local_port</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void *</ret> + <nametext>enif_priv_data(ErlNifEnv* env)</nametext></name> + <fsummary>Get the private data of a NIF library.</fsummary> + <desc> + <p>Returns the pointer to the private data that was set by + <seealso marker="#load"><c>load</c></seealso>, + <seealso marker="#reload"><c>reload</c></seealso>, or + <seealso marker="#upgrade"><c>upgrade</c></seealso>.</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>Creates an error exception with the term <c>reason</c> to be + returned from a NIF, and associates it with 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.</p> + <p>The return value from <c>enif_raise_exception</c> can only be used + as the return value from the NIF that invoked it (directly or + indirectly) or be passed to <seealso marker="#enif_is_exception"> + <c>enif_is_exception</c></seealso>, but not to any other NIF API + function.</p> + <p>See also <seealso marker="#enif_has_pending_exception"> + <c>enif_has_pending_exception</c></seealso> and + <seealso marker="#enif_make_badarg"> + <c>enif_make_badarg</c></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> + <desc> + <p>Changes the size of a binary <c>bin</c>. The source binary + can be read-only, in which case it is left untouched and + a mutable copy is allocated and assigned to <c>*bin</c>.</p> + <p>Returns <c>true</c> on success, or <c>false</c> 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> + <desc> + <p>Releases a binary obtained from + <seealso marker="#enif_alloc_binary"> + <c>enif_alloc_binary</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_release_resource(void* obj)</nametext></name> + <fsummary>Release a resource object.</fsummary> + <desc> + <p>Removes a reference to resource object <c>obj</c>obtained from + <seealso marker="#enif_alloc_resource"> + <c>enif_alloc_resource</c></seealso>. + The resource object is destructed when the last reference is removed. + Each call to <c>enif_release_resource</c> must correspond to a + previous call to <c>enif_alloc_resource</c> or + <seealso marker="#enif_keep_resource"> + <c>enif_keep_resource</c></seealso>. + References made by <seealso marker="#enif_make_resource"> + <c>enif_make_resource</c></seealso> + can only be removed by the garbage collector.</p> + </desc> + </func> + + <func> + <name><ret>ErlNifRWLock *</ret> + <nametext>enif_rwlock_create(char *name)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create"> + <c>erl_drv_rwlock_create</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_destroy"> + <c>erl_drv_rwlock_destroy</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rlock"> + <c>erl_drv_rwlock_rlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_runlock"> + <c>erl_drv_rwlock_runlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwlock"> + <c>erl_drv_rwlock_rwlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwunlock"> + <c>erl_drv_rwlock_rwunlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrlock"> + <c>erl_drv_rwlock_tryrlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock"> + <c>erl_drv_rwlock_tryrwlock</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, + const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int + argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM + argv[])</nametext></name> + <fsummary>Schedule a NIF for execution.</fsummary> + <desc> + <p>Schedules NIF <c>fp</c> to execute. This function allows an + application to break up long-running work into multiple regular NIF + calls or to schedule a <seealso marker="#dirty_nifs"> + dirty NIF</seealso> to execute on a dirty scheduler thread.</p> + <p><em>The dirty NIF functionality described here is + experimental.</em> You have to enable support for dirty + schedulers when building OTP to try out the functionality.</p> + <taglist> + <tag><c>fun_name</c></tag> + <item> + <p>Provides a name for the NIF that is scheduled for execution. + If it cannot be converted to an atom, <c>enif_schedule_nif</c> + returns a <c>badarg</c> exception.</p> + </item> + <tag><c>flags</c></tag> + <item> + <p>Must be set to <c>0</c> for a regular NIF. If the emulator was + built with the experimental dirty scheduler support enabled, + <c>flags</c> can be set to either + <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> if the job is expected to be + CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for + jobs that will be I/O-bound. If dirty scheduler threads are not + available in the emulator, an attempt to schedule such a job + results in a <c>badarg</c> exception.</p> + </item> + <tag><c>argc</c> and <c>argv</c></tag> + <item> + <p>Can either be the originals passed into the calling NIF, + or can be values created by the calling NIF.</p> + </item> + </taglist> + <p>The calling NIF must use the return value of + <c>enif_schedule_nif</c> as its own return value.</p> + <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only + schedules the NIF for future execution. The calling NIF does not + block waiting for the scheduled NIF to execute and return. This means + that the calling NIF cannot expect to receive the scheduled NIF + return value and use it for further operations.</p> + </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> + <desc> + <p>Initializes the pid variable <c>*pid</c> to represent the + calling process.</p> + <p>Returns <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> + <desc> + <p>Sends a message to a process.</p> <taglist> <tag><c>env</c></tag> - <item>The environment of the calling process. May not be NULL.</item> - <tag><c>*to_port</c></tag> - <item>The port id of the receiving port. The port id should refer to a - port on the local node.</item> + <item>The environment of the calling process. Must be <c>NULL</c> + only if calling from a created thread.</item> + <tag><c>*to_pid</c></tag> + <item>The pid of the receiving process. The pid is to refer to a + process on the local node.</item> <tag><c>msg_env</c></tag> - <item>The environment of the message term. Can be a process - independent environment allocated with - <seealso marker="#enif_alloc_env">enif_alloc_env</seealso> or NULL.</item> + <item>The environment of the message term. Must be a + process-independent environment allocated with + <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso> + or NULL.</item> <tag><c>msg</c></tag> - <item>The message term to send. The same limitations apply as on the - payload to <seealso marker="erlang#port_command-2">erlang:port_command/2</seealso>.</item> + <item>The message term to send.</item> </taglist> - <p>Using a <c>msg_env</c> of NULL is an optimization which groups together - calls to <c>enif_alloc_env</c>, <c>enif_make_copy</c>, <c>enif_port_command</c> - and <c>enif_free_env</c> into one call. This optimization is only usefull - when a majority of the terms are to be copied from <c>env</c> to the <c>msg_env</c>.</p> - <p>This function return true if the command was successfully sent; otherwise, - false. The call may return false if it detects that the command failed for some - reason. For example, <c>*to_port</c> does not refer to a local port, if currently - executing process, i.e. the sender, is not alive, or if <c>msg</c> is invalid.</p> - <p>See also: <seealso marker="#enif_get_local_port"><c>enif_get_local_port</c></seealso>.</p> - </desc> - </func> - <func><name><ret>void *</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name> - <fsummary>Get the private data of a NIF library</fsummary> - <desc><p>Return the pointer to the private data that was set by <c>load</c>, - <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> - <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> - <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> - <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. - Each call to <c>enif_release_resource</c> must correspond to a previous - call to <c>enif_alloc_resource</c> or - <seealso marker="#enif_keep_resource">enif_keep_resource</seealso>. - References made by <seealso marker="#enif_make_resource">enif_make_resource</seealso> - can only be removed by the garbage collector.</p></desc> - </func> - <func><name><ret>ErlNifRWLock *</ret><nametext>enif_rwlock_create(char *name)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">erl_drv_rwlock_create</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>. - </p></desc> - </func> - <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[])</nametext></name> - <fsummary>Schedule a NIF for execution</fsummary> - <desc> - <p>Schedule NIF <c>fp</c> to execute. This function allows an application to break up long-running - work into multiple regular NIF calls or to schedule a <seealso marker="#dirty_nifs">dirty NIF</seealso> - to execute on a dirty scheduler thread (<em>note that the dirty NIF functionality described here is - experimental</em> and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out).</p> - <p>The <c>fun_name</c> argument provides a name for the NIF being scheduled for execution. If it cannot - be converted to an atom, <c>enif_schedule_nif</c> returns a <c>badarg</c> exception.</p> - <p>The <c>flags</c> argument must be set to 0 for a regular NIF, or if the emulator was built the - experimental dirty scheduler support enabled, <c>flags</c> can be set to either <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> - if the job is expected to be CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will - be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job - will result in a <c>badarg</c> exception.</p> - - <p>The <c>argc</c> and <c>argv</c> arguments can either be the originals passed into the calling NIF, or - they can be values created by the calling NIF.</p> - <p>The calling NIF must use the return value of <c>enif_schedule_nif</c> as its own return value.</p> - <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only schedules the - NIF for future execution. The calling NIF does not block waiting for the scheduled NIF to - execute and return, which means that the calling NIF can't expect to receive the scheduled NIF - return value and use it for further operations.</p> + <p>Returns <c>true</c> if the message is successfully sent. Returns + <c>false</c> if the send operation fails, that is:</p> + <list type="bulleted"> + <item><c>*to_pid</c> does not refer to an alive local process.</item> + <item>The currently executing process (that is, the sender) is not + alive.</item> + </list> + <p>The message environment <c>msg_env</c> with all its terms (including + <c>msg</c>) is invalidated by a successful call to <c>enif_send</c>. + The environment is to either be freed with + <seealso marker="#enif_free_env"> + <c>enif_free_env</c></seealso> of cleared for reuse with + <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>.</p> + <p>If <c>msg_env</c> is set to <c>NULL</c>, the <c>msg</c> term is + copied and the original term and its environemt is still valid after + the call.</p> + <p>This function is only thread-safe when the emulator with SMP support + is used. It can only be used in a non-SMP emulator from a NIF-calling + thread.</p> + <note> + <p>Passing <c>msg_env</c> as <c>NULL</c> is only supported as from + ERTS 8.0 (Erlang/OTP 19).</p> + </note> + </desc> + </func> + + <func> + <name><ret>unsigned</ret> + <nametext>enif_sizeof_resource(void* obj)</nametext></name> + <fsummary>Get the byte size of a resource object.</fsummary> + <desc> + <p>Gets the byte size of resource object <c>obj</c> obtained by + <seealso marker="#enif_alloc_resource"> + <c>enif_alloc_resource</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const + char *format, ...)</nametext></name> + <fsummary>Format strings and Erlang terms.</fsummary> + <desc> + <p>Similar to <c>snprintf</c> but this format string also accepts + <c>"%T"</c>, which formats Erlang terms.</p> + </desc> + </func> + + <func> + <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo + *sys_info_ptr, size_t size)</nametext></name> + <fsummary>Get information about the Erlang runtime system.</fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#driver_system_info"> + <c>driver_system_info</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, + ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name> + <fsummary>Convert a term to the external format.</fsummary> + <desc> + <p>Allocates a new binary with <seealso marker="#enif_alloc_binary"> + <c>enif_alloc_binary</c></seealso> and stores the result of encoding + <c>term</c> according to the Erlang external term format.</p> + <p>Returns <c>true</c> on success, or <c>false</c> if the allocation + fails.</p> + <p>See also <seealso marker="erlang#term_to_binary-1"> + <c>erlang:term_to_binary/1</c></seealso> and + <seealso marker="#enif_binary_to_term"> + <c>enif_binary_to_term</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_thread_create(char *name,ErlNifTid + *tid,void * (*func)(void *),void *args,ErlNifThreadOpts + *opts)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_create"> + <c>erl_drv_thread_create</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_thread_exit(void *resp)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_exit"> + <c>erl_drv_thread_exit</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_join"> + <c>erl_drv_thread_join</c></seealso>.</p> </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> - <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> - <desc><p>Send a message to a process.</p> - <taglist> - <tag><c>env</c></tag> - <item>The environment of the calling process. Must be NULL if and - only if calling from a created thread.</item> - <tag><c>*to_pid</c></tag> - <item>The pid of the receiving process. The pid should refer to a process on the local node.</item> - <tag><c>msg_env</c></tag> - <item>The environment of the message term. Must be a process - independent environment allocated with - <seealso marker="#enif_alloc_env">enif_alloc_env</seealso> or NULL.</item> - <tag><c>msg</c></tag> - <item>The message term to send.</item> - </taglist> - <p>Return true if the message was successfully sent; otherwise, false. The send - operation will fail if <c>*to_pid</c> does not refer to an alive local process, - or if currently executing process, i.e. the sender, is not alive.</p> - <p>The message environment <c>msg_env</c> with all its terms (including - <c>msg</c>) will be invalidated by a successful call to <c>enif_send</c>. The environment - should either be freed with <seealso marker="#enif_free_env">enif_free_env</seealso> - of cleared for reuse with <seealso marker="#enif_clear_env">enif_clear_env</seealso>.</p> - <p>If <c>msg_env</c> is set to NULL the <c>msg</c> term is copied and - the original term and its environemt is still valid after the call.</p> - <p>This function is only thread-safe when the emulator with SMP support is used. - It can only be used in a non-SMP emulator from a NIF-calling thread.</p> - <note><p>Passing <c>msg_env</c> as <c>NULL</c> is only supported since - erts-8.0 (OTP 19).</p></note> - </desc> - </func> - <func><name><ret>unsigned</ret><nametext>enif_sizeof_resource(void* obj)</nametext></name> - <fsummary>Get the byte size of a resource object</fsummary> - <desc><p>Get the byte size of a resource object <c>obj</c> obtained by - <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.</p></desc> - </func> - - <func><name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const char *format, ...)</nametext></name> - <fsummary>Format strings and Erlang terms</fsummary> - <desc> - <p>Similar to <c>snprintf</c> but this format string also accepts <c>"%T"</c> which formats Erlang terms. - </p> - </desc> - </func> - - <func> - <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size)</nametext></name> - <fsummary>Get information about the Erlang runtime system</fsummary> - <desc><p>Same as <seealso marker="erl_driver#driver_system_info">driver_system_info</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name> - <fsummary>Convert a term to the external format</fsummary> - <desc> - <p>Allocates a new binary with <seealso marker="#enif_alloc_binary">enif_alloc_binary</seealso> - and stores the result of encoding <c>term</c> according to the Erlang external term format.</p> - <p>Returns true on success or false if allocation failed.</p> - <p>See also: - <seealso marker="erlang#term_to_binary-1"><c>erlang:term_to_binary/1</c></seealso> and - <seealso marker="#enif_binary_to_term"><c>enif_binary_to_term</c></seealso>. - </p> + + <func> + <name><ret>ErlNifThreadOpts *</ret> + <nametext>enif_thread_opts_create(char *name)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create"> + <c>erl_drv_thread_opts_create</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext> + </name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_destroy"> + <c>erl_drv_thread_opts_destroy</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>ErlNifTid</ret> + <nametext>enif_thread_self(void)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_self"> + <c>erl_drv_thread_self</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_thread_type(void)</nametext></name> + <fsummary>Determine type of current thread</fsummary> + <desc> + <p>Determine the type of currently executing thread. A positive value + indicates a scheduler thread while a negative value or zero indicates + another type of thread. Currently the following specific types exist + (which may be extended in the future):</p> + <taglist> + <tag><c>ERL_NIF_THR_UNDEFINED</c></tag> + <item><p>Undefined thread that is not a scheduler thread.</p></item> + <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag> + <item><p>A normal scheduler thread.</p></item> + <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag> + <item><p>A dirty CPU scheduler thread.</p></item> + <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag> + <item><p>A dirty I/O scheduler thread.</p></item> + </taglist> + </desc> + </func> + + <func> + <name><ret>ErlNifTime</ret> + <nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name> + <fsummary>Get current time offset.</fsummary> + <desc> + <marker id="enif_time_offset"></marker> + <p>Returns the current time offset between + <seealso marker="time_correction#Erlang_Monotonic_Time"> + Erlang monotonic time</seealso> and + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> + converted into the <c>time_unit</c> passed as argument.</p> + <p><c>time_unit</c> is the time unit of the returned value.</p> + <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid + time unit argument or if called from a thread that is not a + scheduler thread.</p> + <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> + and + <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>void *</ret> + <nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_tsd_get"> + <c>erl_drv_tsd_get</c></seealso>.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext> + </name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_create"> + <c>erl_drv_tsd_key_create</c></seealso>.</p> </desc> + </func> + + <func> + <name><ret>void</ret> + <nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy"> + <c>erl_drv_tsd_key_destroy</c></seealso>.</p> + </desc> </func> - <func><name><ret>int</ret><nametext>enif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_create">erl_drv_thread_create</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_thread_exit(void *resp)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_exit">erl_drv_thread_exit</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_join">erl_drv_thread_join </seealso>. - </p></desc> - </func> - <func><name><ret>ErlNifThreadOpts *</ret><nametext>enif_thread_opts_create(char *name)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">erl_drv_thread_opts_create</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy</seealso>. - </p></desc> - </func> - <func><name><ret>ErlNifTid</ret><nametext>enif_thread_self(void)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_self">erl_drv_thread_self</seealso>. - </p></desc> - </func> - <func><name><ret>int</ret><nametext>enif_thread_type(void)</nametext></name> - <fsummary>Determine type of current thread</fsummary> - <desc> - <p>Determine the type of currently executing thread. A positive value - indicates a scheduler thread while a negative value or zero indicates - another type of thread. Currently the following specific types exist - (which may be extended in the future):</p> - <taglist> - <tag><c>ERL_NIF_THR_UNDEFINED</c></tag> - <item><p>Undefined thread that is not a scheduler thread.</p></item> - <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag> - <item><p>A normal scheduler thread.</p></item> - <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag> - <item><p>A dirty CPU scheduler thread.</p></item> - <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag> - <item><p>A dirty I/O scheduler thread.</p></item> - </taglist> - </desc> - </func> - <func> - <name><ret>ErlNifTime</ret><nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name> - <fsummary>Get current Time Offset</fsummary> - <desc> - <marker id="enif_time_offset"></marker> - <p>Arguments:</p> - <taglist> - <tag><c>time_unit</c></tag> - <item>Time unit of returned value.</item> - </taglist> - <p>Returns the current time offset between - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso> - and - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> - converted into the <c>time_unit</c> passed as argument.</p> - <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid - time unit argument, or if called from a thread that is not a - scheduler thread.</p> - <p>See also: - <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> and - <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>. - </p> - </desc> - </func> - - <func><name><ret>int</ret><nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_create">erl_drv_tsd_key_create</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy</seealso>. - </p></desc> - </func> - <func><name><ret>void *</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get</seealso>. - </p></desc> - </func> - <func><name><ret>void</ret><nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name> - <fsummary></fsummary> - <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_set">erl_drv_tsd_set</seealso>. - </p></desc> + + <func> + <name><ret>void</ret> + <nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_tsd_set"> + <c>erl_drv_tsd_set</c></seealso>.</p> + </desc> </func> </funcs> + <section> - <title>SEE ALSO</title> - <p><seealso marker="erlang#load_nif-2">erlang:load_nif/2</seealso></p> + <title>See Also</title> + <p><seealso marker="erlang#load_nif-2"> + <c>erlang:load_nif/2</c></seealso></p> </section> </cref> diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index d3ece37cc5..286bac6c93 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -30,70 +30,70 @@ <file>erl_prim_loader.xml</file> </header> <module>erl_prim_loader</module> - <modulesummary>Low Level Erlang Loader</modulesummary> + <modulesummary>Low-level Erlang loader.</modulesummary> <description> - <p><c>erl_prim_loader</c> is used to load all Erlang modules into - the system. The start script is also fetched with this low level + <p>This module is used to load all Erlang modules into + the system. The start script is also fetched with this low-level loader.</p> + <p><c>erl_prim_loader</c> knows about the environment and how to fetch modules.</p> - <p>The <c>-loader Loader</c> command line flag can be used to - choose the method used by the <c>erl_prim_loader</c>. Two + + <p>Command-line flag <c>-loader Loader</c> can be used to + choose the method used by <c>erl_prim_loader</c>. Two <c>Loader</c> methods are supported by the Erlang runtime system: <c>efile</c> and <c>inet</c>.</p> - - <warning><p>The support for loading of code from archive files is - experimental. The sole purpose of releasing it before it is ready - is to obtain early feedback. The file format, semantics, - interfaces etc. may be changed in a future release. The functions - <c>list_dir/1</c> and <c>read_file_info/1</c> as well as the flag - <c>-loader_debug</c> are also experimental</p></warning> - </description> <funcs> <func> <name name="get_file" arity="1"/> - <fsummary>Get a file</fsummary> + <fsummary>Get a file.</fsummary> <desc> - <p>This function fetches a file using the low level loader. - <c><anno>Filename</anno></c> is either an absolute file name or just the name - of the file, for example <c>"lists.beam"</c>. If an internal + <p>Fetches a file using the low-level loader. + <c><anno>Filename</anno></c> is either an absolute filename or only + the name of the file, for example, <c>"lists.beam"</c>. If an internal path is set to the loader, this path is used to find the file. <c><anno>FullName</anno></c> is the complete name of the fetched file. <c><anno>Bin</anno></c> is the contents of the file as a binary.</p> - - <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example + <p><c><anno>Filename</anno></c> can also be a file in an archive, + for example, <c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia.beam</c>. - See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p> + For information about archive files, see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </desc> </func> + <func> <name name="get_path" arity="0"/> - <fsummary>Get the path set in the loader</fsummary> + <fsummary>Get the path set in the loader.</fsummary> <desc> - <p>This function gets the path set in the loader. The path is - set by the <c>init</c> process according to information found - in the start script.</p> + <p>Gets the path set in the loader. The path is + set by the <seealso marker="init"><c>init(3)</c></seealso> + process according to information found in the start script.</p> </desc> </func> + <func> <name name="list_dir" arity="1"/> - <fsummary>List files in a directory</fsummary> + <fsummary>List files in a directory.</fsummary> <desc> <p>Lists all the files in a directory. Returns - <c>{ok, <anno>Filenames</anno>}</c> if successful. Otherwise, it returns + <c>{ok, <anno>Filenames</anno>}</c> if successful, otherwise <c>error</c>. <c><anno>Filenames</anno></c> is a list of the names of all the files in the directory. The names are not sorted.</p> - <p>The <c><anno>Dir</anno></c> can also be a directory in an archive. For example + <p><c><anno>Dir</anno></c> can also be a directory in an archive, + for example, <c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>. - See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p> + For information about archive files, see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </desc> </func> + <func> <name name="read_file_info" arity="1"/> - <fsummary>Get information about a file</fsummary> + <fsummary>Get information about a file.</fsummary> <desc> <p>Retrieves information about a file. Returns <c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise @@ -103,22 +103,25 @@ from which the function is called:</p> <code type="none"> -include_lib("kernel/include/file.hrl").</code> - <p>See <seealso marker="kernel:file">file(3)</seealso> for more info about - the record <c>file_info</c>.</p> - <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example + <p>For more information about the record <c>file_info</c>, see + <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p> + <p><c><anno>Filename</anno></c> can also be a file in an archive, + for example, <c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia</c>. - See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p> + For information about archive files, see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </desc> </func> + <func> <name name="read_link_info" arity="1"/> - <fsummary>Get information about a link or file</fsummary> + <fsummary>Get information about a link or file.</fsummary> <desc> - <p>This function works like - <seealso marker="#read_file_info/1">read_file_info/1</seealso> + <p>Works like + <seealso marker="#read_file_info/1"><c>read_file_info/1</c></seealso> except that if <c><anno>Filename</anno></c> is a symbolic link, - information about the link will be returned in the <c>file_info</c> - record and the <c>type</c> field of the record will be set to + information about the link is returned in the <c>file_info</c> + record and the <c>type</c> field of the record is set to <c>symlink</c>.</p> <p>If <c><anno>Filename</anno></c> is not a symbolic link, this function returns exactly the same result as <c>read_file_info/1</c>. @@ -126,20 +129,23 @@ is always equivalent to <c>read_file_info/1</c>.</p> </desc> </func> + <func> <name name="set_path" arity="1"/> - <fsummary>Set the path of the loader</fsummary> + <fsummary>Set the path of the loader.</fsummary> <desc> - <p>This function sets the path of the loader if <c>init</c> + <p>Sets the path of the loader if + <seealso marker="init"><c>init(3)</c></seealso> interprets a <c>path</c> command in the start script.</p> </desc> </func> </funcs> <section> - <title>Command Line Flags</title> + <title>Command-Line Flags</title> <p>The <c>erl_prim_loader</c> module interprets the following - command line flags:</p> + command-line flags:</p> + <taglist> <tag><c>-loader Loader</c></tag> <item> @@ -147,37 +153,38 @@ <c>erl_prim_loader</c>. <c>Loader</c> can be <c>efile</c> (use the local file system) or <c>inet</c> (load using the <c>boot_server</c> on another Erlang node).</p> - <p>If the <c>-loader</c> flag is omitted, it defaults to + <p>If flag <c>-loader</c> is omitted, it defaults to <c>efile</c>.</p> </item> <tag><c>-loader_debug</c></tag> <item> <p>Makes the <c>efile</c> loader write some debug information, - such as the reason for failures, while it handles files.</p> + such as the reason for failures, while it handles files.</p> </item> <tag><c>-hosts Hosts</c></tag> <item> <p>Specifies which other Erlang nodes the <c>inet</c> loader - can use. This flag is mandatory if the <c>-loader inet</c> - flag is present. On each host, there must be on Erlang node - with the <seealso - marker="kernel:erl_boot_server">erl_boot_server(3)</seealso> - which handles the load requests. - <c>Hosts</c> is a list of IP addresses (hostnames + can use. This flag is mandatory if flag <c>-loader inet</c> + is present. On each host, there must be on Erlang node + with the <seealso marker="kernel:erl_boot_server"> + <c>erl_boot_server(3)</c></seealso>, + which handles the load requests. + <c>Hosts</c> is a list of IP addresses (hostnames are not acceptable).</p> </item> <tag><c>-setcookie Cookie</c></tag> <item> <p>Specifies the cookie of the Erlang runtime system. This flag - is mandatory if the <c>-loader inet</c> flag is present.</p> + is mandatory if flag <c>-loader inet</c> is present.</p> </item> </taglist> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="init">init(3)</seealso>, - <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso></p> + <title>See Also</title> + <p><seealso marker="init"><c>init(3)</c></seealso>, + <seealso marker="kernel:erl_boot_server"> + <c>erl_boot_server(3)</c></seealso></p> </section> </erlref> diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index 7841fdfd63..83eef374ca 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -29,487 +29,618 @@ <rev></rev> </header> <module>erl_tracer</module> - <modulesummary>Erlang Tracer Behaviour</modulesummary> + <modulesummary>Erlang tracer behavior.</modulesummary> <description> - <p>A behaviour module for implementing the back end of the erlang - tracing system. The functions in this module will be called whenever - a trace probe is triggered. Both the <c>enabled</c> and <c>trace</c> - functions are called in the context of the entity that triggered the - trace probe. - This means that the overhead by having the tracing enabled will be - greatly effected by how much time is spent in these functions. So do as - little work as possible in these functions.</p> + <p>This behavior module implements the back end of the Erlang + tracing system. The functions in this module are called whenever + a trace probe is triggered. Both the <c>enabled</c> and <c>trace</c> + functions are called in the context of the entity that triggered the + trace probe. + This means that the overhead by having the tracing enabled is + greatly effected by how much time is spent in these functions. So, do as + little work as possible in these functions.</p> + <note> - <p>All functions in this behaviour have to be implemented as NIF's. - This is a limitation that may the lifted in the future. - There is an <seealso marker="#example">example tracer module nif</seealso> - implementation at the end of this page.</p> + <p>All functions in this behavior must be implemented as NIFs. + This limitation can be removed in a future releases. + An <seealso marker="#example">example tracer module NIF</seealso> + implementation is provided at the end of this page.</p> </note> + <warning> <p>Do not send messages or issue port commands to the <c>Tracee</c> - in any of the callbacks. Doing so is not allowed and can cause all - sorts of strange behaviour, including but not limited to infinite - recursions.</p> + in any of the callbacks. This is not allowed and can cause all + sorts of strange behavior, including, but not limited to, infinite + recursions.</p> </warning> </description> <datatypes> - <datatype> <name name="trace_tag_send" /> </datatype> - <datatype> <name name="trace_tag_receive" /> </datatype> - <datatype> <name name="trace_tag_call" /> </datatype> - <datatype> <name name="trace_tag_procs" /> </datatype> - <datatype> <name name="trace_tag_ports" /> </datatype> - <datatype> <name name="trace_tag_running_procs" /> </datatype> - <datatype> <name name="trace_tag_running_ports" /> </datatype> - <datatype> <name name="trace_tag_gc" /> </datatype> <datatype> - <name name="trace_tag" /> + <name name="trace_tag_call"/> + </datatype> + <datatype> + <name name="trace_tag_gc"/> + </datatype> + <datatype> + <name name="trace_tag_ports"/> + </datatype> + <datatype> + <name name="trace_tag_procs"/> + </datatype> + <datatype> + <name name="trace_tag_receive"/> + </datatype> + <datatype> + <name name="trace_tag_running_ports"/> + </datatype> + <datatype> + <name name="trace_tag_running_procs"/> + </datatype> + <datatype> + <name name="trace_tag_send"/> + </datatype> + <datatype> + <name name="trace_tag"/> <desc> - <p>The different trace tags that the tracer will be called with. - Each trace tag is described in greater detail in - <seealso marker="#Module:trace/5">Module:trace/5</seealso> - </p> + <p>The different trace tags that the tracer is called with. + Each trace tag is described in detail in + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>.</p> </desc> </datatype> <datatype> - <name name="tracee" /> + <name name="tracee"/> <desc> - <p>The process or port that the trace belongs to. - </p> + <p>The process or port that the trace belongs to.</p> </desc> </datatype> <datatype> - <name name="trace_opts" /> + <name name="trace_opts"/> <desc> - <p>The options for the tracee.</p> + <p>The options for the tracee:</p> <taglist> <tag><c>timestamp</c></tag> - <item>If set the tracer has been requested to include a timestamp.</item> + <item>If set the tracer has been requested to include a + time stamp.</item> <tag><c>extra</c></tag> <item>If set the tracepoint has included additonal data about - the trace event. What the additional data is depends on which - <c>TraceTag</c> has been triggered. The <c>extra</c> trace data - corresponds to the fifth elemnt in the trace tuples described in - <seealso marker="erlang#trace_3_trace_messages">erlang:trace/3</seealso>.</item> + the trace event. What the additional data is depends on which + <c>TraceTag</c> has been triggered. The <c>extra</c> trace data + corresponds to the fifth element in the trace tuples described in + <seealso marker="erlang#trace_3_trace_messages"> + erlang:trace/3</seealso>.</item> <tag><c>match_spec_result</c></tag> <item>If set the tracer has been requested to include the output - of a match specification that was run.</item> + of a match specification that was run.</item> <tag><c>scheduler_id</c></tag> - <item>Set the scheduler id is to be included by the tracer.</item> + <item>If set the scheduler id is to be included by the tracer.</item> </taglist> </desc> </datatype> <datatype> - <name name="tracer_state" /> + <name name="tracer_state"/> <desc> - <p> - The state which is given when calling - <seealso marker="erlang#trace-3"><c>erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}])</c></seealso>. - The tracer state is an immutable value that is passed to erl_tracer callbacks and should - contain all the data that is needed to generate the trace event. - </p> + <p>The state specified when calling + <seealso marker="erlang#trace-3"> + <c>erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}])</c></seealso>. + The tracer state is an immutable value that is passed to + <c>erl_tracer</c> callbacks and is to + contain all the data that is needed to generate the trace event.</p> </desc> </datatype> </datatypes> <section> - <title>CALLBACK FUNCTIONS</title> - <p>The following functions - should be exported from a <c>erl_tracer</c> callback module.</p> - <taglist> - <tag><seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso></tag> - <item>Mandatory</item> - <tag><seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso></tag> - <item>Mandatory</item> - <tag><seealso marker="#Module:enabled_procs/3"><c>Module:enabled_procs/3</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:trace_procs/5"><c>Module:trace_procs/5</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:enabled_ports/3"><c>Module:enabled_ports/3</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:trace_ports/5"><c>Module:trace_ports/5</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:enabled_running_ports/3"><c>Module:enabled_running_ports/3</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:trace_running_ports/5"><c>Module:trace_running_ports/5</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:enabled_running_procs/3"><c>Module:enabled_running_procs/3</c></seealso></tag> - <item>Optional</item> - <tag><seealso marker="#Module:trace_running_procs/5"><c>Module:trace_running_procs/5</c></seealso></tag> - <item>Optional</item> + <title>Callback Functions</title> + <p>The following functions are to be exported from an <c>erl_tracer</c> + callback module:</p> + + <taglist> + <tag><seealso marker="#Module:enabled/3"> + <c>Module:enabled/3</c></seealso></tag> + <item>Mandatory</item> + <tag><seealso marker="#Module:trace/5"> + <c>Module:trace/5</c></seealso></tag> + <item>Mandatory</item> + <tag><seealso marker="#Module:enabled_call/3"> + <c>Module:enabled_call/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_call/5"> + <c>Module:trace_call/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_garbage_collection/3"> + <c>Module:enabled_garbage_collection/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_garbage_collection/5"> + <c>Module:trace_garbage_collection/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_ports/3"> + <c>Module:enabled_ports/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_ports/5"> + <c>Module:trace_ports/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_procs/3"> + <c>Module:enabled_procs/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_procs/5"> + <c>Module:trace_procs/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_receive/3"> + <c>Module:enabled_receive/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_receive/5"> + <c>Module:trace_receive/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_running_ports/3"> + <c>Module:enabled_running_ports/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_running_ports/5"> + <c>Module:trace_running_ports/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_running_procs/3"> + <c>Module:enabled_running_procs/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_running_procs/5"> + <c>Module:trace_running_procs/5</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:enabled_send/3"> + <c>Module:enabled_send/3</c></seealso></tag> + <item>Optional</item> + <tag><seealso marker="#Module:trace_send/5"> + <c>Module:trace_send/5</c></seealso></tag> + <item>Optional</item> </taglist> - </section> <funcs> <func> <name>Module:enabled(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> + <fsummary>Check if a trace event is to be generated.</fsummary> <type> - <v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso> | trace_status</v> + <v>TraceTag = <seealso marker="#type-trace_tag"> + trace_tag()</seealso> | trace_status</v> <v>TracerState = term()</v> <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> + <v>Result = trace | discard | remove</v> </type> <desc> - <p>This callback will be called whenever a tracepoint is triggered. It - allows the tracer to decide whether a trace should be generated or not. - This check is made as early as possible in order to limit the amount of - overhead associated with tracing. If <c>trace</c> is returned the - necessary trace data will be created and the trace call-back of the tracer - will be called. If <c>discard</c> is returned, this trace call - will be discarded and no call to trace will be done. - </p> - <p><c>trace_status</c> is a special type of <c>TraceTag</c> which is used - to check if the tracer should still be active. It is called in multiple - scenarios, but most significantly it is used when tracing is started - using this tracer. If <c>remove</c> is returned when the <c>trace_status</c> - is checked, the tracer will be removed from the tracee.</p> - <p>This function may be called multiple times per tracepoint, so it - is important that it is both fast and side effect free.</p> + <p>This callback is called whenever a tracepoint is triggered. It + allows the tracer to decide whether a trace is to be generated or not. + This check is made as early as possible to limit the amount of + overhead associated with tracing. If <c>trace</c> is returned, the + necessary trace data is created and the trace callback of the tracer + is called. If <c>discard</c> is returned, this trace call is + discarded and no call to trace is done.</p> + <p><c>trace_status</c> is a special type of <c>TraceTag</c>, which is + used to check if the tracer is still to be active. It is called in + multiple scenarios, but most significantly it is used when tracing + is started using this tracer. If <c>remove</c> is returned when the + <c>trace_status</c> is checked, the tracer is removed from the + tracee.</p> + <p>This function can be called multiple times per tracepoint, so it + is important that it is both fast and without side effects.</p> </desc> </func> + <func> - <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> + <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> <type> - <v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso></v> + <v>TraceTag = <seealso marker="#type-trace_tag_call"> + trace_tag_call()</seealso></v> <v>TracerState = term()</v> <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> + <v>Result = trace | discard | remove</v> </type> <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled/3">Module:enabled/3</seealso> - callback returned <c>trace</c>. In it any side effects needed by - the tracer should be done. The tracepoint payload is located in - the <c>TraceTerm</c>. The content of the TraceTerm depends on which - <c>TraceTag</c> has been triggered. - The <c>TraceTerm</c> corresponds to the - fourth element in the trace tuples described in - <seealso marker="erlang#trace_3_trace_messages">erlang:trace/3</seealso>. - If the trace tuple has five elements, the fifth element will be sent as - the <c>extra</c> value in the <c>Opts</c> maps.</p> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>call | return_to</c></seealso> + is triggered.</p> + <p>If <c>enabled_call/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> </desc> </func> + <func> - <name name="trace">Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result</name> - <fsummary>Check if a sequence trace event should be generated.</fsummary> + <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> <type> + <v>TraceTag = <seealso marker="#type-trace_tag_gc"> + trace_tag_gc()</seealso></v> <v>TracerState = term()</v> - <v>Label = term()</v> - <v>SeqTraceInfo = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> </type> <desc> - <p>The <c>TraceTag</c> <c>seq_trace</c> is handled a little bit - differently. There is not <c>Tracee</c> for seq_trace, instead the - <c>Label</c> associated with the seq_trace event is given. - For more info on what <c>Label</c> and <c>SeqTraceInfo</c> can be - see the <seealso marker="kernel:seq_trace">seq_trace</seealso> manual.</p> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>garbage_collection</c></seealso> + is triggered.</p> + <p>If <c>enabled_garbage_collection/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> </desc> </func> <func> - <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_procs">trace_tag_procs()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>procs</c></seealso> - is triggered.</p> - <p>If <c>enabled_procs/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_ports"> + trace_tag_ports()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> + </type> + <desc> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>ports</c></seealso> + is triggered.</p> + <p>If <c>enabled_ports/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_procs">trace_tag()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_procs/3">Module:enabled_procs/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_procs/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_procs"> + trace_tag_procs()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> + </type> + <desc> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>procs</c></seealso> + is triggered.</p> + <p>If <c>enabled_procs/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_ports">trace_tag_ports()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>ports</c></seealso> - is triggered.</p> - <p>If <c>enabled_ports/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result + </name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_receive"> + trace_tag_receive()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> + </type> + <desc> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>'receive'</c></seealso> + is triggered.</p> + <p>If <c>enabled_receive/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_ports">trace_tag()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_ports/3">Module:enabled_ports/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_ports/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> + Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_running_ports"> + trace_tag_running_ports()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> + </type> + <desc> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>running_ports</c></seealso> + is triggered.</p> + <p>If <c>enabled_running_ports/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_running_procs">trace_tag_running_procs()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>running_procs | running</c></seealso> - is triggered.</p> - <p>If <c>enabled_running_procs/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> + Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_running_procs"> + trace_tag_running_procs()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> + </type> + <desc> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"> + <c>running_procs | running</c></seealso> + is triggered.</p> + <p>If <c>enabled_running_procs/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_running_procs">trace_tag_running_procs()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_running_procs/3">Module:enabled_running_procs/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_running_procs/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_send"> + trace_tag_send()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>Result = trace | discard | remove</v> + </type> + <desc> + <p>This callback is called whenever a tracepoint with trace flag + <seealso marker="erlang#trace-3"><c>send</c></seealso> + is triggered.</p> + <p>If <c>enabled_send/3</c> is undefined, + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_running_ports">trace_tag_running_ports()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>running_ports</c></seealso> - is triggered.</p> - <p>If <c>enabled_running_ports/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm, + Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag"> + trace_tag()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso> + callback returned <c>trace</c>. In it any side effects needed by + the tracer are to be done. The tracepoint payload is located in + the <c>TraceTerm</c>. The content of the <c>TraceTerm</c> + depends on which <c>TraceTag</c> is triggered. + <c>TraceTerm</c> corresponds to the + fourth element in the trace tuples described in + <seealso marker="erlang#trace_3_trace_messages"> + <c>erlang:trace/3</c></seealso>.</p> + <p>If the trace tuple has five elements, the fifth element + will be sent as the <c>extra</c> value in the <c>Opts</c> maps.</p> + </desc> </func> <func> - <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_running_ports">trace_tag_running_ports()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_running_ports/3">Module:enabled_running_ports/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_running_ports/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name name="trace">Module:trace(seq_trace, TracerState, Label, + SeqTraceInfo, Opts) -> Result</name> + <fsummary>Check if a sequence trace event is to be generated.</fsummary> + <type> + <v>TracerState = term()</v> + <v>Label = term()</v> + <v>SeqTraceInfo = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>The <c>TraceTag</c> <c>seq_trace</c> is handled slightly + differently. There is no <c>Tracee</c> for <c>seq_trace</c>, instead + the <c>Label</c> associated with the <c>seq_trace</c> event is + specified.</p> + <p>For more information on what <c>Label</c> and <c>SeqTraceInfo</c> + can be, see <seealso marker="kernel:seq_trace"> + <c>seq_trace(3)</c></seealso>.</p> + </desc> </func> <func> - <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_call">trace_tag_call()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>call | return_to</c></seealso> - is triggered.</p> - <p>If <c>enabled_call/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, + Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_call"> + trace_tag_call()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_call/3"> + <c>Module:enabled_call/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_call/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_call">trace_tag_call()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_call/3">Module:enabled_call/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_call/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, + TraceTerm, Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_gc"> + trace_tag_gc()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_garbage_collection/3"> + <c>Module:enabled_garbage_collection/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_garbage_collection/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_send">trace_tag_send()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>send</c></seealso> - is triggered.</p> - <p>If <c>enabled_send/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, + Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_ports"> + trace_tag()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_ports/3"> + <c>Module:enabled_ports/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_ports/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_send">trace_tag_send()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_send/3">Module:enabled_send/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_send/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, + Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_procs"> + trace_tag()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_procs/3"> + <c>Module:enabled_procs/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_procs/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_receive">trace_tag_receive()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>'receive'</c></seealso> - is triggered.</p> - <p>If <c>enabled_receive/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, + Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_receive"> + trace_tag_receive()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_receive/3"> + <c>Module:enabled_receive/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_receive/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_receive">trace_tag_receive()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_receive/3">Module:enabled_receive/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_receive/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, + TraceTerm, Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_running_ports"> + trace_tag_running_ports()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_running_ports/3"> + <c>Module:enabled_running_ports/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_running_ports/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_gc">trace_tag_gc()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>Result = trace | discard | remove</v> - </type> - <desc> - <p>This callback will be called whenever a tracepoint with trace flag - <seealso marker="erlang#trace-3"><c>garbage_collection</c></seealso> - is triggered.</p> - <p>If <c>enabled_garbage_collection/3</c> is not defined <c>enabled/3</c> will be called instead.</p> - </desc> + <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, + TraceTerm, Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_running_procs"> + trace_tag_running_procs()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_running_procs/3"> + <c>Module:enabled_running_procs/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_running_procs/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> <func> - <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> - <fsummary>Check if a trace event should be generated.</fsummary> - <type> - <v>TraceTag = <seealso marker="#type-trace_tag_gc">trace_tag_gc()</seealso></v> - <v>TracerState = term()</v> - <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> - <v>FirstTraceTerm = term()</v> - <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> - <v>Result = ok</v> - </type> - <desc> - <p>This callback will be called when a tracepoint is triggered and - the <seealso marker="#Module:enabled_garbage_collection/3">Module:enabled_garbage_collection/3</seealso> - callback returned <c>trace</c>.</p> - <p>If <c>trace_garbage_collection/5</c> is not defined <c>trace/5</c> will be called instead.</p> - </desc> + <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, + Opts) -> Result</name> + <fsummary>Check if a trace event is to be generated.</fsummary> + <type> + <v>TraceTag = <seealso marker="#type-trace_tag_send"> + trace_tag_send()</seealso></v> + <v>TracerState = term()</v> + <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v> + <v>TraceTerm = term()</v> + <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v> + <v>Result = ok</v> + </type> + <desc> + <p>This callback is called when a tracepoint is triggered and the + <seealso marker="#Module:enabled_send/3"> + <c>Module:enabled_send/3</c></seealso> + callback returned <c>trace</c>.</p> + <p>If <c>trace_send/5</c> is undefined, + <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso> + is called instead.</p> + </desc> </func> - </funcs> + <section> <marker id="example"></marker> - <title>Erl Tracer Module example</title> - <p>In the example below a tracer module with a nif backend sends a message - for each <c>send</c> trace tag containing only the sender and receiver. - Using this tracer module, a much more lightweight message tracer is - used that only records who sent messages to who.</p> - <p>Here is an example session using it on Linux.</p> + <title>Erl Tracer Module Example</title> + <p>In this example, a tracer module with a NIF back end sends a + message for each <c>send</c> trace tag containing only the sender and + receiver. Using this tracer module, a much more lightweight message + tracer is used, which only records who sent messages to who.</p> + + <p>The following is an example session using it on Linux:</p> + <pre> $ gcc -I erts-8.0/include/ -fPIC -shared -o erl_msg_tracer.so erl_msg_tracer.c $ erl @@ -532,9 +663,10 @@ ok {trace,#Port<0.490>,<0.4.0>} {ok,<0.40.0>} {trace,<0.41.0>,<0.27.0>} -5> - </pre> - <p>erl_msg_tracer.erl</p> +5></pre> + + <p><c>erl_msg_tracer.erl</c>:</p> + <pre> -module(erl_msg_tracer). @@ -546,10 +678,11 @@ load() -> enabled(_, _, _) -> error. -trace(_, _, _,_, _) -> - error. - </pre> - <p>erl_msg_tracer.c</p> +trace(_, _, _, _, _) -> + error.</pre> + + <p><c>erl_msg_tracer.c</c>:</p> + <pre> #include "erl_nif.h" @@ -640,7 +773,6 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } return enif_make_atom(env, "ok"); -} - </pre> +}</pre> </section> </erlref> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 6289f033b2..d0a3a77e43 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -32,18 +32,21 @@ <module>erlang</module> <modulesummary>The Erlang BIFs.</modulesummary> <description> - <p>By convention, most Built-In Functions (BIFs) are seen as being + <p>By convention, most Built-In Functions (BIFs) are included in this module. Some of the BIFs are viewed more or less as part of the Erlang programming language and are <em>auto-imported</em>. Thus, it is not necessary to specify the module name. For example, the calls <c>atom_to_list(Erlang)</c> and <c>erlang:atom_to_list(Erlang)</c> are identical.</p> + <p>Auto-imported BIFs are listed without module prefix. BIFs listed with module prefix are not auto-imported.</p> + <p>BIFs can fail for various reasons. All BIFs fail with reason <c>badarg</c> if they are called with arguments of an incorrect type. The other reasons are described in the description of each individual BIF.</p> + <p>Some BIFs can be used in guard tests and are marked with "Allowed in guard tests".</p> </description> @@ -53,100 +56,129 @@ <name>ext_binary()</name> <desc> <p>A binary data object, structured according to - the Erlang external term format.</p> + the Erlang external term format.</p> </desc> </datatype> - <datatype> <name name="message_queue_data"></name> - <desc><p>See <seealso marker="#process_flag_message_queue_data"><c>erlang:process_flag(message_queue_data, MQD)</c></seealso>.</p> + <desc> + <p>See <seealso marker="#process_flag_message_queue_data"> + <c>process_flag(message_queue_data, MQD)</c></seealso>.</p> </desc> </datatype> - <datatype> <name name="timestamp"></name> - <desc><p>See <seealso marker="#timestamp/0">erlang:timestamp/0</seealso>.</p> + <desc> + <p>See <seealso marker="#timestamp/0"> + <c>erlang:timestamp/0</c></seealso>.</p> </desc> </datatype> <datatype> <name name="time_unit"></name> - <desc><p><marker id="type_time_unit"/> - Supported time unit representations:</p> + <desc> + <marker id="type_time_unit"/> + <p>Supported time unit representations:</p> <taglist> - <tag><c>PartsPerSecond :: integer() >= 1</c></tag> - <item><p>Time unit expressed in parts per second. That is, - the time unit equals <c>1/PartsPerSecond</c> second.</p></item> - + <tag><c>PartsPerSecond :: integer() >= 1</c></tag> + <item> + <p>Time unit expressed in parts per second. That is, + the time unit equals <c>1/PartsPerSecond</c> second.</p> + </item> + <tag><c>second</c></tag> + <item> + <p>Symbolic representation of the time unit + represented by the integer <c>1</c>.</p> + </item> + <tag><c>millisecond</c></tag> + <item> + <p>Symbolic representation of the time unit + represented by the integer <c>1000</c>.</p> + </item> + <tag><c>microsecond</c></tag> + <item> + <p>Symbolic representation of the time unit + represented by the integer <c>1000000</c>.</p> + </item> + <tag><c>nanosecond</c></tag> + <item> + <p>Symbolic representation of the time unit + represented by the integer <c>1000000000</c>.</p> + </item> + <tag><c>native</c></tag> + <item> + <p>Symbolic representation of the native time unit + used by the Erlang runtime system.</p> + <p>The <c>native</c> time unit is determined at + runtime system start, and remains the same until + the runtime system terminates. If a runtime system + is stopped and then started again (even on the same + machine), the <c>native</c> time unit of the new + runtime system instance can differ from the + <c>native</c> time unit of the old runtime system + instance.</p> + <p>One can get an approximation of the <c>native</c> + time unit by calling + <seealso marker="erlang:convert_time_unit/3"> + <c>erlang:convert_time_unit(1, second, native)</c></seealso>. + The result equals the number + of whole <c>native</c> time units per second. If + the number of <c>native</c> time units per second does not + add up to a whole number, the result is rounded downwards.</p> + <note> + <p>The value of the <c>native</c> time unit gives + you more or less no information about the + quality of time values. It sets a limit for the + <seealso marker="time_correction#Time_Resolution"> + resolution</seealso> and for the + <seealso marker="time_correction#Time_Precision"> + precision</seealso> of time values, + but it gives no information about the + <seealso marker="time_correction#Time_Accuracy"> + accuracy</seealso> of time values. The resolution of + the <c>native</c> time unit and the resolution of time + values can differ significantly.</p> + </note> + </item> + <tag><c>perf_counter</c></tag> + <item> + <p>Symbolic representation of the performance counter + time unit used by the Erlang runtime system.</p> + <p>The <c>perf_counter</c> time unit behaves much in the same way + as the <c>native</c> time unit. That is, it can differ between + runtime restarts. To get values of this type, call + <seealso marker="kernel:os#perf_counter/0"> + <c>os:perf_counter/0</c></seealso>.</p> + </item> + <tag><seealso marker="#type_deprecated_time_unit"><c>deprecated_time_unit()</c></seealso></tag> + <item><p> + Deprecated symbolic representations kept for backwards-compatibility. + </p></item> + </taglist> + <p>The <c>time_unit/0</c> type can be extended. + To convert time values between time units, use + <seealso marker="#convert_time_unit/3"> + <c>erlang:convert_time_unit/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="deprecated_time_unit"></name> + <desc><marker id="type_deprecated_time_unit"/> + <p>The <seealso marker="#type_time_unit"><c>time_unit()</c></seealso> + type also consist of the following <em>deprecated</em> symbolic + time units:</p> + <taglist> <tag><c>seconds</c></tag> - <item><p>Symbolic representation of the time unit - represented by the integer <c>1</c>.</p></item> + <item><p>Same as <seealso marker="#type_time_unit"><c>second</c></seealso>.</p></item> <tag><c>milli_seconds</c></tag> - <item><p>Symbolic representation of the time unit - represented by the integer <c>1000</c>.</p></item> + <item><p>Same as <seealso marker="#type_time_unit"><c>millisecond</c></seealso>.</p></item> <tag><c>micro_seconds</c></tag> - <item><p>Symbolic representation of the time unit - represented by the integer <c>1000000</c>.</p></item> + <item><p>Same as <seealso marker="#type_time_unit"><c>microsecond</c></seealso>.</p></item> <tag><c>nano_seconds</c></tag> - <item><p>Symbolic representation of the time unit - represented by the integer <c>1000000000</c>.</p></item> - - <tag><c>native</c></tag> - <item><p>Symbolic representation of the native time unit - used by the Erlang runtime system.</p> - - <p>The <c>native</c> time unit is determined at - runtime system start, and remains the same until - the runtime system terminates. If a runtime system - is stopped and then started again (even on the same - machine), the <c>native</c> time unit of the new - runtime system instance can differ from the - <c>native</c> time unit of the old runtime system - instance.</p> - - <p>One can get an approximation of the <c>native</c> - time unit by calling <c>erlang:convert_time_unit(1, - seconds, native)</c>. The result equals the number - of whole <c>native</c> time units per second. In case - the number of <c>native</c> time units per second does - not add up to a whole number, the result is rounded downwards.</p> - - <note> - <p>The value of the <c>native</c> time unit gives - you more or less no information at all about the - quality of time values. It sets a limit for - the - <seealso marker="time_correction#Time_Resolution">resolution</seealso> - as well as for the - <seealso marker="time_correction#Time_Precision">precision</seealso> - of time values, - but it gives absolutely no information at all about the - <seealso marker="time_correction#Time_Accuracy">accuracy</seealso> - of time values. The resolution of the <c>native</c> time - unit and the resolution of time values can differ - significantly.</p> - </note> - </item> - - <tag><c>perf_counter</c></tag> - <item><p>Symbolic representation of the performance counter - time unit used by the Erlang runtime system.</p> - - <p>The <c>perf_counter</c> time unit behaves much in the same way - as the <c>native</c> time unit. That is it might differ inbetween - run-time restarts. You get values of this type by calling - <seealso marker="kernel:os#perf_counter/0"><c>os:perf_counter()</c></seealso> - </p> - </item> - + <item><p>Same as <seealso marker="#type_time_unit"><c>nanosecond</c></seealso>.</p></item> </taglist> - - <p>The <c>time_unit/0</c> type may be extended. Use - <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso> - in order to convert time values between time units.</p> - </desc> </datatype> </datatypes> @@ -175,61 +207,60 @@ <func> <name name="adler32" arity="1"/> - <fsummary>Computes adler32 checksum.</fsummary> + <fsummary>Compute adler32 checksum.</fsummary> <desc> <p>Computes and returns the adler32 checksum for - <c><anno>Data</anno></c>.</p> + <c><anno>Data</anno></c>.</p> </desc> </func> <func> <name name="adler32" arity="2"/> - <fsummary>Computes adler32 checksum.</fsummary> + <fsummary>Compute adler32 checksum.</fsummary> <desc> <p>Continues computing the adler32 checksum by combining - the previous checksum, <c><anno>OldAdler</anno></c>, with - the checksum of <c><anno>Data</anno></c>.</p> + the previous checksum, <c><anno>OldAdler</anno></c>, with + the checksum of <c><anno>Data</anno></c>.</p> <p>The following code:</p> <code> - X = erlang:adler32(Data1), - Y = erlang:adler32(X,Data2).</code> - <p>assigns the same value to <c>Y</c> as this:</p> +X = erlang:adler32(Data1), +Y = erlang:adler32(X,Data2).</code> + <p>assigns the same value to <c>Y</c> as this:</p> <code> - Y = erlang:adler32([Data1,Data2]).</code> +Y = erlang:adler32([Data1,Data2]).</code> </desc> </func> <func> <name name="adler32_combine" arity="3"/> - <fsummary>Combines two adler32 checksums.</fsummary> + <fsummary>Combine two adler32 checksums.</fsummary> <desc> <p>Combines two previously computed adler32 checksums. - This computation requires the size of the data object for - the second checksum to be known.</p> + This computation requires the size of the data object for + the second checksum to be known.</p> <p>The following code:</p> <code> - Y = erlang:adler32(Data1), - Z = erlang:adler32(Y,Data2).</code> +Y = erlang:adler32(Data1), +Z = erlang:adler32(Y,Data2).</code> <p>assigns the same value to <c>Z</c> as this:</p> <code> - X = erlang:adler32(Data1), - Y = erlang:adler32(Data2), - Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> +X = erlang:adler32(Data1), +Y = erlang:adler32(Data2), +Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </desc> </func> <func> <name name="append_element" arity="2"/> - <fsummary>Appends an extra element to a tuple.</fsummary> + <fsummary>Append an extra element to a tuple.</fsummary> <desc> <p>Returns a new tuple that has one element more than <c><anno>Tuple1</anno></c>, and contains the elements in - <c><anno>Tuple1</anno></c> + <c><anno>Tuple1</anno></c> followed by <c><anno>Term</anno></c> as the last element. - Semantically equivalent to + Semantically equivalent to <c>list_to_tuple(tuple_to_list(<anno>Tuple1</anno>) ++ - [<anno>Term</anno>])</c>, but much faster.</p> - <p>Example:</p> + [<anno>Term</anno>])</c>, but much faster. Example:</p> <pre> > <input>erlang:append_element({one, two}, three).</input> {one,two,three}</pre> @@ -238,32 +269,31 @@ <func> <name name="apply" arity="2"/> - <fsummary>Applies a function to an argument list.</fsummary> + <fsummary>Apply a function to an argument list.</fsummary> <desc> <p>Calls a fun, passing the elements in <c><anno>Args</anno></c> - as arguments.</p> + as arguments.</p> <p>If the number of elements in the arguments are known at compile time, the call is better written as <c><anno>Fun</anno>(Arg1, Arg2, ... ArgN)</c>.</p> <warning> - <p>Earlier, <c><anno>Fun</anno></c> could also be given as + <p>Earlier, <c><anno>Fun</anno></c> could also be specified as <c>{Module, Function}</c>, equivalent to - <c>apply(Module, Function, Args)</c>. This use is - deprecated and will stop working in a future release.</p> + <c>apply(Module, Function, Args)</c>. <em>This use is + deprecated and will stop working in a future release.</em></p> </warning> </desc> </func> <func> <name name="apply" arity="3"/> - <fsummary>Applies a function to an argument list.</fsummary> + <fsummary>Apply a function to an argument list.</fsummary> <desc> <p>Returns the result of applying <c>Function</c> in <c><anno>Module</anno></c> to <c><anno>Args</anno></c>. - The applied function must + The applied function must be exported from <c>Module</c>. The arity of the function is - the length of <c>Args</c>.</p> - <p>Example:</p> + the length of <c>Args</c>. Example:</p> <pre> > <input>apply(lists, reverse, [[a, b, c]]).</input> [c,b,a] @@ -271,39 +301,43 @@ "Erlang"</pre> <p>If the number of arguments are known at compile time, the call is better written as - <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ..., ArgN)</c>.</p> - <p>Failure: <c>error_handler:undefined_function/3</c> is called + <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ..., + ArgN)</c>.</p> + <p>Failure: <seealso marker="kernel:error_handler#undefined_function/3"> + <c>error_handler:undefined_function/3</c></seealso> is called if the applied function is not exported. The error handler can be redefined (see - <seealso marker="#process_flag/2">process_flag/2</seealso>). + <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>). If <c>error_handler</c> is undefined, or if the user has redefined the default <c>error_handler</c> so the replacement - module is undefined, an error with the reason <c>undef</c> + module is undefined, an error with reason <c>undef</c> is generated.</p> </desc> </func> <func> <name name="atom_to_binary" arity="2"/> - <fsummary>Returns the binary representation of an atom.</fsummary> + <fsummary>Return the binary representation of an atom.</fsummary> <desc> <p>Returns a binary corresponding to the text representation of <c><anno>Atom</anno></c>. If <c><anno>Encoding</anno></c> - is <c>latin1</c>, there is one byte for each character + is <c>latin1</c>, one byte exists for each character in the text representation. If <c><anno>Encoding</anno></c> is <c>utf8</c> or <c>unicode</c>, the characters are encoded using UTF-8 (that is, characters from 128 through 255 are encoded in two bytes).</p> - <note><p><c>atom_to_binary(<anno>Atom</anno>, latin1)</c> never - fails because the text representation of an atom can only - contain characters from 0 through 255. In a future release, - the text representation - of atoms can be allowed to contain any Unicode character and - <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> will then fail if the - text representation for <c><anno>Atom</anno></c> contains a Unicode - character greater than 255.</p></note> + <note> + <p><c>atom_to_binary(<anno>Atom</anno>, latin1)</c> never + fails, as the text representation of an atom can only + contain characters from 0 through 255. In a future release, + the text representation + of atoms can be allowed to contain any Unicode character and + <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> then fails if the + text representation for <c><anno>Atom</anno></c> contains a Unicode + character > 255.</p> + </note> <p>Example:</p> <pre> > <input>atom_to_binary('Erlang', latin1).</input> @@ -325,12 +359,12 @@ <func> <name name="binary_part" arity="2"/> - <fsummary>Extracts a part of a binary.</fsummary> + <fsummary>Extract a part of a binary.</fsummary> <desc> <p>Extracts the part of the binary described by <c><anno>PosLen</anno></c>.</p> <p>Negative length can be used to extract bytes at the end - of a binary, for example:</p> + of a binary, for example:</p> <code> 1> Bin = <<1,2,3,4,5,6,7,8,9,10>>. 2> binary_part(Bin,{byte_size(Bin), -5}). @@ -342,43 +376,44 @@ 1> Bin = <<1,2,3>> 2> binary_part(Bin,{0,2}). <<1,2>></code> - <p>For details about the <c><anno>PosLen</anno></c> semantics, see the - <seealso marker="stdlib:binary">binary</seealso> - manual page in <c>STDLIB</c>.</p> + <p>For details about the <c><anno>PosLen</anno></c> semantics, see + <seealso marker="stdlib:binary"><c>binary(3)</c></seealso>.</p> <p>Allowed in guard tests.</p> </desc> </func> <func> <name name="binary_part" arity="3"/> - <fsummary>Extracts a part of a binary.</fsummary> + <fsummary>Extract a part of a binary.</fsummary> <desc> <p>The same as <c>binary_part(<anno>Subject</anno>, - {<anno>Start</anno>, <anno>Length</anno>})</c>.</p> + {<anno>Start</anno>, <anno>Length</anno>})</c>.</p> <p>Allowed in guard tests.</p> </desc> </func> <func> <name name="binary_to_atom" arity="2"/> - <fsummary>Converts from text representation to an atom.</fsummary> + <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is - <c><anno>Binary</anno></c>. - If <c><anno>Encoding</anno></c> is <c>latin1</c>, no - translation of bytes in the binary is done. - If <c><anno>Encoding</anno></c> - is <c>utf8</c> or <c>unicode</c>, the binary must contain - valid UTF-8 sequences. Only Unicode characters up - to 255 are allowed.</p> - <note><p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> fails if - the binary contains Unicode characters greater than 255. - In a future release, such Unicode characters can be allowed - and <c>binary_to_atom(<anno>Binary</anno>, utf8)</c> does then not fail. - For more information on Unicode support in atoms, see the - <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8 - encoded atoms</seealso> - in Section "External Term Format" in the User's Guide.</p></note> + <c><anno>Binary</anno></c>. + If <c><anno>Encoding</anno></c> is <c>latin1</c>, no + translation of bytes in the binary is done. + If <c><anno>Encoding</anno></c> + is <c>utf8</c> or <c>unicode</c>, the binary must contain + valid UTF-8 sequences. Only Unicode characters up + to 255 are allowed.</p> + <note> + <p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> fails if + the binary contains Unicode characters > 255. + In a future release, such Unicode characters can be allowed and + <c>binary_to_atom(<anno>Binary</anno>, utf8)</c> does then not fail. + For more information about Unicode support in atoms, see the + <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8 + encoded atoms</seealso> + in section "External Term Format" in the User's Guide.</p> + </note> <p>Examples:</p> <pre> > <input>binary_to_atom(<<"Erlang">>, latin1).</input> @@ -392,10 +427,10 @@ <func> <name name="binary_to_existing_atom" arity="2"/> - <fsummary>Converts from text representation to an atom.</fsummary> + <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>As - <seealso marker="#binary_to_atom/2">binary_to_atom/2</seealso>, + <seealso marker="#binary_to_atom/2"><c>binary_to_atom/2</c></seealso>, but the atom must exist.</p> <p>Failure: <c>badarg</c> if the atom does not exist.</p> </desc> @@ -403,7 +438,7 @@ <func> <name name="binary_to_float" arity="1"/> - <fsummary>Converts from text representation to a float.</fsummary> + <fsummary>Convert from text representation to a float.</fsummary> <desc> <p>Returns the float whose text representation is <c><anno>Binary</anno></c>, for example:</p> @@ -417,7 +452,7 @@ <func> <name name="binary_to_integer" arity="1"/> - <fsummary>Converts from text representation to an integer.</fsummary> + <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation is <c><anno>Binary</anno></c>, for example:</p> @@ -431,10 +466,11 @@ <func> <name name="binary_to_integer" arity="2"/> - <fsummary>Converts from text representation to an integer.</fsummary> + <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation in base - <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>, for example:</p> + <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>, for + example:</p> <pre> > <input>binary_to_integer(<<"3FF">>, 16).</input> 1023</pre> @@ -445,7 +481,7 @@ <func> <name name="binary_to_list" arity="1"/> - <fsummary>Converts a binary to a list.</fsummary> + <fsummary>Convert a binary to a list.</fsummary> <desc> <p>Returns a list of integers corresponding to the bytes of <c><anno>Binary</anno></c>.</p> @@ -454,86 +490,83 @@ <func> <name name="binary_to_list" arity="3"/> - <fsummary>Converts part of a binary to a list.</fsummary> - <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>)</type_desc> + <fsummary>Convert part of a binary to a list.</fsummary> + <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>) + </type_desc> <desc> <p>As <c>binary_to_list/1</c>, but returns a list of integers corresponding to the bytes from position <c><anno>Start</anno></c> to position <c><anno>Stop</anno></c> in <c><anno>Binary</anno></c>. - The positions in the + The positions in the binary are numbered starting from 1.</p> - <note><p>The one-based indexing for binaries used by - this function is deprecated. New code is to use - <seealso marker="stdlib:binary#bin_to_list/3">binary:bin_to_list/3</seealso> - in <c>STDLIB</c> instead. All functions in module - <c>binary</c> consistently use zero-based indexing.</p></note> - </desc> - </func> - - <func> - <name name="bitstring_to_list" arity="1"/> - <fsummary>Converts a bitstring to a list.</fsummary> - <desc> - <p>Returns a list of integers corresponding to the bytes of - <c><anno>Bitstring</anno></c>. If the number of bits in the binary - is not divisible by 8, the last element of the list is a bitstring - containing the remaining 1-7 bits.</p> + <note> + <p><em>The one-based indexing for binaries used by + this function is deprecated.</em> New code is to use + <seealso marker="stdlib:binary#bin_to_list/3"> + <c>binary:bin_to_list/3</c></seealso> + in STDLIB instead. All functions in module + <c>binary</c> consistently use zero-based indexing.</p> + </note> </desc> </func> <func> <name name="binary_to_term" arity="1"/> - <fsummary>Decodes an Erlang external term format binary.</fsummary> + <fsummary>Decode an Erlang external term format binary.</fsummary> <desc> <p>Returns an Erlang term that is the result of decoding binary object <c><anno>Binary</anno></c>, which must be encoded according to the Erlang external term format.</p> - <warning><p>When decoding binaries from untrusted sources, - consider using <c>binary_to_term/2</c> to prevent Denial - of Service attacks.</p></warning> + <warning> + <p>When decoding binaries from untrusted sources, + consider using <c>binary_to_term/2</c> to prevent Denial + of Service attacks.</p> + </warning> <p>See also - <seealso marker="#term_to_binary/1">term_to_binary/1</seealso> - and - <seealso marker="#binary_to_term/2">binary_to_term/2</seealso>.</p> + <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso> + and <seealso marker="#binary_to_term/2"> + <c>binary_to_term/2</c></seealso>.</p> </desc> </func> <func> <name name="binary_to_term" arity="2"/> - <fsummary>Decodes an Erlang external term format binary.</fsummary> + <fsummary>Decode an Erlang external term format binary.</fsummary> <desc> <p>As <c>binary_to_term/1</c>, but takes options that affect decoding - of the binary.</p> + of the binary.</p> + <p>Option:</p> <taglist> <tag><c>safe</c></tag> <item> <p>Use this option when receiving binaries from an untrusted - source.</p> + source.</p> <p>When enabled, it prevents decoding data that can be used to - attack the Erlang system. In the event of receiving unsafe - data, decoding fails with a <c>badarg</c> error.</p> + attack the Erlang system. In the event of receiving unsafe + data, decoding fails with a <c>badarg</c> error.</p> <p>This prevents creation of new atoms directly, - creation of new atoms indirectly (as they are embedded in - certain structures, such as process identifiers, - refs, and funs), and - creation of new external function references. - None of those resources are garbage collected, so unchecked - creation of them can exhaust available memory.</p> + creation of new atoms indirectly (as they are embedded in + certain structures, such as process identifiers, + refs, and funs), and + creation of new external function references. + None of those resources are garbage collected, so unchecked + creation of them can exhaust available memory.</p> </item> </taglist> <p>Failure: <c>badarg</c> if <c>safe</c> is specified and unsafe - data is decoded.</p> + data is decoded.</p> <p>See also - <seealso marker="#term_to_binary/1">term_to_binary/1</seealso>, - <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>, - and - <seealso marker="#list_to_existing_atom/1">list_to_existing_atom/1</seealso>.</p> + <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>, + <seealso marker="#binary_to_term/1"> + <c>binary_to_term/1</c></seealso>, and + <seealso marker="#list_to_existing_atom/1"> + <c>list_to_existing_atom/1</c></seealso>.</p> </desc> </func> <func> <name name="bit_size" arity="1"/> - <fsummary>Returns the size of a bitstring.</fsummary> + <fsummary>Return the size of a bitstring.</fsummary> <desc> <p>Returns an integer that is the size in bits of <c><anno>Bitstring</anno></c>, for example:</p> @@ -547,15 +580,26 @@ </func> <func> + <name name="bitstring_to_list" arity="1"/> + <fsummary>Convert a bitstring to a list.</fsummary> + <desc> + <p>Returns a list of integers corresponding to the bytes of + <c><anno>Bitstring</anno></c>. If the number of bits in the binary + is not divisible by 8, the last element of the list is a bitstring + containing the remaining 1-7 bits.</p> + </desc> + </func> + + <func> <name name="bump_reductions" arity="1"/> - <fsummary>Increments the reduction counter.</fsummary> + <fsummary>Increment the reduction counter.</fsummary> <desc> <p>This implementation-dependent function increments the reduction counter for the calling process. In the Beam emulator, the reduction counter is normally incremented by one for each function and BIF call. A context switch is forced when the counter reaches the maximum number of - reductions for a process (2000 reductions in OTP R12B).</p> + reductions for a process (2000 reductions in Erlang/OTP R12B).</p> <warning> <p>This BIF can be removed in a future version of the Beam machine without prior warning. It is unlikely to be @@ -566,13 +610,12 @@ <func> <name name="byte_size" arity="1"/> - <fsummary>Returns the size of a bitstring (or binary).</fsummary> + <fsummary>Return the size of a bitstring (or binary).</fsummary> <desc> <p>Returns an integer that is the number of bytes needed to contain <c><anno>Bitstring</anno></c>. That is, if the number of bits in <c><anno>Bitstring</anno></c> is not divisible by 8, the resulting - number of bytes is rounded <em>up</em>.</p> - <p>Examples:</p> + number of bytes is rounded <em>up</em>. Examples:</p> <pre> > <input>byte_size(<<433:16,3:3>>).</input> 3 @@ -583,134 +626,127 @@ </func> <func> - <name name="cancel_timer" arity="2"/> - <fsummary>Cancels a timer.</fsummary> + <name name="cancel_timer" arity="1"/> + <fsummary>Cancel a timer.</fsummary> <desc> - <p> - Cancels a timer that has been created by - <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>, - or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>. - <c><anno>TimerRef</anno></c> identifies the timer, and - was returned by the BIF that created the timer. - </p> - <p>Available <c><anno>Option</anno></c>s:</p> + <p>Cancels a timer. The same as calling + <seealso marker="#cancel_timer/2"> + <c>erlang:cancel_timer(TimerRef, [])</c></seealso>.</p> + </desc> + </func> + + <func> + <name name="cancel_timer" arity="2"/> + <fsummary>Cancel a timer.</fsummary> + <desc> + <p>Cancels a timer that has been created by + <seealso marker="#start_timer/4"> + <c>erlang:start_timer</c></seealso> or + <seealso marker="#send_after/4"><c>erlang:send_after</c></seealso>. + <c><anno>TimerRef</anno></c> identifies the timer, and + was returned by the BIF that created the timer.</p> + <p><c><anno>Option</anno></c>s:</p> <taglist> <tag><c>{async, Async}</c></tag> <item> - <p> - Asynchronous request for cancellation. <c>Async</c> - defaults to <c>false</c> which will cause the - cancellation to be performed synchronously. When - <c>Async</c> is set to <c>true</c>, the cancel - operation is performed asynchronously. That is, - <c>erlang:cancel_timer()</c> will send an asynchronous - request for cancellation to the timer service that - manages the timer, and then return <c>ok</c>. - </p> - </item> + <p>Asynchronous request for cancellation. <c>Async</c> + defaults to <c>false</c>, which causes the + cancellation to be performed synchronously. When + <c>Async</c> is set to <c>true</c>, the cancel + operation is performed asynchronously. That is, + <c>cancel_timer()</c> sends an asynchronous + request for cancellation to the timer service that + manages the timer, and then returns <c>ok</c>.</p> + </item> <tag><c>{info, Info}</c></tag> <item> - <p> - Request information about the <c><anno>Result</anno></c> - of the cancellation. <c>Info</c> defaults to <c>true</c> - which means the <c><anno>Result</anno></c> is - given. When <c>Info</c> is set to <c>false</c>, no - information about the result of the cancellation - is given. When the operation is performed</p> - <taglist> - <tag>synchronously</tag> - <item> - <p> - If <c>Info</c> is <c>true</c>, the <c>Result</c> is - returned by <c>erlang:cancel_timer()</c>; otherwise, - <c>ok</c> is returned. - </p> - </item> - <tag>asynchronously</tag> - <item> - <p> - If <c>Info</c> is <c>true</c>, a message on the form - <c>{cancel_timer, <anno>TimerRef</anno>, - <anno>Result</anno>}</c> is sent to the - caller of <c>erlang:cancel_timer()</c> when the - cancellation operation has been performed; otherwise, - no message is sent. - </p> - </item> - </taglist> - </item> - </taglist> - <p> - More <c><anno>Option</anno></c>s may be added in the future. - </p> - <p>If <c><anno>Result</anno></c> is an integer, it represents - the time in milli-seconds left until the canceled timer would - have expired.</p> - <p> - If <c><anno>Result</anno></c> is <c>false</c>, a - timer corresponding to <c><anno>TimerRef</anno></c> could not - be found. This can be either because the timer had expired, - already had been canceled, or because <c><anno>TimerRef</anno></c> - never corresponded to a timer. Even if the timer had expired, - it does not tell you whether or not the timeout message has - arrived at its destination yet. - </p> - <note> - <p> - The timer service that manages the timer may be co-located - with another scheduler than the scheduler that the calling - process is executing on. If this is the case, communication - with the timer service takes much longer time than if it - is located locally. If the calling process is in critical - path, and can do other things while waiting for the result - of this operation, or is not interested in the result of - the operation, you want to use option <c>{async, true}</c>. - If using option <c>{async, false}</c>, the calling - process blocks until the operation has been performed. - </p> - </note> - <p>See also + <p>Requests information about the <c><anno>Result</anno></c> + of the cancellation. <c>Info</c> defaults to <c>true</c>, + which means the <c><anno>Result</anno></c> is + given. When <c>Info</c> is set to <c>false</c>, no + information about the result of the cancellation + is given.</p> + <list type="bulleted"> + <item> + <p>When <c>Async</c> is <c>false</c>: + if <c>Info</c> is <c>true</c>, the <c>Result</c> is + returned by <c>erlang:cancel_timer()</c>. otherwise + <c>ok</c> is returned.</p> + </item> + <item> + <p>When <c>Async</c> is <c>true</c>: + if <c>Info</c> is <c>true</c>, a message on the form + <c>{cancel_timer, <anno>TimerRef</anno>, + <anno>Result</anno>}</c> is sent to the + caller of <c>erlang:cancel_timer()</c> when the + cancellation operation has been performed, otherwise + no message is sent.</p> + </item> + </list> + </item> + </taglist> + <p>More <c><anno>Option</anno></c>s may be added in the future.</p> + <p>If <c><anno>Result</anno></c> is an integer, it represents + the time in milliseconds left until the canceled timer would + have expired.</p> + <p>If <c><anno>Result</anno></c> is <c>false</c>, a + timer corresponding to <c><anno>TimerRef</anno></c> could not + be found. This can be either because the timer had expired, + already had been canceled, or because <c><anno>TimerRef</anno></c> + never corresponded to a timer. Even if the timer had expired, + it does not tell you if the time-out message has + arrived at its destination yet.</p> + <note> + <p>The timer service that manages the timer can be co-located + with another scheduler than the scheduler that the calling + process is executing on. If so, communication + with the timer service takes much longer time than if it + is located locally. If the calling process is in critical + path, and can do other things while waiting for the result + of this operation, or is not interested in the result of + the operation, you want to use option <c>{async, true}</c>. + If using option <c>{async, false}</c>, the calling + process blocks until the operation has been performed.</p> + </note> + <p>See also <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>, - <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>, - and - <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p> - </desc> - </func> - <func> - <name name="cancel_timer" arity="1"/> - <fsummary>Cancels a timer.</fsummary> - <desc> - <p>Cancels a timer. The same as calling - <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer(TimerRef, - [])</c></seealso>.</p> + <seealso marker="#start_timer/4"> + <c>erlang:start_timer/4</c></seealso>, and + <seealso marker="#read_timer/2"> + <c>erlang:read_timer/2</c></seealso>.</p> </desc> </func> + <func> <name name="check_old_code" arity="1"/> - <fsummary>Checks if a module has old code.</fsummary> + <fsummary>Check if a module has old code.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Module</anno></c> has old code, - otherwise <c>false</c>.</p> - <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p> + otherwise <c>false</c>.</p> + <p>See also <seealso marker="kernel:code"> + <c>code(3)</c></seealso>.</p> </desc> </func> <func> <name name="check_process_code" arity="2"/> - <fsummary>Checks if a process executes old code for a module.</fsummary> + <fsummary>Check if a process executes old code for a module.</fsummary> <desc> <p>The same as - <seealso marker="#check_process_code/3"><c>erlang:check_process_code(<anno>Pid</anno>, <anno>Module</anno>, [])</c></seealso>.</p> + <seealso marker="#check_process_code/3"> + <c>check_process_code(<anno>Pid</anno>, <anno>Module</anno>, [])</c> + </seealso>.</p> </desc> </func> <func> <name name="check_process_code" arity="3"/> - <fsummary>Checks if a process executes old code for a module.</fsummary> + <fsummary>Check if a process executes old code for a module.</fsummary> <desc> - <p>Checks if the node local process identified by <c><anno>Pid</anno></c> - executes old code for <c><anno>Module</anno></c>.</p> - <p>The available <c><anno>Option</anno></c>s are as follows:</p> + <p>Checks if the node local process identified by + <c><anno>Pid</anno></c> + executes old code for <c><anno>Module</anno></c>.</p> + <p><c><anno>Option</anno></c>s:</p> <taglist> <tag><c>{allow_gc, boolean()}</c></tag> <item> @@ -718,30 +754,30 @@ the operation. If <c>{allow_gc, false}</c> is passed, and a garbage collection is needed to determine the result of the operation, the operation is aborted (see - information on <c><anno>CheckResult</anno></c> in the following). + information on <c><anno>CheckResult</anno></c> below). The default is to allow garbage collection, that is, <c>{allow_gc, true}</c>.</p> - </item> + </item> <tag><c>{async, RequestId}</c></tag> <item> <p>The function <c>check_process_code/3</c> returns the value <c>async</c> immediately after the request has been sent. When the request has been processed, the process that called this function is passed a - message on the form - <c>{check_process_code, <anno>RequestId</anno>, <anno>CheckResult</anno>}</c>.</p> - </item> + message on the form <c>{check_process_code, <anno>RequestId</anno>, + <anno>CheckResult</anno>}</c>.</p> + </item> </taglist> <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and - no <c>async</c> option has been passed, the operation - is performed at once. Otherwise a request for - the operation is sent to the process identified by - <c><anno>Pid</anno></c>, and is handled when - appropriate. If no <c>async</c> option has been passed, - the caller blocks until <c><anno>CheckResult</anno></c> - is available and can be returned.</p> + no <c>async</c> option has been passed, the operation + is performed at once. Otherwise a request for + the operation is sent to the process identified by + <c><anno>Pid</anno></c>, and is handled when + appropriate. If no <c>async</c> option has been passed, + the caller blocks until <c><anno>CheckResult</anno></c> + is available and can be returned.</p> <p><c><anno>CheckResult</anno></c> informs about the result of - the request as follows:</p> + the request as follows:</p> <taglist> <tag><c>true</c></tag> <item> @@ -751,54 +787,82 @@ code for this module, or the process has references to old code for this module, or the process contains funs that references old code for this module.</p> - </item> + </item> <tag><c>false</c></tag> <item> <p>The process identified by <c><anno>Pid</anno></c> does not execute old code for <c><anno>Module</anno></c>.</p> - </item> + </item> <tag><c>aborted</c></tag> <item> <p>The operation was aborted, as the process needed to be garbage collected to determine the operation result, and the operation was requested - by passing option <c>{allow_gc, false}</c>.</p></item> + by passing option <c>{allow_gc, false}</c>.</p> + </item> </taglist> - <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p> + <note> + <p> + Up until ERTS version 8.*, the check process code operation + checks for all types of references to the old code. That is, + direct references (e.g. return addresses on the process + stack), indirect references (<c>fun</c>s in process + context), and references to literals in the code. + </p> + <p> + As of ERTS version 9.0, the check process code operation + only checks for direct references to the code. Indirect + references via <c>fun</c>s will be ignored. If such + <c>fun</c>s exist and are used after a purge of the old + code, an exception will be raised upon usage (same as + the case when the <c>fun</c> is received by the process + after the purge). Literals will be taken care of (copied) + at a later stage. This behavior can as of ERTS version + 8.1 be enabled when + <seealso marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP_Configuring">building OTP</seealso>, + and will automatically be enabled if dirty scheduler + support is enabled. + </p> + </note> + <p>See also <seealso marker="kernel:code"> + <c>code(3)</c></seealso>.</p> <p>Failures:</p> <taglist> <tag><c>badarg</c></tag> - <item>If <c><anno>Pid</anno></c> is not a node local process identifier. - </item> + <item>If <c><anno>Pid</anno></c> is not a node local process + identifier. + </item> <tag><c>badarg</c></tag> <item>If <c><anno>Module</anno></c> is not an atom. - </item> + </item> <tag><c>badarg</c></tag> <item>If <c><anno>OptionList</anno></c> is an invalid list of options. - </item> + </item> </taglist> </desc> </func> <func> <name name="convert_time_unit" arity="3"/> - <fsummary>Converts time unit of a time value.</fsummary> + <fsummary>Convert time unit of a time value.</fsummary> <desc> - <p>Converts the <c><anno>Time</anno></c> value of time unit - <c><anno>FromUnit</anno></c> to the corresponding - <c><anno>ConvertedTime</anno></c> value of time unit - <c><anno>ToUnit</anno></c>. The result is rounded - using the floor function.</p> - - <warning><p>You may lose accuracy and precision when converting - between time units. In order to minimize such loss, collect all - data at <c>native</c> time unit and do the conversion on the end - result.</p></warning> + <p>Converts the <c><anno>Time</anno></c> value of time unit + <c><anno>FromUnit</anno></c> to the corresponding + <c><anno>ConvertedTime</anno></c> value of time unit + <c><anno>ToUnit</anno></c>. The result is rounded + using the floor function.</p> + <warning> + <p>You can lose accuracy and precision when converting + between time units. To minimize such loss, collect all + data at <c>native</c> time unit and do the conversion on the end + result.</p> + </warning> </desc> </func> + <func> <name name="crc32" arity="1"/> - <fsummary>Computes crc32 (IEEE 802.3) checksum.</fsummary> + <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary> <desc> <p>Computes and returns the crc32 (IEEE 802.3 style) checksum for <c><anno>Data</anno></c>.</p> @@ -807,37 +871,37 @@ <func> <name name="crc32" arity="2"/> - <fsummary>Computes crc32 (IEEE 802.3) checksum.</fsummary> + <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary> <desc> <p>Continues computing the crc32 checksum by combining - the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum of - <c><anno>Data</anno></c>.</p> + the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum + of <c><anno>Data</anno></c>.</p> <p>The following code:</p> <code> - X = erlang:crc32(Data1), - Y = erlang:crc32(X,Data2).</code> +X = erlang:crc32(Data1), +Y = erlang:crc32(X,Data2).</code> <p>assigns the same value to <c>Y</c> as this:</p> <code> - Y = erlang:crc32([Data1,Data2]).</code> +Y = erlang:crc32([Data1,Data2]).</code> </desc> </func> <func> <name name="crc32_combine" arity="3"/> - <fsummary>Combines two crc32 (IEEE 802.3) checksums.</fsummary> + <fsummary>Combine two crc32 (IEEE 802.3) checksums.</fsummary> <desc> <p>Combines two previously computed crc32 checksums. - This computation requires the size of the data object for - the second checksum to be known.</p> + This computation requires the size of the data object for + the second checksum to be known.</p> <p>The following code:</p> <code> - Y = erlang:crc32(Data1), - Z = erlang:crc32(Y,Data2).</code> +Y = erlang:crc32(Data1), +Z = erlang:crc32(Y,Data2).</code> <p>assigns the same value to <c>Z</c> as this:</p> <code> - X = erlang:crc32(Data1), - Y = erlang:crc32(Data2), - Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> +X = erlang:crc32(Data1), +Y = erlang:crc32(Data2), +Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </desc> </func> @@ -847,8 +911,7 @@ <desc> <p>Returns the current date as <c>{Year, Month, Day}</c>.</p> <p>The time zone and Daylight Saving Time correction depend on - the underlying OS.</p> - <p>Example:</p> + the underlying OS. Example:</p> <pre> > <input>date().</input> {1995,2,19}</pre> @@ -857,28 +920,29 @@ <func> <name name="decode_packet" arity="3"/> - <fsummary>Extracts a protocol packet from a binary.</fsummary> + <fsummary>Extract a protocol packet from a binary.</fsummary> <desc> <p>Decodes the binary <c><anno>Bin</anno></c> according to the packet - protocol specified by <c><anno>Type</anno></c>. Similar to the packet - handling done by sockets with option {packet,<anno>Type</anno>}.</p> + protocol specified by <c><anno>Type</anno></c>. Similar to the packet + handling done by sockets with option + <c>{packet,<anno>Type</anno>}.</c></p> <p>If an entire packet is contained in <c><anno>Bin</anno></c>, it is - returned together with the remainder of the binary as - <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p> + returned together with the remainder of the binary as + <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p> <p>If <c><anno>Bin</anno></c> does not contain the entire packet, - <c>{more,<anno>Length</anno>}</c> is returned. - <c><anno>Length</anno></c> is either the - expected <em>total size</em> of the packet, or <c>undefined</c> - if the expected packet size is unknown. <c>decode_packet</c> - can then be called again with more data added.</p> + <c>{more,<anno>Length</anno>}</c> is returned. + <c><anno>Length</anno></c> is either the + expected <em>total size</em> of the packet, or <c>undefined</c> + if the expected packet size is unknown. <c>decode_packet</c> + can then be called again with more data added.</p> <p>If the packet does not conform to the protocol format, - <c>{error,<anno>Reason</anno>}</c> is returned.</p> - <p>The following <c>Type</c>s are valid:</p> + <c>{error,<anno>Reason</anno>}</c> is returned.</p> + <p><c>Type</c>s:</p> <taglist> <tag><c>raw | 0</c></tag> <item> <p>No packet handling is done. The entire binary is - returned unless it is empty.</p> + returned unless it is empty.</p> </item> <tag><c>1 | 2 | 4</c></tag> <item> @@ -890,10 +954,10 @@ </item> <tag><c>line</c></tag> <item> - <p>A packet is a line terminated by a delimiter byte, - default is the latin1 newline character. The delimiter - byte is included in the returned packet unless the line - was truncated according to option <c>line_length</c>.</p> + <p>A packet is a line-terminated by a delimiter byte, + default is the latin-1 newline character. The delimiter + byte is included in the returned packet unless the line + was truncated according to option <c>line_length</c>.</p> </item> <tag><c>asn1 | cdr | sunrm | fcgi | tpkt</c></tag> <item> @@ -910,50 +974,51 @@ <tag><c>http | httph | http_bin | httph_bin</c></tag> <item> <p>The Hypertext Transfer Protocol. The packets - are returned with the format according to - <c><anno>HttpPacket</anno></c> described earlier. - A packet is either a - request, a response, a header, or an end of header - mark. Invalid lines are returned as - <c><anno>HttpError</anno></c>.</p> + are returned with the format according to + <c><anno>HttpPacket</anno></c> described earlier. + A packet is either a + request, a response, a header, or an end of header + mark. Invalid lines are returned as + <c><anno>HttpError</anno></c>.</p> <p>Recognized request methods and header fields are returned - as atoms. Others are returned as strings. Strings of - unrecognized header fields are formatted with only - capital letters first and after hyphen characters, for - example, <c>"Sec-Websocket-Key"</c>.</p> + as atoms. Others are returned as strings. Strings of + unrecognized header fields are formatted with only + capital letters first and after hyphen characters, for + example, <c>"Sec-Websocket-Key"</c>.</p> <p>The protocol type <c>http</c> is only to be used for - the first line when an <c><anno>HttpRequest</anno></c> or an - <c><anno>HttpResponse</anno></c> is expected. - The following calls are to use <c>httph</c> to get - <c><anno>HttpHeader</anno></c>s until - <c>http_eoh</c> is returned, which marks the end of the - headers and the beginning of any following message body.</p> + the first line when an <c><anno>HttpRequest</anno></c> or an + <c><anno>HttpResponse</anno></c> is expected. + The following calls are to use <c>httph</c> to get + <c><anno>HttpHeader</anno></c>s until + <c>http_eoh</c> is returned, which marks the end of the + headers and the beginning of any following message body.</p> <p>The variants <c>http_bin</c> and <c>httph_bin</c> return - strings (<c>HttpString</c>) as binaries instead of lists.</p> + strings (<c>HttpString</c>) as binaries instead of lists.</p> </item> </taglist> - <p>The following options are available:</p> - <taglist> - <tag><c>{packet_size, integer() >= 0}</c></tag> - <item><p>Sets the maximum allowed size of the packet body. - If the packet header indicates that the length of the - packet is longer than the maximum allowed length, the - packet is considered invalid. Default is 0, which means - no size limit.</p> - </item> - <tag><c>{line_length, integer() >= 0}</c></tag> - <item><p>For packet type <c>line</c>, lines longer than + <p>Options:</p> + <taglist> + <tag><c>{packet_size, integer() >= 0}</c></tag> + <item><p>Sets the maximum allowed size of the packet body. + If the packet header indicates that the length of the + packet is longer than the maximum allowed length, the + packet is considered invalid. Defaults to 0, which means + no size limit.</p> + </item> + <tag><c>{line_length, integer() >= 0}</c></tag> + <item> + <p>For packet type <c>line</c>, lines longer than the indicated length are truncated.</p> - <p>Option <c>line_length</c> also applies to <c>http*</c> - packet types as an alias for option <c>packet_size</c> - if <c>packet_size</c> itself is not set. This use is - only intended for backward compatibility.</p> - </item> - <tag><c>{line_delimiter, 0 =< byte() =< 255}</c></tag> - <item><p>For packet type <c>line</c>, sets the delimiting byte. - Default is the latin1 character <c>$\n</c>.</p> - </item> - </taglist> + <p>Option <c>line_length</c> also applies to <c>http*</c> + packet types as an alias for option <c>packet_size</c> + if <c>packet_size</c> itself is not set. This use is + only intended for backward compatibility.</p> + </item> + <tag><c>{line_delimiter, 0 =< byte() =< 255}</c></tag> + <item><p>For packet type <c>line</c>, sets the delimiting byte. + Default is the latin-1 character <c>$\n</c>.</p> + </item> + </taglist> <p>Examples:</p> <pre> > <input>erlang:decode_packet(1,<<3,"abcd">>,[]).</input> @@ -965,7 +1030,7 @@ <func> <name name="delete_element" arity="2"/> - <fsummary>Deletes element at index in a tuple.</fsummary> + <fsummary>Delete element at index in a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc> <desc> <p>Returns a new tuple with element at <c><anno>Index</anno></c> @@ -978,16 +1043,16 @@ <func> <name name="delete_module" arity="1"/> - <fsummary>Makes the current code for a module old.</fsummary> + <fsummary>Make the current code for a module old.</fsummary> <desc> - <p>Makes the current code for <c><anno>Module</anno></c> become old code, - and deletes all references for this module from the export table. + <p>Makes the current code for <c><anno>Module</anno></c> become old + code and deletes all references for this module from the export table. Returns <c>undefined</c> if the module does not exist, otherwise <c>true</c>.</p> <warning> <p>This BIF is intended for the code server (see - <seealso marker="kernel:code">code(3)</seealso>) and is not - to be used elsewhere.</p> + <seealso marker="kernel:code"><c>code(3)</c></seealso>) + and is not to be used elsewhere.</p> </warning> <p>Failure: <c>badarg</c> if there already is an old version of <c>Module</c>.</p> @@ -996,54 +1061,56 @@ <func> <name name="demonitor" arity="1"/> - <fsummary>Stops monitoring.</fsummary> + <fsummary>Stop monitoring.</fsummary> <desc> <p>If <c><anno>MonitorRef</anno></c> is a reference that the calling process obtained by calling - <seealso marker="#monitor/2">monitor/2</seealso>, + <seealso marker="#monitor/2"><c>monitor/2</c></seealso>, this monitoring is turned off. If the monitoring is already turned off, nothing happens.</p> <p>Once <c>demonitor(<anno>MonitorRef</anno>)</c> has returned, it is guaranteed that no <c>{'DOWN', <anno>MonitorRef</anno>, _, _, _}</c> message, because of the monitor, will be placed in the caller message queue - in the future. A <c>{'DOWN', + in the future. However, a <c>{'DOWN', <anno>MonitorRef</anno>, _, _, _}</c> message can have been placed in the caller message queue before - the call, though. It is therefore usually advisable + the call. It is therefore usually advisable to remove such a <c>'DOWN'</c> message from the message queue after monitoring has been stopped. - <seealso marker="#demonitor/2"><c>demonitor(<anno>MonitorRef</anno>, [flush])</c></seealso> - can be used instead of - <c>demonitor(<anno>MonitorRef</anno>)</c> if this cleanup is wanted.</p> + <seealso marker="#demonitor/2"> + <c>demonitor(<anno>MonitorRef</anno>, [flush])</c></seealso> + can be used instead of <c>demonitor(<anno>MonitorRef</anno>)</c> + if this cleanup is wanted.</p> <note> - <p>Prior to OTP release R11B (ERTS version 5.5) <c>demonitor/1</c> - behaved completely asynchronously, i.e., the monitor was active + <p>Before Erlang/OTP R11B (ERTS 5.5) <c>demonitor/1</c> + behaved completely asynchronously, that is, the monitor was active until the "demonitor signal" reached the monitored entity. This had one undesirable effect. You could never know when you were guaranteed <em>not</em> to receive a <c>DOWN</c> message - due to the monitor.</p> - <p>Current behavior can be viewed as two combined operations: + because of the monitor.</p> + <p>The current behavior can be viewed as two combined operations: asynchronously send a "demonitor signal" to the monitored entity - and ignore any future results of the monitor. </p> + and ignore any future results of the monitor.</p> </note> <p>Failure: It is an error if <c><anno>MonitorRef</anno></c> refers to a monitoring started by another process. Not all such cases are cheap to check. If checking is cheap, the call fails with - <c>badarg</c> for example, if <c><anno>MonitorRef</anno></c> is a + <c>badarg</c>, for example if <c><anno>MonitorRef</anno></c> is a remote reference.</p> </desc> </func> <func> <name name="demonitor" arity="2"/> - <fsummary>Stops monitoring.</fsummary> + <fsummary>Stop monitoring.</fsummary> <desc> <p>The returned value is <c>true</c> unless <c>info</c> is part of <c><anno>OptionList</anno></c>.</p> <p><c>demonitor(<anno>MonitorRef</anno>, [])</c> is equivalent to - <seealso marker="#demonitor/1"><c>demonitor(<anno>MonitorRef</anno>)</c></seealso>.</p> - <p>The available <c><anno>Option</anno></c>s are as follows:</p> + <seealso marker="#demonitor/1"> + <c>demonitor(<anno>MonitorRef</anno>)</c></seealso>.</p> + <p><c><anno>Option</anno></c>s:</p> <taglist> <tag><c>flush</c></tag> <item> @@ -1054,28 +1121,28 @@ <p>Calling <c>demonitor(<anno>MonitorRef</anno>, [flush])</c> is equivalent to the following, but more efficient:</p> <code type="none"> - demonitor(MonitorRef), - receive - {_, MonitorRef, _, _, _} -> - true - after 0 -> - true - end</code> +demonitor(MonitorRef), +receive + {_, MonitorRef, _, _, _} -> + true +after 0 -> + true +end</code> </item> <tag><c>info</c></tag> <item> <p>The returned value is one of the following:</p> <taglist> <tag><c>true</c></tag> - <item>The monitor was found and removed. In this case, + <item><p>The monitor was found and removed. In this case, no <c>'DOWN'</c> message corresponding to this - monitor has been delivered and will not be delivered. + monitor has been delivered and will not be delivered.</p> </item> <tag><c>false</c></tag> - <item>The monitor was not found and could not be removed. + <item><p>The monitor was not found and could not be removed. This probably because someone already has placed a <c>'DOWN'</c> message corresponding to this monitor - in the caller message queue. + in the caller message queue.</p> </item> </taglist> <p>If option <c>info</c> is combined with option <c>flush</c>, @@ -1090,21 +1157,21 @@ <taglist> <tag><c>badarg</c></tag> <item>If <c><anno>OptionList</anno></c> is not a list. - </item> + </item> <tag><c>badarg</c></tag> <item>If <c><anno>Option</anno></c> is an invalid option. - </item> + </item> <tag><c>badarg</c></tag> <item>The same failure as for - <seealso marker="#demonitor/1">demonitor/1</seealso>. - </item> + <seealso marker="#demonitor/1"><c>demonitor/1</c></seealso>. + </item> </taglist> </desc> </func> <func> <name name="disconnect_node" arity="1"/> - <fsummary>Forces the disconnection of a node.</fsummary> + <fsummary>Force the disconnection of a node.</fsummary> <desc> <p>Forces the disconnection of a node. This appears to the node <c><anno>Node</anno></c> as if the local node has crashed. @@ -1118,7 +1185,7 @@ <func> <name name="display" arity="1"/> - <fsummary>Prints a term on standard output.</fsummary> + <fsummary>Print a term on standard output.</fsummary> <desc> <p>Prints a text representation of <c><anno>Term</anno></c> on the standard output.</p> @@ -1130,7 +1197,7 @@ <func> <name name="element" arity="2"/> - <fsummary>Returns the Nth element of a tuple.</fsummary> + <fsummary>Return the Nth element of a tuple.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> <p>Returns the <c><anno>N</anno></c>th element (numbering from 1) of @@ -1144,7 +1211,7 @@ b</pre> <func> <name name="erase" arity="0"/> - <fsummary>Returns and deletes the process dictionary.</fsummary> + <fsummary>Return and delete the process dictionary.</fsummary> <desc> <p>Returns the process dictionary and deletes it, for example:</p> @@ -1158,13 +1225,13 @@ b</pre> <func> <name name="erase" arity="1"/> - <fsummary>Returns and deletes a value from the process dictionary.</fsummary> + <fsummary>Return and delete a value from the process dictionary. + </fsummary> <desc> <p>Returns the value <c><anno>Val</anno></c> associated with <c><anno>Key</anno></c> and deletes it from the process dictionary. Returns <c>undefined</c> if no value is associated with - <c><anno>Key</anno></c>.</p> - <p>Example:</p> + <c><anno>Key</anno></c>. Example:</p> <pre> > <input>put(key1, {merry, lambs, are, playing}),</input> <input>X = erase(key1),</input> @@ -1175,16 +1242,15 @@ b</pre> <func> <name name="error" arity="1"/> - <fsummary>Stops execution with a given reason.</fsummary> + <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with the reason <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. The exit reason is <c>{<anno>Reason</anno>, Where}</c>, where <c>Where</c> is a list of the functions most recently called (the current - function first). Since evaluating this function causes - the process to terminate, it has no return value.</p> - <p>Example:</p> + function first). As evaluating this function causes + the process to terminate, it has no return value. Example:</p> <pre> > <input>catch error(foobar).</input> {'EXIT',{foobar,[{erl_eval,do_apply,5}, @@ -1197,7 +1263,7 @@ b</pre> <func> <name name="error" arity="2"/> - <fsummary>Stops execution with a given reason.</fsummary> + <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with the reason <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> @@ -1207,21 +1273,20 @@ b</pre> function first). <c><anno>Args</anno></c> is expected to be the list of arguments for the current function; in Beam it is used to provide the arguments for the current function in - the term <c>Where</c>. Since evaluating this function causes + the term <c>Where</c>. As evaluating this function causes the process to terminate, it has no return value.</p> </desc> </func> <func> <name name="exit" arity="1"/> - <fsummary>Stops execution with a given reason.</fsummary> + <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with exit reason <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> - is any term. Since + is any term. As evaluating this function causes the process to terminate, it - has no return value.</p> - <p>Example:</p> + has no return value. Example:</p> <pre> > <input>exit(foobar).</input> ** exception exit: foobar @@ -1232,43 +1297,44 @@ b</pre> <func> <name name="exit" arity="2"/> - <fsummary>Sends an exit signal to a process or a port.</fsummary> + <fsummary>Send an exit signal to a process or a port.</fsummary> <desc> <p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to the process or port identified by <c><anno>Pid</anno></c>.</p> <p>The following behavior applies if <c><anno>Reason</anno></c> is any term, except <c>normal</c> or <c>kill</c>:</p> - <list type="bulleted"> - <item>If <c><anno>Pid</anno></c> is not trapping exits, - <c><anno>Pid</anno></c> - itself exits with exit reason <c><anno>Reason</anno></c>. - </item> - <item>If <c><anno>Pid</anno></c> is trapping exits, the exit - signal is transformed into a message - <c>{'EXIT', From, <anno>Reason</anno>}</c> - and delivered to the message queue of <c><anno>Pid</anno></c>. - </item> - <item><c>From</c> is the process identifier of the process - that sent the exit signal. See also - <seealso marker="#process_flag/2">process_flag/2</seealso>. - </item> - </list> - <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>, + <list type="bulleted"> + <item><p>If <c><anno>Pid</anno></c> is not trapping exits, <c><anno>Pid</anno></c> - does not exit. If it is trapping exits, the exit signal is - transformed into a message <c>{'EXIT', From, normal}</c> - and delivered to its message queue.</p> - <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>, - that is, if <c>exit(<anno>Pid</anno>, kill)</c> is called, - an untrappable exit signal is sent to <c><anno>Pid</anno></c>, - which unconditionally exits with exit reason <c>killed</c>. - </p> + itself exits with exit reason <c><anno>Reason</anno></c>.</p> + </item> + <item><p>If <c><anno>Pid</anno></c> is trapping exits, the exit + signal is transformed into a message + <c>{'EXIT', From, <anno>Reason</anno>}</c> + and delivered to the message queue of <c><anno>Pid</anno></c>.</p> + </item> + <item><p><c>From</c> is the process identifier of the process + that sent the exit signal. See also + <seealso marker="#process_flag/2"> + <c>process_flag/2</c></seealso>.</p> + </item> + </list> + <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>, + <c><anno>Pid</anno></c> + does not exit. If it is trapping exits, the exit signal is + transformed into a message <c>{'EXIT', From, normal}</c> + and delivered to its message queue.</p> + <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>, + that is, if <c>exit(<anno>Pid</anno>, kill)</c> is called, + an untrappable exit signal is sent to <c><anno>Pid</anno></c>, + which unconditionally exits with exit reason <c>killed</c>.</p> </desc> </func> <func> <name name="external_size" arity="1"/> - <fsummary>Calculates the maximum size for a term encoded in the Erlang external term format.</fsummary> + <fsummary>Calculate the maximum size for a term encoded in the Erlang + external term format.</fsummary> <desc> <p>Calculates, without doing the encoding, the maximum byte size for a term encoded in the Erlang external term format. The following @@ -1279,13 +1345,15 @@ b</pre> > <input>true = Size1 =< Size2.</input> true</pre> <p>This is equivalent to a call to:</p> -<code>erlang:external_size(<anno>Term</anno>, [])</code> +<code> +erlang:external_size(<anno>Term</anno>, [])</code> </desc> </func> <func> <name name="external_size" arity="2"/> - <fsummary>Calculates the maximum size for a term encoded in the Erlang external term format.</fsummary> + <fsummary>Calculate the maximum size for a term encoded in the Erlang + external term format.</fsummary> <desc> <p>Calculates, without doing the encoding, the maximum byte size for a term encoded in the Erlang external term format. The following @@ -1297,13 +1365,14 @@ true</pre> true</pre> <p>Option <c>{minor_version, <anno>Version</anno>}</c> specifies how floats are encoded. For a detailed description, see - <seealso marker="#term_to_binary/2">term_to_binary/2</seealso>.</p> + <seealso marker="#term_to_binary/2"> + <c>term_to_binary/2</c></seealso>.</p> </desc> </func> <func> <name name="float" arity="1"/> - <fsummary>Converts a number to a float.</fsummary> + <fsummary>Convert a number to a float.</fsummary> <desc> <p>Returns a float by converting <c><anno>Number</anno></c> to a float, for example:</p> @@ -1314,7 +1383,8 @@ true</pre> <note> <p>If used on the top level in a guard, it tests whether the argument is a floating point number; for clarity, use - <seealso marker="#is_float/1">is_float/1</seealso> instead.</p> + <seealso marker="#is_float/1"><c>is_float/1</c></seealso> + instead.</p> <p>When <c>float/1</c> is used in an expression in a guard, such as '<c>float(A) == 4.0</c>', it converts a number as described earlier.</p> @@ -1333,13 +1403,14 @@ true</pre> <func> <name name="float_to_binary" arity="2"/> - <fsummary>Text representation of a float formatted using given options.</fsummary> + <fsummary>Text representation of a float formatted using specified + options.</fsummary> <desc> <p>Returns a binary corresponding to the text representation of <c><anno>Float</anno></c> using fixed decimal point formatting. <c><anno>Options</anno></c> behaves in the same - way as <seealso marker="#float_to_list/2">float_to_list/2</seealso>.</p> - <p>Examples:</p> + way as <seealso marker="#float_to_list/2"> + <c>float_to_list/2</c></seealso>. Examples:</p> <pre> > <input>float_to_binary(7.12, [{decimals, 4}]).</input> <<"7.1200">> @@ -1359,27 +1430,29 @@ true</pre> <func> <name name="float_to_list" arity="2"/> - <fsummary>Text representation of a float formatted using given options.</fsummary> + <fsummary>Text representation of a float formatted using specified + options.</fsummary> <desc> <p>Returns a string corresponding to the text representation - of <c>Float</c> using fixed decimal point formatting. The - options are as follows:</p> + of <c>Float</c> using fixed decimal point formatting.</p> + <p>Available options:</p> <list type="bulleted"> - <item>If option <c>decimals</c> is specified, the returned value + <item><p>If option <c>decimals</c> is specified, the returned value contains at most <c>Decimals</c> number of digits past the - decimal point. If the number does not fit in the internal - static buffer of 256 bytes, the function throws <c>badarg</c>. + decimal point. If the number does not fit in the internal + static buffer of 256 bytes, the function throws <c>badarg</c>.</p> </item> - <item>If option <c>compact</c> is provided, the trailing zeros + <item><p>If option <c>compact</c> is specified, the trailing zeros at the end of the list are truncated. This option is only - meaningful together with option <c>decimals</c>. + meaningful together with option <c>decimals</c>.</p> </item> - <item>If option <c>scientific</c> is provided, the float is + <item><p>If option <c>scientific</c> is specified, the float is formatted using scientific notation with <c>Decimals</c> - digits of precision. + digits of precision.</p> </item> - <item>If <c>Options</c> is <c>[]</c>, the function behaves as - <seealso marker="#float_to_list/1">float_to_list/1</seealso>. + <item><p>If <c>Options</c> is <c>[]</c>, the function behaves as + <seealso marker="#float_to_list/1"> + <c>float_to_list/1</c></seealso>.</p> </item> </list> <p>Examples:</p> @@ -1406,16 +1479,16 @@ true</pre> </warning> <p>Two types of funs have slightly different semantics:</p> <list type="bulleted"> - <item>A fun created by <c>fun M:F/A</c> is called an + <item><p>A fun created by <c>fun M:F/A</c> is called an <em>external</em> fun. Calling it will always call the function <c>F</c> with arity <c>A</c> in the latest code for module <c>M</c>. Notice that module <c>M</c> does not even - need to be loaded when the fun <c>fun M:F/A</c> is created. + need to be loaded when the fun <c>fun M:F/A</c> is created.</p> </item> - <item>All other funs are called <em>local</em>. When a local fun + <item><p>All other funs are called <em>local</em>. When a local fun is called, the same version of the code that created the fun is called (even if a newer version of the module has been - loaded). + loaded).</p> </item> </list> <p>The following elements are always present in the list @@ -1479,14 +1552,14 @@ true</pre> <tag><c>{new_uniq, Uniq}</c></tag> <item> <p><c>Uniq</c> (a binary) is a unique value for this fun. It - is calculated from the compiled code for the entire module.</p> + is calculated from the compiled code for the entire module.</p> </item> <tag><c>{uniq, Uniq}</c></tag> <item> <p><c>Uniq</c> (an integer) is a unique value for this fun. - As from OTP R15, this integer is calculated from the - compiled code for the entire module. Before OTP R15, this - integer was based on only the body of the fun.</p> + As from Erlang/OTP R15, this integer is calculated from the + compiled code for the entire module. Before Erlang/OTP R15, this + integer was based on only the body of the fun.</p> </item> </taglist> </desc> @@ -1508,7 +1581,7 @@ true</pre> <c>uniq</c>, and <c>pid</c>. For an external fun, the value of any of these items is always the atom <c>undefined</c>.</p> <p>See - <seealso marker="#fun_info/1">erlang:fun_info/1</seealso>.</p> + <seealso marker="#fun_info/1"><c>erlang:fun_info/1</c></seealso>.</p> </desc> </func> @@ -1523,20 +1596,24 @@ true</pre> <func> <name name="function_exported" arity="3"/> - <fsummary>Checks if a function is exported and loaded.</fsummary> + <fsummary>Check if a function is exported and loaded.</fsummary> <desc> - <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded - and contains an exported function <c><anno>Function</anno>/<anno>Arity</anno></c>, - or if there is a BIF (a built-in function implemented in C) - with the given name, otherwise returns <c>false</c>.</p> - <note><p>This function used to return false for built-in - functions before the 18.0 release.</p></note> + <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is + loaded and contains an exported function + <c><anno>Function</anno>/<anno>Arity</anno></c>, + or if there is a BIF (a built-in function implemented in C) + with the specified name, otherwise returns <c>false</c>.</p> + <note> + <p>This function used to return <c>false</c> for BIFs + before Erlang/OTP 18.0.</p> + </note> </desc> </func> <func> <name name="garbage_collect" arity="0"/> - <fsummary>Forces an immediate garbage collection of the calling process.</fsummary> + <fsummary>Force an immediate garbage collection of the calling process. + </fsummary> <desc> <p>Forces an immediate garbage collection of the executing process. The function is not to be used unless @@ -1551,73 +1628,76 @@ true</pre> <func> <name name="garbage_collect" arity="1"/> - <fsummary>Garbage collects a process.</fsummary> + <fsummary>Garbage collect a process.</fsummary> <desc> <p>The same as - <seealso marker="#garbage_collect/2"><c>garbage_collect(<anno>Pid</anno>, [])</c></seealso>.</p> + <seealso marker="#garbage_collect/2"> + <c>garbage_collect(<anno>Pid</anno>, [])</c></seealso>.</p> </desc> </func> <func> <name name="garbage_collect" arity="2"/> - <fsummary>Garbage collects a process.</fsummary> + <fsummary>Garbage collect a process.</fsummary> <desc> <p>Garbage collects the node local process identified by - <c><anno>Pid</anno></c>.</p> - <p>The available <c><anno>Option</anno></c>s are as follows:</p> + <c><anno>Pid</anno></c>.</p> + <p><c><anno>Option</anno></c>:</p> <taglist> <tag><c>{async, RequestId}</c></tag> <item>The function <c>garbage_collect/2</c> returns - the value <c>async</c> immediately after the request - has been sent. When the request has been processed, the - process that called this function is passed a message on + the value <c>async</c> immediately after the request + has been sent. When the request has been processed, the + process that called this function is passed a message on the form <c>{garbage_collect, <anno>RequestId</anno>, <anno>GCResult</anno>}</c>. </item> </taglist> - <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and - no <c>async</c> option has been passed, the garbage - collection is performed at once, that is, the same as calling - <seealso marker="#garbage_collect/0">garbage_collect/0</seealso>. - Otherwise a request for garbage collection - is sent to the process identified by <c><anno>Pid</anno></c>, - and will be handled when appropriate. If no <c>async</c> - option has been passed, the caller blocks until - <c><anno>GCResult</anno></c> is available and can be returned.</p> - <p><c><anno>GCResult</anno></c> informs about the result of - the garbage collection request as follows:</p> + <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and + no <c>async</c> option has been passed, the garbage + collection is performed at once, that is, the same as calling + <seealso marker="#garbage_collect/0"> + <c>garbage_collect/0</c></seealso>. + Otherwise a request for garbage collection + is sent to the process identified by <c><anno>Pid</anno></c>, + and will be handled when appropriate. If no <c>async</c> + option has been passed, the caller blocks until + <c><anno>GCResult</anno></c> is available and can be returned.</p> + <p><c><anno>GCResult</anno></c> informs about the result of + the garbage collection request as follows:</p> <taglist> <tag><c>true</c></tag> <item> - The process identified by <c><anno>Pid</anno></c> has - been garbage collected. - </item> + The process identified by <c><anno>Pid</anno></c> has + been garbage collected. + </item> <tag><c>false</c></tag> <item> - No garbage collection was performed, as - the process identified by <c><anno>Pid</anno></c> - terminated before the request could be satisfied. - </item> + No garbage collection was performed, as + the process identified by <c><anno>Pid</anno></c> + terminated before the request could be satisfied. + </item> </taglist> <p>Notice that the same caveats apply as for - <seealso marker="#garbage_collect/0">garbage_collect/0</seealso>.</p> + <seealso marker="#garbage_collect/0"> + <c>garbage_collect/0</c></seealso>.</p> <p>Failures:</p> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Pid</anno></c> is not a node local process identifier. - </item> + If <c><anno>Pid</anno></c> is not a node local process identifier. + </item> <tag><c>badarg</c></tag> <item> - If <c><anno>OptionList</anno></c> is an invalid list of options. - </item> + If <c><anno>OptionList</anno></c> is an invalid list of options. + </item> </taglist> </desc> </func> <func> <name name="get" arity="0"/> - <fsummary>Returns the process dictionary.</fsummary> + <fsummary>Return the process dictionary.</fsummary> <desc> <p>Returns the process dictionary as a list of <c>{<anno>Key</anno>, <anno>Val</anno>}</c> tuples, for example:</p> @@ -1632,12 +1712,11 @@ true</pre> <func> <name name="get" arity="1"/> - <fsummary>Returns a value from the process dictionary.</fsummary> + <fsummary>Return a value from the process dictionary.</fsummary> <desc> - <p>Returns the value <c><anno>Val</anno></c> associated with <c><anno>Key</anno></c> in - the process dictionary, or <c>undefined</c> if <c><anno>Key</anno></c> - does not exist.</p> - <p>Example:</p> + <p>Returns the value <c><anno>Val</anno></c> associated with + <c><anno>Key</anno></c> in the process dictionary, or <c>undefined</c> + if <c><anno>Key</anno></c> does not exist. Example:</p> <pre> > <input>put(key1, merry),</input> <input>put(key2, lambs),</input> @@ -1649,7 +1728,7 @@ true</pre> <func> <name name="get_cookie" arity="0"/> - <fsummary>Gets the magic cookie of the local node.</fsummary> + <fsummary>Get the magic cookie of the local node.</fsummary> <desc> <p>Returns the magic cookie of the local node if the node is alive, otherwise the atom <c>nocookie</c>.</p> @@ -1658,9 +1737,11 @@ true</pre> <func> <name name="get_keys" arity="0"/> - <fsummary>Return a list of all keys from the process dictionary</fsummary> + <fsummary>Return a list of all keys from the process dictionary. + </fsummary> <desc> - <p>Returns a list of keys all keys present in the process dictionary.</p> + <p>Returns a list of all keys present in the process dictionary, + for example:</p> <pre> > <input>put(dog, {animal,1}),</input> <input>put(cow, {animal,2}),</input> @@ -1669,9 +1750,10 @@ true</pre> [dog,cow,lamb]</pre> </desc> </func> + <func> <name name="get_keys" arity="1"/> - <fsummary>Returns a list of keys from the process dictionary.</fsummary> + <fsummary>Return a list of keys from the process dictionary.</fsummary> <desc> <p>Returns a list of keys that are associated with the value <c><anno>Val</anno></c> in the process dictionary, for example:</p> @@ -1689,48 +1771,49 @@ true</pre> <func> <name name="get_stacktrace" arity="0"/> - <fsummary>Gets the call stack back-trace of the last exception.</fsummary> + <fsummary>Get the call stack back-trace of the last exception.</fsummary> <type name="stack_item"/> <desc> <p>Gets the call stack back-trace (<em>stacktrace</em>) of the last exception in the calling process as a list of - <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c> tuples. - Field <c><anno>Arity</anno></c> in the first tuple can be the + <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c> + tuples. Field <c><anno>Arity</anno></c> in the first tuple can be the argument list of that function call instead of an arity integer, depending on the exception.</p> <p>If there has not been any exceptions in a process, the stacktrace is <c>[]</c>. After a code change for the process, the stacktrace can also be reset to <c>[]</c>.</p> - <p>The stacktrace is the same data as the <c>catch</c> operator + <p>The stacktrace is the same data as operator <c>catch</c> returns, for example:</p> - <p><c>{'EXIT',{badarg,Stacktrace}} = catch abs(x)</c></p> - <p><c><anno>Location</anno></c> is a (possibly empty) list + <pre> +{'EXIT',{badarg,Stacktrace}} = catch abs(x)</pre> + <p><c><anno>Location</anno></c> is a (possibly empty) list of two-tuples that - can indicate the location in the source code of the function. - The first element is an atom describing the type of - information in the second element. The following - items can occur:</p> - <taglist> - <tag><c>file</c></tag> - <item>The second element of the tuple is a string (list of - characters) representing the file name of the source file - of the function. - </item> - <tag><c>line</c></tag> - <item>The second element of the tuple is the line number - (an integer greater than zero) in the source file - where the exception occurred or the function was called. - </item> - </taglist> + can indicate the location in the source code of the function. + The first element is an atom describing the type of + information in the second element. The following + items can occur:</p> + <taglist> + <tag><c>file</c></tag> + <item>The second element of the tuple is a string (list of + characters) representing the filename of the source file + of the function. + </item> + <tag><c>line</c></tag> + <item>The second element of the tuple is the line number + (an integer > 0) in the source file + where the exception occurred or the function was called. + </item> + </taglist> <p>See also - <seealso marker="#error/1">erlang:error/1</seealso> and - <seealso marker="#error/2">erlang:error/2</seealso>.</p> + <seealso marker="#error/1"><c>error/1</c></seealso> and + <seealso marker="#error/2"><c>error/2</c></seealso>.</p> </desc> </func> <func> <name name="group_leader" arity="0"/> - <fsummary>Gets the group leader for the calling process.</fsummary> + <fsummary>Get the group leader for the calling process.</fsummary> <desc> <p>Returns the process identifier of the group leader for the process evaluating the function.</p> @@ -1738,14 +1821,14 @@ true</pre> groups have a <em>group leader</em>. All I/O from the group is channeled to the group leader. When a new process is spawned, it gets the same group leader as the spawning - process. Initially, at system start-up, <c>init</c> is both + process. Initially, at system startup, <c>init</c> is both its own group leader and the group leader of all processes.</p> </desc> </func> <func> <name name="group_leader" arity="2"/> - <fsummary>Sets the group leader for a process.</fsummary> + <fsummary>Set the group leader for a process.</fsummary> <desc> <p>Sets the group leader of <c><anno>Pid</anno></c> to <c><anno>GroupLeader</anno></c>. @@ -1753,72 +1836,72 @@ true</pre> certain shell is to have another group leader than <c>init</c>.</p> <p>See also - <seealso marker="#group_leader/0">group_leader/0</seealso>.</p> + <seealso marker="#group_leader/0"><c>group_leader/0</c></seealso>.</p> </desc> </func> <func> <name name="halt" arity="0"/> - <fsummary>Halts the Erlang runtime system and indicates normal exit to the calling environment.</fsummary> + <fsummary>Halt the Erlang runtime system and indicate normal exit to + the calling environment.</fsummary> <desc> - <p>The same as - <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>.</p> - <p>Example:</p> + <p>The same as + <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>. Example:</p> <pre> > <input>halt().</input> -os_prompt% </pre> +os_prompt%</pre> </desc> </func> <func> <name name="halt" arity="1"/> - <fsummary>Halts the Erlang runtime system.</fsummary> + <fsummary>Halt the Erlang runtime system.</fsummary> <desc> - <p>The same as - <seealso marker="#halt/2"><c>halt(<anno>Status</anno>, [])</c></seealso>.</p> - <p>Example:</p> + <p>The same as <seealso marker="#halt/2"> + <c>halt(<anno>Status</anno>, [])</c></seealso>. Example:</p> <pre> > <input>halt(17).</input> os_prompt% <input>echo $?</input> 17 -os_prompt% </pre> +os_prompt%</pre> </desc> </func> <func> <name name="halt" arity="2"/> - <fsummary>Halts the Erlang runtime system.</fsummary> + <fsummary>Halt the Erlang runtime system.</fsummary> <desc> <p><c><anno>Status</anno></c> must be a non-negative integer, a string, - or the atom <c>abort</c>. - Halts the Erlang runtime system. Has no return value. - Depending on <c><anno>Status</anno></c>, the following occurs:</p> - <taglist> - <tag>integer()</tag> - <item>The runtime system exits with integer value - <c><anno>Status</anno></c> - as status code to the calling environment (OS). - </item> - <tag>string()</tag> - <item>An Erlang crash dump is produced with <c><anno>Status</anno></c> - as slogan. Then the runtime system exits with status code <c>1</c>. - Note that only code points in the range 0-255 may be used - and the string will be truncated if longer than 200 characters. - </item> - <tag><c>abort</c></tag> - <item> - The runtime system aborts producing a core dump, if that is - enabled in the OS. - </item> - </taglist> - <note><p>On many platforms, the OS supports only status - codes 0-255. A too large status code will be truncated by clearing - the high bits.</p></note> - <p>For integer <c><anno>Status</anno></c>, the Erlang runtime system + or the atom <c>abort</c>. + Halts the Erlang runtime system. Has no return value. + Depending on <c><anno>Status</anno></c>, the following occurs:</p> + <taglist> + <tag>integer()</tag> + <item>The runtime system exits with integer value + <c><anno>Status</anno></c> + as status code to the calling environment (OS). + </item> + <tag>string()</tag> + <item>An Erlang crash dump is produced with <c><anno>Status</anno></c> + as slogan. Then the runtime system exits with status code <c>1</c>. + Note that only code points in the range 0-255 may be used + and the string will be truncated if longer than 200 characters. + </item> + <tag><c>abort</c></tag> + <item>The runtime system aborts producing a core dump, if that is + enabled in the OS. + </item> + </taglist> + <note> + <p>On many platforms, the OS supports only status + codes 0-255. A too large status code is truncated by clearing + the high bits.</p> + </note> + <p>For integer <c><anno>Status</anno></c>, the Erlang runtime system closes all ports and allows async threads to finish their operations before exiting. To exit without such flushing, use - <c><anno>Option</anno></c> as <c>{flush,false}</c>.</p> - <p>For statuses <c>string()</c> and <c>abort</c>, option + <c><anno>Option</anno></c> as <c>{flush,false}</c>.</p> + <p>For statuses <c>string()</c> and <c>abort</c>, option <c>flush</c> is ignored and flushing is <em>not</em> done.</p> </desc> </func> @@ -1830,13 +1913,15 @@ os_prompt% </pre> <p>Returns a hash value for <c><anno>Term</anno></c> within the range <c>1..<anno>Range</anno></c>. The maximum range is 1..2^27-1.</p> <warning> - <p>This BIF is deprecated, as the hash value can differ on - different architectures. The hash values for integer - terms higher than 2^27 and large binaries are + <p><em>This BIF is deprecated, as the hash value can differ on + different architectures.</em> The hash values for integer + terms > 2^27 and large binaries are poor. The BIF is retained for backward compatibility reasons (it can have been used to hash records into a file), but all new code is to use one of the BIFs - <c>erlang:phash/2</c> or <c>erlang:phash2/1,2</c> instead.</p> + <seealso marker="#phash/2"><c>erlang:phash/2</c></seealso> or + <seealso marker="#phash2/1"><c>erlang:phash2/1,2</c></seealso> + instead.</p> </warning> </desc> </func> @@ -1858,7 +1943,7 @@ os_prompt% </pre> <func> <name name="hibernate" arity="3"/> - <fsummary>Hibernates a process until a message is sent to it.</fsummary> + <fsummary>Hibernate a process until a message is sent to it.</fsummary> <desc> <p>Puts the calling process into a wait state where its memory allocation has been reduced as much as possible. This is @@ -1866,15 +1951,15 @@ os_prompt% </pre> soon.</p> <p>The process is awaken when a message is sent to it, and control resumes in <c><anno>Module</anno>:<anno>Function</anno></c> with - the arguments given by <c><anno>Args</anno></c> with the call + the arguments specified by <c><anno>Args</anno></c> with the call stack emptied, meaning that the process terminates when that function returns. Thus <c>erlang:hibernate/3</c> never returns to its caller.</p> <p>If the process has any message in its message queue, the process is awakened immediately in the same way as described earlier.</p> - <p>In more technical terms, what <c>erlang:hibernate/3</c> does - is the following. It discards the call stack for the process, + <p>In more technical terms, <c>erlang:hibernate/3</c> + discards the call stack for the process, and then garbage collects the process. After this, all live data is in one continuous heap. The heap is then shrunken to the exact same size as the live data @@ -1886,11 +1971,12 @@ os_prompt% </pre> size is changed to a size not smaller than the minimum heap size.</p> <p>Notice that emptying the call stack means that any surrounding - <c>catch</c> is removed and must be reinserted after + <c>catch</c> is removed and must be re-inserted after hibernation. One effect of this is that processes started using <c>proc_lib</c> (also indirectly, such as <c>gen_server</c> processes), are to use - <seealso marker="stdlib:proc_lib#hibernate/3">proc_lib:hibernate/3</seealso> + <seealso marker="stdlib:proc_lib#hibernate/3"> + <c>proc_lib:hibernate/3</c></seealso> instead, to ensure that the exception handler continues to work when the process wakes up.</p> </desc> @@ -1898,15 +1984,16 @@ os_prompt% </pre> <func> <name name="insert_element" arity="3"/> - <fsummary>Inserts an element at index in a tuple.</fsummary> - <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + 1</type_desc> + <fsummary>Insert an element at index in a tuple.</fsummary> + <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + + 1</type_desc> <desc> <p>Returns a new tuple with element <c><anno>Term</anno></c> - inserted at position - <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>. - All elements from position <c><anno>Index</anno></c> and upwards are - pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>.</p> - <p>Example:</p> + inserted at position + <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>. + All elements from position <c><anno>Index</anno></c> and upwards are + pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>. + Example:</p> <pre> > <input>erlang:insert_element(2, {one, two, three}, new).</input> {one,new,two,three}</pre> @@ -1930,8 +2017,8 @@ os_prompt% </pre> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a binary corresponding to the text - representation of <c><anno>Integer</anno></c> in base - <c><anno>Base</anno></c>, for example:</p> + representation of <c><anno>Integer</anno></c> in base + <c><anno>Base</anno></c>, for example:</p> <pre> > <input>integer_to_binary(1023, 16).</input> <<"3FF">></pre> @@ -1964,8 +2051,21 @@ os_prompt% </pre> </func> <func> + <name name="iolist_size" arity="1"/> + <fsummary>Size of an iolist.</fsummary> + <desc> + <p>Returns an integer, that is the size in bytes, + of the binary that would be the result of + <c>iolist_to_binary(<anno>Item</anno>)</c>, for example:</p> + <pre> +> <input>iolist_size([1,2|<<3,4>>]).</input> +4</pre> + </desc> + </func> + + <func> <name name="iolist_to_binary" arity="1"/> - <fsummary>Converts an iolist to a binary.</fsummary> + <fsummary>Convert an iolist to a binary.</fsummary> <desc> <p>Returns a binary that is made from the integers and binaries in <c><anno>IoListOrBinary</anno></c>, for example:</p> @@ -1982,21 +2082,8 @@ os_prompt% </pre> </func> <func> - <name name="iolist_size" arity="1"/> - <fsummary>Size of an iolist.</fsummary> - <desc> - <p>Returns an integer that is the size in bytes - of the binary that would be the result of - <c>iolist_to_binary(<anno>Item</anno>)</c>, for example:</p> - <pre> -> <input>iolist_size([1,2|<<3,4>>]).</input> -4</pre> - </desc> - </func> - - <func> <name name="is_alive" arity="0"/> - <fsummary>Checks whether the local node is alive.</fsummary> + <fsummary>Check whether the local node is alive.</fsummary> <desc> <p>Returns <c>true</c> if the local node is alive (that is, if the node can be part of a distributed system), otherwise @@ -2006,7 +2093,7 @@ os_prompt% </pre> <func> <name name="is_atom" arity="1"/> - <fsummary>Checks whether a term is an atom.</fsummary> + <fsummary>Check whether a term is an atom.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom, otherwise <c>false</c>.</p> @@ -2016,18 +2103,18 @@ os_prompt% </pre> <func> <name name="is_binary" arity="1"/> - <fsummary>Checks whether a term is a binary.</fsummary> + <fsummary>Check whether a term is a binary.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary, otherwise <c>false</c>.</p> - <p>A binary always contains a complete number of bytes.</p> + <p>A binary always contains a complete number of bytes.</p> <p>Allowed in guard tests.</p> </desc> </func> <func> <name name="is_bitstring" arity="1"/> - <fsummary>Checks whether a term is a bitstring.</fsummary> + <fsummary>Check whether a term is a bitstring.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a bitstring (including a binary), otherwise <c>false</c>.</p> @@ -2037,7 +2124,7 @@ os_prompt% </pre> <func> <name name="is_boolean" arity="1"/> - <fsummary>Checks whether a term is a boolean.</fsummary> + <fsummary>Check whether a term is a boolean.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is the atom <c>true</c> or the atom <c>false</c> (that is, a boolean). @@ -2048,18 +2135,18 @@ os_prompt% </pre> <func> <name name="is_builtin" arity="3"/> - <fsummary>Checks if a function is a BIF implemented in C.</fsummary> + <fsummary>Check if a function is a BIF implemented in C.</fsummary> <desc> <p>This BIF is useful for builders of cross-reference tools.</p> <p>Returns <c>true</c> if <c><anno>Module</anno>:<anno>Function</anno>/<anno>Arity</anno></c> - is a BIF implemented in C, otherwise <c>false</c>.</p> + is a BIF implemented in C, otherwise <c>false</c>.</p> </desc> </func> <func> <name name="is_float" arity="1"/> - <fsummary>Checks whether a term is a float.</fsummary> + <fsummary>Check whether a term is a float.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point number, otherwise <c>false</c>.</p> @@ -2069,7 +2156,7 @@ os_prompt% </pre> <func> <name name="is_function" arity="1"/> - <fsummary>Checks whether a term is a fun.</fsummary> + <fsummary>Check whether a term is a fun.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun, otherwise <c>false</c>.</p> @@ -2079,7 +2166,8 @@ os_prompt% </pre> <func> <name name="is_function" arity="2"/> - <fsummary>Checks whether a term is a fun with a given arity.</fsummary> + <fsummary>Check whether a term is a fun with a specified given arity. + </fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun that can be applied with <c><anno>Arity</anno></c> number of arguments, otherwise @@ -2090,7 +2178,7 @@ os_prompt% </pre> <func> <name name="is_integer" arity="1"/> - <fsummary>Checks whether a term is an integer.</fsummary> + <fsummary>Check whether a term is an integer.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer, otherwise <c>false</c>.</p> @@ -2100,7 +2188,7 @@ os_prompt% </pre> <func> <name name="is_list" arity="1"/> - <fsummary>Checks whether a term is a list.</fsummary> + <fsummary>Check whether a term is a list.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with zero or more elements, otherwise <c>false</c>.</p> @@ -2110,7 +2198,7 @@ os_prompt% </pre> <func> <name name="is_map" arity="1"/> - <fsummary>Checks whether a term is a map.</fsummary> + <fsummary>Check whether a term is a map.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a map, otherwise <c>false</c>.</p> @@ -2120,7 +2208,7 @@ os_prompt% </pre> <func> <name name="is_number" arity="1"/> - <fsummary>Checks whether a term is a number.</fsummary> + <fsummary>Check whether a term is a number.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer or a floating point number. Otherwise returns <c>false</c>.</p> @@ -2130,7 +2218,7 @@ os_prompt% </pre> <func> <name name="is_pid" arity="1"/> - <fsummary>Checks whether a term is a process identifier.</fsummary> + <fsummary>Check whether a term is a process identifier.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a process identifier, otherwise <c>false</c>.</p> @@ -2140,7 +2228,7 @@ os_prompt% </pre> <func> <name name="is_port" arity="1"/> - <fsummary>Checks whether a term is a port.</fsummary> + <fsummary>Check whether a term is a port.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier, otherwise <c>false</c>.</p> @@ -2150,26 +2238,26 @@ os_prompt% </pre> <func> <name name="is_process_alive" arity="1"/> - <fsummary>Checks whether a process is alive.</fsummary> + <fsummary>Check whether a process is alive.</fsummary> <desc> - <p><c><anno>Pid</anno></c> must refer to a process at the local node.</p> + <p><c><anno>Pid</anno></c> must refer to a process at the local + node.</p> <p>Returns <c>true</c> if the process exists and is alive, that is, is not exiting and has not exited. Otherwise returns - <c>false</c>. - </p> + <c>false</c>.</p> </desc> </func> <func> <name name="is_record" arity="2"/> - <fsummary>Checks whether a term appears to be a record.</fsummary> + <fsummary>Check whether a term appears to be a record.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple and its first element is <c><anno>RecordTag</anno></c>. Otherwise returns <c>false</c>.</p> <note> <p>Normally the compiler treats calls to <c>is_record/2</c> - specially. It emits code to verify that <c><anno>Term</anno></c> + especially. It emits code to verify that <c><anno>Term</anno></c> is a tuple, that its first element is <c><anno>RecordTag</anno></c>, and that the size is correct. However, if <c><anno>RecordTag</anno></c> is @@ -2183,7 +2271,7 @@ os_prompt% </pre> <func> <name name="is_record" arity="3"/> - <fsummary>Checks whether a term appears to be a record.</fsummary> + <fsummary>Check whether a term appears to be a record.</fsummary> <desc> <p><c><anno>RecordTag</anno></c> must be an atom.</p> <p>Returns <c>true</c> if @@ -2202,7 +2290,7 @@ os_prompt% </pre> <func> <name name="is_reference" arity="1"/> - <fsummary>Checks whether a term is a reference.</fsummary> + <fsummary>Check whether a term is a reference.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference, otherwise <c>false</c>.</p> @@ -2212,7 +2300,7 @@ os_prompt% </pre> <func> <name name="is_tuple" arity="1"/> - <fsummary>Checks whether a term is a tuple.</fsummary> + <fsummary>Check whether a term is a tuple.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple, otherwise <c>false</c>.</p> @@ -2234,7 +2322,7 @@ os_prompt% </pre> <func> <name name="link" arity="1"/> - <fsummary>Creates a link to another process (or port).</fsummary> + <fsummary>Create a link to another process (or port).</fsummary> <desc> <p>Creates a link between the calling process and another process (or port) <c><anno>PidOrPort</anno></c>, if there is @@ -2244,34 +2332,34 @@ os_prompt% </pre> <p>If <c><anno>PidOrPort</anno></c> does not exist, the behavior of the BIF depends on if the calling process is trapping exits or not (see - <seealso marker="#process_flag/2">process_flag/2</seealso>):</p> + <seealso marker="#process_flag/2"> + <c>process_flag/2</c></seealso>):</p> <list type="bulleted"> - <item>If the calling process is not trapping exits, and - checking <c><anno>PidOrPort</anno></c> is cheap - (that is, if <c><anno>PidOrPort</anno></c> - is local), <c>link/1</c> fails with reason <c>noproc</c>.</item> - <item>Otherwise, if the calling process is trapping exits, - and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c> - returns <c>true</c>, but an exit signal with reason <c>noproc</c> - is sent to the calling process.</item> + <item><p>If the calling process is not trapping exits, and + checking <c><anno>PidOrPort</anno></c> is cheap + (that is, if <c><anno>PidOrPort</anno></c> + is local), <c>link/1</c> fails with reason <c>noproc</c>.</p></item> + <item><p>Otherwise, if the calling process is trapping exits, + and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c> + returns <c>true</c>, but an exit signal with reason <c>noproc</c> + is sent to the calling process.</p></item> </list> </desc> </func> <func> <name name="list_to_atom" arity="1"/> - <fsummary>Converts from text representation to an atom.</fsummary> + <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is <c><anno>String</anno></c>.</p> - <p><c><anno>String</anno></c> can only contain ISO-latin-1 - characters (that is, - numbers less than 256) as the implementation does not - allow unicode characters equal to or above 256 in atoms. - For more information on Unicode support in atoms, see - <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8 + <p><c><anno>String</anno></c> can only contain ISO-latin-1 + characters (that is, numbers < 256) as the implementation does not + allow Unicode characters equal to or above 256 in atoms. + For more information on Unicode support in atoms, see + <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8 encoded atoms</seealso> - in Section "External Term Format" in the User's Guide.</p> + in section "External Term Format" in the User's Guide.</p> <p>Example:</p> <pre> > <input>list_to_atom("Erlang").</input> @@ -2281,7 +2369,7 @@ os_prompt% </pre> <func> <name name="list_to_binary" arity="1"/> - <fsummary>Converts a list to a binary.</fsummary> + <fsummary>Convert a list to a binary.</fsummary> <desc> <p>Returns a binary that is made from the integers and binaries in <c><anno>IoList</anno></c>, for example:</p> @@ -2299,13 +2387,13 @@ os_prompt% </pre> <func> <name name="list_to_bitstring" arity="1"/> - <fsummary>Converts a list to a bitstring.</fsummary> + <fsummary>Convert a list to a bitstring.</fsummary> <type name="bitstring_list"/> <desc> <p>Returns a bitstring that is made from the integers and bitstrings in <c><anno>BitstringList</anno></c>. (The last tail in - <c><anno>BitstringList</anno></c> is allowed to be a bitstring.)</p> - <p>Example:</p> + <c><anno>BitstringList</anno></c> is allowed to be a bitstring.) + Example:</p> <pre> > <input>Bin1 = <<1,2,3>>.</input> <<1,2,3>> @@ -2320,7 +2408,7 @@ os_prompt% </pre> <func> <name name="list_to_existing_atom" arity="1"/> - <fsummary>Converts from text representation to an atom.</fsummary> + <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is <c><anno>String</anno></c>, @@ -2332,7 +2420,7 @@ os_prompt% </pre> <func> <name name="list_to_float" arity="1"/> - <fsummary>Converts from text representation to a float.</fsummary> + <fsummary>Convert from text representation to a float.</fsummary> <desc> <p>Returns the float whose text representation is <c><anno>String</anno></c>, for example:</p> @@ -2346,7 +2434,7 @@ os_prompt% </pre> <func> <name name="list_to_integer" arity="1"/> - <fsummary>Converts from text representation to an integer.</fsummary> + <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation is <c><anno>String</anno></c>, for example:</p> @@ -2360,7 +2448,7 @@ os_prompt% </pre> <func> <name name="list_to_integer" arity="2"/> - <fsummary>Converts from text representation to an integer.</fsummary> + <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation in base <c><anno>Base</anno></c> is <c><anno>String</anno></c>, @@ -2375,7 +2463,7 @@ os_prompt% </pre> <func> <name name="list_to_pid" arity="1"/> - <fsummary>Converts from text representation to a pid.</fsummary> + <fsummary>Convert from text representation to a pid.</fsummary> <desc> <p>Returns a process identifier whose text representation is a <c><anno>String</anno></c>, for example:</p> @@ -2386,14 +2474,14 @@ os_prompt% </pre> representation of a process identifier.</p> <warning> <p>This BIF is intended for debugging and is not to be used - in application programs.</p> + in application programs.</p> </warning> </desc> </func> <func> <name name="list_to_tuple" arity="1"/> - <fsummary>Converts a list to a tuple.</fsummary> + <fsummary>Convert a list to a tuple.</fsummary> <desc> <p>Returns a tuple corresponding to <c><anno>List</anno></c>, for example</p> @@ -2406,7 +2494,7 @@ os_prompt% </pre> <func> <name name="load_module" arity="2"/> - <fsummary>Loads object code for a module.</fsummary> + <fsummary>Load object code for a module.</fsummary> <desc> <p>If <c><anno>Binary</anno></c> contains the object code for module <c><anno>Module</anno></c>, this BIF loads that object code. If @@ -2417,23 +2505,21 @@ os_prompt% </pre> that code.</p> <p>Returns either <c>{module, <anno>Module</anno>}</c>, or <c>{error, <anno>Reason</anno>}</c> if loading fails. - <c><anno>Reason</anno></c> is any of the following:</p> + <c><anno>Reason</anno></c> is one of the following:</p> <taglist> <tag><c>badfile</c></tag> - <item> - <p>The object code in <c><anno>Binary</anno></c> has an - incorrect format <em>or</em> the object code contains code - for another module than <c><anno>Module</anno></c>.</p> + <item>The object code in <c><anno>Binary</anno></c> has an + incorrect format <em>or</em> the object code contains code + for another module than <c><anno>Module</anno></c>. </item> <tag><c>not_purged</c></tag> - <item> - <p><c><anno>Binary</anno></c> contains a module that cannot be - loaded because old code for this module already exists.</p> + <item><c><anno>Binary</anno></c> contains a module that cannot be + loaded because old code for this module already exists. </item> </taglist> <warning> <p>This BIF is intended for the code server (see - <seealso marker="kernel:code">code(3)</seealso>) + <seealso marker="kernel:code"><c>code(3)</c></seealso>) and is not to be used elsewhere.</p> </warning> </desc> @@ -2441,33 +2527,36 @@ os_prompt% </pre> <func> <name name="load_nif" arity="2"/> - <fsummary>Loads NIF library.</fsummary> + <fsummary>Load NIF library.</fsummary> <desc> <note> - <p>Before OTP R14B, NIFs were an - experimental feature. Versions before OTP R14B can - have different and possibly incompatible NIF semantics and - interfaces. For example, in OTP R13B03 the return value on - failure was <c>{error,Reason,Text}</c>.</p> + <p>Before Erlang/OTP R14B, NIFs were an + experimental feature. Versions before Erlang/OTP R14B can + have different and possibly incompatible NIF semantics and + interfaces. For example, in Erlang/OTP R13B03 the return value on + failure was <c>{error,Reason,Text}</c>.</p> </note> <p>Loads and links a dynamic library containing native - implemented functions (NIFs) for a module. <c><anno>Path</anno></c> - is a file path to the shareable object/dynamic library file minus - the OS-dependent file extension (<c>.so</c> for Unix and - <c>.dll</c> for Windows. For information on how to - implement a NIF library, see - <seealso marker="erl_nif">erl_nif</seealso>.</p> + implemented functions (NIFs) for a module. <c><anno>Path</anno></c> + is a file path to the shareable object/dynamic library file minus + the OS-dependent file extension (<c>.so</c> for Unix and + <c>.dll</c> for Windows). Notice that on most OSs the library has + to have a different name on disc when an upgrade of the nif is + done. If the name is the same, but the contents differ, the + old library may be loaded instead. For information on how to + implement a NIF library, see + <seealso marker="erl_nif"><c>erl_nif(3)</c></seealso>.</p> <p><c><anno>LoadInfo</anno></c> can be any term. It is passed on to - the library as part of the initialization. A good practice is - to include a module version number to support future code - upgrade scenarios.</p> + the library as part of the initialization. A good practice is + to include a module version number to support future code + upgrade scenarios.</p> <p>The call to <c>load_nif/2</c> must be made - <em>directly</em> from the Erlang code of the module that the - NIF library belongs to. It returns either <c>ok</c>, or - <c>{error,{<anno>Reason</anno>,Text}}</c> if loading fails. - <c><anno>Reason</anno></c> is one of the following atoms - while <c><anno>Text</anno></c> is a human readable string that - can give more information about the failure:</p> + <em>directly</em> from the Erlang code of the module that the + NIF library belongs to. It returns either <c>ok</c>, or + <c>{error,{<anno>Reason</anno>,Text}}</c> if loading fails. + <c><anno>Reason</anno></c> is one of the following atoms + while <c><anno>Text</anno></c> is a human readable string that + can give more information about the failure:</p> <taglist> <tag><c>load_failed</c></tag> <item>The OS failed to load the NIF library. @@ -2490,11 +2579,12 @@ os_prompt% </pre> <func> <name name="loaded" arity="0"/> - <fsummary>Lists all loaded modules.</fsummary> + <fsummary>List all loaded modules.</fsummary> <desc> <p>Returns a list of all loaded Erlang modules (current and old code), including preloaded modules.</p> - <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p> + <p>See also <seealso marker="kernel:code"> + <c>code(3)</c></seealso>.</p> </desc> </func> @@ -2515,13 +2605,13 @@ os_prompt% </pre> <func> <name name="localtime_to_universaltime" arity="1"/> - <fsummary>Converts from local to Universal Time Coordinated (UTC) date and time.</fsummary> + <fsummary>Convert from local to Universal Time Coordinated (UTC) date + and time.</fsummary> <desc> <p>Converts local date and time to Universal Time Coordinated (UTC), if supported by the underlying OS. Otherwise no conversion is done and <c><anno>Localtime</anno></c> - is returned.</p> - <p>Example:</p> + is returned. Example:</p> <pre> > <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).</input> {{1996,11,6},{13,45,17}}</pre> @@ -2532,15 +2622,16 @@ os_prompt% </pre> <func> <name name="localtime_to_universaltime" arity="2"/> - <fsummary>Converts from local to Universal Time Coordinated (UTC) date and time.</fsummary> + <fsummary>Convert from local to Universal Time Coordinated (UTC) date + and time.</fsummary> <desc> <p>Converts local date and time to Universal Time Coordinated (UTC) as <c>erlang:localtime_to_universaltime/1</c>, but the caller decides if Daylight Saving Time is active.</p> - <p>If <c><anno>IsDst</anno> == true</c>, <c><anno>Localtime</anno></c> is - during Daylight Saving Time, if <c><anno>IsDst</anno> == false</c> it is - not. If <c><anno>IsDst</anno> == undefined</c>, the underlying OS can - guess, which is the same as calling + <p>If <c><anno>IsDst</anno> == true</c>, <c><anno>Localtime</anno></c> + is during Daylight Saving Time, if <c><anno>IsDst</anno> == false</c> + it is not. If <c><anno>IsDst</anno> == undefined</c>, the underlying + OS can guess, which is the same as calling <c>erlang:localtime_to_universaltime(<anno>Localtime</anno>)</c>.</p> <p>Examples:</p> <pre> @@ -2557,24 +2648,27 @@ os_prompt% </pre> <func> <name name="make_ref" arity="0"/> - <fsummary>Returns a unique reference.</fsummary> + <fsummary>Return a unique reference.</fsummary> <desc> - <p>Returns a <seealso marker="doc/efficiency_guide:advanced#unique_references">unique - reference</seealso>. The reference is unique among - connected nodes.</p> - <warning><p>Known issue: When a node is restarted multiple - times with the same node name, references created - on a newer node can be mistaken for a reference - created on an older node with the same node name.</p></warning> + <p>Returns a + <seealso marker="doc/efficiency_guide:advanced#unique_references"> + unique reference</seealso>. The reference is unique among + connected nodes.</p> + <warning> + <p>Known issue: When a node is restarted multiple + times with the same node name, references created + on a newer node can be mistaken for a reference + created on an older node with the same node name.</p> + </warning> </desc> </func> <func> <name name="make_tuple" arity="2"/> - <fsummary>Creates a new tuple of a given arity.</fsummary> + <fsummary>Create a new tuple of a specified arity.</fsummary> <desc> - <p>Creates a new tuple of the given <c><anno>Arity</anno></c>, where all - elements are <c><anno>InitialValue</anno></c>, for example:</p> + <p>Creates a new tuple of the specified <c><anno>Arity</anno></c>, where + all elements are <c><anno>InitialValue</anno></c>, for example:</p> <pre> > <input>erlang:make_tuple(4, []).</input> {[],[],[],[]}</pre> @@ -2583,17 +2677,16 @@ os_prompt% </pre> <func> <name name="make_tuple" arity="3"/> - <fsummary>Creates a new tuple with given arity and contents.</fsummary> + <fsummary>Create a new tuple with specifed arity and contents.</fsummary> <desc> <p>Creates a tuple of size <c><anno>Arity</anno></c>, where each element has value <c><anno>DefaultValue</anno></c>, and then fills in - values from <c><anno>InitList</anno></c>. + values from <c><anno>InitList</anno></c>. Each list element in <c><anno>InitList</anno></c> - must be a two-tuple, where the first element is a position in the - newly created tuple and the second element is any term. If a - position occurs more than once in the list, the term corresponding - to the last occurrence is used.</p> - <p>Example:</p> + must be a two-tuple, where the first element is a position in the + newly created tuple and the second element is any term. If a + position occurs more than once in the list, the term corresponding + to the last occurrence is used. Example:</p> <pre> > <input>erlang:make_tuple(5, [], [{2,ignored},{5,zz},{2,aa}]).</input> {{[],aa,[],[],zz}</pre> @@ -2602,7 +2695,7 @@ os_prompt% </pre> <func> <name name="map_size" arity="1"/> - <fsummary>Returns the size of a map.</fsummary> + <fsummary>Return the size of a map.</fsummary> <desc> <p>Returns an integer, which is the number of key-value pairs in <c><anno>Map</anno></c>, for example:</p> @@ -2615,74 +2708,74 @@ os_prompt% </pre> <func> <name name="match_spec_test" arity="3"/> - <fsummary>Test that a match specification works</fsummary> - <desc> - <p> - This function is a utility to test a match_spec used in calls to - <seealso marker="stdlib:ets#select/2">ets:select/2</seealso> and - <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>. - The function both tests MatchSpec for "syntactic" correctness and - runs the match_spec against the object. If the match_spec contains - errors, the tuple {error, Errors} is returned where Errors is a list - of natural language descriptions of what was wrong with the match_spec. - </p> - <p> - If the <c><anno>Type</anno></c> is <c>table</c> the object to match - against should be a tuple. The function then returns - {ok,Result,[],Warnings} where Result is what would have been the - result in a real ets:select/2 call or false if the match_spec does - not match the object tuple. - </p> - - <p> - If <c><anno>Type</anno></c> is <c>trace</c> the object to match - against should be a list. The function returns - {ok, Result, Flags, Warnings} where Result is <c>true</c> if a trace - message should be emitted, <c>false</c> if a trace message should not - be emitted or the message term to be appended to the trace message. - Flags is a list containing all the trace flags that will be enabled, - at the moment this is only <c>return_trace</c>. - </p> - - <p> - This is a useful debugging and test tool, especially when writing complicated - match specifications. - </p> - <p> - See also - <seealso marker="stdlib:ets#test_ms/2">ets:test_ms/2</seealso>. - </p> + <fsummary>Test that a match specification works.</fsummary> + <desc> + <p>Tests a match specification used in calls to + <seealso marker="stdlib:ets#select/2"><c>ets:select/2</c></seealso> + and <seealso marker="#trace_pattern/3"> + <c>erlang:trace_pattern/3</c></seealso>. + The function tests both a match specification for "syntactic" + correctness and runs the match specification against the object. If + the match specification contains errors, the tuple <c>{error, + Errors}</c> is returned, where <c>Errors</c> is a list of natural + language descriptions of what was wrong with the match + specification.</p> + <p>If <c><anno>Type</anno></c> is <c>table</c>, the object to match + against is to be a tuple. The function then returns + <c>{ok,Result,[],Warnings}</c>, where <c>Result</c> is what would + have been the result in a real <c>ets:select/2</c> call, or + <c>false</c> if the match specification does not match the object + tuple.</p> + <p>If <c><anno>Type</anno></c> is <c>trace</c>, the object to match + against is to be a list. The function returns + <c>{ok, Result, Flags, Warnings}</c>, where <c>Result</c> is one of + the following:</p> + <list type="bulleted"> + <item><c>true</c> if a trace message is to be emitted</item> + <item><c>false</c> if a trace message is not to be emitted</item> + <item>The message term to be appended to the trace message</item> + </list> + <p><c>Flags</c> is a list containing all the trace flags to be enabled, + currently this is only <c>return_trace</c>.</p> + <p>This is a useful debugging and test tool, especially when writing + complicated match specifications.</p> + <p>See also + <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2</c></seealso>.</p> </desc> </func> <func> <name name="max" arity="2"/> - <fsummary>Returns the largest of two terms.</fsummary> + <fsummary>Return the largest of two terms.</fsummary> <desc> <p>Returns the largest of <c><anno>Term1</anno></c> and <c><anno>Term2</anno></c>. - If the terms are equal, <c><anno>Term1</anno></c> is returned.</p> + If the terms are equal, <c><anno>Term1</anno></c> is returned.</p> </desc> </func> <func> <name name="md5" arity="1"/> - <fsummary>Computes an MD5 message digest.</fsummary> + <fsummary>Compute an MD5 message digest.</fsummary> <desc> <p>Computes an MD5 message digest from <c><anno>Data</anno></c>, where the length of the digest is 128 bits (16 bytes). <c><anno>Data</anno></c> is a binary or a list of small integers and binaries.</p> - <p>For more information about MD5, see RFC 1321 - The - MD5 Message-Digest Algorithm.</p> - <warning><p>The MD5 Message-Digest Algorithm is <em>not</em> considered - safe for code-signing or software-integrity purposes.</p></warning> + <p>For more information about MD5, see + <url href="https://www.ietf.org/rfc/rfc1321.txt"> + RFC 1321 - The MD5 Message-Digest Algorithm</url>.</p> + <warning> + <p>The MD5 Message-Digest Algorithm is <em>not</em> considered + safe for code-signing or software-integrity purposes.</p> + </warning> </desc> </func> <func> <name name="md5_final" arity="1"/> - <fsummary>Finishes the update of an MD5 context and returns the computed MD5 message digest.</fsummary> + <fsummary>Finish the update of an MD5 context and return the computed + MD5 message digest.</fsummary> <desc> <p>Finishes the update of an MD5 <c><anno>Context</anno></c> and returns the computed <c>MD5</c> message digest.</p> @@ -2691,18 +2784,19 @@ os_prompt% </pre> <func> <name name="md5_init" arity="0"/> - <fsummary>Creates an MD5 context.</fsummary> + <fsummary>Create an MD5 context.</fsummary> <desc> - <p>Creates an MD5 context, to be used in subsequent calls to + <p>Creates an MD5 context, to be used in the following calls to <c>md5_update/2</c>.</p> </desc> </func> <func> <name name="md5_update" arity="2"/> - <fsummary>Updates an MD5 context with data and returns a new context.</fsummary> + <fsummary>Update an MD5 context with data and return a new context. + </fsummary> <desc> - <p>Updates an MD5 <c><anno>Context</anno></c> with + <p>Update an MD5 <c><anno>Context</anno></c> with <c><anno>Data</anno></c> and returns a <c><anno>NewContext</anno></c>.</p> </desc> @@ -2718,7 +2812,7 @@ os_prompt% </pre> element is a tuple <c>{Type, Size}</c>. The first element <c><anno>Type</anno></c> is an atom describing memory type. The second element <c><anno>Size</anno></c> is the memory size in bytes.</p> - <p>The memory types are as follows:</p> + <p>Memory types:</p> <taglist> <tag><c>total</c></tag> <item> @@ -2770,7 +2864,7 @@ os_prompt% </pre> </item> <tag><c>ets</c></tag> <item> - <p>The total amount of memory currently allocated for ets + <p>The total amount of memory currently allocated for ETS tables. This memory is part of the memory presented as <c>system</c> memory.</p> </item> @@ -2778,9 +2872,9 @@ os_prompt% </pre> <item> <p>Only on 64-bit halfword emulator. The total amount of memory allocated in low memory areas - that are restricted to less than 4 GB, although - the system can have more memory.</p> - <p>Can be removed in a future release of the halfword + that are restricted to < 4 GB, although + the system can have more memory.</p> + <p>Can be removed in a future release of the halfword emulator.</p> </item> <tag><c>maximum</c></tag> @@ -2790,8 +2884,9 @@ os_prompt% </pre> when the emulator is run with instrumentation.</p> <p>For information on how to run the emulator with instrumentation, see - <seealso marker="tools:instrument">instrument(3)</seealso> - and/or <seealso marker="erts:erl">erl(1)</seealso>.</p> + <seealso marker="tools:instrument"> + <c>instrument(3)</c></seealso> + and/or <seealso marker="erl"><c>erl(1)</c></seealso>.</p> </item> </taglist> <note> @@ -2808,20 +2903,20 @@ os_prompt% </pre> <p>As the <c>total</c> value is the sum of <c>processes</c> and <c>system</c>, the error in <c>system</c> propagates to the <c>total</c> value.</p> - <p>The different amounts of memory that are summed are - <em>not</em> gathered atomically, which introduces - an error in the result.</p> + <p>The different amounts of memory that are summed are + <em>not</em> gathered atomically, which introduces + an error in the result.</p> </note> <p>The different values have the following relation to each other. Values beginning with an uppercase letter is not part of the result.</p> <code type="none"> - total = processes + system - processes = processes_used + ProcessesNotUsed - system = atom + binary + code + ets + OtherSystem - atom = atom_used + AtomNotUsed - RealTotal = processes + RealSystem - RealSystem = system + MissedSystem</code> +total = processes + system +processes = processes_used + ProcessesNotUsed +system = atom + binary + code + ets + OtherSystem +atom = atom_used + AtomNotUsed +RealTotal = processes + RealSystem +RealSystem = system + MissedSystem</code> <p>More tuples in the returned list can be added in a future release.</p> <note> @@ -2831,20 +2926,20 @@ os_prompt% </pre> the emulator stacks are not supposed to be included. That is, the <c>total</c> value is <em>not</em> supposed to be equal to the total size of all pages mapped to the emulator.</p> - <p>Furthermore, because of fragmentation and prereservation of + <p>Also, because of fragmentation and prereservation of memory areas, the size of the memory segments containing the dynamically allocated memory blocks can be much larger than the total size of the dynamically allocated memory blocks.</p> </note> - <note> - <p>As from <c>ERTS</c> 5.6.4, <c>erlang:memory/0</c> requires that - all <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso> - allocators are enabled (default behavior).</p> - </note> + <note> + <p>As from ERTS 5.6.4, <c>erlang:memory/0</c> requires that + all <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso> + allocators are enabled (default behavior).</p> + </note> <p>Failure: <c>notsup</c> if an - <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso> - allocator has been disabled.</p> + <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso> + allocator has been disabled.</p> </desc> </func> @@ -2854,61 +2949,61 @@ os_prompt% </pre> <fsummary>Information about dynamically allocated memory.</fsummary> <type name="memory_type"/> <desc> - <p>Returns the memory size in bytes allocated for memory of - type <c><anno>Type</anno></c>. The argument can also be given as a list + <p>Returns the memory size in bytes allocated for memory of type + <c><anno>Type</anno></c>. The argument can also be specified as a list of <c>memory_type()</c> atoms, in which case a corresponding list of <c>{memory_type(), Size :: integer >= 0}</c> tuples is returned.</p> - <note> - <p>As from <c>ERTS</c> version 5.6.4, + <note> + <p>As from ERTS 5.6.4, <c>erlang:memory/1</c> requires that - all <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso> - allocators are enabled (default behavior).</p> - </note> + all <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso> + allocators are enabled (default behavior).</p> + </note> <p>Failures:</p> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Type</anno></c> is not one of the memory types + If <c><anno>Type</anno></c> is not one of the memory types listed in the description of - <seealso marker="#memory/0">erlang:memory/0</seealso>. - </item> + <seealso marker="#memory/0"><c>erlang:memory/0</c></seealso>. + </item> <tag><c>badarg</c></tag> <item> - If <c>maximum</c> is passed as <c><anno>Type</anno></c> and + If <c>maximum</c> is passed as <c><anno>Type</anno></c> and the emulator is not run in instrumented mode. - </item> + </item> <tag><c>notsup</c></tag> <item> - If an <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso> - allocator has been disabled. - </item> - </taglist> + If an <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso> + allocator has been disabled. + </item> + </taglist> <p>See also - <seealso marker="#memory/0">erlang:memory/0</seealso>.</p> + <seealso marker="#memory/0"><c>erlang:memory/0</c></seealso>.</p> </desc> </func> <func> <name name="min" arity="2"/> - <fsummary>Returns the smallest of two terms.</fsummary> + <fsummary>Return the smallest of two terms.</fsummary> <desc> <p>Returns the smallest of <c><anno>Term1</anno></c> and <c><anno>Term2</anno></c>. - If the terms are equal, <c><anno>Term1</anno></c> is returned.</p> + If the terms are equal, <c><anno>Term1</anno></c> is returned.</p> </desc> </func> <func> <name name="module_loaded" arity="1"/> - <fsummary>Checks if a module is loaded.</fsummary> + <fsummary>Check if a module is loaded.</fsummary> <desc> <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded, otherwise <c>false</c>. It does not attempt to load the module.</p> <warning> <p>This BIF is intended for the code server (see - <seealso marker="kernel:code">code(3)</seealso>) and is not to be - used elsewhere.</p> + <seealso marker="kernel:code"><c>code(3)</c></seealso>) + and is not to be used elsewhere.</p> </warning> </desc> </func> @@ -2917,7 +3012,7 @@ os_prompt% </pre> <name name="monitor" arity="2" clause_i="1"/> <name name="monitor" arity="2" clause_i="2"/> <name name="monitor" arity="2" clause_i="3"/> - <fsummary>Starts monitoring.</fsummary> + <fsummary>Start monitoring.</fsummary> <type name="registered_name"/> <type name="registered_process_identifier"/> <type name="monitor_process_identifier"/> @@ -2925,35 +3020,39 @@ os_prompt% </pre> <desc> <p>Sends a monitor request of type <c><anno>Type</anno></c> to the entity identified by <c><anno>Item</anno></c>. If the monitored entity - does not exist or when it dies, the caller of <c>monitor/2</c> will - be notified by a message on the following format:</p> - <code type="none">{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code> - <note><p>The monitor request is an asynchronous signal. That is, it - takes time before the signal reaches its destination.</p></note> + does not exist or it changes monitored state, the caller of + <c>monitor/2</c> is notified by a message on the following format:</p> + <code type="none"> +{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code> + <note> + <p>The monitor request is an asynchronous signal. That is, it + takes time before the signal reaches its destination.</p> + </note> <p><c><anno>Type</anno></c> can be one of the following atoms: <c>process</c>, <c>port</c> or <c>time_offset</c>.</p> - <p>A monitor is triggered only once, after that it is removed from - both monitoring process and the monitored entity. - Monitors are fired when the monitored process or port terminates, - does not exist at the moment of creation, or if the connection to - it is lost. In the case with connection, we lose knowledge about - the fact if it still exists or not. The monitoring is also turned off - when <seealso marker="#demonitor/1">demonitor/1</seealso> - is called.</p> - - <p>When monitoring by name please note, that the <c>RegisteredName</c> - is resolved to <c>pid()</c> or <c>port()</c> only once - at the moment of monitor instantiation, later changes to the name - registration will not affect the existing monitor.</p> - - <p>When a monitor is triggered, a <c>'DOWN'</c> message that has the - following pattern <c>{'DOWN', MonitorRef, Type, Object, Info}</c> - is sent to the monitoring process.</p> - - <p>In monitor message <c>MonitorRef</c> and <c>Type</c> are the same as - described earlier, and:</p> + <p>A <c>process</c> or <c>port</c> monitor is triggered only once, + after that it is removed from both monitoring process and + the monitored entity. Monitors are fired when the monitored process + or port terminates, does not exist at the moment of creation, + or if the connection to it is lost. If the connection to it is lost, + we do not know if it still exists. The monitoring is also turned off + when <seealso marker="#demonitor/1">demonitor/1</seealso> is + called.</p> + + <p>A <c>process</c> or <c>port</c> monitor by name + resolves the <c>RegisteredName</c> to <c>pid()</c> or <c>port()</c> + only once at the moment of monitor instantiation, later changes to + the name registration will not affect the existing monitor.</p> + + <p>When a <c>process</c> or <c>port</c> monitor is triggered, + a <c>'DOWN'</c> message is sent that has the following pattern:</p> + <code type="none"> +{'DOWN', MonitorRef, Type, Object, Info}</code> + + <p>In the monitor message <c>MonitorRef</c> and <c>Type</c> are the + same as described earlier, and:</p> <taglist> <tag><c>Object</c></tag> <item> @@ -2980,14 +3079,14 @@ os_prompt% </pre> implemented), the call fails with <c>badarg</c>.</p> <note> <p>The format of the <c>'DOWN'</c> message changed in ERTS - version 5.2 (OTP R9B) for monitoring + 5.2 (Erlang/OTP R9B) for monitoring <em>by registered name</em>. Element <c>Object</c> of the <c>'DOWN'</c> message could in earlier versions sometimes be the process identifier of the monitored process and sometimes be the registered name. Now element <c>Object</c> is always a tuple consisting of the registered name and - the node name. Processes on new nodes (ERTS version 5.2 - or higher) always get <c>'DOWN'</c> messages on + the node name. Processes on new nodes (ERTS 5.2 + or higher versions) always get <c>'DOWN'</c> messages on the new format even if they are monitoring processes on old nodes. Processes on old nodes always get <c>'DOWN'</c> messages on the old format.</p> @@ -2996,11 +3095,11 @@ os_prompt% </pre> <taglist> <tag>Monitoring a <marker id="monitor_process"/><c>process</c></tag> <item> - <p>Creates monitor between the current process and another - process identified by <c><anno>Item</anno></c>, which can be a - <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or - a tuple <c>{RegisteredName, Node}</c> for a registered process, - located elsewhere.</p> + <p>Creates monitor between the current process and another + process identified by <c><anno>Item</anno></c>, which can be a + <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or + a tuple <c>{RegisteredName, Node}</c> for a registered process, + located elsewhere.</p> </item> <tag>Monitoring a <marker id="monitor_port"/><c>port</c></tag> @@ -3016,60 +3115,62 @@ os_prompt% </pre> <tag>Monitoring a <marker id="monitor_time_offset"/><c>time_offset</c></tag> <item> - <p>Monitor changes in - <seealso marker="#time_offset/0">time offset</seealso> - between - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso> and - <seealso marker="time_correction#Erlang_System_Time">Erlang - system time</seealso>. There is only one valid - <c><anno>Item</anno></c> in combination with the - <c>time_offset <anno>Type</anno></c>, namely the atom - <c>clock_service</c>. Note that the atom <c>clock_service</c> is - <em>not</em> the registered name of a process. In this specific - case it serves as an identifier of the runtime system internal - clock service at current runtime system instance.</p> + <p>Monitors changes in + <seealso marker="#time_offset/0"><c>time offset</c></seealso> + between + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso> and + <seealso marker="time_correction#Erlang_System_Time">Erlang + system time</seealso>. One valid <c><anno>Item</anno></c> + exists in combination with the + <c>time_offset <anno>Type</anno></c>, namely the atom + <c>clock_service</c>. Notice that the atom <c>clock_service</c> is + <em>not</em> the registered name of a process. In this + case it serves as an identifier of the runtime system internal + clock service at current runtime system instance.</p> <p>The monitor is triggered when the time offset is changed. - This either if the time offset value is changed, or if the - offset is changed from preliminary to final during - <seealso marker="#system_flag_time_offset">finalization - of the time offset</seealso> when the - <seealso marker="time_correction#Single_Time_Warp_Mode">single - time warp mode</seealso> is used. When a change from preliminary - to final time offset is made, the monitor will be triggered once - regardless of whether the time offset value was actually changed - or not.</p> + This either if the time offset value is changed, or if the + offset is changed from preliminary to final during + <seealso marker="#system_flag_time_offset">finalization + of the time offset</seealso> when the + <seealso marker="time_correction#Single_Time_Warp_Mode">single + time warp mode</seealso> is used. When a change from preliminary + to final time offset is made, the monitor is triggered once + regardless of whether the time offset value was changed + or not.</p> <p>If the runtime system is in - <seealso marker="time_correction#Multi_Time_Warp_Mode">multi - time warp mode</seealso>, the time offset will be changed when - the runtime system detects that the - <seealso marker="time_correction#OS_System_Time">OS system - time</seealso> has changed. The runtime system will, however, - not detect this immediately when it happens. A task checking - the time offset is scheduled to execute at least once a minute, - so under normal operation this should be detected within a - minute, but during heavy load it might take longer time.</p> - - <p>The monitor will <em>not</em> be automatically removed - after it has been triggered. That is, repeated changes of - the time offset will trigger the monitor repeatedly.</p> - - <p>When the monitor is triggered a <c>'CHANGE'</c> message will - be sent to the monitoring process. A <c>'CHANGE'</c> message has - the following pattern:</p> - <code type="none">{'CHANGE', MonitorRef, Type, Item, NewTimeOffset}</code> - <p>where <c>MonitorRef</c>, <c><anno>Type</anno></c>, and - <c><anno>Item</anno></c> are the same as described above, and - <c>NewTimeOffset</c> is the new time offset.</p> + <seealso marker="time_correction#Multi_Time_Warp_Mode">multi + time warp mode</seealso>, the time offset is changed when + the runtime system detects that the + <seealso marker="time_correction#OS_System_Time">OS system + time</seealso> has changed. The runtime system does, however, + not detect this immediately when it occurs. A task checking + the time offset is scheduled to execute at least once a minute, + so under normal operation this is to be detected within a + minute, but during heavy load it can take longer time.</p> + + <p>The monitor is <em>not</em> automatically removed + after it has been triggered. That is, repeated changes of + the time offset trigger the monitor repeatedly.</p> + + <p>When the monitor is triggered a <c>'CHANGE'</c> message is + sent to the monitoring process. A <c>'CHANGE'</c> message has + the following pattern:</p> + <code type="none"> +{'CHANGE', MonitorRef, Type, Item, NewTimeOffset}</code> + <p>where <c>MonitorRef</c>, <c><anno>Type</anno></c>, and + <c><anno>Item</anno></c> are the same as described above, and + <c>NewTimeOffset</c> is the new time offset.</p> <p>When the <c>'CHANGE'</c> message has been received you are - guaranteed not to retrieve the old time offset when calling - <seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso>. - Note that you can observe the change of the time offset - when calling <c>erlang:time_offset()</c> before you - get the <c>'CHANGE'</c> message.</p> + guaranteed not to retrieve the old time offset when calling + <seealso marker="#time_offset/0"> + <c>erlang:time_offset()</c></seealso>. + Notice that you can observe the change of the time offset + when calling <c>erlang:time_offset()</c> before you + get the <c>'CHANGE'</c> message.</p> </item> </taglist> @@ -3080,20 +3181,19 @@ os_prompt% </pre> <p>The monitor functionality is expected to be extended. That is, other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s are expected to be supported in a future release.</p> - <note> <p>If or when <c>monitor/2</c> is extended, other - possible values for <c>Tag</c>, <c>Object</c> and - <c>Info</c> in the monitor message will be introduced.</p> + possible values for <c>Tag</c>, <c>Object</c>, and + <c>Info</c> in the monitor message will be introduced.</p> </note> </desc> </func> <func> <name name="monitor_node" arity="2"/> - <fsummary>Monitors the status of a node.</fsummary> + <fsummary>Monitor the status of a node.</fsummary> <desc> - <p>Monitors the status of the node <c><anno>Node</anno></c>. + <p>Monitor the status of the node <c><anno>Node</anno></c>. If <c><anno>Flag</anno></c> is <c>true</c>, monitoring is turned on. If <c><anno>Flag</anno></c> is <c>false</c>, monitoring is turned off.</p> @@ -3115,23 +3215,23 @@ os_prompt% </pre> <func> <name name="monitor_node" arity="3"/> - <fsummary>Monitors the status of a node.</fsummary> + <fsummary>Monitor the status of a node.</fsummary> <desc> <p>Behaves as - <seealso marker="#monitor_node/2">monitor_node/2</seealso> + <seealso marker="#monitor_node/2"><c>monitor_node/2</c></seealso> except that it allows an - extra option to be given, namely <c>allow_passive_connect</c>. + extra option to be specified, namely <c>allow_passive_connect</c>. This option allows the BIF to wait the normal network connection time-out for the <em>monitored node</em> to connect itself, even if it cannot be actively connected from this node (that is, it is blocked). The state where this can be useful - can only be achieved by using the <c>Kernel</c> option + can only be achieved by using the Kernel option <c>dist_auto_connect once</c>. If that option is not used, option <c>allow_passive_connect</c> has no effect.</p> <note> <p>Option <c>allow_passive_connect</c> is used internally and is seldom needed in applications where the - network topology and the <c>Kernel</c> options in effect + network topology and the Kernel options in effect are known in advance.</p> </note> <p>Failure: <c>badarg</c> if the local node is not alive or the @@ -3143,70 +3243,77 @@ os_prompt% </pre> <name name="monotonic_time" arity="0"/> <fsummary>Current Erlang monotonic time.</fsummary> <desc> - <p>Returns the current - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso> in <c>native</c> - <seealso marker="#type_time_unit">time unit</seealso>. This - is a monotonically increasing time since some unspecified point in - time.</p> - - <note><p>This is a - <seealso marker="time_correction#Monotonically_Increasing">monotonically increasing</seealso> time, but <em>not</em> a - <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly monotonically increasing</seealso> - time. That is, consecutive calls to - <c>erlang:monotonic_time/0</c> can produce the same result.</p> - - <p>Different runtime system instances will use different - unspecified points in time as base for their Erlang monotonic clocks. - That is, it is <em>pointless</em> comparing monotonic times from - different runtime system instances. Different runtime system instances - may also place this unspecified point in time different relative - runtime system start. It may be placed in the future (time at start - is a negative value), the past (time at start is a - positive value), or the runtime system start (time at start is - zero). The monotonic time at runtime system start can be - retrieved by calling - <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>.</p></note> + <p>Returns the current + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso> in <c>native</c> + <seealso marker="#type_time_unit">time unit</seealso>. This + is a monotonically increasing time since some unspecified point in + time.</p> + <note> + <p>This is a + <seealso marker="time_correction#Monotonically_Increasing"> + monotonically increasing</seealso> time, but <em>not</em> a + <seealso marker="time_correction#Strictly_Monotonically_Increasing"> + strictly monotonically increasing</seealso> + time. That is, consecutive calls to + <c>erlang:monotonic_time/0</c> can produce the same result.</p> + <p>Different runtime system instances will use different unspecified + points in time as base for their Erlang monotonic clocks. + That is, it is <em>pointless</em> comparing monotonic times from + different runtime system instances. Different runtime system + instances can also place this unspecified point in time different + relative runtime system start. It can be placed in the future (time + at start is a negative value), the past (time at start is a + positive value), or the runtime system start (time at start is + zero). The monotonic time at runtime system start can be + retrieved by calling + <seealso marker="#system_info_start_time"> + <c>erlang:system_info(start_time)</c></seealso>.</p> + </note> </desc> </func> + <func> <name name="monotonic_time" arity="1"/> - <fsummary>Current Erlang monotonic time</fsummary> + <fsummary>Current Erlang monotonic time.</fsummary> <desc> - <p>Returns the current - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso> converted - into the <c><anno>Unit</anno></c> passed as argument.</p> - - <p>Same as calling - <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso><c>, - native, <anno>Unit</anno>)</c> - however optimized for commonly used <c><anno>Unit</anno></c>s.</p> + <p>Returns the current + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso> converted + into the <c><anno>Unit</anno></c> passed as argument.</p> + <p>Same as calling + <seealso marker="#convert_time_unit/3"> + <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso + marker="#monotonic_time/0"> + <c>erlang:monotonic_time()</c></seealso><c>, + native, <anno>Unit</anno>)</c>, + however optimized for commonly used <c><anno>Unit</anno></c>s.</p> </desc> </func> + <func> <name name="nif_error" arity="1"/> - <fsummary>Stops execution with a given reason.</fsummary> + <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Works exactly like - <seealso marker="#error/1">erlang:error/1</seealso>, but - <c>Dialyzer</c> thinks that this BIF will return an arbitrary - term. When used in a stub function for a NIF to generate an - exception when the NIF library is not loaded, <c>Dialyzer</c> - does not generate false warnings.</p> + <seealso marker="#error/1"><c>error/1</c></seealso>, but + Dialyzer thinks that this BIF will return an arbitrary + term. When used in a stub function for a NIF to generate an + exception when the NIF library is not loaded, Dialyzer + does not generate false warnings.</p> </desc> </func> <func> <name name="nif_error" arity="2"/> - <fsummary>Stops execution with a given reason.</fsummary> + <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Works exactly like - <seealso marker="#error/2">erlang:error/2</seealso>, but - <c>Dialyzer</c> thinks that this BIF will return an arbitrary - term. When used in a stub function for a NIF to generate an - exception when the NIF library is not loaded, <c>Dialyzer</c> - does not generate false warnings.</p> + <seealso marker="#error/2"><c>error/2</c></seealso>, but + Dialyzer thinks that this BIF will return an arbitrary + term. When used in a stub function for a NIF to generate an + exception when the NIF library is not loaded, Dialyzer + does not generate false warnings.</p> </desc> </func> @@ -3246,10 +3353,10 @@ os_prompt% </pre> <name name="nodes" arity="1"/> <fsummary>All nodes of a certain type in the system.</fsummary> <desc> - <p>Returns a list of nodes according to the argument given. - The returned result when the argument is a list, is the list + <p>Returns a list of nodes according to the argument specified. + The returned result, when the argument is a list, is the list of nodes satisfying the disjunction(s) of the list elements.</p> - <p><c><anno>NodeType</anno></c> can be any of the following:</p> + <p><c><anno>NodeType</anno></c>s:</p> <taglist> <tag><c>visible</c></tag> <item> @@ -3270,13 +3377,13 @@ os_prompt% </pre> <tag><c>known</c></tag> <item> <p>Nodes that are known to this node. That is, connected - nodes and nodes referred to by process identifiers, port - identifiers and references located on this node. - The set of known nodes is garbage collected. Notice that - this garbage collection can be delayed. For more - information, see - <seealso marker="erlang#system_info_delayed_node_table_gc">delayed_node_table_gc</seealso>. - </p> + nodes and nodes referred to by process identifiers, port + identifiers, and references located on this node. + The set of known nodes is garbage collected. Notice that + this garbage collection can be delayed. For more + information, see + <seealso marker="erlang#system_info_delayed_node_table_gc"> + <c>erlang:system_info(delayed_node_table_gc)</c></seealso>.</p> </item> </taglist> <p>Some equalities: <c>[node()] = nodes(this)</c>, @@ -3290,20 +3397,22 @@ os_prompt% </pre> <fsummary>Elapsed time since 00:00 GMT.</fsummary> <type name="timestamp"/> <desc> - <warning><p><em>This function is deprecated! Do not use it!</em> - See the users guide chapter - <seealso marker="time_correction">Time and Time Correction</seealso> - for more information. Specifically the - <seealso marker="time_correction#Dos_and_Donts">Dos and Dont's</seealso> - section for information on what to use instead of <c>erlang:now/0</c>. - </p></warning> - <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c> which is + <warning> + <p><em>This function is deprecated. Do not use it.</em></p> + <p>For more information, see section + <seealso marker="time_correction">Time and Time Correction</seealso> + in the User's Guide. Specifically, section + <seealso marker="time_correction#Dos_and_Donts"> + Dos and Dont's</seealso> describes what to use instead of + <c>erlang:now/0</c>.</p> + </warning> + <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c>, which is the elapsed time since 00:00 GMT, January 1, 1970 (zero hour), - on the assumption that the underlying OS supports this. + if provided by the underlying OS. Otherwise some other point in time is chosen. It is also - guaranteed that subsequent calls to this BIF return + guaranteed that the following calls to this BIF return continuously increasing values. Hence, the return value from - <c>now()</c> can be used to generate unique time-stamps. + <c>erlang:now/0</c> can be used to generate unique time stamps. If it is called in a tight loop on a fast machine, the time of the node can become skewed.</p> <p>Can only be used to check the local time of day if @@ -3314,28 +3423,31 @@ os_prompt% </pre> <func> <name name="open_port" arity="2"/> - <fsummary>Opens a port.</fsummary> + <fsummary>Open a port.</fsummary> <desc> <p>Returns a port identifier as the result of opening a new Erlang port. A port can be seen as an external Erlang process.</p> - <p>The name of the executable as well as the arguments - given in <c>cd</c>, <c>env</c>, <c>args</c>, and <c>arg0</c> are - subject to Unicode file name translation if the system is running - in Unicode file name mode. To avoid - translation or to force, for example UTF-8, supply the executable - and/or arguments as a binary in the correct - encoding. For details, see the module - <seealso marker="kernel:file">file</seealso>, the function - <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, and the - <seealso marker="stdlib:unicode_usage">STDLIB </seealso> - User's Guide.</p> - <note><p>The characters in the name (if given as a list) can - only be higher than 255 if the Erlang Virtual Machine is started - in Unicode file name translation mode. Otherwise the name - of the executable is limited to the ISO-latin-1 - character set.</p></note> - <p><c><anno>PortName</anno></c> can be any of the following:</p> + <p>The name of the executable as well as the arguments + specifed in <c>cd</c>, <c>env</c>, <c>args</c>, and <c>arg0</c> are + subject to Unicode filename translation if the system is running + in Unicode filename mode. To avoid + translation or to force, for example UTF-8, supply the executable + and/or arguments as a binary in the correct + encoding. For details, see the module + <seealso marker="kernel:file"><c>file(3)</c></seealso>, the + function <seealso marker="kernel:file#native_name_encoding/0"> + <c>file:native_name_encoding/0</c></seealso> in Kernel, and + the <seealso marker="stdlib:unicode_usage"> + <c>Using Unicode in Erlang</c></seealso> User's Guide.</p> + <note> + <p>The characters in the name (if specified as a list) can + only be > 255 if the Erlang virtual machine is started + in Unicode filename translation mode. Otherwise the name + of the executable is limited to the ISO Latin-1 + character set.</p> + </note> + <p><c><anno>PortName</anno></c>s:</p> <taglist> <tag><c>{spawn, <anno>Command</anno>}</c></tag> <item> @@ -3354,55 +3466,57 @@ os_prompt% </pre> <c>vfork</c>, setting environment variable <c>ERL_NO_VFORK</c> to any value causes <c>fork</c> to be used instead.</p> - <p>For external programs, <c>PATH</c> is searched - (or an equivalent method is used to find programs, - depending on OS). This is done by invoking - the shell on certain platforms. The first space-separated - token of the command is considered as the - name of the executable (or driver). This (among other - things) makes this option unsuitable for running - programs having spaces in file names or directory names. - If spaces in executable file names are desired, use - <c>{spawn_executable, <anno>Command</anno>}</c> instead.</p> + <p>For external programs, <c>PATH</c> is searched + (or an equivalent method is used to find programs, + depending on the OS). This is done by invoking + the shell on certain platforms. The first space-separated + token of the command is considered as the + name of the executable (or driver). This (among other + things) makes this option unsuitable for running + programs with spaces in filenames or directory names. + If spaces in executable filenames are desired, use + <c>{spawn_executable, <anno>Command</anno>}</c> instead.</p> </item> <tag><c>{spawn_driver, <anno>Command</anno>}</c></tag> <item> - <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands the - first (space-separated) token of the command to be the name of a - loaded driver. If no driver with that name is loaded, a - <c>badarg</c> error is raised.</p> + <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands + the first (space-separated) token of the command to be the name + of a loaded driver. If no driver with that name is loaded, a + <c>badarg</c> error is raised.</p> </item> <tag><c>{spawn_executable, <anno>FileName</anno>}</c></tag> <item> - <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs - external executables. <c><anno>FileName</anno></c> in its whole - is used as the name of the executable, including any spaces. - If arguments are to be passed, the <c><anno>PortSettings</anno></c> - <c>args</c> and <c>arg0</c> can be used.</p> - <p>The shell is usually not invoked to start the - program, it is executed directly. <c>PATH</c> (or + <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs + external executables. <c><anno>FileName</anno></c> in its whole + is used as the name of the executable, including any spaces. + If arguments are to be passed, the + <c><anno>PortSettings</anno></c> + <c>args</c> and <c>arg0</c> can be used.</p> + <p>The shell is usually not invoked to start the + program, it is executed directly. <c>PATH</c> (or equivalent) is not searched. To find a program - in <c>PATH</c> to execute, use - <seealso marker="kernel:os#find_executable/1">os:find_executable/1</seealso>.</p> - <p>Only if a shell script or <c>.bat</c> file is - executed, the appropriate command interpreter is - invoked implicitly, but there is still no - command argument expansion or implicit <c>PATH</c> search.</p> - <p>If <c><anno>FileName</anno></c> cannot be run, an error - exception is raised, with the POSIX error code as the reason. - The error reason can differ between OSs. - Typically the error <c>enoent</c> is raised when an - attempt is made to run a program that is not found and - <c>eacces</c> is raised when the given file is not - executable.</p> + in <c>PATH</c> to execute, use + <seealso marker="kernel:os#find_executable/1"> + <c>os:find_executable/1</c></seealso>.</p> + <p>Only if a shell script or <c>.bat</c> file is + executed, the appropriate command interpreter is + invoked implicitly, but there is still no + command-argument expansion or implicit <c>PATH</c> search.</p> + <p>If <c><anno>FileName</anno></c> cannot be run, an error + exception is raised, with the POSIX error code as the reason. + The error reason can differ between OSs. + Typically the error <c>enoent</c> is raised when an + attempt is made to run a program that is not found and + <c>eacces</c> is raised when the specified file is not + executable.</p> </item> <tag><c>{fd, <anno>In</anno>, <anno>Out</anno>}</c></tag> <item> <p>Allows an Erlang process to access any currently opened file descriptors used by Erlang. The file descriptor - <c><anno>In</anno></c> can be used for standard input, and the file - descriptor <c><anno>Out</anno></c> for standard output. It is only - used for various servers in the Erlang OS (<c>shell</c> + <c><anno>In</anno></c> can be used for standard input, and the + file descriptor <c><anno>Out</anno></c> for standard output. + It is only used for various servers in the Erlang OS (<c>shell</c> and <c>user</c>). Hence, its use is limited.</p> </item> </taglist> @@ -3411,7 +3525,8 @@ os_prompt% </pre> <taglist> <tag><c>{packet, <anno>N</anno>}</c></tag> <item> - <p>Messages are preceded by their length, sent in <c><anno>N</anno></c> + <p>Messages are preceded by their length, sent in + <c><anno>N</anno></c> bytes, with the most significant byte first. The valid values for <c>N</c> are 1, 2, and 4.</p> </item> @@ -3424,16 +3539,16 @@ os_prompt% </pre> <tag><c>{line, <anno>L</anno>}</c></tag> <item> <p>Messages are delivered on a per line basis. Each line - (delimited by the OS-dependent new line sequence) is + (delimited by the OS-dependent newline sequence) is delivered in a single message. The message data format is <c>{Flag, Line}</c>, where <c>Flag</c> is <c>eol</c> or <c>noeol</c>, and <c>Line</c> is the - data delivered (without the new line sequence).</p> + data delivered (without the newline sequence).</p> <p><c><anno>L</anno></c> specifies the maximum line length in bytes. Lines longer than this are delivered in more than one message, with <c>Flag</c> set to <c>noeol</c> for all but the last message. If end of file is encountered - anywhere else than immediately following a new line + anywhere else than immediately following a newline sequence, the last line is also delivered with <c>Flag</c> set to <c>noeol</c>. Otherwise lines are delivered with <c>Flag</c> set to <c>eol</c>.</p> @@ -3443,14 +3558,14 @@ os_prompt% </pre> <tag><c>{cd, <anno>Dir</anno>}</c></tag> <item> <p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and - <c>{spawn_executable, <anno>FileName</anno>}</c>. + <c>{spawn_executable, <anno>FileName</anno>}</c>. The external program starts using <c><anno>Dir</anno></c> as its working directory. <c><anno>Dir</anno></c> must be a string.</p> </item> <tag><c>{env, <anno>Env</anno>}</c></tag> <item> - <p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and - <c>{spawn_executable, <anno>FileName</anno>}</c>. + <p>Only valid for <c>{spawn, <anno>Command</anno>}</c>, and + <c>{spawn_executable, <anno>FileName</anno>}</c>. The environment of the started process is extended using the environment specifications in <c><anno>Env</anno></c>.</p> <p><c><anno>Env</anno></c> is to be a list of tuples @@ -3461,56 +3576,58 @@ os_prompt% </pre> port process. Both <c><anno>Name</anno></c> and <c><anno>Val</anno></c> must be strings. The one exception is <c><anno>Val</anno></c> being the atom - <c>false</c> (in analogy with <c>os:getenv/1</c>), which - removes the environment variable.</p> - </item> - <tag><c>{args, [ string() | binary() ]}</c></tag> - <item> - <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c> - and specifies arguments to the executable. Each argument - is given as a separate string and (on Unix) eventually - ends up as one element each in the argument vector. On - other platforms, a similar behavior is mimicked.</p> - <p>The arguments are not expanded by the shell before - being supplied to the executable. Most notably this - means that file wild card expansion does not happen. - To expand wild cards for the arguments, use - <seealso marker="stdlib:filelib#wildcard/1">filelib:wildcard/1</seealso>. - Notice that even if - the program is a Unix shell script, meaning that the - shell ultimately is invoked, wild card expansion - does not happen, and the script is provided with the - untouched arguments. On Windows, wild card expansion - is always up to the program itself, therefore this is - not an issue issue.</p> - <p>The executable name (also known as <c>argv[0]</c>) - is not to be given in this list. The proper executable name - is automatically used as argv[0], where applicable.</p> - <p>If you explicitly want to set the - program name in the argument vector, option <c>arg0</c> - can be used.</p> - </item> - <tag><c>{arg0, string() | binary()}</c></tag> - <item> - <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c> - and explicitly specifies the program name argument when - running an executable. This can in some circumstances, - on some OSs, be desirable. How the program - responds to this is highly system-dependent and no specific - effect is guaranteed.</p> - </item> + <c>false</c> (in analogy with + <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso>, + which removes the environment variable.</p> + </item> + <tag><c>{args, [ string() | binary() ]}</c></tag> + <item> + <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c> + and specifies arguments to the executable. Each argument + is specified as a separate string and (on Unix) eventually + ends up as one element each in the argument vector. On + other platforms, a similar behavior is mimicked.</p> + <p>The arguments are not expanded by the shell before + they are supplied to the executable. Most notably this + means that file wildcard expansion does not occur. + To expand wildcards for the arguments, use + <seealso marker="stdlib:filelib#wildcard/1"> + <c>filelib:wildcard/1</c></seealso>. + Notice that even if + the program is a Unix shell script, meaning that the + shell ultimately is invoked, wildcard expansion + does not occur, and the script is provided with the + untouched arguments. On Windows, wildcard expansion + is always up to the program itself, therefore this is + not an issue.</p> + <p>The executable name (also known as <c>argv[0]</c>) + is not to be specified in this list. The proper executable name + is automatically used as <c>argv[0]</c>, where applicable.</p> + <p>If you explicitly want to set the + program name in the argument vector, option <c>arg0</c> + can be used.</p> + </item> + <tag><c>{arg0, string() | binary()}</c></tag> + <item> + <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c> + and explicitly specifies the program name argument when + running an executable. This can in some circumstances, + on some OSs, be desirable. How the program + responds to this is highly system-dependent and no specific + effect is guaranteed.</p> + </item> <tag><c>exit_status</c></tag> <item> <p>Only valid for <c>{spawn, <anno>Command</anno>}</c>, where <c><anno>Command</anno></c> refers to an external program, and - for <c>{spawn_executable, <anno>FileName</anno>}</c>.</p> + for <c>{spawn_executable, <anno>FileName</anno>}</c>.</p> <p>When the external process connected to the port exits, a message of the form <c>{Port,{exit_status,Status}}</c> is sent to the connected process, where <c>Status</c> is the exit status of the external process. If the program aborts on Unix, the same convention is used as the shells do (that is, 128+signal).</p> - <p>If option <c>eof</c> is also given, the messages <c>eof</c> + <p>If option <c>eof</c> is specified also, the messages <c>eof</c> and <c>exit_status</c> appear in an unspecified order.</p> <p>If the port program closes its <c>stdout</c> without exiting, option <c>exit_status</c> does not work.</p> @@ -3518,7 +3635,7 @@ os_prompt% </pre> <tag><c>use_stdio</c></tag> <item> <p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and - <c>{spawn_executable, <anno>FileName</anno>}</c>. It + <c>{spawn_executable, <anno>FileName</anno>}</c>. It allows the standard input and output (file descriptors 0 and 1) of the spawned (Unix) process for communication with Erlang.</p> @@ -3538,14 +3655,14 @@ os_prompt% </pre> <tag><c>overlapped_io</c></tag> <item> <p>Affects ports to external programs on Windows only. The - standard input and standard output handles of the port program - are, if this option is supplied, opened with flag - <c>FILE_FLAG_OVERLAPPED</c>, so that the port program can - (and must) do - overlapped I/O on its standard handles. This is not normally - the case for simple port programs, but an option of value for the - experienced Windows programmer. <em>On all other platforms, this - option is silently discarded.</em></p> + standard input and standard output handles of the port program + are, if this option is supplied, opened with flag + <c>FILE_FLAG_OVERLAPPED</c>, so that the port program can + (and must) do + overlapped I/O on its standard handles. This is not normally + the case for simple port programs, but an option of value for the + experienced Windows programmer. <em>On all other platforms, this + option is silently discarded.</em></p> </item> <tag><c>in</c></tag> <item> @@ -3570,27 +3687,28 @@ os_prompt% </pre> <tag><c>hide</c></tag> <item> <p>When running on Windows, suppresses creation of a new - console window when spawning the port program. - (This option has no effect on other platforms.)</p> + console window when spawning the port program. + (This option has no effect on other platforms.)</p> </item> <tag><c>{parallelism, Boolean}</c></tag> <item> - <marker id="open_port_parallelism"></marker> + <marker id="open_port_parallelism"></marker> <p>Sets scheduler hint for port parallelism. If set to - <c>true</c>, the Virtual Machine schedules port tasks; - when doing so, it improves parallelism in the system. If set - to <c>false</c>, the Virtual Machine tries to - perform port tasks immediately, improving latency at the - expense of parallelism. The default can be set at system startup - by passing command-line argument - <seealso marker="erl#+spp">+spp</seealso> to <c>erl(1)</c>.</p> + <c>true</c>, the virtual machine schedules port tasks; + when doing so, it improves parallelism in the system. If set + to <c>false</c>, the virtual machine tries to + perform port tasks immediately, improving latency at the + expense of parallelism. The default can be set at system startup + by passing command-line argument + <seealso marker="erl#+spp"><c>+spp</c></seealso> to + <c>erl(1)</c>.</p> </item> </taglist> <p>Default is <c>stream</c> for all port types and <c>use_stdio</c> for spawned ports.</p> - <p>Failure: If the port cannot be opened, the exit reason is - <c>badarg</c>, <c>system_limit</c>, or the POSIX error code that - most closely describes the error, or <c>einval</c> if no POSIX + <p>Failure: if the port cannot be opened, the exit reason is + <c>badarg</c>, <c>system_limit</c>, or the POSIX error code that + most closely describes the error, or <c>einval</c> if no POSIX code is appropriate:</p> <taglist> <tag><c>badarg</c></tag> @@ -3616,11 +3734,11 @@ os_prompt% </pre> <item>Full file table (for the entire OS). </item> <tag><c>eacces</c></tag> - <item><c>Command</c> given in <c>{spawn_executable, Command}</c> + <item><c>Command</c> specified in <c>{spawn_executable, Command}</c> does not point out an executable file. </item> <tag><c>enoent</c></tag> - <item><c><anno>FileName</anno></c> given in + <item><c><anno>FileName</anno></c> specified in <c>{spawn_executable, <anno>FileName</anno>}</c> does not point out an existing file. </item> @@ -3630,13 +3748,12 @@ os_prompt% </pre> errors arising when sending messages to it are reported to the owning process using signals of the form <c>{'EXIT', Port, PosixCode}</c>. For the possible values of - <c>PosixCode</c>, see the - <seealso marker="kernel:file">file(3)</seealso> - manual page in <c>Kernel</c>.</p> - <p>The maximum number of ports that can be open at the same + <c>PosixCode</c>, see + <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p> + <p>The maximum number of ports that can be open at the same time can be configured by passing command-line flag - <seealso marker="erl#max_ports"><c>+Q</c></seealso> to - <c>erl(1)</c>.</p> + <seealso marker="erl#max_ports"><c>+Q</c></seealso> to + <c>erl(1)</c>.</p> </desc> </func> @@ -3647,11 +3764,11 @@ os_prompt% </pre> <desc> <p>Portable hash function that gives the same hash for the same Erlang term regardless of machine architecture and - <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 4.9.1.1). + ERTS version (the BIF was introduced in ERTS 4.9.1.1). The function returns a hash value for <c><anno>Term</anno></c> within the range <c>1..<anno>Range</anno></c>. The maximum value for - <c><anno>Range</anno></c> is 2^32.</p> + <c><anno>Range</anno></c> is 2^32.</p> <p>This BIF can be used instead of the old deprecated BIF <c>erlang:hash/2</c>, as it calculates better hashes for all data types, but consider using <c>phash2/1,2</c> instead.</p> @@ -3667,11 +3784,11 @@ os_prompt% </pre> <desc> <p>Portable hash function that gives the same hash for the same Erlang term regardless of machine architecture and - <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 5.2). + ERTS version (the BIF was introduced in ERTS 5.2). The function returns a hash value for - <c><anno>Term</anno></c> within the range - <c>0..<anno>Range</anno>-1</c>. The maximum value for - <c><anno>Range</anno></c> is 2^32. When without argument + <c><anno>Term</anno></c> within the range + <c>0..<anno>Range</anno>-1</c>. The maximum value for + <c><anno>Range</anno></c> is 2^32. When without argument <c><anno>Range</anno></c>, a value in the range 0..2^27-1 is returned.</p> <p>This BIF is always to be used for hashing terms. It @@ -3691,66 +3808,109 @@ os_prompt% </pre> representation of <c><anno>Pid</anno></c>.</p> <warning> <p>This BIF is intended for debugging and is not to be used - in application programs.</p> + in application programs.</p> </warning> </desc> </func> <func> + <name name="port_call" arity="3"/> + <fsummary>Perform a synchronous call to a port with term data.</fsummary> + <desc> + <p>Performs a synchronous call to a port. The meaning of + <c><anno>Operation</anno></c> and <c><anno>Data</anno></c> + depends on the port, that is, + on the port driver. Not all port drivers support this feature.</p> + <p><c><anno>Port</anno></c> is a port identifier, + referring to a driver.</p> + <p><c><anno>Operation</anno></c> is an integer, which is passed on to + the driver.</p> + <p><c><anno>Data</anno></c> is any Erlang term. This data is converted + to binary term format and sent to the port.</p> + <p>Returns a term from the driver. The meaning of the returned + data also depends on the port driver.</p> + <p>Failures:</p> + <taglist> + <tag><c>badarg</c></tag> + <item> + If <c><anno>Port</anno></c> is not an identifier of an open port, + or the registered name of an open port. If the calling + process was previously linked to the closed port, + identified by <c><anno>Port</anno></c>, the exit signal + from the port is guaranteed to be delivered before this + <c>badarg</c> exception occurs. + </item> + <tag><c>badarg</c></tag> + <item> + If <c><anno>Operation</anno></c> does not fit in a 32-bit integer. + </item> + <tag><c>badarg</c></tag> + <item> + If the port driver does not support synchronous control operations. + </item> + <tag><c>badarg</c></tag> + <item> + If the port driver so decides for any reason (probably + something wrong with <c><anno>Operation</anno></c> + or <c><anno>Data</anno></c>). + </item> + </taglist> + </desc> + </func> + + <func> <name name="port_close" arity="1"/> - <fsummary>Closes an open port.</fsummary> - <desc> - <p>Closes an open port. Roughly the same as - <c><anno>Port</anno> ! {self(), close}</c> except for the error behavior - (see the following), being synchronous, and that the port does - <em>not</em> reply with <c>{Port, closed}</c>. Any process can - close a port with <c>port_close/1</c>, not only the port owner - (the connected process). If the calling process is linked to + <fsummary>Close an open port.</fsummary> + <desc> + <p>Closes an open port. Roughly the same as <c><anno>Port</anno> ! + {self(), close}</c> except for the error behavior + (see below), being synchronous, and that the port does + <em>not</em> reply with <c>{Port, closed}</c>. Any process can + close a port with <c>port_close/1</c>, not only the port owner + (the connected process). If the calling process is linked to the port identified by <c><anno>Port</anno></c>, the exit - signal from the port is guaranteed to be delivered before - <c>port_close/1</c> returns.</p> + signal from the port is guaranteed to be delivered before + <c>port_close/1</c> returns.</p> <p>For comparison: <c><anno>Port</anno> ! {self(), close}</c> - only fails with <c>badarg</c> if <c><anno>Port</anno></c> does - not refer to a port or a process. If <c><anno>Port</anno></c> - is a closed port, nothing happens. If <c><anno>Port</anno></c> + only fails with <c>badarg</c> if <c><anno>Port</anno></c> does + not refer to a port or a process. If <c><anno>Port</anno></c> + is a closed port, nothing happens. If <c><anno>Port</anno></c> is an open port and the calling process is the port owner, - the port replies with <c>{Port, closed}</c> when all buffers - have been flushed and the port really closes. If the calling - process is not the port owner, the <em>port owner</em> fails - with <c>badsig</c>.</p> + the port replies with <c>{Port, closed}</c> when all buffers + have been flushed and the port really closes. If the calling + process is not the port owner, the <em>port owner</em> fails + with <c>badsig</c>.</p> <p>Notice that any process can close a port using <c><anno>Port</anno> ! {PortOwner, close}</c> as if it itself was the port owner, but the reply always goes to the port owner.</p> - <p>As from OTP R16, <c><anno>Port</anno> ! {PortOwner, close}</c> is truly - asynchronous. Notice that this operation has always been - documented as an asynchronous operation, while the underlying - implementation has been synchronous. <c>port_close/1</c> is - however still fully synchronous. This because of its error - behavior.</p> - <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an identifier - of an open port, or the registered name of an open port. - If the calling process was previously linked to the closed - port, identified by <c><anno>Port</anno></c>, the exit - signal from the port is guaranteed to be delivered before - this <c>badarg</c> exception occurs.</p> + <p>As from Erlang/OTP R16, + <c><anno>Port</anno> ! {PortOwner, close}</c> is truly + asynchronous. Notice that this operation has always been + documented as an asynchronous operation, while the underlying + implementation has been synchronous. <c>port_close/1</c> is + however still fully synchronous because of its error behavior.</p> + <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an + identifier of an open port, or the registered name of an open port. + If the calling process was previously linked to the closed + port, identified by <c><anno>Port</anno></c>, the exit + signal from the port is guaranteed to be delivered before + this <c>badarg</c> exception occurs.</p> </desc> </func> <func> <name name="port_command" arity="2"/> - <fsummary>Sends data to a port.</fsummary> + <fsummary>Send data to a port.</fsummary> <desc> <p>Sends data to a port. Same as - <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except - for the error - behavior and being synchronous (see the following). Any process - can send data to a port with <c>port_command/2</c>, not only the - port owner (the connected process).</p> - <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> - only fails with <c>badarg</c> if <c><anno>Port</anno></c> - does not refer to a port or a process. If - <c><anno>Port</anno></c> is a closed port, the data message - disappears + <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except for + the error behavior and being synchronous (see below). Any process + can send data to a port with <c>port_command/2</c>, not only the + port owner (the connected process).</p> + <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command, + Data}}</c> only fails with <c>badarg</c> if <c><anno>Port</anno></c> + does not refer to a port or a process. If <c><anno>Port</anno></c> is + a closed port, the data message disappears without a sound. If <c><anno>Port</anno></c> is open and the calling process is not the port owner, the <em>port owner</em> fails with <c>badsig</c>. The port owner fails with <c>badsig</c> @@ -3758,57 +3918,58 @@ os_prompt% </pre> <p>Notice that any process can send to a port using <c><anno>Port</anno> ! {PortOwner, {command, <anno>Data</anno>}}</c> as if it itself was the port owner.</p> - <p>If the port is busy, the calling process is suspended - until the port is not busy any more.</p> - <p>As from OTP-R16, <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> - is truly asynchronous. Notice that this operation has always been - documented as an asynchronous operation, while the underlying - implementation has been synchronous. <c>port_command/2</c> is - however still fully synchronous. This because of its error - behavior.</p> + <p>If the port is busy, the calling process is suspended + until the port is not busy any more.</p> + <p>As from Erlang/OTP R16, + <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> + is truly asynchronous. Notice that this operation has always been + documented as an asynchronous operation, while the underlying + implementation has been synchronous. <c>port_command/2</c> is + however still fully synchronous because of its error behavior.</p> <p>Failures:</p> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Port</anno></c> is not an identifier of an open - port, or the registered name of an open port. If the - calling process was previously linked to the closed port, - identified by <c><anno>Port</anno></c>, the exit signal - from the port is guaranteed to be delivered before this - <c>badarg</c> exception occurs. - </item> + <p>If <c><anno>Port</anno></c> is not an identifier of an open + port, or the registered name of an open port. If the + calling process was previously linked to the closed port, + identified by <c><anno>Port</anno></c>, the exit signal + from the port is guaranteed to be delivered before this + <c>badarg</c> exception occurs.</p> + </item> <tag><c>badarg</c></tag> <item> - If <c><anno>Data</anno></c> is an invalid I/O list. - </item> - </taglist> + <p>If <c><anno>Data</anno></c> is an invalid I/O list.</p> + </item> + </taglist> </desc> </func> <func> <name name="port_command" arity="3"/> - <fsummary>Sends data to a port.</fsummary> + <fsummary>Send data to a port.</fsummary> <desc> <p>Sends data to a port. <c>port_command(Port, Data, [])</c> - equals <c>port_command(Port, Data)</c>.</p> - <p>If the port command is aborted, <c>false</c> is returned, - otherwise <c>true</c>.</p> - <p>If the port is busy, the calling process is suspended - until the port is not busy any more.</p> - <p>The following <c><anno>Option</anno></c>s are valid:</p> + equals <c>port_command(Port, Data)</c>.</p> + <p>If the port command is aborted, <c>false</c> is returned, + otherwise <c>true</c>.</p> + <p>If the port is busy, the calling process is suspended + until the port is not busy anymore.</p> + <p><c><anno>Option</anno></c>s:</p> <taglist> <tag><c>force</c></tag> <item>The calling process is not suspended if the port is - busy, instead the port command is forced through. The - call fails with a <c>notsup</c> exception if the - driver of the port does not support this. For more - information, see driver flag - <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>. + busy, instead the port command is forced through. The + call fails with a <c>notsup</c> exception if the + driver of the port does not support this. For more + information, see driver flag + <seealso marker="driver_entry#driver_flags"> + <c>![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]</c></seealso>. </item> <tag><c>nosuspend</c></tag> <item>The calling process is not suspended if the port is - busy, instead the port command is aborted and - <c>false</c> is returned. + busy, instead the port command is aborted and + <c>false</c> is returned. </item> </taglist> <note> @@ -3818,34 +3979,34 @@ os_prompt% </pre> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Port</anno></c> is not an identifier of an open - port, or the registered name of an open port. If the - calling process was previously linked to the closed port, - identified by <c><anno>Port</anno></c>, the exit signal - from the port is guaranteed to be delivered before this - <c>badarg</c> exception occurs. - </item> + If <c><anno>Port</anno></c> is not an identifier of an open + port, or the registered name of an open port. If the + calling process was previously linked to the closed port, + identified by <c><anno>Port</anno></c>, the exit signal + from the port is guaranteed to be delivered before this + <c>badarg</c> exception occurs. + </item> <tag><c>badarg</c></tag> <item> - If <c><anno>Data</anno></c> is an invalid I/O list. - </item> + If <c><anno>Data</anno></c> is an invalid I/O list. + </item> <tag><c>badarg</c></tag> <item> - If <c><anno>OptionList</anno></c> is an invalid option list. - </item> + If <c><anno>OptionList</anno></c> is an invalid option list. + </item> <tag><c>notsup</c></tag> <item> - If option <c>force</c> has been passed, but the - driver of the port does not allow forcing through - a busy port. - </item> - </taglist> + If option <c>force</c> has been passed, but the + driver of the port does not allow forcing through + a busy port. + </item> + </taglist> </desc> </func> <func> <name name="port_connect" arity="2"/> - <fsummary>Sets the owner of a port.</fsummary> + <fsummary>Set the owner of a port.</fsummary> <desc> <p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>. Roughly the same as @@ -3853,14 +4014,14 @@ os_prompt% </pre> except for the following:</p> <list type="bulleted"> <item> - <p>The error behavior differs, see the following.</p> + <p>The error behavior differs, see below.</p> </item> <item> <p>The port does <em>not</em> reply with <c>{Port,connected}</c>.</p> </item> <item> - <p><c>port_connect/1</c> is synchronous, see the following.</p> + <p><c>port_connect/1</c> is synchronous, see below.</p> </item> <item> <p>The new port owner gets linked to the port.</p> @@ -3872,7 +4033,7 @@ os_prompt% </pre> <c>port_connect/2</c>.</p> <p>For comparison: <c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</c> - only fails with <c>badarg</c> if <c><anno>Port</anno></c> + only fails with <c>badarg</c> if <c><anno>Port</anno></c> does not refer to a port or a process. If <c><anno>Port</anno></c> is a closed port, nothing happens. If <c><anno>Port</anno></c> @@ -3882,40 +4043,39 @@ os_prompt% </pre> the port, while the new is not. If <c><anno>Port</anno></c> is an open port and the calling process is not the port owner, the <em>port owner</em> fails with <c>badsig</c>. The port - owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not an - existing local process identifier.</p> + owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not + an existing local process identifier.</p> <p>Notice that any process can set the port owner using <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> as if it itself was the port owner, but the reply always goes to the port owner.</p> - <p>As from OTP-R16, - <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> is - truly asynchronous. Notice that this operation has always been - documented as an asynchronous operation, while the underlying - implementation has been synchronous. <c>port_connect/2</c> is - however still fully synchronous. This because of its error - behavior.</p> + <p>As from Erlang/OTP R16, + <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> + is truly asynchronous. Notice that this operation has always been + documented as an asynchronous operation, while the underlying + implementation has been synchronous. <c>port_connect/2</c> is + however still fully synchronous because of its error behavior.</p> <p>Failures:</p> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Port</anno></c> is not an identifier of an open port, or - the registered name of an open port. If the calling - process was previously linked to the closed port, - identified by <c><anno>Port</anno></c>, the exit signal - from the port is guaranteed to be delivered before this - <c>badarg</c> exception occurs. - </item> + If <c><anno>Port</anno></c> is not an identifier of an open port, + or the registered name of an open port. If the calling + process was previously linked to the closed port, + identified by <c><anno>Port</anno></c>, the exit signal + from the port is guaranteed to be delivered before this + <c>badarg</c> exception occurs. + </item> <tag><c>badarg</c></tag> - <item>If process identified by <c>Pid</c> is not an existing - local process.</item> - </taglist> + <item>If the process identified by <c>Pid</c> is not an existing + local process.</item> + </taglist> </desc> </func> <func> <name name="port_control" arity="3"/> - <fsummary>Performs a synchronous control operation on a port.</fsummary> + <fsummary>Perform a synchronous control operation on a port.</fsummary> <desc> <p>Performs a synchronous control operation on a port. The meaning of <c><anno>Operation</anno></c> and @@ -3929,71 +4089,24 @@ os_prompt% </pre> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Port</anno></c> is not an open port or the registered - name of an open port. - </item> + If <c><anno>Port</anno></c> is not an open port or the registered + name of an open port. + </item> <tag><c>badarg</c></tag> <item> - If <c><anno>Operation</anno></c> cannot fit in a 32-bit integer. - </item> + If <c><anno>Operation</anno></c> cannot fit in a 32-bit integer. + </item> <tag><c>badarg</c></tag> <item> - If the port driver does not support synchronous control - operations. - </item> + If the port driver does not support synchronous control operations. + </item> <tag><c>badarg</c></tag> <item> - If the port driver so decides for any reason (probably + If the port driver so decides for any reason (probably something wrong with <c><anno>Operation</anno></c> or <c><anno>Data</anno></c>). - </item> - </taglist> - </desc> - </func> - - <func> - <name name="port_call" arity="3"/> - <fsummary>Performs a synchronous call to a port with term data.</fsummary> - <desc> - <p>Performs a synchronous call to a port. The meaning of - <c><anno>Operation</anno></c> and <c><anno>Data</anno></c> - depends on the port, that is, - on the port driver. Not all port drivers support this feature.</p> - <p><c><anno>Port</anno></c> is a port identifier, - referring to a driver.</p> - <p><c><anno>Operation</anno></c> is an integer, which is passed on to - the driver.</p> - <p><c><anno>Data</anno></c> is any Erlang term. This data is converted - to binary term format and sent to the port.</p> - <p>Returns a term from the driver. The meaning of the returned - data also depends on the port driver.</p> - <p>Failures:</p> - <taglist> - <tag><c>badarg</c></tag> - <item> - If <c><anno>Port</anno></c> is not an identifier of an open port, - or the registered name of an open port. If the calling - process was previously linked to the closed port, - identified by <c><anno>Port</anno></c>, the exit signal - from the port is guaranteed to be delivered before this - <c>badarg</c> exception occurs. - </item> - <tag><c>badarg</c></tag> - <item> - If <c><anno>Operation</anno></c> does not fit in a 32-bit integer. - </item> - <tag><c>badarg</c></tag> - <item> - If the port driver does not support synchronous control - operations. - </item> - <tag><c>badarg</c></tag> - <item> - If the port driver so decides for any reason (probably - something wrong with <c><anno>Operation</anno></c> - or <c><anno>Data</anno></c>). - </item> - </taglist> + </item> + </taglist> </desc> </func> @@ -4005,11 +4118,11 @@ os_prompt% </pre> <c><anno>Port</anno></c>, or <c>undefined</c> if the port is not open. The order of the tuples is undefined, and all the tuples are not mandatory. - If the port is closed and the calling process - was previously linked to the port, the exit signal from the - port is guaranteed to be delivered before <c>port_info/1</c> - returns <c>undefined</c>.</p> - <p>The result contains information about the following + If the port is closed and the calling process + was previously linked to the port, the exit signal from the + port is guaranteed to be delivered before <c>port_info/1</c> + returns <c>undefined</c>.</p> + <p>The result contains information about the following <c>Item</c>s:</p> <list type="bulleted"> <item><c>registered_name</c> (if the port has a registered @@ -4022,9 +4135,9 @@ os_prompt% </pre> <item><c>output</c></item> </list> <p>For more information about the different <c>Item</c>s, see - <seealso marker="#port_info/2">port_info/2</seealso>.</p> + <seealso marker="#port_info/2"><c>port_info/2</c></seealso>.</p> <p>Failure: <c>badarg</c> if <c>Port</c> is not a local port - identifier, or an atom.</p> + identifier, or an atom.</p> </desc> </func> @@ -4032,15 +4145,15 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="1"/> <fsummary>Information about the connected process of a port.</fsummary> <desc> - <p><c><anno>Pid</anno></c> is the process identifier of the process - connected to the port.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Pid</anno></c> is the process identifier of the process + connected to the port.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4048,15 +4161,15 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="2"/> <fsummary>Information about the internal index of a port.</fsummary> <desc> - <p><c><anno>Index</anno></c> is the internal index of the port. This - index can be used to separate ports.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Index</anno></c> is the internal index of the port. This + index can be used to separate ports.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4064,15 +4177,15 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="3"/> <fsummary>Information about the input of a port.</fsummary> <desc> - <p><c><anno>Bytes</anno></c> is the total number of bytes - read from the port.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Bytes</anno></c> is the total number of bytes + read from the port.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4080,15 +4193,15 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="4"/> <fsummary>Information about the links of a port.</fsummary> <desc> - <p><c><anno>Pids</anno></c> is a list of the process identifiers - of the processes that the port is linked to.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Pids</anno></c> is a list of the process identifiers + of the processes that the port is linked to.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4096,7 +4209,7 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="5"/> <fsummary>Information about the locking of a port.</fsummary> <desc> - <p><c><anno>Locking</anno></c> is one of the following:</p> + <p><c><anno>Locking</anno></c> is one of the following:</p> <list type="bulleted"> <item><c>false</c> (emulator without SMP support)</item> <item><c>port_level</c> (port-specific locking)</item> @@ -4104,13 +4217,13 @@ os_prompt% </pre> </list> <p>Notice that these results are highly implementation-specific and can change in a future release.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4118,17 +4231,17 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="6"/> <fsummary>Information about the memory size of a port.</fsummary> <desc> - <p><c><anno>Bytes</anno></c> is the total number of - bytes allocated for this port by the runtime system. The - port itself can have allocated memory that is not - included in <c><anno>Bytes</anno></c>.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Bytes</anno></c> is the total number of + bytes allocated for this port by the runtime system. The + port itself can have allocated memory that is not + included in <c><anno>Bytes</anno></c>.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4136,15 +4249,15 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="7"/> <fsummary>Information about the monitors of a port.</fsummary> <desc> - <p><c><anno>Monitors</anno></c> represent processes that this port - monitors.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Monitors</anno></c> represent processes monitored by + this port.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4168,15 +4281,15 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="9"/> <fsummary>Information about the name of a port.</fsummary> <desc> - <p><c><anno>Name</anno></c> is the command name set by - <seealso marker="#open_port/2">open_port/2</seealso>.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Name</anno></c> is the command name set by + <seealso marker="#open_port/2"><c>open_port/2</c></seealso>.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4184,18 +4297,18 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="10"/> <fsummary>Information about the OS pid of a port.</fsummary> <desc> - <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent) - of an OS process created with - <seealso marker="#open_port/2">open_port({spawn | spawn_executable, - Command}, Options)</seealso>. If the port is not the result of spawning - an OS process, the value is <c>undefined</c>.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent) + of an OS process created with + <seealso marker="#open_port/2"><c>open_port({spawn | spawn_executable, + Command}, Options)</c></seealso>. If the port is not the result of + spawning an OS process, the value is <c>undefined</c>.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4203,18 +4316,18 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="11"/> <fsummary>Information about the output of a port.</fsummary> <desc> - <p><c><anno>Bytes</anno></c> is the total number of bytes written - to the port from Erlang processes using - <seealso marker="#port_command/2">port_command/2</seealso>, - <seealso marker="#port_command/3">port_command/3</seealso>, - or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Bytes</anno></c> is the total number of bytes written + to the port from Erlang processes using + <seealso marker="#port_command/2"><c>port_command/2</c></seealso>, + <seealso marker="#port_command/3"><c>port_command/3</c></seealso>, + or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4222,10 +4335,10 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="12"/> <fsummary>Information about the parallelism hint of a port.</fsummary> <desc> - <p><c><anno>Boolean</anno></c> corresponds to the port parallelism - hint being used by this port. For more information, see option - <seealso marker="#open_port_parallelism">parallelism</seealso> - of <seealso marker="#open_port/2">open_port/2</seealso>.</p> + <p><c><anno>Boolean</anno></c> corresponds to the port parallelism + hint used by this port. For more information, see option + <seealso marker="#open_port_parallelism"><c>parallelism</c></seealso> + of <seealso marker="#open_port/2"><c>open_port/2</c></seealso>.</p> </desc> </func> @@ -4233,16 +4346,16 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="13"/> <fsummary>Information about the queue size of a port.</fsummary> <desc> - <p><c><anno>Bytes</anno></c> is the total number - of bytes queued by the port using the <c>ERTS</c> driver queue - implementation.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>Bytes</anno></c> is the total number + of bytes queued by the port using the ERTS driver queue + implementation.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4250,15 +4363,16 @@ os_prompt% </pre> <name name="port_info" arity="2" clause_i="14"/> <fsummary>Information about the registered name of a port.</fsummary> <desc> - <p><c><anno>RegisteredName</anno></c> is the registered name of - the port. If the port has no registered name, <c>[]</c> is returned.</p> - <p>If the port identified by <c><anno>Port</anno></c> is not open, - <c>undefined</c> is returned. If the port is closed and the - calling process was previously linked to the port, the exit - signal from the port is guaranteed to be delivered before - <c>port_info/2</c> returns <c>undefined</c>.</p> + <p><c><anno>RegisteredName</anno></c> is the registered name of + the port. If the port has no registered name, <c>[]</c> is + returned.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local - port identifier, or an atom.</p> + port identifier, or an atom.</p> </desc> </func> @@ -4270,24 +4384,24 @@ os_prompt% </pre> representation of the port identifier <c><anno>Port</anno></c>.</p> <warning> <p>This BIF is intended for debugging. It is not to be used - in application programs.</p> + in application programs.</p> </warning> </desc> </func> <func> <name name="ports" arity="0"/> - <fsummary>Lists all existing ports.</fsummary> + <fsummary>List all existing ports.</fsummary> <desc> - <p>Returns a list of port identifiers corresponding to all the - ports existing on the local node.</p> - <p>Notice that an exiting port exists, but is not open.</p> + <p>Returns a list of port identifiers corresponding to all the + ports existing on the local node.</p> + <p>Notice that an exiting port exists, but is not open.</p> </desc> </func> <func> <name name="pre_loaded" arity="0"/> - <fsummary>Lists all pre-loaded modules.</fsummary> + <fsummary>List all preloaded modules.</fsummary> <desc> <p>Returns a list of Erlang modules that are preloaded in the system. As all loading of code is done through the file @@ -4298,12 +4412,13 @@ os_prompt% </pre> <func> <name name="process_display" arity="2"/> - <fsummary>Writes information about a local process on standard error.</fsummary> + <fsummary>Write information about a local process on standard error. + </fsummary> <desc> <p>Writes information about the local process <c><anno>Pid</anno></c> on standard error. The only allowed value for the atom - <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents of - the call stack, including information about the call chain, with + <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents + of the call stack, including information about the call chain, with the current function printed first. The format of the output is not further defined.</p> </desc> @@ -4311,7 +4426,7 @@ os_prompt% </pre> <func> <name name="process_flag" arity="2" clause_i="1"/> - <fsummary>Sets process flag <c>trap_exit</c> for the calling process.</fsummary> + <fsummary>Set process flag trap_exit for the calling process.</fsummary> <desc> <p>When <c>trap_exit</c> is set to <c>true</c>, exit signals arriving to a process are converted to <c>{'EXIT', From, Reason}</c> @@ -4322,13 +4437,14 @@ os_prompt% </pre> linked processes. Application processes are normally not to trap exits.</p> <p>Returns the old value of the flag.</p> - <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p> + <p>See also <seealso marker="#exit/2"><c>exit/2</c></seealso>.</p> </desc> </func> <func> <name name="process_flag" arity="2" clause_i="2"/> - <fsummary>Sets process flag <c>error_handler</c> for the calling process.</fsummary> + <fsummary>Set process flag error_handler for the calling process. + </fsummary> <desc> <p>Used by a process to redefine the error handler for undefined function calls and undefined registered @@ -4339,11 +4455,12 @@ os_prompt% </pre> </desc> </func> - <marker id="process_flag_min_heap_size"/> <func> <name name="process_flag" arity="2" clause_i="3"/> - <fsummary>Sets process flag <c>min_heap_size</c> for the calling process.</fsummary> + <fsummary>Set process flag min_heap_size for the calling process. + </fsummary> <desc> + <marker id="process_flag_min_heap_size"/> <p>Changes the minimum heap size for the calling process.</p> <p>Returns the old value of the flag.</p> </desc> @@ -4351,90 +4468,79 @@ os_prompt% </pre> <func> <name name="process_flag" arity="2" clause_i="4"/> - <fsummary>Sets process flag <c>min_bin_vheap_size</c> for the calling process.</fsummary> + <fsummary>Set process flag min_bin_vheap_size for the calling process. + </fsummary> <desc> <p>Changes the minimum binary virtual heap size for the calling process.</p> <p>Returns the old value of the flag.</p> </desc> </func> - <marker id="process_flag_max_heap_size"/> + <func> <name name="process_flag" arity="2" clause_i="5"/> - <fsummary>Sets process flag <c>max_heap_size</c> for the calling process.</fsummary> + <fsummary>Set process flag max_heap_size for the calling process. + </fsummary> <type name="max_heap_size"/> <desc> - <p> - This flag sets the maximum heap size for the calling process. + <marker id="process_flag_max_heap_size"/> + <p>This flag sets the maximum heap size for the calling process. If <c><anno>MaxHeapSize</anno></c> is an integer, the system default values for <c>kill</c> and <c>error_logger</c> are used. </p> <taglist> <tag><c>size</c></tag> <item> - <p> - The maximum size in words of the process. If set to zero, the - heap size limit is disabled. Badarg will be thrown if the value is - smaller than - <seealso marker="#process_flag_min_heap_size"><c>min_heap_size</c></seealso>. - The size check is only done when a garbage collection is triggered. - </p> - <p> - <c>size</c> is the entire heap of the process when garbage collection - is triggered, this includes all generational heaps, the process stack, + <p>The maximum size in words of the process. If set to zero, the + heap size limit is disabled. <c>badarg</c> is be thrown if the + value is smaller than <seealso marker="#process_flag_min_heap_size"> + <c>min_heap_size</c></seealso>. The size check is only done when + a garbage collection is triggered.</p> + <p><c>size</c> is the entire heap of the process when garbage collection + is triggered. This includes all generational heaps, the process stack, any <seealso marker="#process_flag_message_queue_data"> - messages that are considered to be part of the heap</seealso> and any - extra memory that the garbage collector needs during collection. - </p> - <p> - <c>size</c> is the same as can be retrieved using + messages that are considered to be part of the heap</seealso>, and any + extra memory that the garbage collector needs during collection.</p> + <p><c>size</c> is the same as can be retrieved using <seealso marker="#process_info_total_heap_size"> <c>erlang:process_info(Pid, total_heap_size)</c></seealso>, - or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c> - and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info"> - <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>. - </p> + or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c> + and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info"> + <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>.</p> </item> <tag><c>kill</c></tag> <item> - <p> - When set to <c>true</c> the runtime system will send an + <p>When set to <c>true</c>, the runtime system sends an untrappable exit signal with reason <c>kill</c> to the process if the maximum heap size is reached. The garbage collection - that triggered the <c>kill</c> will not be completed, instead the - process will exit as soon as is possible. When set to <c>false</c> - no exit signal will be sent to the process, instead it will - continue executing. - </p> - <p> - If <c>kill</c> is not defined in the map + that triggered the <c>kill</c> is not completed, instead the + process exits as soon as possible. When set to <c>false</c>, + no exit signal is sent to the process, instead it continues + executing.</p> + <p>If <c>kill</c> is not defined in the map, the system default will be used. The default system default - is <c>true</c>. It can be changed by either the erl - <seealso marker="erl#+hmaxk">+hmaxk</seealso> option, - or <seealso marker="#system_flag_max_heap_size"><c> - erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. - </p> + is <c>true</c>. It can be changed by either option + <seealso marker="erl#+hmaxk">+hmaxk</seealso> in <c>erl(1)</c>, + or <seealso marker="#system_flag_max_heap_size"> + <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> <tag><c>error_logger</c></tag> <item> - <p> - When set to <c>true</c> the runtime system will send a - message to the current <seealso marker="kernel:error_logger"><c>error_logger</c></seealso> + <p>When set to <c>true</c>, the runtime system sends a + message to the current <seealso marker="kernel:error_logger"> + <c>error_logger</c></seealso> containing details about the process when the maximum - heap size is reached. One <c>error_logger</c> report will - be sent each time the limit is reached. - </p> - <p> - If <c>error_logger</c> is not defined in the map the system - default will be used. The default system default is <c>true</c>. - It can be changed by either the erl <seealso marker="erl#+hmaxel">+hmaxel</seealso> - option, or <seealso marker="#system_flag_max_heap_size"><c> - erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. - </p> + heap size is reached. One <c>error_logger</c> report is sent + each time the limit is reached.</p> + <p>If <c>error_logger</c> is not defined in the map, the system + default is used. The default system default is <c>true</c>. + It can be changed by either the option + <seealso marker="erl#+hmaxel">+hmaxel</seealso> int <c>erl(1)</c>, + or <seealso marker="#system_flag_max_heap_size"> + <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> </taglist> - <p> - The heap size of a process is quite hard to predict, especially the + <p>The heap size of a process is quite hard to predict, especially the amount of memory that is used during the garbage collection. When contemplating using this option, it is recommended to first run it in production with <c>kill</c> set to <c>false</c> and inspect @@ -4444,60 +4550,57 @@ os_prompt% </pre> </p> </desc> </func> - <marker id="process_flag_message_queue_data"/> + <func> <name name="process_flag" arity="2" clause_i="6"/> - <fsummary>Set process flag <c>message_queue_data</c> for the calling process</fsummary> + <fsummary>Set process flag message_queue_data for the calling process. + </fsummary> <type name="message_queue_data"/> <desc> - <p>This flag determines how messages in the message queue - are stored. When the flag is:</p> + <marker id="process_flag_message_queue_data"/> + <p>This flag determines how messages in the message queue + are stored, as follows:</p> <taglist> <tag><c>off_heap</c></tag> - <item><p> - <em>All</em> messages in the message queue will be stored - outside of the process heap. This implies that <em>no</em> - messages in the message queue will be part of a garbage - collection of the process. - </p></item> + <item> + <p><em>All</em> messages in the message queue will be stored + outside of the process heap. This implies that <em>no</em> + messages in the message queue will be part of a garbage + collection of the process.</p> + </item> <tag><c>on_heap</c></tag> - <item><p> - All messages in the message queue will eventually be - placed on heap. They may however temporarily be stored - off heap. This is how messages always have been stored - up until ERTS version 8.0. - </p></item> + <item> + <p>All messages in the message queue will eventually be + placed on heap. They can however temporarily be stored + off heap. This is how messages always have been stored + up until ERTS 8.0.</p> + </item> </taglist> - <p> - The default <c>message_queue_data</c> process flag is determined - by the <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> - <c>erl</c> command line argument. - </p> - <p> - If the process potentially may get a hugh amount of messages, - you are recommended to set the flag to <c>off_heap</c>. This - since a garbage collection with lots of messages placed on - the heap may become extremly expensive and the process may - consume large amounts of memory. Performance of the - actual message passing is however generally better when not - using the <c>off_heap</c> flag. - </p> - <p> - When changing this flag messages will be moved. This work - has been initiated but not completed when this function - call returns. - </p> + <p>The default <c>message_queue_data</c> process flag is determined + by command-line argument <seealso marker="erl#+hmqd"> + <c>+hmqd</c></seealso> in <c>erl(1)</c>.</p> + <p>If the process potentially can get many messages, + you are advised to set the flag to <c>off_heap</c>. This + because a garbage collection with many messages placed on + the heap can become extremly expensive and the process can + consume large amounts of memory. Performance of the + actual message passing is however generally better when not + using flag <c>off_heap</c>.</p> + <p>When changing this flag messages will be moved. This work + has been initiated but not completed when this function + call returns.</p> <p>Returns the old value of the flag.</p> </desc> </func> + <func> <name name="process_flag" arity="2" clause_i="7"/> - <fsummary>Sets process flag <c>priority</c> for the calling process.</fsummary> + <fsummary>Set process flag priority for the calling process.</fsummary> <type name="priority_level"/> <desc> <p><marker id="process_flag_priority"></marker> Sets the process priority. <c><anno>Level</anno></c> is an atom. - There are four priority levels: <c>low</c>, + Four priority levels exist: <c>low</c>, <c>normal</c>, <c>high</c>, and <c>max</c>. Default is <c>normal</c>.</p> <note> @@ -4511,23 +4614,23 @@ os_prompt% </pre> <c>low</c> are interleaved. Processes on priority <c>low</c> are selected for execution less frequently than processes on priority <c>normal</c>.</p> - <p>When there are runnable processes on priority <c>high</c>, + <p>When runnable processes on priority <c>high</c> exist, no processes on priority <c>low</c> or <c>normal</c> are - selected for execution. Notice however, that this does + selected for execution. Notice however that this does <em>not</em> mean that no processes on priority <c>low</c> - or <c>normal</c> can run when there are processes - running on priority <c>high</c>. On the runtime + or <c>normal</c> can run when processes + are running on priority <c>high</c>. On the runtime system with SMP support, more processes can be running - in parallel than processes on priority <c>high</c>, that is, + in parallel than processes on priority <c>high</c>. That is, a <c>low</c> and a <c>high</c> priority process can execute at the same time.</p> - <p>When there are runnable processes on priority <c>max</c>, + <p>When runnable processes on priority <c>max</c> exist, no processes on priority <c>low</c>, <c>normal</c>, or <c>high</c> are selected for execution. As with priority <c>high</c>, processes on lower priorities can execute in parallel with processes on priority <c>max</c>.</p> - <p>Scheduling is preemptive. Regardless of priority, a process - is preempted when it has consumed more than a certain number + <p>Scheduling is pre-emptive. Regardless of priority, a process + is pre-empted when it has consumed more than a certain number of reductions since the last time it was selected for execution.</p> <note> @@ -4543,7 +4646,7 @@ os_prompt% </pre> take this into account and handle such scenarios by yourself.</p> <p>Making calls from a <c>high</c> priority process into code - that you have no control over can cause the <c>high</c> + that you has no control over can cause the <c>high</c> priority process to wait for a process with lower priority. That is, effectively decreasing the priority of the <c>high</c> priority process during the call. Even if this @@ -4557,8 +4660,8 @@ os_prompt% </pre> <em>especially</em> priority <c>high</c>. A process on priority <c>high</c> is only to perform work for short periods. Busy looping for - long periods in a <c>high</c> priority process does - most likely cause problems, as important OTP servers + long periods in a <c>high</c> priority process causes + most likely problems, as important OTP servers run on priority <c>normal</c>.</p> <p>Returns the old value of the flag.</p> </desc> @@ -4566,10 +4669,10 @@ os_prompt% </pre> <func> <name name="process_flag" arity="2" clause_i="8"/> - <fsummary>Sets process flag <c>save_calls</c> for the calling process.</fsummary> + <fsummary>Set process flag save_calls for the calling process.</fsummary> <desc> <p><c><anno>N</anno></c> must be an integer in the interval 0..10000. - If <c><anno>N</anno></c> is greater than 0, call saving is made + If <c><anno>N</anno></c> > 0, call saving is made active for the process. This means that information about the <c><anno>N</anno></c> most recent global function calls, BIF calls, sends, and @@ -4580,12 +4683,12 @@ os_prompt% </pre> explicitly mentioned. Only a fixed amount of information is saved, as follows:</p> <list type="bulleted"> - <item>A tuple <c>{Module, Function, Arity}</c> for - function calls</item> - <item> The atoms <c>send</c>, <c>'receive'</c>, and + <item><p>A tuple <c>{Module, Function, Arity}</c> for + function calls</p></item> + <item><p>The atoms <c>send</c>, <c>'receive'</c>, and <c>timeout</c> for sends and receives (<c>'receive'</c> when a message is received and <c>timeout</c> when a - receive times out)</item> + receive times out)</p></item> </list> <p>If <c>N</c> = 0, call saving is disabled for the process, which is the @@ -4597,7 +4700,7 @@ os_prompt% </pre> <func> <name name="process_flag" arity="2" clause_i="9"/> - <fsummary>Sets process flag <c>sensitive</c> for the calling process.</fsummary> + <fsummary>Set process flag sensitive for the calling process.</fsummary> <desc> <p>Sets or clears flag <c>sensitive</c> for the current process. When a process has been marked as sensitive by calling @@ -4607,13 +4710,13 @@ os_prompt% </pre> <p>Features that are disabled include (but are not limited to) the following:</p> <list type="bulleted"> - <item>Tracing: Trace flags can still be set for the process, + <item><p>Tracing. Trace flags can still be set for the process, but no trace messages of any kind are generated. (If flag <c>sensitive</c> is turned off, trace messages are again - generated if any trace flags are set.)</item> - <item>Sequential tracing: The sequential trace token is + generated if any trace flags are set.)</p></item> + <item><p>Sequential tracing. The sequential trace token is propagated as usual, but no sequential trace messages are - generated.</item> + generated.</p></item> </list> <p><c>process_info/1,2</c> cannot be used to read out the message queue or the process dictionary (both are returned @@ -4623,19 +4726,19 @@ os_prompt% </pre> are omitted.</p> <p>If <c>{save_calls,N}</c> has been set for the process, no function calls are saved to the call saving list. - (The call saving list is not cleared. Furthermore, send, receive, - and timeout events are still added to the list.)</p> + (The call saving list is not cleared. Also, send, receive, + and time-out events are still added to the list.)</p> <p>Returns the old value of the flag.</p> </desc> </func> <func> <name name="process_flag" arity="3"/> - <fsummary>Sets process flags for a process.</fsummary> + <fsummary>Set process flags for a process.</fsummary> <desc> <p>Sets certain flags for the process <c><anno>Pid</anno></c>, in the same manner as - <seealso marker="#process_flag/2">process_flag/2</seealso>. + <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>. Returns the old value of the flag. The valid values for <c><anno>Flag</anno></c> are only a subset of those allowed in <c>process_flag/2</c>, namely <c>save_calls</c>.</p> @@ -4650,46 +4753,46 @@ os_prompt% </pre> <type name="process_info_result_item"/> <type name="priority_level"/> <type name="stack_item"/> - <type name="max_heap_size" /> - <type name="message_queue_data" /> + <type name="max_heap_size"/> + <type name="message_queue_data"/> <desc> <p>Returns a list containing <c><anno>InfoTuple</anno></c>s with - miscellaneous information about the process identified by - <c>Pid</c>, or <c>undefined</c> if the process is not alive.</p> - <p>The order of the <c><anno>InfoTuple</anno></c>s is undefined and - all <c><anno>InfoTuple</anno></c>s are not mandatory. + miscellaneous information about the process identified by + <c>Pid</c>, or <c>undefined</c> if the process is not alive.</p> + <p>The order of the <c><anno>InfoTuple</anno></c>s is undefined and + all <c><anno>InfoTuple</anno></c>s are not mandatory. The <c><anno>InfoTuple</anno></c>s - part of the result can be changed without prior notice.</p> - <p>The <c><anno>InfoTuple</anno></c>s with the following items - are part of the result:</p> + part of the result can be changed without prior notice.</p> + <p>The <c><anno>InfoTuple</anno></c>s with the following items + are part of the result:</p> <list type="bulleted"> <item><c>current_function</c></item> <item><c>initial_call</c></item> <item><c>status</c></item> - <item><c>message_queue_len</c></item> + <item><c>message_queue_len</c></item> <item><c>messages</c></item> <item><c>links</c></item> - <item><c>dictionary</c></item> + <item><c>dictionary</c></item> <item><c>trap_exit</c></item> <item><c>error_handler</c></item> - <item><c>priority</c></item> + <item><c>priority</c></item> <item><c>group_leader</c></item> <item><c>total_heap_size</c></item> - <item><c>heap_size</c></item> + <item><c>heap_size</c></item> <item><c>stack_size</c></item> <item><c>reductions</c></item> - <item><c>garbage_collection</c></item> + <item><c>garbage_collection</c></item> </list> <p>If the process identified by <c><anno>Pid</anno></c> has a registered name, - also an <c><anno>InfoTuple</anno></c> with item <c>registered_name</c> - appears.</p> - <p>For information about specific <c><anno>InfoTuple</anno></c>s, see - <seealso marker="#process_info/2">process_info/2</seealso>.</p> + also an <c><anno>InfoTuple</anno></c> with item <c>registered_name</c> + is included.</p> + <p>For information about specific <c><anno>InfoTuple</anno></c>s, see + <seealso marker="#process_info/2"><c>process_info/2</c></seealso>.</p> <warning> <p>This BIF is intended for <em>debugging only</em>. For - all other purposes, use - <seealso marker="#process_info/2">process_info/2</seealso>.</p> + all other purposes, use <seealso marker="#process_info/2"> + <c>process_info/2</c></seealso>.</p> </warning> <p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a local process.</p> @@ -4704,38 +4807,39 @@ os_prompt% </pre> <type name="process_info_result_item"/> <type name="stack_item"/> <type name="priority_level"/> - <type name="max_heap_size" /> - <type name="message_queue_data" /> + <type name="max_heap_size"/> + <type name="message_queue_data"/> <desc> <p>Returns information about the process identified by - <c><anno>Pid</anno></c>, as specified by - <c><anno>Item</anno></c> or <c><anno>ItemList</anno></c>. - Returns <c>undefined</c> if the process is not alive.</p> - <p>If the process is alive and a single <c><anno>Item</anno></c> - is given, the returned value is the corresponding - <c><anno>InfoTuple</anno></c>, unless <c>Item =:= registered_name</c> - and the process has no registered name. In this case, - <c>[]</c> is returned. This strange behavior is because of - historical reasons, and is kept for backward compatibility.</p> - <p>If <c><anno>ItemList</anno></c> is given, the result is - <c><anno>InfoTupleList</anno></c>. - The <c><anno>InfoTuple</anno></c>s in - <c><anno>InfoTupleList</anno></c> appear with the corresponding - <c><anno>Item</anno></c>s in the same order as the - <c><anno>Item</anno></c>s appeared - in <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s can - appear multiple times in <c><anno>ItemList</anno></c>.</p> - <note><p>If <c>registered_name</c> is part of <c><anno>ItemList</anno></c> - and the process has no name registered a - <c>{registered_name, []}</c>, <c><anno>InfoTuple</anno></c> - <em>will</em> appear in the resulting - <c><anno>InfoTupleList</anno></c>. This - behavior is different when a single - <c>Item =:= registered_name</c> is given, and when - <c>process_info/1</c> is used.</p> - </note> - <p>The following <c><anno>InfoTuple</anno></c>s with corresponding - <c><anno>Item</anno></c>s are valid:</p> + <c><anno>Pid</anno></c>, as specified by + <c><anno>Item</anno></c> or <c><anno>ItemList</anno></c>. + Returns <c>undefined</c> if the process is not alive.</p> + <p>If the process is alive and a single <c><anno>Item</anno></c> + is specified, the returned value is the corresponding + <c><anno>InfoTuple</anno></c>, unless <c>Item =:= registered_name</c> + and the process has no registered name. In this case, + <c>[]</c> is returned. This strange behavior is because of + historical reasons, and is kept for backward compatibility.</p> + <p>If <c><anno>ItemList</anno></c> is specified, the result is + <c><anno>InfoTupleList</anno></c>. + The <c><anno>InfoTuple</anno></c>s in + <c><anno>InfoTupleList</anno></c> are included with the corresponding + <c><anno>Item</anno></c>s in the same order as the + <c><anno>Item</anno></c>s were included + in <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s can + be included multiple times in <c><anno>ItemList</anno></c>.</p> + <note> + <p>If <c>registered_name</c> is part of <c><anno>ItemList</anno></c> + and the process has no name registered, a + <c>{registered_name, []}</c>, <c><anno>InfoTuple</anno></c> + <em>will</em> be included in the resulting + <c><anno>InfoTupleList</anno></c>. This + behavior is different when a single + <c>Item =:= registered_name</c> is specified, and when + <c>process_info/1</c> is used.</p> + </note> + <p>Valid <c><anno>InfoTuple</anno></c>s with corresponding + <c><anno>Item</anno></c>s:</p> <taglist> <tag><c>{backtrace, <anno>Bin</anno>}</c></tag> <item> @@ -4748,15 +4852,15 @@ os_prompt% </pre> <tag><c>{binary, <anno>BinInfo</anno>}</c></tag> <item> <p><c><anno>BinInfo</anno></c> is a list containing miscellaneous - information about binaries currently being referred to by this + information about binaries currently referred to by this process. This <c><anno>InfoTuple</anno></c> can be changed or removed without prior notice.</p> </item> <tag><c>{catchlevel, <anno>CatchLevel</anno>}</c></tag> <item> <p><c><anno>CatchLevel</anno></c> is the number of currently active - catches in this process. This <c><anno>InfoTuple</anno></c> can be - changed or removed without prior notice.</p> + catches in this process. This <c><anno>InfoTuple</anno></c> can be + changed or removed without prior notice.</p> </item> <tag><c>{current_function, {<anno>Module</anno>, <anno>Function</anno>, Arity}}</c></tag> @@ -4772,14 +4876,15 @@ os_prompt% </pre> <p><c><anno>Module</anno></c>, <c><anno>Function</anno></c>, <c><anno>Arity</anno></c> is the current function call of the process. - <c><anno>Location</anno></c> is a list of two-tuples describing the - location in the source code.</p> + <c><anno>Location</anno></c> is a list of two-tuples describing + the location in the source code.</p> </item> <tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag> <item> <p>Returns the current call stack back-trace (<em>stacktrace</em>) of the process. The stack has the same format as returned by - <seealso marker="#get_stacktrace/0">erlang:get_stacktrace/0</seealso>.</p> + <seealso marker="#get_stacktrace/0"> + <c>erlang:get_stacktrace/0</c></seealso>.</p> </item> <tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag> <item> @@ -4793,9 +4898,9 @@ os_prompt% </pre> <tag><c>{garbage_collection, <anno>GCInfo</anno>}</c></tag> <item> <p><c><anno>GCInfo</anno></c> is a list containing miscellaneous - information about garbage collection for this process. - The content of <c><anno>GCInfo</anno></c> can be changed without - prior notice.</p> + information about garbage collection for this process. + The content of <c><anno>GCInfo</anno></c> can be changed without + prior notice.</p> </item> <tag> <marker id="process_info_garbage_collection_info"/> @@ -4803,24 +4908,22 @@ os_prompt% </pre> </tag> <item> <p><c><anno>GCInfo</anno></c> is a list containing miscellaneous - detailed information about garbage collection for this process. - The content of <c><anno>GCInfo</anno></c> can be changed without - prior notice. - See <seealso marker="#gc_minor_start">gc_minor_start</seealso> in - <seealso marker="#trace/3">erlang:trace/3</seealso> for details about - what each item means. - </p> + detailed information about garbage collection for this process. + The content of <c><anno>GCInfo</anno></c> can be changed without + prior notice. For details about the meaning of each item, see + <seealso marker="#gc_minor_start"><c>gc_minor_start</c></seealso> + in <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>.</p> </item> <tag><c>{group_leader, <anno>GroupLeader</anno>}</c></tag> <item> - <p><c><anno>GroupLeader</anno></c> is group leader for the I/O of - the process.</p> + <p><c><anno>GroupLeader</anno></c> is the group leader for the I/O + of the process.</p> </item> <tag><c>{heap_size, <anno>Size</anno>}</c></tag> <item> - <p><c><anno>Size</anno></c> is the size in words of the youngest heap - generation of the process. This generation includes - the process stack. This information is highly + <p><c><anno>Size</anno></c> is the size in words of the youngest + heap generation of the process. This generation includes + the process stack. This information is highly implementation-dependent, and can change if the implementation changes.</p> </item> @@ -4834,29 +4937,29 @@ os_prompt% </pre> </item> <tag><c>{links, <anno>PidsAndPorts</anno>}</c></tag> <item> - <p><c><anno>PidsAndPorts</anno></c> is a list of process identifiers - and port identifiers, with processes or ports to which the process - has a link.</p> + <p><c><anno>PidsAndPorts</anno></c> is a list of process identifiers + and port identifiers, with processes or ports to which the process + has a link.</p> </item> <tag><c>{last_calls, false|Calls}</c></tag> <item> <p>The value is <c>false</c> if call saving is not active - for the process (see - <seealso marker="#process_flag/3">process_flag/3</seealso>). + for the process (see <seealso marker="#process_flag/3"> + <c>process_flag/3</c></seealso>). If call saving is active, a list is returned, in which the last element is the most recent called.</p> </item> <tag><c>{memory, <anno>Size</anno>}</c></tag> <item> - <p><c><anno>Size</anno></c> is the size in bytes of the process. This - includes call stack, heap, and internal structures.</p> + <p><c><anno>Size</anno></c> is the size in bytes of the process. + This includes call stack, heap, and internal structures.</p> </item> <tag><c>{message_queue_len, <anno>MessageQueueLen</anno>}</c></tag> <item> <p><c><anno>MessageQueueLen</anno></c> is the number of messages - currently in the message queue of the process. This is - the length of the list <c><anno>MessageQueue</anno></c> returned as - the information item <c>messages</c> (see the following).</p> + currently in the message queue of the process. This is the + length of the list <c><anno>MessageQueue</anno></c> returned as + the information item <c>messages</c> (see below).</p> </item> <tag><c>{messages, <anno>MessageQueue</anno>}</c></tag> <item> @@ -4899,36 +5002,37 @@ os_prompt% </pre> </item> <tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag> <item> - <p>Returns the current state of the <c>message_queue_data</c> - process flag. <c><anno>MQD</anno></c> is either <c>off_heap</c>, - or <c>on_heap</c>. For more information, see the - documentation of - <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data, - MQD)</c></seealso>.</p> + <p>Returns the current state of process flag + <c>message_queue_data</c>. <c><anno>MQD</anno></c> is either + <c>off_heap</c> or <c>on_heap</c>. For more + information, see the documentation of + <seealso marker="#process_flag_message_queue_data"> + <c>process_flag(message_queue_data, MQD)</c></seealso>.</p> </item> <tag><c>{priority, <anno>Level</anno>}</c></tag> <item> <p><c><anno>Level</anno></c> is the current priority level for - the process. For more information on priorities, see - <seealso marker="#process_flag_priority">process_flag(priority, - Level)</seealso>.</p> + the process. For more information on priorities, see + <seealso marker="#process_flag_priority"> + <c>process_flag(priority, Level)</c></seealso>.</p> </item> <tag><c>{reductions, <anno>Number</anno>}</c></tag> <item> - <p><c><anno>Number</anno></c> is the number of reductions executed by - the process.</p> + <p><c><anno>Number</anno></c> is the number of reductions executed + by the process.</p> </item> <tag><c>{registered_name, <anno>Atom</anno>}</c></tag> <item> - <p><c><anno>Atom</anno></c> is the registered name of the process. If - the process has no registered name, this tuple is not + <p><c><anno>Atom</anno></c> is the registered process name. + If the process has no registered name, this tuple is not present in the list.</p> </item> - <tag><c>{sequential_trace_token, [] | <anno>SequentialTraceToken</anno>}</c></tag> + <tag><c>{sequential_trace_token, [] | + <anno>SequentialTraceToken</anno>}</c></tag> <item> <p><c><anno>SequentialTraceToken</anno></c> is the sequential trace - token for the process. This <c><anno>InfoTuple</anno></c> can be - changed or removed without prior notice.</p> + token for the process. This <c><anno>InfoTuple</anno></c> can be + changed or removed without prior notice.</p> </item> <tag><c>{stack_size, <anno>Size</anno>}</c></tag> <item> @@ -4937,9 +5041,9 @@ os_prompt% </pre> </item> <tag><c>{status, <anno>Status</anno>}</c></tag> <item> - <p><c><anno>Status</anno></c> is the status of the process and is one - of the following:</p> - <list type="bulleted"> + <p><c><anno>Status</anno></c> is the status of the process and is + one of the following:</p> + <list type="bulleted"> <item><c>exiting</c></item> <item><c>garbage_collecting</c></item> <item><c>waiting</c> (for a message)</item> @@ -4947,46 +5051,44 @@ os_prompt% </pre> <item><c>runnable</c> (ready to run, but another process is running)</item> <item><c>suspended</c> (suspended on a "busy" port - or by the BIF <c>erlang:suspend_process/[1,2]</c>)</item> + or by the BIF <c>erlang:suspend_process/1,2</c>)</item> </list> </item> <tag><c>{suspending, <anno>SuspendeeList</anno>}</c></tag> <item> <p><c><anno>SuspendeeList</anno></c> is a list of - <c>{<anno>Suspendee</anno>, <anno>ActiveSuspendCount</anno>, - <anno>OutstandingSuspendCount</anno>}</c> tuples. - <c><anno>Suspendee</anno></c> is the process identifier of a - process that has been, or is to be, - suspended by the process identified by <c><anno>Pid</anno></c> - through one of the following BIFs:</p> + <c>{<anno>Suspendee</anno>, <anno>ActiveSuspendCount</anno>, + <anno>OutstandingSuspendCount</anno>}</c> tuples. + <c><anno>Suspendee</anno></c> is the process identifier of a + process that has been, or is to be, + suspended by the process identified by <c><anno>Pid</anno></c> + through the BIF <seealso marker="#suspend_process/2"> + <c>erlang:suspend_process/2</c></seealso> or + <seealso marker="#suspend_process/1"> + <c>erlang:suspend_process/1</c></seealso>.</p> + <p><c><anno>ActiveSuspendCount</anno></c> is the number of + times <c><anno>Suspendee</anno></c> has been suspended by + <c><anno>Pid</anno></c>. + <c><anno>OutstandingSuspendCount</anno></c> is the number of not + yet completed suspend requests sent by <c><anno>Pid</anno></c>, + that is:</p> <list type="bulleted"> <item> - <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso> - </item> - <item> - <seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso> + <p>If <c><anno>ActiveSuspendCount</anno> =/= 0</c>, + <c><anno>Suspendee</anno></c> is + currently in the suspended state.</p> </item> - </list> - <p><c><anno>ActiveSuspendCount</anno></c> is the number of - times <c><anno>Suspendee</anno></c> has been suspended by - <c><anno>Pid</anno></c>. - <c><anno>OutstandingSuspendCount</anno></c> is the number of not yet - completed suspend requests sent by <c><anno>Pid</anno></c>, that is:</p> - <list type="bulleted"> - <item>If <c><anno>ActiveSuspendCount</anno> =/= 0</c>, - <c><anno>Suspendee</anno></c> is - currently in the suspended state. - </item> - <item>If <c><anno>OutstandingSuspendCount</anno> =/= 0</c>, option - <c>asynchronous</c> of <c>erlang:suspend_process/2</c> - has been used and the suspendee has not yet been - suspended by <c><anno>Pid</anno></c>. + <item> + <p>If <c><anno>OutstandingSuspendCount</anno> =/= 0</c>, + option <c>asynchronous</c> of <c>erlang:suspend_process/2</c> + has been used and the suspendee has not yet been + suspended by <c><anno>Pid</anno></c>.</p> </item> </list> - <p>Notice that <c><anno>ActiveSuspendCount</anno></c> and - <c><anno>OutstandingSuspendCount</anno></c> are not the - total suspend count on <c><anno>Suspendee</anno></c>, - only the parts contributed by <c><anno>Pid</anno></c>.</p> + <p>Notice that <c><anno>ActiveSuspendCount</anno></c> and + <c><anno>OutstandingSuspendCount</anno></c> are not the + total suspend count on <c><anno>Suspendee</anno></c>, + only the parts contributed by <c><anno>Pid</anno></c>.</p> </item> <tag> <marker id="process_info_total_heap_size"/> @@ -4994,21 +5096,21 @@ os_prompt% </pre> </tag> <item> <p><c><anno>Size</anno></c> is the total size, in words, of all heap - fragments of the process. This includes the process stack and - any unreceived messages that are considered to be part of the - heap. </p> + fragments of the process. This includes the process stack and + any unreceived messages that are considered to be part of the + heap.</p> </item> <tag><c>{trace, <anno>InternalTraceFlags</anno>}</c></tag> <item> <p><c><anno>InternalTraceFlags</anno></c> is an integer - representing the internal trace flag for this process. - This <c><anno>InfoTuple</anno></c> - can be changed or removed without prior notice.</p> + representing the internal trace flag for this process. + This <c><anno>InfoTuple</anno></c> + can be changed or removed without prior notice.</p> </item> <tag><c>{trap_exit, <anno>Boolean</anno>}</c></tag> <item> <p><c><anno>Boolean</anno></c> is <c>true</c> if the process - is trapping exits, otherwise <c>false</c>.</p> + is trapping exits, otherwise <c>false</c>.</p> </item> </taglist> <p>Notice that not all implementations support all @@ -5016,9 +5118,9 @@ os_prompt% </pre> <p>Failures:</p> <taglist> <tag><c>badarg</c></tag> - <item>If <c><anno>Pid</anno></c> is not a local process.</item> + <item>If <c><anno>Pid</anno></c> is not a local process.</item> <tag><c>badarg</c></tag> - <item>If <c><anno>Item</anno></c> is an invalid item.</item> + <item>If <c><anno>Item</anno></c> is an invalid item.</item> </taglist> </desc> </func> @@ -5028,11 +5130,11 @@ os_prompt% </pre> <fsummary>All processes.</fsummary> <desc> <p>Returns a list of process identifiers corresponding to - all the processes currently existing on the local node.</p> - <p>Notice that an exiting process exists, but is not alive. - That is, <c>is_process_alive/1</c> returns <c>false</c> - for an exiting process, but its process identifier is part - of the result returned from <c>processes/0</c>.</p> + all the processes currently existing on the local node.</p> + <p>Notice that an exiting process exists, but is not alive. + That is, <c>is_process_alive/1</c> returns <c>false</c> + for an exiting process, but its process identifier is part + of the result returned from <c>processes/0</c>.</p> <p>Example:</p> <pre> > <input>processes().</input> @@ -5042,22 +5144,23 @@ os_prompt% </pre> <func> <name name="purge_module" arity="1"/> - <fsummary>Removes old code for a module.</fsummary> + <fsummary>Remove old code for a module.</fsummary> <desc> <p>Removes old code for <c><anno>Module</anno></c>. Before this BIF is used, - <c>erlang:check_process_code/2</c> is to be called to check + <seealso marker="#check_process_code/2"> + <c>check_process_code/2</c></seealso>is to be called to check that no processes execute old code in the module.</p> <warning> <p>This BIF is intended for the code server (see - <seealso marker="kernel:code">code(3)</seealso>) + <seealso marker="kernel:code"><c>code(3)</c></seealso>) and is not to be used elsewhere.</p> </warning> <note> - <p>As from <c>ERTS</c> 8.0 (OTP 19), any lingering processes - that still execute the old code will be killed by this function. - In earlier versions, such incorrect use could cause much - more fatal failures, like emulator crash.</p> + <p>As from ERTS 8.0 (Erlang/OTP 19), any lingering processes + that still execute the old code is killed by this function. + In earlier versions, such incorrect use could cause much + more fatal failures, like emulator crash.</p> </note> <p>Failure: <c>badarg</c> if there is no old code for <c><anno>Module</anno></c>.</p> @@ -5066,14 +5169,13 @@ os_prompt% </pre> <func> <name name="put" arity="2"/> - <fsummary>Adds a new value to the process dictionary.</fsummary> + <fsummary>Add a new value to the process dictionary.</fsummary> <desc> <p>Adds a new <c><anno>Key</anno></c> to the process dictionary, associated with the value <c><anno>Val</anno></c>, and returns <c>undefined</c>. If <c><anno>Key</anno></c> exists, the old value is deleted and replaced by <c><anno>Val</anno></c>, and - the function returns the old value.</p> - <p>Example:</p> + the function returns the old value. Example:</p> <pre> > <input>X = put(name, walrus), Y = put(name, carpenter),</input> <input>Z = get(name),</input> @@ -5089,17 +5191,18 @@ os_prompt% </pre> <func> <name name="raise" arity="3"/> - <fsummary>Stops execution with an exception of given class, reason, and call stack backtrace.</fsummary> + <fsummary>Stop execution with an exception of specified class, reason, + and call stack backtrace.</fsummary> <type name="raise_stacktrace"/> <desc> <p>Stops the execution of the calling process with an - exception of given class, reason, and call stack backtrace + exception of the specified class, reason, and call stack backtrace (<em>stacktrace</em>).</p> <p><c><anno>Class</anno></c> is <c>error</c>, <c>exit</c>, or <c>throw</c>. So, if it were not for the stacktrace, <c>erlang:raise(<anno>Class</anno>, <anno>Reason</anno>, - <anno>Stacktrace</anno>)</c> is - equivalent to <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.</p> + <anno>Stacktrace</anno>)</c> is equivalent to + <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.</p> <p><c><anno>Reason</anno></c> is any term. <c><anno>Stacktrace</anno></c> is a list as returned from <c>get_stacktrace()</c>, that is, a list of @@ -5109,12 +5212,12 @@ os_prompt% </pre> argument list. The stacktrace can also contain <c>{Fun, Args, Location}</c> tuples, where <c>Fun</c> is a local fun and <c>Args</c> is an argument list.</p> - <p>Element <c>Location</c> at the end is optional. - Omitting it is equivalent to specifying an empty list.</p> + <p>Element <c>Location</c> at the end is optional. + Omitting it is equivalent to specifying an empty list.</p> <p>The stacktrace is used as the exception stacktrace for the calling process; it is truncated to the current maximum stacktrace depth.</p> - <p>Since evaluating this function causes the process to + <p>As evaluating this function causes the process to terminate, it has no return value unless the arguments are invalid, in which case the function <em>returns the error reason</em> <c>badarg</c>. If you want to be @@ -5126,78 +5229,68 @@ os_prompt% </pre> </func> <func> - <name name="read_timer" arity="2"/> - <fsummary>Reads the state of a timer.</fsummary> + <name name="read_timer" arity="1"/> + <fsummary>Read the state of a timer.</fsummary> <desc> - <p> - Read the state of a timer that has been created by either - <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>, - or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>. - <c><anno>TimerRef</anno></c> identifies the timer, and - was returned by the BIF that created the timer. - </p> - <p>Available <c><anno>Option</anno>s</c>:</p> + <p>Reads the state of a timer. The same as calling + <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef, + [])</c></seealso>.</p> + </desc> + </func> + + <func> + <name name="read_timer" arity="2"/> + <fsummary>Read the state of a timer.</fsummary> + <desc> + <p>Reads the state of a timer that has been created by either + <seealso marker="#start_timer/4"><c>erlang:start_timer</c></seealso> + or <seealso marker="#send_after/4"><c>erlang:send_after</c></seealso>. + <c><anno>TimerRef</anno></c> identifies the timer, and + was returned by the BIF that created the timer.</p> + <p><c><anno>Option</anno>s</c>:</p> <taglist> - <tag><c>{async, Async}</c></tag> - <item> - <p> - Asynchronous request for state information. <c>Async</c> - defaults to <c>false</c> which will cause the operation - to be performed synchronously. In this case, the <c>Result</c> - is returned by <c>erlang:read_timer()</c>. When - <c>Async</c> is <c>true</c>, <c>erlang:read_timer()</c> - sends an asynchronous request for the state information - to the timer service that manages the timer, and then returns - <c>ok</c>. A message on the format <c>{read_timer, - <anno>TimerRef</anno>, <anno>Result</anno>}</c> is - sent to the caller of <c>erlang:read_timer()</c> when the - operation has been processed. - </p> - </item> + <tag><c>{async, Async}</c></tag> + <item> + <p>Asynchronous request for state information. <c>Async</c> + defaults to <c>false</c>, which causes the operation + to be performed synchronously. In this case, the <c>Result</c> + is returned by <c>erlang:read_timer</c>. When + <c>Async</c> is <c>true</c>, <c>erlang:read_timer</c> + sends an asynchronous request for the state information + to the timer service that manages the timer, and then returns + <c>ok</c>. A message on the format <c>{read_timer, + <anno>TimerRef</anno>, <anno>Result</anno>}</c> is + sent to the caller of <c>erlang:read_timer</c> when the + operation has been processed.</p> + </item> </taglist> - <p> - More <c><anno>Option</anno></c>s may be added in the future. - </p> - <p> - If <c><anno>Result</anno></c> is an integer, it represents the - time in milli-seconds left until the timer expires.</p> - <p> - If <c><anno>Result</anno></c> is <c>false</c>, a - timer corresponding to <c><anno>TimerRef</anno></c> could not - be found. This can be because the timer had expired, - it had been canceled, or because <c><anno>TimerRef</anno></c> - never has corresponded to a timer. Even if the timer has expired, - it does not tell you whether or not the timeout message has - arrived at its destination yet. - </p> - <note> - <p> - The timer service that manages the timer may be co-located - with another scheduler than the scheduler that the calling - process is executing on. If this is the case, communication - with the timer service takes much longer time than if it - is located locally. If the calling process is in critical - path, and can do other things while waiting for the result - of this operation, you want to use option <c>{async, true}</c>. - If using option <c>{async, false}</c>, the calling - process will be blocked until the operation has been - performed. - </p> - </note> + <p>More <c><anno>Option</anno></c>s can be added in the future.</p> + <p>If <c><anno>Result</anno></c> is an integer, it represents the + time in milliseconds left until the timer expires.</p> + <p>If <c><anno>Result</anno></c> is <c>false</c>, a + timer corresponding to <c><anno>TimerRef</anno></c> could not + be found. This because the timer had expired, + or been canceled, or because <c><anno>TimerRef</anno></c> + never has corresponded to a timer. Even if the timer has expired, + it does not tell you whether or not the time-out message has + arrived at its destination yet.</p> + <note> + <p>The timer service that manages the timer can be co-located + with another scheduler than the scheduler that the calling + process is executing on. If so, communication + with the timer service takes much longer time than if it + is located locally. If the calling process is in a critical + path, and can do other things while waiting for the result + of this operation, you want to use option <c>{async, true}</c>. + If using option <c>{async, false}</c>, the calling + process is blocked until the operation has been performed.</p> + </note> <p>See also <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>, - <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>, - and - <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>.</p> - </desc> - </func> - <func> - <name name="read_timer" arity="1"/> - <fsummary>Reads the state of a timer.</fsummary> - <desc> - <p>Read the state of a timer. The same as calling - <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef, - [])</c></seealso>.</p> + <seealso marker="#start_timer/4"> + <c>erlang:start_timer/4</c></seealso>, and + <seealso marker="#cancel_timer/2"> + <c>erlang:cancel_timer/2</c></seealso>.</p> </desc> </func> @@ -5209,21 +5302,20 @@ os_prompt% </pre> representation of <c><anno>Ref</anno></c>.</p> <warning> <p>This BIF is intended for debugging and is not to be used - in application programs.</p> + in application programs.</p> </warning> </desc> </func> <func> <name name="register" arity="2"/> - <fsummary>Registers a name for a pid (or port).</fsummary> + <fsummary>Register a name for a pid (or port).</fsummary> <desc> <p>Associates the name <c><anno>RegName</anno></c> with a process identifier (pid) or a port identifier. <c><anno>RegName</anno></c>, which must be an atom, can be used instead of the pid or port identifier in send operator - (<c><anno>RegName</anno> ! Message</c>).</p> - <p>Example:</p> + (<c><anno>RegName</anno> ! Message</c>). Example:</p> <pre> > <input>register(db, Pid).</input> true</pre> @@ -5249,7 +5341,7 @@ true</pre> <fsummary>All registered names.</fsummary> <desc> <p>Returns a list of names that have been registered using - <seealso marker="#register/2">register/2</seealso>, for + <seealso marker="#register/2"><c>register/2</c></seealso>, for example:</p> <pre> > <input>registered().</input> @@ -5259,20 +5351,21 @@ true</pre> <func> <name name="resume_process" arity="1"/> - <fsummary>Resumes a suspended process.</fsummary> + <fsummary>Resume a suspended process.</fsummary> <desc> <p>Decreases the suspend count on the process identified by - <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> - is previously to have been suspended through - <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso> - or - <seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso> - by the process calling - <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When the - suspend count on <c><anno>Suspendee</anno></c> reaches zero, - <c><anno>Suspendee</anno></c> is resumed, that is, its state - is changed from suspended into the state it had before it was - suspended.</p> + <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> + is previously to have been suspended through + <seealso marker="#suspend_process/2"> + <c>erlang:suspend_process/2</c></seealso> or + <seealso marker="#suspend_process/1"> + <c>erlang:suspend_process/1</c></seealso> + by the process calling + <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When the + suspend count on <c><anno>Suspendee</anno></c> reaches zero, + <c><anno>Suspendee</anno></c> is resumed, that is, its state + is changed from suspended into the state it had before it was + suspended.</p> <warning> <p>This BIF is intended for debugging only.</p> </warning> @@ -5280,29 +5373,29 @@ true</pre> <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Suspendee</anno></c> is not a process identifier. - </item> + If <c><anno>Suspendee</anno></c> is not a process identifier. + </item> <tag><c>badarg</c></tag> <item> - If the process calling <c>erlang:resume_process/1</c> had - not previously increased the suspend count on the process - identified by <c><anno>Suspendee</anno></c>. - </item> + If the process calling <c>erlang:resume_process/1</c> had + not previously increased the suspend count on the process + identified by <c><anno>Suspendee</anno></c>. + </item> <tag><c>badarg</c></tag> <item> - If the process identified by <c><anno>Suspendee</anno></c> - is not alive. - </item> + If the process identified by <c><anno>Suspendee</anno></c> + is not alive. + </item> </taglist> </desc> </func> <func> <name name="round" arity="1"/> - <fsummary>Returns an integer by rounding a number.</fsummary> + <fsummary>Return an integer by rounding a number.</fsummary> <desc> <p>Returns an integer by rounding <c><anno>Number</anno></c>, - for example:</p> + for example:</p> <pre> <input>round(5.5).</input> 6</pre> @@ -5312,7 +5405,7 @@ true</pre> <func> <name name="self" arity="0"/> - <fsummary>Returns pid of the calling process.</fsummary> + <fsummary>Return pid of the calling process.</fsummary> <desc> <p>Returns the process identifier of the calling process, for example:</p> @@ -5325,7 +5418,7 @@ true</pre> <func> <name name="send" arity="2"/> - <fsummary>Sends a message.</fsummary> + <fsummary>Send a message.</fsummary> <type name="dst"/> <desc> <p>Sends a message and returns <c><anno>Msg</anno></c>. This @@ -5339,27 +5432,27 @@ true</pre> <func> <name name="send" arity="3"/> - <fsummary>Sends a message conditionally.</fsummary> + <fsummary>Send a message conditionally.</fsummary> <type name="dst"/> <desc> <p>Either sends a message and returns <c>ok</c>, or does not send - the message but returns something else (see the following). + the message but returns something else (see below). Otherwise the same as - <seealso marker="#send/2">erlang:send/2</seealso>. + <seealso marker="#send/2"><c>erlang:send/2</c></seealso>. For more detailed explanation and warnings, see - <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2,3</seealso>.</p> - <p>The options are as follows:</p> + <seealso marker="#send_nosuspend/2"> + <c>erlang:send_nosuspend/2,3</c></seealso>.</p> + <p>Options:</p> <taglist> <tag><c>nosuspend</c></tag> - <item> - <p>If the sender would have to be suspended to do the send, - <c>nosuspend</c> is returned instead.</p> + <item>If the sender would have to be suspended to do the send, + <c>nosuspend</c> is returned instead. </item> <tag><c>noconnect</c></tag> <item> - <p>If the destination node would have to be auto-connected - to do the send, <c>noconnect</c> is returned - instead.</p> + If the destination node would have to be auto-connected + to do the send, <c>noconnect</c> is returned + instead. </item> </taglist> <warning> @@ -5370,36 +5463,37 @@ true</pre> </func> <func> - <name name="send_after" arity="4"/> - <fsummary>Start a timer</fsummary> + <name name="send_after" arity="3"/> + <fsummary>Start a timer.</fsummary> <desc> - <p> - Starts a timer. When the timer expires, the message - <c><anno>Msg</anno></c> is sent to the process - identified by <c><anno>Dest</anno></c>. Apart from - the format of the timeout message, - <c>erlang:send_after/4</c> works exactly as - <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>.</p> + <p>Starts a timer. The same as calling + <seealso marker="#send_after/4"> + <c>erlang:send_after(<anno>Time</anno>, <anno>Dest</anno>, + <anno>Msg</anno>, [])</c></seealso>.</p> </desc> </func> + <func> - <name name="send_after" arity="3"/> - <fsummary>Starts a timer.</fsummary> + <name name="send_after" arity="4"/> + <fsummary>Start a timer.</fsummary> <desc> - <p>Starts a timer. The same as calling - <seealso marker="#send_after/4"><c>erlang:send_after(<anno>Time</anno>, - <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p> + <p>Starts a timer. When the timer expires, the message + <c><anno>Msg</anno></c> is sent to the process + identified by <c><anno>Dest</anno></c>. Apart from + the format of the time-out message, this function works exactly as + <seealso marker="#start_timer/4"> + <c>erlang:start_timer/4</c></seealso>.</p> </desc> </func> <func> <name name="send_nosuspend" arity="2"/> - <fsummary>Tries to send a message without ever blocking.</fsummary> + <fsummary>Try to send a message without ever blocking.</fsummary> <type name="dst"/> <desc> <p>The same as - <seealso marker="#send/3">erlang:send(<anno>Dest</anno>, - <anno>Msg</anno>, [nosuspend])</seealso>, + <seealso marker="#send/3"><c>erlang:send(<anno>Dest</anno>, + <anno>Msg</anno>, [nosuspend])</c></seealso>, but returns <c>true</c> if the message was sent and <c>false</c> if the message was not sent because the sender would have had to be suspended.</p> @@ -5425,12 +5519,12 @@ true</pre> contradictory to the Erlang programming model. The message is <em>not</em> sent if this function returns <c>false</c>.</p> <p>In many systems, transient states of - overloaded queues are normal. The fact that this function + overloaded queues are normal. Although this function returns <c>false</c> does not mean that the other node is guaranteed to be non-responsive, it could be a temporary overload. Also, a return value of <c>true</c> does only mean that the message can be sent on the (TCP) channel - without blocking, the message is not guaranteed to + without blocking; the message is not guaranteed to arrive at the remote node. For a disconnected non-responsive node, the return value is <c>true</c> (mimics the behavior of operator <c>!</c>). The expected @@ -5444,15 +5538,16 @@ true</pre> <func> <name name="send_nosuspend" arity="3"/> - <fsummary>Tries to send a message without ever blocking.</fsummary> + <fsummary>Try to send a message without ever blocking.</fsummary> <type name="dst"/> <desc> <p>The same as - <seealso marker="#send/3">erlang:send(<anno>Dest</anno>, - <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</seealso>, + <seealso marker="#send/3"><c>erlang:send(<anno>Dest</anno>, + <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</c></seealso>, but with a Boolean return value.</p> <p>This function behaves like - <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2</seealso>, + <seealso marker="#send_nosuspend/2"> + <c>erlang:send_nosuspend/2</c></seealso>, but takes a third parameter, a list of options. The only option is <c>noconnect</c>, which makes the function return <c>false</c> if @@ -5476,14 +5571,15 @@ true</pre> <func> <name name="set_cookie" arity="2"/> - <fsummary>Sets the magic cookie of a node.</fsummary> + <fsummary>Set the magic cookie of a node.</fsummary> <desc> <p>Sets the magic cookie of <c><anno>Node</anno></c> to the atom <c><anno>Cookie</anno></c>. If <c><anno>Node</anno></c> is the local node, the function also sets the cookie of all other unknown nodes to - <c><anno>Cookie</anno></c> (see Section - <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso> + <c><anno>Cookie</anno></c> (see section + <seealso marker="doc/reference_manual:distributed"> + Distributed Erlang</seealso> in the Erlang Reference Manual in System Documentation).</p> <p>Failure: <c>function_clause</c> if the local node is not alive.</p> @@ -5492,12 +5588,12 @@ true</pre> <func> <name name="setelement" arity="3"/> - <fsummary>Sets the Nth element of a tuple.</fsummary> + <fsummary>Set the Nth element of a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno></type_desc> <desc> <p>Returns a tuple that is a copy of argument <c><anno>Tuple1</anno></c> - with the element given by integer argument + with the element specified by integer argument <c><anno>Index</anno></c> (the first element is the element with index 1) replaced by argument <c><anno>Value</anno></c>, for example:</p> @@ -5512,52 +5608,52 @@ true</pre> <fsummary>Size of a tuple or binary.</fsummary> <desc> <p>Returns the number of elements in a tuple or the number of - bytes in a binary or bitstring, for example:</p> + bytes in a binary or bitstring, for example:</p> <pre> > <input>size({morni, mulle, bwange}).</input> 3 > <input>size(<<11, 22, 33>>).</input> -3 -</pre> - <p>For bitstrings the number of whole bytes is returned. That is, if the number of bits +3</pre> + <p>For bitstrings, the number of whole bytes is returned. + That is, if the number of bits in the bitstring is not divisible by 8, the resulting number of bytes is rounded <em>down</em>.</p> <p>Allowed in guard tests.</p> <p>See also <seealso marker="#tuple_size/1"><c>tuple_size/1</c></seealso>, - <seealso marker="#byte_size/1"><c>byte_size/1</c></seealso> - and + <seealso marker="#byte_size/1"><c>byte_size/1</c></seealso>, and <seealso marker="#bit_size/1"><c>bit_size/1</c></seealso>.</p> </desc> </func> <func> <name name="spawn" arity="1"/> - <fsummary>Creates a new process with a fun as entry point.</fsummary> + <fsummary>Create a new process with a fun as entry point.</fsummary> <desc> <p>Returns the process identifier of a new process started by the application of <c><anno>Fun</anno></c> to the empty list <c>[]</c>. Otherwise - works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> + works like <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn" arity="2"/> - <fsummary>Creates a new process with a fun as entry point on a given node.</fsummary> + <fsummary>Create a new process with a fun as entry point on a specified + node.</fsummary> <desc> <p>Returns the process identifier of a new process started by the application of <c><anno>Fun</anno></c> to the empty list <c>[]</c> on <c><anno>Node</anno></c>. If <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like - <seealso marker="#spawn/3">spawn/3</seealso>.</p> + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn" arity="3"/> - <fsummary>Creates a new process with a function as entry point.</fsummary> + <fsummary>Create a new process with a function as entry point.</fsummary> <desc> <p>Returns the process identifier of a new process started by the application of <c><anno>Module</anno>:<anno>Function</anno></c> @@ -5569,7 +5665,7 @@ true</pre> does not exist (where <c>Arity</c> is the length of <c><anno>Args</anno></c>). The error handler can be redefined (see - <seealso marker="#process_flag/2">process_flag/2</seealso>). + <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>). If <c>error_handler</c> is undefined, or the user has redefined the default <c>error_handler</c> and its replacement is undefined, a failure with reason <c>undef</c> occurs.</p> @@ -5582,7 +5678,8 @@ true</pre> <func> <name name="spawn" arity="4"/> - <fsummary>Creates a new process with a function as entry point on a given node.</fsummary> + <fsummary>Create a new process with a function as entry point on a + specified node.</fsummary> <desc> <p>Returns the process identifier (pid) of a new process started by the application @@ -5590,26 +5687,28 @@ true</pre> to <c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like - <seealso marker="#spawn/3">spawn/3</seealso>.</p> + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_link" arity="1"/> - <fsummary>Creates and links to a new process with a fun as entry point.</fsummary> + <fsummary>Create and link to a new process with a fun as entry point. + </fsummary> <desc> <p>Returns the process identifier of a new process started by the application of <c><anno>Fun</anno></c> to the empty list <c>[]</c>. A link is created between the calling process and the new process, atomically. Otherwise works like - <seealso marker="#spawn/3">spawn/3</seealso>.</p> + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_link" arity="2"/> - <fsummary>Creates and links to a new process with a fun as entry point on a specified node.</fsummary> + <fsummary>Create and link to a new process with a fun as entry point on + a specified node.</fsummary> <desc> <p>Returns the process identifier (pid) of a new process started by the application of <c><anno>Fun</anno></c> to the empty @@ -5618,26 +5717,29 @@ true</pre> atomically. If <c><anno>Node</anno></c> does not exist, a useless pid is returned and an exit signal with reason <c>noconnection</c> is sent to the calling - process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> + process. Otherwise works like <seealso marker="#spawn/3"> + <c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_link" arity="3"/> - <fsummary>Creates and links to a new process with a function as entry point.</fsummary> + <fsummary>Create and link to a new process with a function as entry point. + </fsummary> <desc> <p>Returns the process identifier of a new process started by the application of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c>. A link is created between the calling process and the new process, atomically. Otherwise works like - <seealso marker="#spawn/3">spawn/3</seealso>.</p> + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_link" arity="4"/> - <fsummary>Creates and links to a new process with a function as entry point on a given node.</fsummary> + <fsummary>Create and link to a new process with a function as entry point + on a specified node.</fsummary> <desc> <p>Returns the process identifier (pid) of a new process started by the application @@ -5647,109 +5749,113 @@ true</pre> process, atomically. If <c><anno>Node</anno></c> does not exist, a useless pid is returned and an exit signal with reason <c>noconnection</c> is sent to the calling - process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> + process. Otherwise works like <seealso marker="#spawn/3"> + <c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_monitor" arity="1"/> - <fsummary>Creates and monitors a new process with a fun as entry point.</fsummary> + <fsummary>Create and monitor a new process with a fun as entry point. + </fsummary> <desc> <p>Returns the process identifier of a new process, started by the application of <c><anno>Fun</anno></c> to the empty list <c>[]</c>, and a reference for a monitor created to the new process. Otherwise works like - <seealso marker="#spawn/3">spawn/3</seealso>.</p> + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_monitor" arity="3"/> - <fsummary>Creates and monitors a new process with a function as entry point.</fsummary> + <fsummary>Create and monitor a new process with a function as entry point. + </fsummary> <desc> <p>A new process is started by the application of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c>. The process is monitored at the same time. Returns the process identifier and a reference for the monitor. Otherwise works like - <seealso marker="#spawn/3">spawn/3</seealso>.</p> + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p> </desc> </func> <func> <name name="spawn_opt" arity="2"/> - <fsummary>Creates a new process with a fun as entry point.</fsummary> + <fsummary>Create a new process with a fun as entry point.</fsummary> <type name="priority_level"/> - <type name="max_heap_size" /> - <type name="message_queue_data" /> - <type name="spawn_opt_option" /> + <type name="max_heap_size"/> + <type name="message_queue_data"/> + <type name="spawn_opt_option"/> <desc> <p>Returns the process identifier (pid) of a new process started by the application of <c><anno>Fun</anno></c> to the empty list <c>[]</c>. Otherwise works like - <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p> - <p>If option <c>monitor</c> is given, the newly created + <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p> + <p>If option <c>monitor</c> is specified, the newly created process is monitored, and both the pid and reference for - the monitor is returned.</p> + the monitor are returned.</p> </desc> </func> <func> <name name="spawn_opt" arity="3"/> - <fsummary>Creates a new process with a fun as entry point on a given node.</fsummary> + <fsummary>Create a new process with a fun as entry point on a specified + node.</fsummary> <type name="priority_level"/> - <type name="max_heap_size" /> - <type name="message_queue_data" /> - <type name="spawn_opt_option" /> + <type name="max_heap_size"/> + <type name="message_queue_data"/> + <type name="spawn_opt_option"/> <desc> <p>Returns the process identifier (pid) of a new process started by the application of <c><anno>Fun</anno></c> to the empty list <c>[]</c> on <c><anno>Node</anno></c>. If <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like - <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p> + <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p> </desc> </func> <func> <name name="spawn_opt" arity="4"/> - <fsummary>Creates a new process with a function as entry point.</fsummary> + <fsummary>Create a new process with a function as entry point.</fsummary> <type name="priority_level"/> - <type name="max_heap_size" /> - <type name="message_queue_data" /> - <type name="spawn_opt_option" /> + <type name="max_heap_size"/> + <type name="message_queue_data"/> + <type name="spawn_opt_option"/> <desc> <p>Works as - <seealso marker="#spawn/3">spawn/3</seealso>, except that an - extra option list is given when creating the process.</p> - <p>If option <c>monitor</c> is given, the newly created + <seealso marker="#spawn/3"><c>spawn/3</c></seealso>, except that an + extra option list is specified when creating the process.</p> + <p>If option <c>monitor</c> is specified, the newly created process is monitored, and both the pid and reference for - the monitor is returned.</p> - <p>The options are as follows:</p> + the monitor are returned.</p> + <p>Options:</p> <taglist> <tag><c>link</c></tag> <item> <p>Sets a link to the parent process (like - <c>spawn_link/3</c> does).</p> + <seealso marker="#spawn_link/3"><c>spawn_link/3</c></seealso> + does).</p> </item> <tag><c>monitor</c></tag> <item> <p>Monitors the new process (like - <seealso marker="#monitor/2">monitor/2</seealso> does).</p> + <seealso marker="#monitor/2"><c>monitor/2</c></seealso> does).</p> </item> <tag><c>{priority, <anno>Level</anno></c></tag> <item> <p>Sets the priority of the new process. Equivalent to - executing - <seealso marker="#process_flag_priority">process_flag(priority, - <anno>Level</anno>)</seealso> + executing <seealso marker="#process_flag_priority"> + <c>process_flag(priority, <anno>Level</anno>)</c></seealso> in the start function of the new process, except that the priority is set before the process is selected for execution for the first time. For more information on priorities, see - <seealso marker="#process_flag_priority">process_flag(priority, - <anno>Level</anno>)</seealso>.</p> + <seealso marker="#process_flag_priority"> + <c>process_flag(priority, <anno>Level</anno>)</c></seealso>.</p> </item> <tag><c>{fullsweep_after, <anno>Number</anno>}</c></tag> <item> @@ -5772,21 +5878,22 @@ true</pre> <p>A few cases when it can be useful to change <c>fullsweep_after</c>:</p> <list type="bulleted"> - <item>If binaries that are no longer used are to be + <item><p>If binaries that are no longer used are to be thrown away as soon as possible. (Set - <c><anno>Number</anno></c> to zero.) + <c><anno>Number</anno></c> to zero.)</p> </item> - <item>A process that mostly have short-lived data is + <item><p>A process that mostly have short-lived data is fullsweeped seldom or never, that is, the old heap contains mostly garbage. To ensure a fullsweep occasionally, set <c><anno>Number</anno></c> to a - suitable value, such as 10 or 20. + suitable value, such as 10 or 20.</p> </item> <item>In embedded systems with a limited amount of RAM and no virtual memory, you might want to preserve memory by setting <c><anno>Number</anno></c> to zero. (The value can be set globally, see - <seealso marker="#system_flag/2">erlang:system_flag/2</seealso>.) + <seealso marker="#system_flag/2"> + <c>erlang:system_flag/2</c></seealso>.) </item> </list> </item> @@ -5811,7 +5918,7 @@ true</pre> option unless you know that there is problem with execution times or memory consumption, and ensure that the option improves matters.</p> - <p>Gives a minimum binary virtual heap size, in words. + <p>Gives a minimum binary virtual heap size, in words. Setting this value higher than the system default can speed up some processes because less garbage collection is done. @@ -5823,24 +5930,25 @@ true</pre> <tag><c>{max_heap_size, <anno>Size</anno>}</c></tag> <item> <p>Sets the <c>max_heap_size</c> process flag. The default - <c>max_heap_size</c> is determined by the - <seealso marker="erl#+hmax"><c>+hmax</c></seealso> <c>erl</c> - command line argument. For more information, see the - documentation of - <seealso marker="#process_flag_max_heap_size"><c>process_flag(max_heap_size, - <anno>Size</anno>)</c></seealso>.</p> + <c>max_heap_size</c> is determined by command-line argument + <seealso marker="erl#+hmax"><c>+hmax</c></seealso> + in <c>erl(1)</c>. For more information, see the + documentation of <seealso marker="#process_flag_max_heap_size"> + <c>process_flag(max_heap_size, <anno>Size</anno>)</c></seealso>. + </p> </item> <tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag> <item> <p>Sets the state of the <c>message_queue_data</c> process - flag. <c><anno>MQD</anno></c> should be either <c>off_heap</c>, - or <c>on_heap</c>. The default - <c>message_queue_data</c> process flag is determined by the - <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> <c>erl</c> - command line argument. For more information, see the - documentation of - <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data, - <anno>MQD</anno>)</c></seealso>.</p> + flag. <c><anno>MQD</anno></c> is to be either <c>off_heap</c> + or <c>on_heap</c>. The default + <c>message_queue_data</c> process flag is determined by + command-line argument <seealso marker="erl#+hmqd"> + <c>+hmqd</c></seealso> in <c>erl(1)</c>. + For more information, see the documentation of + <seealso marker="#process_flag_message_queue_data"> + <c>process_flag(message_queue_data, + <anno>MQD</anno>)</c></seealso>.</p> </item> </taglist> </desc> @@ -5848,11 +5956,12 @@ true</pre> <func> <name name="spawn_opt" arity="5"/> - <fsummary>Creates a new process with a function as entry point on a given node.</fsummary> + <fsummary>Create a new process with a function as entry point on a + specified node.</fsummary> <type name="priority_level"/> - <type name="max_heap_size" /> - <type name="message_queue_data" /> - <type name="spawn_opt_option" /> + <type name="max_heap_size"/> + <type name="message_queue_data"/> + <type name="spawn_opt_option"/> <desc> <p>Returns the process identifier (pid) of a new process started by the application @@ -5860,23 +5969,24 @@ true</pre> <c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like - <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p> - <note><p>Option <c>monitor</c> is not supported by - <c>spawn_opt/5</c>.</p></note> + <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p> + <note> + <p>Option <c>monitor</c> is not supported by + <c>spawn_opt/5</c>.</p> + </note> </desc> </func> <func> <name name="split_binary" arity="2"/> - <fsummary>Splits a binary into two.</fsummary> + <fsummary>Split a binary into two.</fsummary> <type_desc variable="Pos">0..byte_size(Bin)</type_desc> <desc> <p>Returns a tuple containing the binaries that are the result of splitting <c><anno>Bin</anno></c> into two parts at position <c><anno>Pos</anno></c>. This is not a destructive operation. After the operation, - there are three binaries altogether.</p> - <p>Example:</p> + there are three binaries altogether. Example:</p> <pre> > <input>B = list_to_binary("0123456789").</input> <<"0123456789">> @@ -5892,104 +6002,96 @@ true</pre> </func> <func> + <name name="start_timer" arity="3"/> + <fsummary>Start a timer.</fsummary> + <desc> + <p>Starts a timer. The same as calling + <seealso marker="#start_timer/4"> + <c>erlang:start_timer(<anno>Time</anno>, + <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p> + </desc> + </func> + + <func> <name name="start_timer" arity="4"/> - <fsummary>Starts a timer.</fsummary> + <fsummary>Start a timer.</fsummary> <desc> - <p> - Starts a timer. When the timer expires, the message + <p>Starts a timer. When the timer expires, the message <c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c> - is sent to the process identified by - <c><anno>Dest</anno></c>. - </p> - <p>Available <c><anno>Option</anno></c>s:</p> + is sent to the process identified by <c><anno>Dest</anno></c>.</p> + <p><c><anno>Option</anno></c>s:</p> <taglist> <tag><c>{abs, false}</c></tag> <item> - <p> - This is the default. It means the - <c><anno>Time</anno></c> value is interpreted - as a time in milli-seconds <em>relative</em> current - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso>. - </p> - </item> + <p>This is the default. It means the + <c><anno>Time</anno></c> value is interpreted + as a time in milliseconds <em>relative</em> current + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso>.</p> + </item> <tag><c>{abs, true}</c></tag> <item> - <p> - Absolute <c><anno>Time</anno></c> value. The - <c><anno>Time</anno></c> value is interpreted as an - absolute Erlang monotonic time in milli-seconds. - </p> - </item> + <p>Absolute <c><anno>Time</anno></c> value. The + <c><anno>Time</anno></c> value is interpreted as an + absolute Erlang monotonic time in milliseconds.</p> + </item> </taglist> - <p> - More <c><anno>Option</anno></c>s may be added in the future. - </p> - <p> - The absolute point in time, the timer is set to expire on, - has to be in the interval - <c>[</c><seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso><c>, - </c><seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso><c>]</c>. - Further, if a relative time is specified, the <c><anno>Time</anno></c> value - is not allowed to be negative. - </p> - <p> - If <c><anno>Dest</anno></c> is a <c>pid()</c>, it must - be a <c>pid()</c> of a process created on the current - runtime system instance. This process may or may not - have terminated. If <c><anno>Dest</anno></c> is an - <c>atom()</c>, it is interpreted as the name of a - locally registered process. The process referred to by the - name is looked up at the time of timer expiration. No error - is given if the name does not refer to a process. - </p> - <p> - If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is - automatically canceled if the process referred to by the - <c>pid()</c> is not alive, or if the process exits. This - feature was introduced in ERTS version 5.4.11. Notice that - timers are not automatically canceled when - <c><anno>Dest</anno></c> is an <c>atom()</c>. - </p> + <p>More <c><anno>Option</anno></c>s can be added in the future.</p> + <p>The absolute point in time, the timer is set to expire on, + must be in the interval + <c>[</c><seealso marker="#system_info_start_time"> + <c>erlang:system_info(start_time)</c></seealso><c>, + </c><seealso marker="#system_info_end_time"> + <c>erlang:system_info(end_time)</c></seealso><c>]</c>. + If a relative time is specified, the <c><anno>Time</anno></c> + value is not allowed to be negative.</p> + <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, it must + be a <c>pid()</c> of a process created on the current + runtime system instance. This process has either terminated + or not. If <c><anno>Dest</anno></c> is an + <c>atom()</c>, it is interpreted as the name of a + locally registered process. The process referred to by the + name is looked up at the time of timer expiration. No error + is returned if the name does not refer to a process.</p> + <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is + automatically canceled if the process referred to by the + <c>pid()</c> is not alive, or if the process exits. This + feature was introduced in ERTS 5.4.11. Notice that + timers are not automatically canceled when + <c><anno>Dest</anno></c> is an <c>atom()</c>.</p> <p>See also <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>, - <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>, - and - <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p> + <seealso marker="#cancel_timer/2"> + <c>erlang:cancel_timer/2</c></seealso>, and + <seealso marker="#read_timer/2"> + <c>erlang:read_timer/2</c></seealso>.</p> <p>Failure: <c>badarg</c> if the arguments do not satisfy the requirements specified here.</p> </desc> </func> <func> - <name name="start_timer" arity="3"/> - <fsummary>Starts a timer.</fsummary> - <desc> - <p>Starts a timer. The same as calling - <seealso marker="#start_timer/4"><c>erlang:start_timer(<anno>Time</anno>, - <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p> - </desc> - </func> - - <func> <name name="statistics" arity="1" clause_i="1"/> <fsummary>Information about active processes and ports.</fsummary> - <desc><marker id="statistics_active_tasks"></marker> - <p> - Returns a list where each element represents the amount - of active processes and ports on each run queue and its - associated scheduler. That is, the number of processes and - ports that are ready to run, or are currently running. The - element location in the list corresponds to the scheduler - and its run queue. The first element corresponds to scheduler - number 1 and so on. The information is <em>not</em> gathered - atomically. That is, the result is not necessarily a - consistent snapshot of the state, but instead quite - efficiently gathered. See also, - <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>, - <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, and - <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>. - </p> + <desc> + <marker id="statistics_active_tasks"></marker> + <p>Returns a list where each element represents the amount + of active processes and ports on each run queue and its + associated scheduler. That is, the number of processes and + ports that are ready to run, or are currently running. The + element location in the list corresponds to the scheduler + and its run queue. The first element corresponds to scheduler + number 1 and so on. The information is <em>not</em> gathered + atomically. That is, the result is not necessarily a + consistent snapshot of the state, but instead quite + efficiently gathered.</p> + <p>See also + <seealso marker="#statistics_total_active_tasks"> + <c>statistics(total_active_tasks)</c></seealso>, + <seealso marker="#statistics_run_queue_lengths"> + <c>statistics(run_queue_lengths)</c></seealso>, and + <seealso marker="#statistics_total_run_queue_lengths"> + <c>statistics(total_run_queue_lengths)</c></seealso>.</p> </desc> </func> @@ -6008,11 +6110,13 @@ true</pre> <desc> <marker id="statistics_exact_reductions"></marker> <p>Returns the number of exact reductions.</p> - <note><p><c>statistics(exact_reductions)</c> is - a more expensive operation than - <seealso marker="#statistics_reductions">statistics(reductions)</seealso>, - especially on an Erlang machine with SMP support.</p> - </note> + <note> + <p><c>statistics(exact_reductions)</c> is + a more expensive operation than + <seealso marker="#statistics_reductions"> + statistics(reductions)</seealso>, + especially on an Erlang machine with SMP support.</p> + </note> </desc> </func> @@ -6021,7 +6125,7 @@ true</pre> <fsummary>Information about garbage collection.</fsummary> <desc> <p>Returns information about garbage collection, for example:</p> - <pre> + <pre> > <input>statistics(garbage_collection).</input> {85,23961,0}</pre> <p>This information can be invalid for some implementations.</p> @@ -6033,9 +6137,9 @@ true</pre> <fsummary>Information about I/O.</fsummary> <desc> <p>Returns <c><anno>Input</anno></c>, - which is the total number of bytes - received through ports, and <c><anno>Output</anno></c>, - which is the total number of bytes output to ports.</p> + which is the total number of bytes + received through ports, and <c><anno>Output</anno></c>, + which is the total number of bytes output to ports.</p> </desc> </func> @@ -6044,23 +6148,21 @@ true</pre> <fsummary>Information about microstate accounting.</fsummary> <desc> <marker id="statistics_microstate_accounting"></marker> - <p> - Microstate accounting can be used to measure how much time the Erlang + <p>Microstate accounting can be used to measure how much time the Erlang runtime system spends doing various tasks. It is designed to be as - lightweight as possible, but there will be some overhead when this + lightweight as possible, but some overhead exists when this is enabled. Microstate accounting is meant to be a profiling tool - to help figure out performance bottlenecks. - To <c>start</c>/<c>stop</c>/<c>reset</c> microstate_accounting you use - the system_flag - <seealso marker="#system_flag_microstate_accounting"> - <c>microstate_accounting</c></seealso>. - </p> - <p> - <c>erlang:statistics(microstate_accounting)</c> returns a list of maps - representing some of the OS threads within ERTS. Each map contains - <c>type</c> and <c>id</c> fields that can be used to identify what + to help finding performance bottlenecks. + To <c>start</c>/<c>stop</c>/<c>reset</c> microstate accounting, use + system flag <seealso marker="#system_flag_microstate_accounting"> + <c>microstate_accounting</c></seealso>.</p> + <p><c>statistics(microstate_accounting)</c> returns a list of maps + representing some of the OS threads within ERTS. Each map + contains <c>type</c> and <c>id</c> fields that can be used to + identify what thread it is, and also a counters field that contains data about how much time has been spent in the various states.</p> + <p>Example:</p> <pre> > <input>erlang:statistics(microstate_accounting).</input> [#{counters => #{aux => 1899182914, @@ -6071,12 +6173,11 @@ true</pre> port => 221631, sleep => 5150294100}, id => 1, - type => scheduler}|...] - </pre> + type => scheduler}|...]</pre> <p>The time unit is the same as returned by - <seealso marker="kernel:os#perf_counter/0"> + <seealso marker="kernel:os#perf_counter/0"> <c>os:perf_counter/0</c></seealso>. - So to convert it to milliseconds you could do something like this:</p> + So, to convert it to milliseconds, you can do something like this:</p> <pre> lists:map( fun(#{ counters := Cnt } = M) -> @@ -6084,39 +6185,40 @@ lists:map( erlang:convert_time_unit(PerfCount, perf_counter, 1000) end, Cnt), M#{ counters := MsCnt } - end, erlang:statistics(microstate_accounting)). - </pre> - <p> - It is important to note that these values are not guaranteed to be + end, erlang:statistics(microstate_accounting)).</pre> + <p>Notice that these values are not guaranteed to be the exact time spent in each state. This is because of various - optimisation done in order to keep the overhead as small as possible. - </p> - - <p>Currently the following <c><anno>MSAcc_Thread_Type</anno></c> are available:</p> + optimisation done to keep the overhead as small as possible.</p> + <p><c><anno>MSAcc_Thread_Type</anno></c>s:</p> <taglist> <tag><c>scheduler</c></tag> <item>The main execution threads that do most of the work.</item> - <tag><c>async</c></tag><item>Async threads are used by various - linked-in drivers (mainly the file drivers) do offload non-cpu - intensive work.</item> - <tag><c>aux</c></tag><item>Takes care of any work that is not - specifically assigned to a scheduler.</item> + <tag><c>dirty_cpu_scheduler</c></tag> + <item>The threads for long running cpu intensive work.</item> + <tag><c>dirty_io_scheduler</c></tag> + <item>The threads for long running I/O work.</item> + <tag><c>async</c></tag> + <item>Async threads are used by various linked-in drivers (mainly the + file drivers) do offload non-CPU intensive work.</item> + <tag><c>aux</c></tag> + <item>Takes care of any work that is not + specifically assigned to a scheduler.</item> </taglist> - <p>Currently the following <c><anno>MSAcc_Thread_State</anno></c>s are available. - All states are exclusive, meaning that a thread cannot be in two states - at once. So if you add the numbers of all counters in a thread - you will get the total run-time for that thread.</p> + <p>The following <c><anno>MSAcc_Thread_State</anno></c>s are available. + All states are exclusive, meaning that a thread cannot be in two + states at once. So, if you add the numbers of all counters in a + thread, you get the total runtime for that thread.</p> <taglist> <tag><c>aux</c></tag> <item>Time spent handling auxiliary jobs.</item> <tag><c>check_io</c></tag> <item>Time spent checking for new I/O events.</item> <tag><c>emulator</c></tag> - <item>Time spent executing erlang processes.</item> + <item>Time spent executing Erlang processes.</item> <tag><c>gc</c></tag> <item>Time spent doing garbage collection. When extra states are - enabled this is the time spent doing non-fullsweep garbage - collections.</item> + enabled this is the time spent doing non-fullsweep garbage + collections.</item> <tag><c>other</c></tag> <item>Time spent doing unaccounted things.</item> <tag><c>port</c></tag> @@ -6124,61 +6226,59 @@ lists:map( <tag><c>sleep</c></tag> <item>Time spent sleeping.</item> </taglist> - <p>It is possible to add more fine grained <c><anno>MSAcc_Thread_State</anno></c>s - through configure. - (e.g. <c>./configure --with-microstate-accounting=extra</c>). - Enabling these states will cause a performance degradation when - microstate accounting is turned off and increase the overhead when - it is turned on.</p> + <p>More fine-grained <c><anno>MSAcc_Thread_State</anno></c>s can + be added through configure (such as + <c>./configure --with-microstate-accounting=extra</c>). + Enabling these states causes performance degradation when + microstate accounting is turned off and increases the overhead when + it is turned on.</p> <taglist> <tag><c>alloc</c></tag> <item>Time spent managing memory. Without extra states this time is - spread out over all other states.</item> + spread out over all other states.</item> <tag><c>bif</c></tag> - <item>Time spent in bifs. Without extra states this time is part of - the <c>emulator</c> state.</item> + <item>Time spent in BIFs. Without extra states this time is part of + the <c>emulator</c> state.</item> <tag><c>busy_wait</c></tag> <item>Time spent busy waiting. This is also the state where a - scheduler no longer reports that it is active when using - <seealso marker="#statistics_scheduler_wall_time"> - <c>erlang:statistics(scheduler_wall_time)</c></seealso>. - So if you add all other states but this and sleep and then divide that - by all time in the thread you should get something very similar to the - scheduler_wall_time fraction. Without extra states this time is part - of the <c>other</c> state.</item> + scheduler no longer reports that it is active when using + <seealso marker="#statistics_scheduler_wall_time"> + <c>statistics(scheduler_wall_time)</c></seealso>. So, if you add + all other states but this and sleep, and then divide that by all + time in the thread, you should get something very similar to the + <c>scheduler_wall_time</c> fraction. Without extra states this + time is part of the <c>other</c> state.</item> <tag><c>ets</c></tag> - <item>Time spent executing ETS bifs. Without extra states this time is - part of the <c>emulator</c> state.</item> + <item>Time spent executing ETS BIFs. Without extra states + this time is part of the <c>emulator</c> state.</item> <tag><c>gc_full</c></tag> <item>Time spent doing fullsweep garbage collection. Without extra - states this time is part of the <c>gc</c> state.</item> + states this time is part of the <c>gc</c> state.</item> <tag><c>nif</c></tag> - <item>Time spent in nifs. Without extra states this time is part of - the <c>emulator</c> state.</item> + <item>Time spent in NIFs. Without extra states this time is part of + the <c>emulator</c> state.</item> <tag><c>send</c></tag> <item>Time spent sending messages (processes only). Without extra - states this time is part of the <c>emulator</c> state.</item> + states this time is part of the <c>emulator</c> state.</item> <tag><c>timers</c></tag> <item>Time spent managing timers. Without extra states this time is - part of the <c>other</c> state.</item> + part of the <c>other</c> state.</item> </taglist> - <p>There is a utility module called - <seealso marker="runtime_tools:msacc"><c>msacc</c></seealso> in - runtime_tools that can be used to more easily analyse these - statistics.</p> - - <p> - Returns <c>undefined</c> if the system flag + <p>The utility module + <seealso marker="runtime_tools:msacc"><c>msacc(3)</c></seealso> + can be used to more easily analyse these statistics.</p> + <p>Returns <c>undefined</c> if system flag <seealso marker="#system_flag_microstate_accounting"> - <c>microstate_accounting</c></seealso> - is turned off. - </p> - <p>The list of thread information is unsorted and may appear in - different order between calls.</p> - <note><p>The threads and states are subject to change without any - prior notice.</p></note> + <c>microstate_accounting</c></seealso> is turned off.</p> + <p>The list of thread information is unsorted and can appear in + different order between calls.</p> + <note> + <p>The threads and states are subject to change without any + prior notice.</p> + </note> </desc> </func> + <func> <name name="statistics" arity="1" clause_i="7"/> <fsummary>Information about reductions.</fsummary> @@ -6188,11 +6288,12 @@ lists:map( <pre> > <input>statistics(reductions).</input> {2046,11}</pre> - <note><p>As from <c>ERTS</c> 5.5 (OTP R11B), + <note><p>As from ERTS 5.5 (Erlang/OTP R11B), this value does not include reductions performed in current time slices of currently scheduled processes. If an exact value is wanted, use - <seealso marker="#statistics_exact_reductions">statistics(exact_reductions)</seealso>.</p> + <seealso marker="#statistics_exact_reductions"> + <c>statistics(exact_reductions)</c></seealso>.</p> </note> </desc> </func> @@ -6201,15 +6302,14 @@ lists:map( <name name="statistics" arity="1" clause_i="8"/> <fsummary>Information about the run-queues.</fsummary> <desc><marker id="statistics_run_queue"></marker> - <p> - Returns the total length of the run-queues. That is, the number + <p>Returns the total length of the run-queues. That is, the number of processes and ports that are ready to run on all available - run-queues. The information is gathered atomically. That - is, the result is a consistent snapshot of the state, but - this operation is much more expensive compared to - <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>. - This especially when a large amount of schedulers is used. - </p> + run-queues. The information is gathered atomically. That + is, the result is a consistent snapshot of the state, but + this operation is much more expensive compared to + <seealso marker="#statistics_total_run_queue_lengths"> + <c>statistics(total_run_queue_lengths)</c></seealso>, + especially when a large amount of schedulers is used.</p> </desc> </func> @@ -6217,19 +6317,21 @@ lists:map( <name name="statistics" arity="1" clause_i="9"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc><marker id="statistics_run_queue_lengths"></marker> - <p> - Returns a list where each element represents the amount - of processes and ports ready to run for each run queue. The - element location in the list corresponds to the run queue - of a scheduler. The first element corresponds to the run - queue of scheduler number 1 and so on. The information is - <em>not</em> gathered atomically. That is, the result is - not necessarily a consistent snapshot of the state, but - instead quite efficiently gathered. See also, - <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>, - <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>, and - <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>. - </p> + <p>Returns a list where each element represents the amount + of processes and ports ready to run for each run queue. The + element location in the list corresponds to the run queue + of a scheduler. The first element corresponds to the run + queue of scheduler number 1 and so on. The information is + <em>not</em> gathered atomically. That is, the result is + not necessarily a consistent snapshot of the state, but + instead quite efficiently gathered.</p> + <p>See also + <seealso marker="#statistics_total_run_queue_lengths"> + <c>statistics(total_run_queue_lengths)</c></seealso>, + <seealso marker="#statistics_active_tasks"> + <c>statistics(active_tasks)</c></seealso>, and + <seealso marker="#statistics_total_active_tasks"> + <c>statistics(total_active_tasks)</c></seealso>.</p> </desc> </func> @@ -6239,8 +6341,8 @@ lists:map( <desc> <p>Returns information about runtime, in milliseconds.</p> <p>This is the sum of the runtime for all threads - in the Erlang runtime system and can therefore be greater - than the wall clock time.</p> + in the Erlang runtime system and can therefore be greater + than the wall clock time.</p> <p>Example:</p> <pre> > <input>statistics(runtime).</input> @@ -6252,7 +6354,7 @@ lists:map( <name name="statistics" arity="1" clause_i="11"/> <fsummary>Information about each schedulers work time.</fsummary> <desc> - <marker id="statistics_scheduler_wall_time"></marker> + <marker id="statistics_scheduler_wall_time"></marker> <p>Returns a list of tuples with <c>{<anno>SchedulerId</anno>, <anno>ActiveTime</anno>, <anno>TotalTime</anno>}</c>, where @@ -6260,7 +6362,8 @@ lists:map( <c><anno>ActiveTime</anno></c> is the duration the scheduler has been busy, and <c><anno>TotalTime</anno></c> is the total time duration since - <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso> + <seealso marker="#system_flag_scheduler_wall_time"> + <c>scheduler_wall_time</c></seealso> activation. The time unit is undefined and can be subject to change between releases, OSs, and system restarts. <c>scheduler_wall_time</c> is only to be used to @@ -6272,28 +6375,28 @@ lists:map( that is:</p> <list type="bulleted"> <item>Executing process code</item> - <item>Executing linked-in-driver or NIF code</item> - <item>Executing built-in-functions, or any other runtime - handling</item> + <item>Executing linked-in driver or NIF code</item> + <item>Executing BIFs, or any other runtime handling</item> <item>Garbage collecting</item> <item>Handling any other memory management</item> </list> - <p>Notice that a scheduler can also be busy even if the - OS has scheduled out the scheduler thread.</p> + <p>Notice that a scheduler can also be busy even if the + OS has scheduled out the scheduler thread.</p> <p>Returns <c>undefined</c> if system flag - <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso> - is turned off.</p> + <seealso marker="#system_flag_scheduler_wall_time"> + <c>scheduler_wall_time</c></seealso> is turned off.</p> <p>The list of scheduler information is unsorted and can appear in different order between calls.</p> - <p>Using <c>scheduler_wall_time</c> to calculate scheduler-utilization:</p> -<pre> + <p>Using <c>scheduler_wall_time</c> to calculate + scheduler-utilization:</p> + <pre> > <input>erlang:system_flag(scheduler_wall_time, true).</input> false > <input>Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input> ok</pre> <p>Some time later the user takes another snapshot and calculates scheduler-utilization per scheduler, for example:</p> -<pre> + <pre> > <input>Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input> ok > <input>lists:map(fun({{I, A0, T0}, {I, A1, T1}}) -> @@ -6306,8 +6409,9 @@ ok {6,0.9739235846420741}, {7,0.973237033077876}, {8,0.9741297293248656}]</pre> - <p>Using the same snapshots to calculate a total scheduler-utilization:</p> -<pre> + <p>Using the same snapshots to calculate a total + scheduler-utilization:</p> + <pre> > <input>{A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) -> {Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.</input> 0.9769136803764825</pre> @@ -6323,17 +6427,19 @@ ok <name name="statistics" arity="1" clause_i="12"/> <fsummary>Information about active processes and ports.</fsummary> <desc><marker id="statistics_total_active_tasks"></marker> - <p> - Returns the total amount of active processes and ports in - the system. That is, the number of processes and ports that - are ready to run, or are currently running. The information - is <em>not</em> gathered atomically. That is, the result - is not necessarily a consistent snapshot of the state, but - instead quite efficiently gathered. See also, - <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>, - <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, and - <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>. - </p> + <p>Returns the total amount of active processes and ports in + the system. That is, the number of processes and ports that + are ready to run, or are currently running. The information + is <em>not</em> gathered atomically. That is, the result + is not necessarily a consistent snapshot of the state, but + instead quite efficiently gathered.</p> + <p>See also + <seealso marker="#statistics_active_tasks"> + <c>statistics(active_tasks)</c></seealso>, + <seealso marker="#statistics_run_queue_lengths"> + <c>statistics(run_queue_lengths)</c></seealso>, and + <seealso marker="#statistics_total_run_queue_lengths"> + <c>statistics(total_run_queue_lengths)</c></seealso>.</p> </desc> </func> @@ -6341,18 +6447,19 @@ ok <name name="statistics" arity="1" clause_i="13"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc><marker id="statistics_total_run_queue_lengths"></marker> - <p> - Returns the total length of the run-queues. That is, the number + <p>Returns the total length of the run queues. That is, the number of processes and ports that are ready to run on all available - run-queues. The information is <em>not</em> gathered atomically. - That is, the result is not necessarily a consistent snapshot of - the state, but much more efficiently gathered compared to - <seealso marker="#statistics_run_queue"><c>statistics(run_queue)</c></seealso>. - See also, - <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, - <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>, and - <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>. - </p> + run queues. The information is <em>not</em> gathered atomically. + That is, the result is not necessarily a consistent snapshot of + the state, but much more efficiently gathered compared to + <seealso marker="#statistics_run_queue"> + <c>statistics(run_queue)</c></seealso>.</p> + <p>See also <seealso marker="#statistics_run_queue_lengths"> + <c>statistics(run_queue_lengths)</c></seealso>, + <seealso marker="#statistics_total_active_tasks"> + <c>statistics(total_active_tasks)</c></seealso>, and + <seealso marker="#statistics_active_tasks"> + <c>statistics(active_tasks)</c></seealso>.</p> </desc> </func> @@ -6368,64 +6475,80 @@ ok </func> <func> + <name name="suspend_process" arity="1"/> + <fsummary>Suspend a process.</fsummary> + <desc> + <p>Suspends the process identified by + <c><anno>Suspendee</anno></c>. The same as calling + <seealso marker="#suspend_process/2"> + <c>erlang:suspend_process(<anno>Suspendee</anno>, + [])</c></seealso>.</p> + <warning> + <p>This BIF is intended for debugging only.</p> + </warning> + </desc> + </func> + + <func> <name name="suspend_process" arity="2"/> - <fsummary>Suspends a process.</fsummary> + <fsummary>Suspend a process.</fsummary> <desc> <p>Increases the suspend count on the process identified by - <c><anno>Suspendee</anno></c> and puts it in the suspended - state if it is not - already in that state. A suspended process will not be - scheduled for execution until the process has been resumed.</p> - <p>A process can be suspended by multiple processes and can - be suspended multiple times by a single process. A suspended - process does not leave the suspended state until its suspend - count reaches zero. The suspend count of - <c><anno>Suspendee</anno></c> is decreased when - <seealso marker="#resume_process/1">erlang:resume_process(<anno>Suspendee</anno>)</seealso> - is called by the same process that called - <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>. - All increased suspend - counts on other processes acquired by a process are automatically - decreased when the process terminates.</p> - <p>The options (<c><anno>Opt</anno></c>s) are as follows:</p> + <c><anno>Suspendee</anno></c> and puts it in the suspended + state if it is not + already in that state. A suspended process is not + scheduled for execution until the process has been resumed.</p> + <p>A process can be suspended by multiple processes and can + be suspended multiple times by a single process. A suspended + process does not leave the suspended state until its suspend + count reaches zero. The suspend count of + <c><anno>Suspendee</anno></c> is decreased when + <seealso marker="#resume_process/1"> + <c>erlang:resume_process(<anno>Suspendee</anno>)</c></seealso> + is called by the same process that called + <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>. + All increased suspend + counts on other processes acquired by a process are automatically + decreased when the process terminates.</p> + <p>Options (<c><anno>Opt</anno></c>s):</p> <taglist> <tag><c>asynchronous</c></tag> <item> - A suspend request is sent to the process identified by - <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> - eventually suspends - unless it is resumed before it could suspend. The caller - of <c>erlang:suspend_process/2</c> returns immediately, - regardless of whether <c><anno>Suspendee</anno></c> has - suspended yet or not. The point in time when - <c><anno>Suspendee</anno></c> suspends cannot be deduced - from other events in the system. It is only guaranteed that - <c><anno>Suspendee</anno></c> <em>eventually</em> suspends - (unless it - is resumed). If option <c>asynchronous</c> has <em>not</em> - been passed, the caller of <c>erlang:suspend_process/2</c> is - blocked until <c><anno>Suspendee</anno></c> has suspended. - </item> + <p>A suspend request is sent to the process identified by + <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> + eventually suspends + unless it is resumed before it could suspend. The caller + of <c>erlang:suspend_process/2</c> returns immediately, + regardless of whether <c><anno>Suspendee</anno></c> has + suspended yet or not. The point in time when + <c><anno>Suspendee</anno></c> suspends cannot be deduced + from other events in the system. It is only guaranteed that + <c><anno>Suspendee</anno></c> <em>eventually</em> suspends + (unless it + is resumed). If option <c>asynchronous</c> has <em>not</em> + been passed, the caller of <c>erlang:suspend_process/2</c> is + blocked until <c><anno>Suspendee</anno></c> has suspended.</p> + </item> <tag><c>unless_suspending</c></tag> <item> - The process identified by <c><anno>Suspendee</anno></c> is - suspended unless the calling process already is suspending - <c><anno>Suspendee</anno></c>. - If <c>unless_suspending</c> is combined - with option <c>asynchronous</c>, a suspend request is - sent unless the calling process already is suspending - <c><anno>Suspendee</anno></c> or if a suspend request - already has been sent and is in transit. If the calling - process already is suspending <c><anno>Suspendee</anno></c>, - or if combined with option <c>asynchronous</c> - and a send request already is in transit, - <c>false</c> is returned and the suspend count on - <c><anno>Suspendee</anno></c> remains unchanged. - </item> + <p>The process identified by <c><anno>Suspendee</anno></c> is + suspended unless the calling process already is suspending + <c><anno>Suspendee</anno></c>. + If <c>unless_suspending</c> is combined + with option <c>asynchronous</c>, a suspend request is + sent unless the calling process already is suspending + <c><anno>Suspendee</anno></c> or if a suspend request + already has been sent and is in transit. If the calling + process already is suspending <c><anno>Suspendee</anno></c>, + or if combined with option <c>asynchronous</c> + and a send request already is in transit, + <c>false</c> is returned and the suspend count on + <c><anno>Suspendee</anno></c> remains unchanged.</p> + </item> </taglist> - <p>If the suspend count on the process identified by - <c><anno>Suspendee</anno></c> is increased, <c>true</c> - is returned, otherwise <c>false</c>.</p> + <p>If the suspend count on the process identified by + <c><anno>Suspendee</anno></c> is increased, <c>true</c> + is returned, otherwise <c>false</c>.</p> <warning> <p>This BIF is intended for debugging only.</p> </warning> @@ -6433,58 +6556,44 @@ ok <taglist> <tag><c>badarg</c></tag> <item> - If <c><anno>Suspendee</anno></c> is not a process identifier. - </item> + If <c><anno>Suspendee</anno></c> is not a process identifier. + </item> <tag><c>badarg</c></tag> <item> - If the process identified by <c><anno>Suspendee</anno></c> - is the same process - as the process calling <c>erlang:suspend_process/2</c>. - </item> + If the process identified by <c><anno>Suspendee</anno></c> + is the same process + as the process calling <c>erlang:suspend_process/2</c>. + </item> <tag><c>badarg</c></tag> <item> - If the process identified by <c><anno>Suspendee</anno></c> - is not alive. - </item> + If the process identified by <c><anno>Suspendee</anno></c> + is not alive. + </item> <tag><c>badarg</c></tag> <item> - If the process identified by <c><anno>Suspendee</anno></c> - resides on another node. - </item> + If the process identified by <c><anno>Suspendee</anno></c> + resides on another node. + </item> <tag><c>badarg</c></tag> <item> - If <c><anno>OptList</anno></c> is not a proper list of valid - <c><anno>Opt</anno></c>s. - </item> + If <c><anno>OptList</anno></c> is not a proper list of valid + <c><anno>Opt</anno></c>s. + </item> <tag><c>system_limit</c></tag> <item> - If the process identified by <c><anno>Suspendee</anno></c> - has been suspended - more times by the calling process than can be represented by the - currently used internal data structures. The system limit is - higher than 2,000,000,000 suspends and will never be lower. - </item> + If the process identified by <c><anno>Suspendee</anno></c> + has been suspended + more times by the calling process than can be represented by the + currently used internal data structures. The system limit is + > 2,000,000,000 suspends and will never be lower. + </item> </taglist> </desc> </func> <func> - <name name="suspend_process" arity="1"/> - <fsummary>Suspends a process.</fsummary> - <desc> - <p>Suspends the process identified by - <c><anno>Suspendee</anno></c>. The same as calling - <seealso marker="#suspend_process/2">erlang:suspend_process(<anno>Suspendee</anno>, - [])</seealso>.</p> - <warning> - <p>This BIF is intended for debugging only.</p> - </warning> - </desc> - </func> - - <func> <name name="system_flag" arity="2" clause_i="1"/> - <fsummary>Sets system flag <c>backtrace_depth</c>.</fsummary> + <fsummary>Set system flag <c>backtrace_depth</c>.</fsummary> <desc> <p>Sets the maximum depth of call stack back-traces in the exit reason element of <c>'EXIT'</c> tuples.</p> @@ -6494,7 +6603,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="2"/> - <fsummary>Sets system flag <c>cpu_topology</c>.</fsummary> + <fsummary>Set system flag <c>cpu_topology</c>.</fsummary> <type name="cpu_topology"/> <type name="level_entry"/> <type name="level_tag"/> @@ -6503,52 +6612,52 @@ ok <desc> <warning> <p><marker id="system_flag_cpu_topology"></marker> - This argument is <em>deprecated</em> and scheduled for - removal in <c>ERTS</c> 5.10/OTP R16. Instead of using this - argument, use command-line argument - <seealso marker="erts:erl#+sct">+sct</seealso> in - <c>erl(1)</c>.</p> + <em>This argument is deprecated.</em> + Instead of using this argument, use command-line argument + <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> in + <c>erl(1)</c>.</p> <p>When this argument is removed, a final CPU topology - to use is determined at emulator boot time.</p> + to use is determined at emulator boot time.</p> </warning> <p>Sets the user-defined <c><anno>CpuTopology</anno></c>. - The user-defined - CPU topology overrides any automatically detected - CPU topology. By passing <c>undefined</c> as - <c><anno>CpuTopology</anno></c>, - the system reverts to the CPU topology automatically - detected. The returned value equals the value returned - from <c>erlang:system_info(cpu_topology)</c> before the - change was made.</p> + The user-defined + CPU topology overrides any automatically detected + CPU topology. By passing <c>undefined</c> as + <c><anno>CpuTopology</anno></c>, + the system reverts to the CPU topology automatically + detected. The returned value equals the value returned + from <c>erlang:system_info(cpu_topology)</c> before the + change was made.</p> <p>Returns the old value of the flag.</p> <p>The CPU topology is used when binding schedulers to logical - processors. If schedulers are already bound when the CPU - topology is changed, the schedulers are sent a request - to rebind according to the new CPU topology.</p> + processors. If schedulers are already bound when the CPU + topology is changed, the schedulers are sent a request + to rebind according to the new CPU topology.</p> <p>The user-defined CPU topology can also be set by passing - command-line argument - <seealso marker="erts:erl#+sct">+sct</seealso> to - <c>erl(1)</c>.</p> + command-line argument + <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> to + <c>erl(1)</c>.</p> <p>For information on type <c><anno>CpuTopology</anno></c> - and more, see - <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso> - as well as the command-line flags - <seealso marker="erts:erl#+sct">+sct</seealso> and - <seealso marker="erts:erl#+sbt">+sbt</seealso> in - <c>erl(1)</c>.</p> + and more, see + <seealso marker="#system_info_cpu_topology"> + <c>erlang:system_info(cpu_topology)</c></seealso> + as well as command-line flags + <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> and + <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> in + <c>erl(1)</c>.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="3"/> - <fsummary>Sets <c>system_flag_dirty_cpu_schedulers_online</c>.</fsummary> + <fsummary>Set system_flag_dirty_cpu_schedulers_online.</fsummary> <desc> <p><marker id="system_flag_dirty_cpu_schedulers_online"></marker> - Sets the number of dirty CPU schedulers online. Range is - <![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]>, where <c>N</c> - is the smallest of the return values of - <c>erlang:system_info(dirty_cpu_schedulers)</c> and - <c>erlang:system_info(schedulers_online)</c>.</p> + Sets the number of dirty CPU schedulers online. Range is + <c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>, where <c>N</c> + is the smallest of the return values of + <c>erlang:system_info(dirty_cpu_schedulers)</c> and + <c>erlang:system_info(schedulers_online)</c>.</p> <p>Returns the old value of the flag.</p> <p>The number of dirty CPU schedulers online can change if the number of schedulers online changes. For example, if 12 @@ -6559,20 +6668,22 @@ ok down to 3. Similarly, the number of dirty CPU schedulers online increases proportionally to increases in the number of schedulers online.</p> - <note><p>The dirty schedulers functionality is experimental. - Enable support for dirty schedulers when building OTP to - try out the functionality.</p> + <note> + <p>The dirty schedulers functionality is experimental. + Enable support for dirty schedulers when building OTP to + try out the functionality.</p> </note> <p>For more information, see - <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso> - and - <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>.</p> + <seealso marker="#system_info_dirty_cpu_schedulers"> + <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso> and + <seealso marker="#system_info_dirty_cpu_schedulers_online"> + <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="4"/> - <fsummary>Sets system flag <c>fullsweep_after</c>.</fsummary> + <fsummary>Set system flag fullsweep_after.</fsummary> <desc> <p>Sets system flag <c>fullsweep_after</c>. <c><anno>Number</anno></c> is a non-negative integer indicating @@ -6591,276 +6702,297 @@ ok <func> <name name="system_flag" arity="2" clause_i="5"/> - <fsummary>Set system flag microstate_accounting</fsummary> - <desc><p><marker id="system_flag_microstate_accounting"></marker> - Turns on/off microstate accounting measurements. By passing reset it is possible to reset - all counters to 0.</p> - <p>For more information see, - <seealso marker="#statistics_microstate_accounting">erlang:statistics(microstate_accounting)</seealso>. - </p> + <fsummary>Set system flag microstate_accounting.</fsummary> + <desc> + <p><marker id="system_flag_microstate_accounting"></marker> + Turns on/off microstate accounting measurements. When passing reset, + all counters are reset to 0.</p> + <p>For more information see + <seealso marker="#statistics_microstate_accounting"> + <c>statistics(microstate_accounting)</c></seealso>.</p> </desc> </func> + <func> <name name="system_flag" arity="2" clause_i="6"/> - <fsummary>Sets system flag <c>min_heap_size</c>.</fsummary> + <fsummary>Set system flag min_heap_size.</fsummary> <desc> <p>Sets the default minimum heap size for processes. The size - is given in words. The new <c>min_heap_size</c> effects + is specified in words. The new <c>min_heap_size</c> effects only processes spawned after the change of <c>min_heap_size</c> has been made. <c>min_heap_size</c> can be set for individual processes by using - <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or - <seealso marker="#process_flag/2">process_flag/2</seealso>.</p> + <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso> or + <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.</p> <p>Returns the old value of the flag.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="7"/> - <fsummary>Sets system flag <c>min_bin_vheap_size</c>.</fsummary> + <fsummary>Set system flag min_bin_vheap_size.</fsummary> <desc> <p>Sets the default minimum binary virtual heap size for - processes. The size is given in words. + processes. The size is specified in words. The new <c>min_bin_vhheap_size</c> effects only processes spawned after the change of - <c>min_bin_vhheap_size</c> has been made. + <c>min_bin_vheap_size</c> has been made. <c>min_bin_vheap_size</c> can be set for individual processes by using - <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or - <seealso marker="#process_flag/2">process_flag/2</seealso>.</p> + <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> or + <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.</p> <p>Returns the old value of the flag.</p> </desc> </func> - <marker id="system_flag_max_heap_size"></marker> <func> <name name="system_flag" arity="2" clause_i="8"/> - <fsummary>Sets system flag <c>max_heap_size</c></fsummary> + <fsummary>Set system flag max_heap_size.</fsummary> <type name="max_heap_size"/> <desc> + <marker id="system_flag_max_heap_size"></marker> <p> Sets the default maximum heap size settings for processes. - The size is given in words. The new <c>max_heap_size</c> + The size is specified in words. The new <c>max_heap_size</c> effects only processes spawned efter the change has been made. <c>max_heap_size</c> can be set for individual processes using - <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or - <seealso marker="#process_flag_message_queue_data">process_flag/2</seealso>.</p> + <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> or + <seealso marker="#process_flag_message_queue_data"> + <c>process_flag/2</c></seealso>.</p> <p>Returns the old value of the flag.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="9"/> - <fsummary>Sets system flag <c>multi_scheduling</c>.</fsummary> + <fsummary>Set system flag multi_scheduling.</fsummary> <desc> <p><marker id="system_flag_multi_scheduling"></marker> If multi-scheduling is enabled, more than one scheduler thread is used by the emulator. Multi-scheduling can be blocked in two different ways. Either all schedulers but - one is blocked, or all <em>normal</em> schedulers but - one is blocked. When only normal schedulers are blocked - dirty schedulers are free to continue to schedule - processes.</p> + one is blocked, or all <em>normal</em> schedulers but + one is blocked. When only normal schedulers are blocked, + dirty schedulers are free to continue to schedule + processes.</p> <p>If <c><anno>BlockState</anno> =:= block</c>, multi-scheduling is blocked. That is, one and only one scheduler thread will - execute. If <c><anno>BlockState</anno> =:= unblock</c> and no one + execute. If <c><anno>BlockState</anno> =:= unblock</c> and no one else blocks multi-scheduling, and this process has blocked only once, multi-scheduling is unblocked.</p> <p>If <c><anno>BlockState</anno> =:= block_normal</c>, normal - multi-scheduling is blocked. That is, only one normal scheduler - thread will execute, but multiple dirty schedulers may execute. - If <c><anno>BlockState</anno> =:= unblock_normal</c> and no one + multi-scheduling is blocked. That is, only one normal scheduler + thread will execute, but multiple dirty schedulers can execute. + If <c><anno>BlockState</anno> =:= unblock_normal</c> and no one else blocks normal multi-scheduling, and this process has blocked only once, normal multi-scheduling is unblocked.</p> - <p>One process can block multi-scheduling as well as normal - multi-scheduling multiple times. If a process has blocked - multiple times, it must unblock exactly as many times as it - has blocked before it has released its multi-scheduling - block. If a process that has blocked multi-scheduling or normal - multi scheduling exits, it automatically releases its blocking - of multi-scheduling and normal multi-scheduling.</p> + <p>One process can block multi-scheduling and normal + multi-scheduling multiple times. If a process has blocked + multiple times, it must unblock exactly as many times as it + has blocked before it has released its multi-scheduling + block. If a process that has blocked multi-scheduling or normal + multi-scheduling exits, it automatically releases its blocking + of multi-scheduling and normal multi-scheduling.</p> <p>The return values are <c>disabled</c>, <c>blocked</c>, <c>blocked_normal</c>, or <c>enabled</c>. The returned value - describes the state just after the call to + describes the state just after the call to <c>erlang:system_flag(multi_scheduling, <anno>BlockState</anno>)</c> has been made. For information about the return values, see - <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p> + <seealso marker="#system_info_multi_scheduling"> + <c>erlang:system_info(multi_scheduling)</c></seealso>.</p> <note><p>Blocking of multi-scheduling and normal multi-scheduling - is normally not needed. If you feel that you need to use these - features, consider it a few more times again. Blocking - multi-scheduling is only to be used as a last resort, as it is - most likely a <em>very inefficient</em> way to solve the problem.</p> + is normally not needed. If you feel that you need to use these + features, consider it a few more times again. Blocking + multi-scheduling is only to be used as a last resort, as it is + most likely a <em>very inefficient</em> way to solve the problem.</p> </note> <p>See also - <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>, - <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>, - <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, and - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> + <seealso marker="#system_info_multi_scheduling"> + <c>erlang:system_info(multi_scheduling)</c></seealso>, + <seealso marker="#system_info_normal_multi_scheduling_blockers"> + <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>, + <seealso marker="#system_info_multi_scheduling_blockers"> + <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>, and + <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="10"/> - <fsummary>Sets system flag <c>scheduler_bind_type</c>.</fsummary> + <fsummary>Set system flag scheduler_bind_type.</fsummary> <type name="scheduler_bind_type"/> <desc> <warning> <p><marker id="system_flag_scheduler_bind_type"></marker> - This argument is <em>deprecated</em> and scheduled for - removal in <c>ERTS</c> 5.10/OTP R16. Instead of using this - argument, use command-line argument - <seealso marker="erts:erl#+sbt">+sbt</seealso> in <c>erl(1)</c>. - When this argument is removed, a final scheduler bind - type to use is determined at emulator boot time.</p> + <em>This argument is deprecated.</em> + Instead of using this argument, use command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> in + <c>erl(1)</c>. When this argument is removed, a final scheduler bind + type to use is determined at emulator boot time.</p> </warning> <p>Controls if and how schedulers are bound to logical - processors.</p> + processors.</p> <p>When <c>erlang:system_flag(scheduler_bind_type, <anno>How</anno>)</c> - is called, an asynchronous signal is sent to all schedulers - online, causing them to try to bind or unbind as requested.</p> + is called, an asynchronous signal is sent to all schedulers + online, causing them to try to bind or unbind as requested.</p> <note><p>If a scheduler fails to bind, this is often silently - ignored, as it is not always possible to verify valid - logical processor identifiers. If an error is reported, - it is reported to <c>error_logger</c>. To verify that the - schedulers have bound as requested, call - <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p> + ignored, as it is not always possible to verify valid + logical processor identifiers. If an error is reported, + it is reported to <c>error_logger</c>. To verify that the + schedulers have bound as requested, call + <seealso marker="#system_info_scheduler_bindings"> + <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p> </note> <p>Schedulers can be bound on newer Linux, - Solaris, FreeBSD, and Windows systems, but more systems will be - supported in future releases.</p> + Solaris, FreeBSD, and Windows systems, but more systems will be + supported in future releases.</p> <p>In order for the runtime system to be able to bind schedulers, - the CPU topology must be known. If the runtime system fails - to detect the CPU topology automatically, it can be defined. - For more information on how to define the CPU topology, see - command-line flag <seealso marker="erts:erl#+sct">+sct</seealso> - in <c>erl(1)</c>.</p> + the CPU topology must be known. If the runtime system fails + to detect the CPU topology automatically, it can be defined. + For more information on how to define the CPU topology, see + command-line flag <seealso marker="erts:erl#+sct"> + <c>+sct</c></seealso> in <c>erl(1)</c>.</p> <p>The runtime system does by default <em>not</em> bind schedulers - to logical processors.</p> + to logical processors.</p> <note><p>If the Erlang runtime system is the only OS - process binding threads to logical processors, this - improves the performance of the runtime system. However, - if other OS processes (for example, another Erlang - runtime system) also bind threads to logical processors, - there can be a performance penalty instead. Sometimes this - performance penalty can be severe. If so, it is recommended - to not bind the schedulers.</p> + process binding threads to logical processors, this + improves the performance of the runtime system. However, + if other OS processes (for example, another Erlang + runtime system) also bind threads to logical processors, + there can be a performance penalty instead. Sometimes this + performance penalty can be severe. If so, it is recommended + to not bind the schedulers.</p> </note> <p>Schedulers can be bound in different ways. Argument - <c><anno>How</anno></c> determines how schedulers are - bound and can be any of the following:</p> + <c><anno>How</anno></c> determines how schedulers are + bound and can be any of the following:</p> <taglist> <tag><c>unbound</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt u</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt u</c></seealso> in + <c>erl(1)</c>. + </item> <tag><c>no_spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt ns</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt ns</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>thread_spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt ts</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt ts</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>processor_spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt ps</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt ps</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt s</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt s</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>no_node_thread_spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt nnts</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt nnts</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>no_node_processor_spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt nnps</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt nnps</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>thread_no_node_processor_spread</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt tnnps</c></seealso> + in <c>erl(1)</c>. + </item> <tag><c>default_bind</c></tag> - <item><p>Same as command-line argument - <seealso marker="erts:erl#+sbt">+sbt db</seealso> in <c>erl(1)</c>. - </p></item> + <item>Same as command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt db</c></seealso> + in <c>erl(1)</c>. + </item> </taglist> <p>The returned value equals <c><anno>How</anno></c> before flag <c>scheduler_bind_type</c> was changed.</p> <p>Failures:</p> <taglist> <tag><c>notsup</c></tag> - <item> - <p>If binding of schedulers is not supported.</p> + <item>If binding of schedulers is not supported. </item> <tag><c>badarg</c></tag> - <item> - <p>If <c><anno>How</anno></c> is not one of the documented - alternatives.</p> + <item>If <c><anno>How</anno></c> is not one of the documented + alternatives. </item> <tag><c>badarg</c></tag> - <item> - <p>If CPU topology information is unavailable.</p> + <item>If CPU topology information is unavailable. </item> </taglist> - <p>The scheduler bind type can also be set by passing - command-line argument - <seealso marker="erts:erl#+sbt">+sbt</seealso> to <c>erl(1)</c>.</p> + <p>The scheduler bind type can also be set by passing command-line + argument <seealso marker="erts:erl#+sbt"> + <c>+sbt</c></seealso> to <c>erl(1)</c>.</p> <p>For more information, see - <seealso marker="#system_info_scheduler_bind_type">erlang:system_info(scheduler_bind_type)</seealso>, - <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>, - as well as command-line flags - <seealso marker="erts:erl#+sbt">+sbt</seealso> - and <seealso marker="erts:erl#+sct">+sct</seealso> - in <c>erl(1)</c>.</p> + <seealso marker="#system_info_scheduler_bind_type"> + <c>erlang:system_info(scheduler_bind_type)</c></seealso>, + <seealso marker="#system_info_scheduler_bindings"> + <c>erlang:system_info(scheduler_bindings)</c></seealso>, + as well as command-line flags + <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> + and <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> + in <c>erl(1)</c>.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="11"/> - <fsummary>Sets system flag <c>scheduler_wall_time</c>.</fsummary> - <desc><p><marker id="system_flag_scheduler_wall_time"></marker> - Turns on or off scheduler wall time measurements.</p> - <p>For more information, see - <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.</p> + <fsummary>Set system flag scheduler_wall_time.</fsummary> + <desc> + <p><marker id="system_flag_scheduler_wall_time"></marker> + Turns on or off scheduler wall time measurements.</p> + <p>For more information, see + <seealso marker="#statistics_scheduler_wall_time"> + <c>statistics(scheduler_wall_time)</c></seealso>.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="12"/> - <fsummary>Sets system flag <c>schedulers_online</c>.</fsummary> + <fsummary>Set system flag schedulers_online.</fsummary> <desc> <p><marker id="system_flag_schedulers_online"></marker> - Sets the number of schedulers online. Range is - <![CDATA[1 <= SchedulersOnline <= erlang:system_info(schedulers)]]>.</p> + Sets the number of schedulers online. Range is + <c><![CDATA[1 <= SchedulersOnline <= + erlang:system_info(schedulers)]]></c>.</p> <p>Returns the old value of the flag.</p> <p>If the emulator was built with support for - <seealso marker="#system_flag_dirty_cpu_schedulers_online">dirty schedulers</seealso>, - changing the number of schedulers online can also change the - number of dirty CPU schedulers online. For example, if 12 - schedulers and 6 dirty CPU schedulers are online, and - <c>system_flag/2</c> is used to set the number of schedulers - online to 6, then the number of dirty CPU schedulers online - is automatically decreased by half as well, down to 3. - Similarly, the number of dirty CPU schedulers online increases - proportionally to increases in the number of schedulers online.</p> + <seealso marker="#system_flag_dirty_cpu_schedulers_online"> + dirty schedulers</seealso>, + changing the number of schedulers online can also change the + number of dirty CPU schedulers online. For example, if 12 + schedulers and 6 dirty CPU schedulers are online, and + <c>system_flag/2</c> is used to set the number of schedulers + online to 6, then the number of dirty CPU schedulers online + is automatically decreased by half as well, down to 3. + Similarly, the number of dirty CPU schedulers online increases + proportionally to increases in the number of schedulers online.</p> <p>For more information, see - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso> - and - <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.</p> + <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso> and + <seealso marker="#system_info_schedulers_online"> + <c>erlang:system_info(schedulers_online)</c></seealso>.</p> </desc> </func> <func> <name name="system_flag" arity="2" clause_i="13"/> - <fsummary>Sets system flag <c>trace_control_word</c>.</fsummary> + <fsummary>Set system flag trace_control_word.</fsummary> <desc> <p>Sets the value of the node trace control word to <c><anno>TCW</anno></c>, which is to be an unsigned integer. - For more information, see the function - <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso> - in Section "Match Specifications in Erlang" in the + For more information, see function + <seealso marker="erts:match_spec#set_tcw"><c>set_tcw</c></seealso> + in section "Match Specifications in Erlang" in the User's Guide.</p> <p>Returns the old value of the flag.</p> </desc> @@ -6868,29 +7000,30 @@ ok <func> <name name="system_flag" arity="2" clause_i="14"/> - <fsummary>Finalize the Time Offset</fsummary> + <fsummary>Finalize the time offset.</fsummary> <desc> <p><marker id="system_flag_time_offset"></marker> - Finalizes the <seealso marker="#time_offset/0">time offset</seealso> - when <seealso marker="time_correction#Single_Time_Warp_Mode">single - time warp mode</seealso> is used. If another time warp mode - is used, the time offset state is left unchanged.</p> - <p>Returns the old state identifier. That is:</p> - <list> - <item><p>If <c>preliminary</c> is returned, finalization was - performed and the time offset is now final.</p></item> - - <item><p>If <c>final</c> is returned, the time offset was - already in the final state. This either because another - <c>erlang:system_flag(time_offset, finalize)</c> call, or - because <seealso marker="time_correction#No_Time_Warp_Mode">no - time warp mode</seealso> is used.</p></item> - - <item><p>If <c>volatile</c> is returned, the time offset - cannot be finalized because - <seealso marker="time_correction#Multi_Time_Warp_Mode">multi - time warp mode</seealso> is used.</p></item> - </list> + Finalizes the <seealso marker="#time_offset/0">time offset</seealso> + when <seealso marker="time_correction#Single_Time_Warp_Mode">single + time warp mode</seealso> is used. If another time warp mode + is used, the time offset state is left unchanged.</p> + <p>Returns the old state identifier, that is:</p> + <list> + <item><p>If <c>preliminary</c> is returned, finalization was + performed and the time offset is now final.</p> + </item> + <item><p>If <c>final</c> is returned, the time offset was + already in the final state. This either because another + <c>erlang:system_flag(time_offset, finalize)</c> call or + because <seealso marker="time_correction#No_Time_Warp_Mode">no + time warp mode</seealso> is used.</p> + </item> + <item><p>If <c>volatile</c> is returned, the time offset + cannot be finalized because + <seealso marker="time_correction#Multi_Time_Warp_Mode">multi-time + warp mode</seealso> is used.</p> + </item> + </list> </desc> </func> @@ -6907,7 +7040,7 @@ ok <type variable="Settings" name_i="2"/> <type variable="Alloc" name_i="3"/> <desc> - <marker id="system_info_allocator_tags"></marker> + <marker id="system_info_allocator_tags"></marker> <p>Returns various information about the allocators of the current system (emulator) as specified by <c><anno>Item</anno></c>:</p> @@ -6926,17 +7059,18 @@ ok <p><c>erlang:system_info(allocated_areas)</c> is intended for debugging, and the content is highly implementation-dependent. The content of the results - therefore changes when needed without prior notice.</p> + therefore changes when needed without prior notice.</p> <p>Notice that the sum of these values is <em>not</em> the total amount of memory allocated by the emulator. Some values are part of other values, and some memory areas are not part of the result. For information about the total amount of memory allocated by the emulator, see - <seealso marker="#memory/0">erlang:memory/0,1</seealso>.</p> + <seealso marker="#memory/0"> + <c>erlang:memory/0,1</c></seealso>.</p> </item> <tag><c>allocator</c></tag> <item> - <marker id="system_info_allocator"></marker> + <marker id="system_info_allocator"></marker> <p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>, <anno>Features</anno>, <anno>Settings</anno></c>, where:</p> <list type="bulleted"> @@ -6966,27 +7100,30 @@ ok </item> </list> <p>See also "System Flags Effecting erts_alloc" in - <seealso marker="erts:erts_alloc#flags">erts_alloc(3)</seealso>.</p> + <seealso marker="erts:erts_alloc#flags"> + <c>erts_alloc(3)</c></seealso>.</p> </item> <tag><c>alloc_util_allocators</c></tag> <item> - <marker id="system_info_alloc_util_allocators"></marker> - <p>Returns a list of the names of all allocators using - the <c>ERTS</c> internal <c>alloc_util</c> framework - as atoms. For more information, see Section - <seealso marker="erts:erts_alloc#alloc_util">"The - alloc_util framework" in erts_alloc(3)</seealso>.</p> + <marker id="system_info_alloc_util_allocators"></marker> + <p>Returns a list of the names of all allocators using + the ERTS internal <c>alloc_util</c> framework + as atoms. For more information, see section + <seealso marker="erts:erts_alloc#alloc_util">The + alloc_util framework</seealso> + in <c>erts_alloc(3)</c>.</p> </item> <tag><c>{allocator, <anno>Alloc</anno>}</c></tag> <item> - <marker id="system_info_allocator_tuple"></marker> + <marker id="system_info_allocator_tuple"></marker> <p>Returns information about the specified allocator. - As from <c>ERTS</c> 5.6.1, the return value is a list - of <c>{instance, InstanceNo, InstanceInfo}</c> tuples, - where <c>InstanceInfo</c> contains information about - a specific instance of the allocator. If <c><anno>Alloc</anno></c> is not a - recognized allocator, <c>undefined</c> is returned. - If <c><anno>Alloc</anno></c> is disabled, + As from ERTS 5.6.1, the return value is a list + of <c>{instance, InstanceNo, InstanceInfo}</c> tuples, + where <c>InstanceInfo</c> contains information about + a specific instance of the allocator. + If <c><anno>Alloc</anno></c> is not a + recognized allocator, <c>undefined</c> is returned. + If <c><anno>Alloc</anno></c> is disabled, <c>false</c> is returned.</p> <p>Notice that the information returned is highly implementation-dependent and can be changed or removed @@ -6995,15 +7132,14 @@ ok as it can be of interest for others it has been briefly documented.</p> <p>The recognized allocators are listed in - <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>. - Information about super carriers can be obtained from - <c>ERTS</c> 8.0 with <c>{allocator, erts_mmap}</c> or from - <c>ERTS</c> 5.10.4, the returned list when calling with - <c>{allocator, mseg_alloc}</c> also includes an - <c>{erts_mmap, _}</c> tuple as one element in the list.</p> - + <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>. + Information about super carriers can be obtained from + ERTS 8.0 with <c>{allocator, erts_mmap}</c> or from + ERTS 5.10.4; the returned list when calling with + <c>{allocator, mseg_alloc}</c> also includes an + <c>{erts_mmap, _}</c> tuple as one element in the list.</p> <p>After reading the <c>erts_alloc(3)</c> documentation, - the returned information + the returned information more or less speaks for itself, but it can be worth explaining some things. Call counts are presented by two values, the first value is giga calls, and the second @@ -7019,19 +7155,20 @@ ok <item>The third is the maximum value since the emulator was started.</item> </list> - <p>If only one value is present, it is the current value. + <p>If only one value is present, it is the current value. <c>fix_alloc</c> memory block types are presented by two values. The first value is the memory pool size and the second value is the used memory size.</p> </item> <tag><c>{allocator_sizes, <anno>Alloc</anno>}</c></tag> <item> - <marker id="system_info_allocator_sizes"></marker> - <p>Returns various size information for the specified - allocator. The information returned is a subset of the - information returned by - <seealso marker="#system_info_allocator_tuple"><c>erlang:system_info({allocator, <anno>Alloc</anno>})</c></seealso>. - </p> + <marker id="system_info_allocator_sizes"></marker> + <p>Returns various size information for the specified + allocator. The information returned is a subset of the + information returned by + <seealso marker="#system_info_allocator_tuple"> + <c>erlang:system_info({allocator, + <anno>Alloc</anno>})</c></seealso>.</p> </item> </taglist> </desc> @@ -7065,69 +7202,74 @@ ok The <c>info_list()</c> can be extended in a future release. </type_desc> <desc> - <marker id="system_info_cpu_topology_tags"></marker> - <marker id="system_info_cpu_topology"></marker> + <marker id="system_info_cpu_topology_tags"></marker> + <marker id="system_info_cpu_topology"></marker> <p>Returns various information about the CPU topology of the current system (emulator) as specified by <c><anno>Item</anno></c>:</p> <taglist> <tag><c>cpu_topology</c></tag> <item> - <p>Returns the <c><anno>CpuTopology</anno></c> currently used by - the emulator. The CPU topology is used when binding schedulers - to logical processors. The CPU topology used is the - <seealso marker="erlang#system_info_cpu_topology_defined">user-defined CPU topology</seealso>, - if such exists, otherwise the - <seealso marker="erlang#system_info_cpu_topology_detected">automatically detected CPU topology</seealso>, - if such exists. If no CPU topology - exists, <c>undefined</c> is returned.</p> - <p><c>node</c> refers to Non-Uniform Memory Access (NUMA) - nodes. <c>thread</c> refers to hardware threads - (for example, Intel hyper-threads).</p> + <p>Returns the <c><anno>CpuTopology</anno></c> currently used by + the emulator. The CPU topology is used when binding schedulers + to logical processors. The CPU topology used is the + <seealso marker="erlang#system_info_cpu_topology_defined"> + user-defined CPU topology</seealso>, + if such exists, otherwise the + <seealso marker="erlang#system_info_cpu_topology_detected"> + automatically detected CPU topology</seealso>, + if such exists. If no CPU topology + exists, <c>undefined</c> is returned.</p> + <p><c>node</c> refers to Non-Uniform Memory Access (NUMA) + nodes. <c>thread</c> refers to hardware threads + (for example, Intel hyper-threads).</p> <p>A level in term <c><anno>CpuTopology</anno></c> can be - omitted if only one entry exists and - <c><anno>InfoList</anno></c> is empty.</p> - <p><c>thread</c> can only be a sub level to <c>core</c>. - <c>core</c> can be a sub level to <c>processor</c> - or <c>node</c>. <c>processor</c> can be on the - top level or a sub level to <c>node</c>. <c>node</c> - can be on the top level or a sub level to - <c>processor</c>. That is, NUMA nodes can be processor - internal or processor external. A CPU topology can - consist of a mix of processor internal and external - NUMA nodes, as long as each logical CPU belongs to - <em>one</em> NUMA node. Cache hierarchy is not part of - the <c><anno>CpuTopology</anno></c> type, but will be in a - future release. Other things can also make it into the CPU - topology in a future release. In other words, expect the - <c><anno>CpuTopology</anno></c> type to change.</p> + omitted if only one entry exists and + <c><anno>InfoList</anno></c> is empty.</p> + <p><c>thread</c> can only be a sublevel to <c>core</c>. + <c>core</c> can be a sublevel to <c>processor</c> + or <c>node</c>. <c>processor</c> can be on the + top level or a sublevel to <c>node</c>. <c>node</c> + can be on the top level or a sublevel to + <c>processor</c>. That is, NUMA nodes can be processor + internal or processor external. A CPU topology can + consist of a mix of processor internal and external + NUMA nodes, as long as each logical CPU belongs to + <em>one</em> NUMA node. Cache hierarchy is not part of + the <c><anno>CpuTopology</anno></c> type, but will be in a + future release. Other things can also make it into the CPU + topology in a future release. So, expect the + <c><anno>CpuTopology</anno></c> type to change.</p> </item> <tag><c>{cpu_topology, defined}</c></tag> <item> - <marker id="system_info_cpu_topology_defined"></marker> + <marker id="system_info_cpu_topology_defined"></marker> <p>Returns the user-defined <c><anno>CpuTopology</anno></c>. - For more information, see command-line flag - <seealso marker="erts:erl#+sct">+sct</seealso> in - <c>erl(1)</c> and argument - <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p> + For more information, see command-line flag + <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> in + <c>erl(1)</c> and argument + <seealso marker="#system_info_cpu_topology"> + <c>cpu_topology</c></seealso>.</p> </item> <tag><c>{cpu_topology, detected}</c></tag> <item> - <marker id="system_info_cpu_topology_detected"></marker> + <marker id="system_info_cpu_topology_detected"></marker> <p>Returns the automatically detected - <c><anno>CpuTopology</anno>y</c>. The - emulator detects the CPU topology on some newer - Linux, Solaris, FreeBSD, and Windows systems. - On Windows system with more than 32 logical processors, - the CPU topology is not detected.</p> + <c><anno>CpuTopology</anno>y</c>. The + emulator detects the CPU topology on some newer + Linux, Solaris, FreeBSD, and Windows systems. + On Windows system with more than 32 logical processors, + the CPU topology is not detected.</p> <p>For more information, see argument - <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p> + <seealso marker="#system_info_cpu_topology"> + <c>cpu_topology</c></seealso>.</p> </item> <tag><c>{cpu_topology, used}</c></tag> <item> <p>Returns <c><anno>CpuTopology</anno></c> used by the emulator. For more information, see argument - <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p> + <seealso marker="#system_info_cpu_topology"> + <c>cpu_topology</c></seealso>.</p> </item> </taglist> </desc> @@ -7144,13 +7286,14 @@ ok <type name="message_queue_data"/> <type name="max_heap_size"/> <desc> + <p>Returns information about the default process heap settings:</p> <taglist> <tag><c>fullsweep_after</c></tag> <item> <p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is the <c>fullsweep_after</c> garbage collection setting used by default. For more information, see - <c>garbage_collection</c> described in the following.</p> + <c>garbage_collection</c> described below.</p> </item> <tag><c>garbage_collection</c></tag> <item> @@ -7159,50 +7302,52 @@ ok <c>spawn</c> or <c>spawn_link</c> uses these garbage collection settings. The default settings can be changed by using - <seealso marker="#system_flag/2">system_flag/2</seealso>. - <seealso marker="#spawn_opt/4">spawn_opt/4</seealso> + <seealso marker="#system_flag/2"> + <c>erlang:system_flag/2</c></seealso>. + <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> can spawn a process that does not use the default settings.</p> </item> <tag><c>max_heap_size</c></tag> <item> - <p>Returns <c>{max_heap_size, <anno>MaxHeapSize</anno>}</c>, + <p>Returns <c>{max_heap_size, <anno>MaxHeapSize</anno>}</c>, where <c><anno>MaxHeapSize</anno></c> is the current - system-wide max heap size settings for spawned processes. - This setting can be set using the <c>erl</c> command line - flags <seealso marker="erl#+hmax"><c>+hmax</c></seealso>, + system-wide maximum heap size settings for spawned processes. + This setting can be set using the command-line flags + <seealso marker="erl#+hmax"><c>+hmax</c></seealso>, <seealso marker="erl#+hmaxk"><c>+hmaxk</c></seealso> and - <seealso marker="erl#+hmaxel"><c>+hmaxel</c></seealso>. It can - also be changed at run-time using + <seealso marker="erl#+hmaxel"><c>+hmaxel</c></seealso> in + <c>erl(1)</c>. It can also be changed at runtime using <seealso marker="#system_flag_max_heap_size"> - <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. - For more details about the <c>max_heap_size</c> process flag + <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. + For more details about the <c>max_heap_size</c> process flag, see <seealso marker="#process_flag_max_heap_size"> - <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>. - </p> + <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> - <tag><c>min_heap_size</c></tag> + <tag><c>min_heap_size</c></tag> <item> - <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>, + <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>, where <c><anno>MinHeapSize</anno></c> is the current system-wide minimum heap size for spawned processes.</p> </item> - <tag><marker id="system_info_message_queue_data"/><c>message_queue_data</c></tag> + <tag><marker id="system_info_message_queue_data"/> + <c>message_queue_data</c></tag> <item> <p>Returns the default value of the <c>message_queue_data</c> - process flag which is either <c>off_heap</c>, or <c>on_heap</c>. - This default is set by the <c>erl</c> command line argument - <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso>. For more information on the - <c>message_queue_data</c> process flag, see documentation of - <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data, - MQD)</c></seealso>.</p> + process flag, which is either <c>off_heap</c> or <c>on_heap</c>. + This default is set by command-line argument + <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> in + <c>erl(1)</c>. For more information on the + <c>message_queue_data</c> process flag, see documentation of + <seealso marker="#process_flag_message_queue_data"> + <c>process_flag(message_queue_data, MQD)</c></seealso>.</p> </item> - <tag><c>min_bin_vheap_size</c></tag> + <tag><c>min_bin_vheap_size</c></tag> <item> - <p>Returns <c>{min_bin_vheap_size, + <p>Returns <c>{min_bin_vheap_size, <anno>MinBinVHeapSize</anno>}</c>, where <c><anno>MinBinVHeapSize</anno></c> is the current system-wide - minimum binary virtual heap size for spawned processes.</p> + minimum binary virtual heap size for spawned processes.</p> </item> </taglist> </desc> @@ -7273,24 +7418,25 @@ ok <tag><c>allocated_areas</c>, <c>allocator</c>, <c>alloc_util_allocators</c>, <c>allocator_sizes</c></tag> <item> - <p>See <seealso marker="#system_info_allocator_tags">above</seealso>.</p> + <p>See <seealso marker="#system_info_allocator_tags"> + above</seealso>.</p> </item> <tag><c>build_type</c></tag> <item> <p>Returns an atom describing the build type of the runtime - system. This is normally the atom <c>opt</c> for optimized. - Other possible return values are <c>debug</c>, <c>purify</c>, - <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>, - <c>gprof</c>, and <c>lcnt</c>. Possible return values - can be added or removed at any time without prior notice.</p> + system. This is normally the atom <c>opt</c> for optimized. + Other possible return values are <c>debug</c>, <c>purify</c>, + <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>, + <c>gprof</c>, and <c>lcnt</c>. Possible return values + can be added or removed at any time without prior notice.</p> </item> <tag><c>c_compiler_used</c></tag> <item> <p>Returns a two-tuple describing the C compiler used when - compiling the runtime system. The first element is an - atom describing the name of the compiler, or <c>undefined</c> - if unknown. The second element is a term describing the - version of the compiler, or <c>undefined</c> if unknown.</p> + compiling the runtime system. The first element is an + atom describing the name of the compiler, or <c>undefined</c> + if unknown. The second element is a term describing the + version of the compiler, or <c>undefined</c> if unknown.</p> </item> <tag><c>check_io</c></tag> <item> @@ -7307,12 +7453,13 @@ ok Erlang/OTP release that the current emulator has been set to be backward compatible with. The compatibility mode can be configured at startup by using command-line flag - <seealso marker="erts:erl#compat_rel">+R</seealso> in + <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in <c>erl(1)</c>.</p> </item> <tag><c>cpu_topology</c></tag> <item> - <p>See <seealso marker="#system_info_cpu_topology_tags">above</seealso>.</p> + <p>See <seealso + marker="#system_info_cpu_topology_tags">above</seealso>.</p> </item> <tag><c>creation</c></tag> <item> @@ -7328,22 +7475,22 @@ ok </item> <tag><c>debug_compiled</c></tag> <item> - <p>Returns <c>true</c> if the emulator has been debug - compiled, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if the emulator has been + debug-compiled, otherwise <c>false</c>.</p> </item> <tag><c>delayed_node_table_gc</c></tag> <item> <marker id="system_info_delayed_node_table_gc"></marker> <p>Returns the amount of time in seconds garbage collection - of an entry in a node table is delayed. This limit can be set - on startup by passing the command line flag - <seealso marker="erts:erl#+zdntgc">+zdntgc</seealso> - to <c>erl</c>. For more information see the documentation of the - command line flag.</p> + of an entry in a node table is delayed. This limit can be set + on startup by passing command-line flag + <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso> + to <c>erl(1)</c>. For more information, see the documentation of + the command-line flag.</p> </item> <tag><c>dirty_cpu_schedulers</c></tag> <item> - <marker id="system_info_dirty_cpu_schedulers"></marker> + <marker id="system_info_dirty_cpu_schedulers"></marker> <p>Returns the number of dirty CPU scheduler threads used by the emulator. Dirty CPU schedulers execute CPU-bound native functions, such as NIFs, linked-in driver code, @@ -7355,23 +7502,31 @@ ok can be changed at any time. The number of dirty CPU schedulers can be set at startup by passing command-line flag - <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> or - <seealso marker="erts:erl#+SDPcpu">+SDPcpu</seealso> in + <seealso marker="erts:erl#+SDcpu"><c>+SDcpu</c></seealso> or + <seealso marker="erts:erl#+SDPcpu"><c>+SDPcpu</c></seealso> in <c>erl(1)</c>.</p> - <p>Notice that the dirty schedulers functionality is + <p>Notice that the dirty schedulers functionality is experimental. Enable support for dirty schedulers when building OTP to try out the functionality.</p> <p>See also - <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>, - <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>, - <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>, - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>, - <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, and - <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.</p> + <seealso marker="#system_flag_dirty_cpu_schedulers_online"> + <c>erlang:system_flag(dirty_cpu_schedulers_online, + DirtyCPUSchedulersOnline)</c></seealso>, + <seealso marker="#system_info_dirty_cpu_schedulers_online"> + <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>, + <seealso marker="#system_info_dirty_io_schedulers"> + <c>erlang:system_info(dirty_io_schedulers)</c></seealso>, + <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>, + <seealso marker="#system_info_schedulers_online"> + <c>erlang:system_info(schedulers_online)</c></seealso>, and + <seealso marker="#system_flag_schedulers_online"> + <c>erlang:system_flag(schedulers_online, + SchedulersOnline)</c></seealso>.</p> </item> <tag><c>dirty_cpu_schedulers_online</c></tag> <item> - <marker id="system_info_dirty_cpu_schedulers_online"></marker> + <marker id="system_info_dirty_cpu_schedulers_online"></marker> <p>Returns the number of dirty CPU schedulers online. The return value satisfies <c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>, @@ -7380,51 +7535,60 @@ ok <c>erlang:system_info(schedulers_online)</c>.</p> <p>The number of dirty CPU schedulers online can be set at startup by passing command-line flag - <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> in + <seealso marker="erts:erl#+SDcpu"><c>+SDcpu</c></seealso> in <c>erl(1)</c>.</p> <p>Notice that the dirty schedulers functionality is experimental. Enable support for dirty schedulers when building OTP to try out the functionality.</p> <p>For more information, see - <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>, - <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>, - <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, and - <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.</p> + <seealso marker="#system_info_dirty_cpu_schedulers"> + <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>, + <seealso marker="#system_info_dirty_io_schedulers"> + <c>erlang:system_info(dirty_io_schedulers)</c></seealso>, + <seealso marker="#system_info_schedulers_online"> + <c>erlang:system_info(schedulers_online)</c></seealso>, and + <seealso marker="#system_flag_dirty_cpu_schedulers_online"> + <c>erlang:system_flag(dirty_cpu_schedulers_online, + DirtyCPUSchedulersOnline)</c></seealso>.</p> </item> <tag><c>dirty_io_schedulers</c></tag> <item> - <marker id="system_info_dirty_io_schedulers"></marker> + <marker id="system_info_dirty_io_schedulers"></marker> <p>Returns the number of dirty I/O schedulers as an integer. Dirty I/O schedulers execute I/O-bound native functions, such as NIFs and linked-in driver code, which cannot be managed cleanly by the normal emulator schedulers.</p> <p>This value can be set at startup by passing command-line - argument <seealso marker="erts:erl#+SDio">+SDio</seealso> + argument <seealso marker="erts:erl#+SDio"><c>+SDio</c></seealso> in <c>erl(1)</c>.</p> - <p>Notice that the dirty schedulers functionality is + <p>Notice that the dirty schedulers functionality is experimental. Enable support for dirty schedulers when building OTP to try out the functionality.</p> <p>For more information, see - <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>, - <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>, and - <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.</p> + <seealso marker="#system_info_dirty_cpu_schedulers"> + <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>, + <seealso marker="#system_info_dirty_cpu_schedulers_online"> + <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>, + and <seealso marker="#system_flag_dirty_cpu_schedulers_online"> + <c>erlang:system_flag(dirty_cpu_schedulers_online, + DirtyCPUSchedulersOnline)</c></seealso>.</p> </item> <tag><c>dist</c></tag> <item> <p>Returns a binary containing a string of distribution information formatted as in Erlang crash dumps. For more - information, see Section - <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso> + information, see section <seealso marker="erts:crash_dump"> + How to interpret the Erlang crash dumps</seealso> in the User's Guide.</p> </item> <tag><c>dist_buf_busy_limit</c></tag> <item> - <marker id="system_info_dist_buf_busy_limit"></marker> + <marker id="system_info_dist_buf_busy_limit"></marker> <p>Returns the value of the distribution buffer busy limit - in bytes. This limit can be set at startup by passing - command-line flag - <seealso marker="erts:erl#+zdbbl">+zdbbl</seealso> - to <c>erl</c>.</p> + in bytes. This limit can be set at startup by passing + command-line flag + <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso> + to <c>erl(1)</c>.</p> </item> <tag><c>dist_ctrl</c></tag> <item> @@ -7442,61 +7606,63 @@ ok <item> <p>Returns a string containing the Erlang driver version used by the runtime system. It has the form - <seealso marker="erts:erl_driver#version_management">"<major ver>.<minor ver>"</seealso>.</p> + <seealso marker="erts:erl_driver#version_management"> + "<major ver>.<minor ver>"</seealso>.</p> </item> <tag><c>dynamic_trace</c></tag> <item> <p>Returns an atom describing the dynamic trace framework - compiled into the virtual machine. It can be - <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a - commercial or standard build, it is always <c>none</c>. - The other return values indicate a custom configuration - (for example, <c>./configure --with-dynamic-trace=dtrace</c>). - For more information about dynamic tracing, see the - <seealso marker="runtime_tools:dyntrace">dyntrace</seealso> - manual page and the - <c>README.dtrace</c>/<c>README.systemtap</c> files in the - Erlang source code top directory.</p> + compiled into the virtual machine. It can be + <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a + commercial or standard build, it is always <c>none</c>. + The other return values indicate a custom configuration + (for example, <c>./configure --with-dynamic-trace=dtrace</c>). + For more information about dynamic tracing, see + <seealso marker="runtime_tools:dyntrace"> + <c>dyntrace(3)</c></seealso> manual page and the + <c>README.dtrace</c>/<c>README.systemtap</c> files in the + Erlang source code top directory.</p> </item> <tag><c>dynamic_trace_probes</c></tag> <item> <p>Returns a <c>boolean()</c> indicating if dynamic trace - probes (<c>dtrace</c> or <c>systemtap</c>) are built into - the emulator. This can only be <c>true</c> if the Virtual - Machine was built for dynamic tracing (that is, - <c>system_info(dynamic_trace)</c> returns - <c>dtrace</c> or <c>systemtap</c>).</p> - </item> - <tag><marker id="system_info_end_time"/><c>end_time</c></tag> - <item><p>The last <seealso marker="#monotonic_time/0">Erlang monotonic - time</seealso> in <c>native</c> - <seealso marker="#type_time_unit">time unit</seealso> that - can be represented internally in the current Erlang runtime system - instance. The time between the - <seealso marker="#system_info_start_time">start time</seealso> and - the end time is at least a quarter of a millennium.</p></item> + probes (<c>dtrace</c> or <c>systemtap</c>) are built into + the emulator. This can only be <c>true</c> if the virtual + machine was built for dynamic tracing (that is, + <c>system_info(dynamic_trace)</c> returns + <c>dtrace</c> or <c>systemtap</c>).</p> + </item> + <tag><marker id="system_info_end_time"/><c>end_time</c></tag> + <item> + <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic + time</seealso> in <c>native</c> + <seealso marker="#type_time_unit">time unit</seealso> that + can be represented internally in the current Erlang runtime system + instance. The time between the + <seealso marker="#system_info_start_time">start time</seealso> and + the end time is at least a quarter of a millennium.</p> + </item> <tag><c>elib_malloc</c></tag> <item> <p>This option will be removed in a future release. - The return value will always be <c>false</c>, as the - <c>elib_malloc</c> allocator has been removed.</p> + The return value will always be <c>false</c>, as the + <c>elib_malloc</c> allocator has been removed.</p> </item> - <tag><marker id="system_info_eager_check_io"/><c>eager_check_io</c></tag> + <tag><marker id="system_info_eager_check_io"/> + <c>eager_check_io</c></tag> <item> - <p> - Returns the value of the <c>erl</c> command line flag - <seealso marker="erl#+secio">+secio</seealso> - which is either <c>true</c> or <c>false</c>. See the - documentation of the command line flag for information about - the different values. - </p> + <p>Returns the value of command-line flag + <seealso marker="erl#+secio"><c>+secio</c></seealso> in + <c>erl(1)</c>, which is either <c>true</c> or <c>false</c>. + For information about the different values, see the + documentation of the command-line flag.</p> </item> <tag><c>ets_limit</c></tag> <item> <p>Returns the maximum number of ETS tables allowed. This limit can be increased at startup by passing command-line flag - <seealso marker="erts:erl#+e">+e</seealso> to + <seealso marker="erts:erl#+e"><c>+e</c></seealso> to <c>erl(1)</c> or by setting environment variable <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang runtime system.</p> @@ -7514,10 +7680,10 @@ ok <taglist> <tag><c>private</c></tag> <item> - <p>Each process has a heap reserved for its use and no - references between heaps of different processes are - allowed. Messages passed between processes are copied - between heaps.</p> + Each process has a heap reserved for its use and no + references between heaps of different processes are + allowed. Messages passed between processes are copied + between heaps. </item> </taglist> </item> @@ -7525,8 +7691,9 @@ ok <item> <p>Returns a binary containing a string of miscellaneous system information formatted as in Erlang crash dumps. - For more information, see Section - <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso> + For more information, see section + <seealso marker="erts:crash_dump"> + How to interpret the Erlang crash dumps</seealso> in the User's Guide.</p> </item> <tag><c>kernel_poll</c></tag> @@ -7538,38 +7705,39 @@ ok <item> <p>Returns a binary containing a string of loaded module information formatted as in Erlang crash dumps. For more - information, see Section - <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso> - in the User's Guide.</p> + information, see section + <seealso marker="erts:crash_dump">How to interpret the Erlang + crash dumps</seealso> in the User's Guide.</p> </item> - <tag><c>logical_processors</c></tag> + <tag><c>logical_processors</c></tag> <item> - <marker id="logical_processors"></marker> + <marker id="logical_processors"></marker> <p>Returns the detected number of logical processors configured - in the system. The return value is either an integer, or - the atom <c>unknown</c> if the emulator cannot - detect the configured logical processors.</p> + in the system. The return value is either an integer, or + the atom <c>unknown</c> if the emulator cannot + detect the configured logical processors.</p> </item> - <tag><c>logical_processors_available</c></tag> + <tag><c>logical_processors_available</c></tag> <item> - <marker id="logical_processors_available"></marker> + <marker id="logical_processors_available"></marker> <p>Returns the detected number of logical processors available - to the Erlang runtime system. The return value is either an - integer, or the atom <c>unknown</c> if the emulator - cannot detect the available logical processors. The number - of available logical processors is less than or equal to - the number of - <seealso marker="#logical_processors_online">logical processors online</seealso>.</p> + to the Erlang runtime system. The return value is either an + integer, or the atom <c>unknown</c> if the emulator + cannot detect the available logical processors. The number + of available logical processors is less than or equal to + the number of <seealso marker="#logical_processors_online"> + logical processors online</seealso>.</p> </item> - <tag><c>logical_processors_online</c></tag> + <tag><c>logical_processors_online</c></tag> <item> - <marker id="logical_processors_online"></marker> + <marker id="logical_processors_online"></marker> <p>Returns the detected number of logical processors online on - the system. The return value is either an integer, - or the atom <c>unknown</c> if the emulator cannot - detect logical processors online. The number of logical - processors online is less than or equal to the number of - <seealso marker="#logical_processors">logical processors configured</seealso>.</p> + the system. The return value is either an integer, + or the atom <c>unknown</c> if the emulator cannot + detect logical processors online. The number of logical + processors online is less than or equal to the number of + <seealso marker="#logical_processors">logical processors + configured</seealso>.</p> </item> <tag><c>machine</c></tag> <item> @@ -7578,17 +7746,16 @@ ok <tag><c>modified_timing_level</c></tag> <item> <p>Returns the modified timing-level (an integer) if - modified timing is enabled, otherwise, <c>undefined</c>. + modified timing is enabled, otherwise <c>undefined</c>. For more information about modified timing, see command-line flag - <seealso marker="erts:erl#+T">+T</seealso> + <seealso marker="erts:erl#+T"><c>+T</c></seealso> in <c>erl(1)</c></p> </item> <tag><c>multi_scheduling</c></tag> <item> - <marker id="system_info_multi_scheduling"></marker> - <p>Returns <c>disabled</c>, <c>blocked</c>, <c>blocked_normal</c>, - or <c>enabled</c>:</p> + <marker id="system_info_multi_scheduling"></marker> + <p>Returns one of the following:</p> <taglist> <tag><c>disabled</c></tag> <item> @@ -7607,9 +7774,9 @@ ok <item> <p>The emulator has more than one scheduler thread, but all normal scheduler threads except one are - blocked. Note that dirty schedulers are not - blocked, and may schedule Erlang processes and - execute native code.</p> + blocked. Notice that dirty schedulers are not + blocked, and can schedule Erlang processes and + execute native code.</p> </item> <tag><c>enabled</c></tag> <item> @@ -7620,189 +7787,204 @@ ok </item> </taglist> <p>See also - <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>, - <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, - <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>, - and - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> + <seealso marker="#system_flag_multi_scheduling"> + <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>, + <seealso marker="#system_info_multi_scheduling_blockers"> + <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>, + <seealso marker="#system_info_normal_multi_scheduling_blockers"> + <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>, + and <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>.</p> </item> <tag><c>multi_scheduling_blockers</c></tag> <item> - <marker id="system_info_multi_scheduling_blockers"></marker> + <marker id="system_info_multi_scheduling_blockers"></marker> <p>Returns a list of <c><anno>Pid</anno></c>s when multi-scheduling is blocked, otherwise the empty list is returned. The <c><anno>Pid</anno></c>s in the list - represent all the processes currently + represent all the processes currently blocking multi-scheduling. A <c><anno>Pid</anno></c> occurs only once in the list, even if the corresponding process has blocked multiple times.</p> <p>See also - <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>, - <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>, - <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>, - - and - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> + <seealso marker="#system_flag_multi_scheduling"> + <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>, + <seealso marker="#system_info_multi_scheduling"> + <c>erlang:system_info(multi_scheduling)</c></seealso>, + <seealso marker="#system_info_normal_multi_scheduling_blockers"> + <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>, + and <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>.</p> </item> <tag><c>nif_version</c></tag> <item> - <p>Returns a string containing the version of the Erlang NIF interface - used by the runtime system. It is on the form - "<major ver>.<minor ver>".</p> + <p>Returns a string containing the version of the Erlang NIF + interface used by the runtime system. It is on the form + "<major ver>.<minor ver>".</p> </item> <tag><c>normal_multi_scheduling_blockers</c></tag> <item> - <marker id="system_info_normal_multi_scheduling_blockers"></marker> + <marker id="system_info_normal_multi_scheduling_blockers"></marker> <p>Returns a list of <c><anno>Pid</anno></c>s when - normal multi-scheduling is blocked (i.e. all normal schedulers - but one is blocked), otherwise the empty list is returned. - The <c><anno>Pid</anno></c>s in the list represent all the - processes currently blocking normal multi-scheduling. - A <c><anno>Pid</anno></c> occurs only once in the list, even if - the corresponding process has blocked multiple times.</p> + normal multi-scheduling is blocked (that is, all normal schedulers + but one is blocked), otherwise the empty list is returned. + The <c><anno>Pid</anno></c>s in the list represent all the + processes currently blocking normal multi-scheduling. + A <c><anno>Pid</anno></c> occurs only once in the list, even if + the corresponding process has blocked multiple times.</p> <p>See also - <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>, - <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>, - <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, - - and - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> - </item> - <tag><marker id="system_info_otp_release"/><c>otp_release</c></tag> - <item> - <marker id="system_info_otp_release"></marker> + <seealso marker="#system_flag_multi_scheduling"> + <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>, + <seealso marker="#system_info_multi_scheduling"> + <c>erlang:system_info(multi_scheduling)</c></seealso>, + <seealso marker="#system_info_multi_scheduling_blockers"> + <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>, + and <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>.</p> + </item> + <tag><marker id="system_info_otp_release"/> + <c>otp_release</c></tag> + <item> + <marker id="system_info_otp_release"></marker> <p>Returns a string containing the OTP release number of the - OTP release that the currently executing <c>ERTS</c> application is - part of.</p> - <p>As from OTP 17, the OTP release number corresponds to - the major OTP version number. No - <c>erlang:system_info()</c> argument gives the exact OTP - version. This is because the exact OTP version in the general case - is difficult to determine. For more information, see the description - of versions in <seealso marker="doc/system_principles:versions"> - System principles</seealso> in System Documentation.</p> - </item> - <tag><marker id="system_info_os_monotonic_time_source"/><c>os_monotonic_time_source</c></tag> + OTP release that the currently executing ERTS application + is part of.</p> + <p>As from Erlang/OTP 17, the OTP release number corresponds to + the major OTP version number. No + <c>erlang:system_info()</c> argument gives the exact OTP + version. This is because the exact OTP version in the general case + is difficult to determine. For more information, see the + description of versions in + <seealso marker="doc/system_principles:versions"> + System principles</seealso> in System Documentation.</p> + </item> + <tag><marker id="system_info_os_monotonic_time_source"/> + <c>os_monotonic_time_source</c></tag> <item> <p>Returns a list containing information about the source of - <seealso marker="erts:time_correction#OS_Monotonic_Time">OS - monotonic time</seealso> that is used by the runtime system.</p> - <p>If <c>[]</c> is returned, no OS monotonic time is - available. The list contains two-tuples with <c>Key</c>s - as first element, and <c>Value</c>s as second element. The - order of these tuples is undefined. The following - tuples can be part of the list, but more tuples can be - introduced in the future:</p> - <taglist> - <tag><c>{function, Function}</c></tag> - <item><p><c>Function</c> is the name of the function - used. This tuple always exist if OS monotonic time is - available to the runtime system.</p></item> - - <tag><c>{clock_id, ClockId}</c></tag> - <item><p>This tuple only exist if <c>Function</c> - can be used with different clocks. <c>ClockId</c> - corresponds to the clock identifier used when calling - <c>Function</c>.</p></item> - - <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag> - <item><p>Highest possible - <seealso marker="time_correction#Time_Resolution">resolution</seealso> - of current OS monotonic time source as parts per - second. If no resolution information can be retrieved - from the OS, <c>OsMonotonicTimeResolution</c> is - set to the resolution of the time unit of - <c>Function</c>s return value. That is, the actual - resolution can be lower than - <c>OsMonotonicTimeResolution</c>. Also note that - the resolution does not say anything about the - <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>, - and whether the - <seealso marker="time_correction#Time_Precision">precision</seealso> - do align with the resolution. You do, - however, know that the precision is not better than - <c>OsMonotonicTimeResolution</c>.</p></item> - - <tag><c>{extended, Extended}</c></tag> - <item><p><c>Extended</c> equals <c>yes</c> if - the range of time values has been extended; - otherwise, <c>Extended</c> equals <c>no</c>. The - range needs to be extended if <c>Function</c> - returns values that wrap fast. This typically - is the case when the return value is a 32-bit - value.</p></item> - - <tag><c>{parallel, Parallel}</c></tag> - <item><p><c>Parallel</c> equals <c>yes</c> if - <c>Function</c> is called in parallel from multiple - threads. If it is not called in parallel, because - calls needs to be serialized, <c>Parallel</c> equals - <c>no</c>.</p></item> - - <tag><c>{time, OsMonotonicTime}</c></tag> - <item><p><c>OsMonotonicTime</c> equals current OS - monotonic time in <c>native</c> - <seealso marker="#type_time_unit">time unit</seealso>.</p></item> - </taglist> - </item> - <tag><marker id="system_info_os_system_time_source"/><c>os_system_time_source</c></tag> + <seealso marker="erts:time_correction#OS_Monotonic_Time">OS + monotonic time</seealso> that is used by the runtime system.</p> + <p>If <c>[]</c> is returned, no OS monotonic time is + available. The list contains two-tuples with <c>Key</c>s + as first element, and <c>Value</c>s as second element. The + order of these tuples is undefined. The following + tuples can be part of the list, but more tuples can be + introduced in the future:</p> + <taglist> + <tag><c>{function, Function}</c></tag> + <item><p><c>Function</c> is the name of the function + used. This tuple always exists if OS monotonic time is + available to the runtime system.</p> + </item> + <tag><c>{clock_id, ClockId}</c></tag> + <item><p>This tuple only exists if <c>Function</c> + can be used with different clocks. <c>ClockId</c> + corresponds to the clock identifier used when calling + <c>Function</c>.</p> + </item> + <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag> + <item><p>Highest possible + <seealso marker="time_correction#Time_Resolution"> + resolution</seealso> + of current OS monotonic time source as parts per + second. If no resolution information can be retrieved + from the OS, <c>OsMonotonicTimeResolution</c> is + set to the resolution of the time unit of + <c>Function</c>s return value. That is, the actual + resolution can be lower than + <c>OsMonotonicTimeResolution</c>. Notice that + the resolution does not say anything about the + <seealso marker="time_correction#Time_Accuracy"> + accuracy</seealso> or whether the + <seealso marker="time_correction#Time_Precision"> + precision</seealso> aligns with the resolution. You do, + however, know that the precision is not better than + <c>OsMonotonicTimeResolution</c>.</p> + </item> + <tag><c>{extended, Extended}</c></tag> + <item><p><c>Extended</c> equals <c>yes</c> if + the range of time values has been extended; + otherwise <c>Extended</c> equals <c>no</c>. The + range must be extended if <c>Function</c> + returns values that wrap fast. This typically + is the case when the return value is a 32-bit value.</p> + </item> + <tag><c>{parallel, Parallel}</c></tag> + <item><p><c>Parallel</c> equals <c>yes</c> if + <c>Function</c> is called in parallel from multiple + threads. If it is not called in parallel, because + calls must be serialized, <c>Parallel</c> equals + <c>no</c>.</p> + </item> + <tag><c>{time, OsMonotonicTime}</c></tag> + <item><p><c>OsMonotonicTime</c> equals current OS + monotonic time in <c>native</c> + <seealso marker="#type_time_unit">time unit</seealso>.</p> + </item> + </taglist> + </item> + <tag><marker id="system_info_os_system_time_source"/> + <c>os_system_time_source</c></tag> <item> <p>Returns a list containing information about the source of - <seealso marker="erts:time_correction#OS_System_Time">OS - system time</seealso> that is used by the runtime system.</p> - <p>The list contains two-tuples with <c>Key</c>s - as first element, and <c>Value</c>s as second element. The - order if these tuples is undefined. The following - tuples can be part of the list, but more tuples can be - introduced in the future:</p> - <taglist> - <tag><c>{function, Function}</c></tag> - <item><p><c>Function</c> is the name of the funcion - used.</p></item> - - <tag><c>{clock_id, ClockId}</c></tag> - <item><p>This tuple only exist if <c>Function</c> - can be used with different clocks. <c>ClockId</c> - corresponds to the clock identifier used when calling - <c>Function</c>.</p></item> - - <tag><c>{resolution, OsSystemTimeResolution}</c></tag> - <item><p>Highest possible - <seealso marker="time_correction#Time_Resolution">resolution</seealso> - of current OS system time source as parts per - second. If no resolution information can be retrieved - from the OS, <c>OsSystemTimeResolution</c> is - set to the resolution of the time unit of - <c>Function</c>s return value. That is, the actual - resolution may be lower than - <c>OsSystemTimeResolution</c>. Also note that - the resolution does not say anything about the - <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>, - and whether the - <seealso marker="time_correction#Time_Precision">precision</seealso> - do align with the resolution. You do, - however, know that the precision is not better than - <c>OsSystemTimeResolution</c>.</p></item> - - <tag><c>{parallel, Parallel}</c></tag> - <item><p><c>Parallel</c> equals <c>yes</c> if - <c>Function</c> is called in parallel from multiple - threads. If it is not called in parallel, because - calls needs to be serialized, <c>Parallel</c> equals - <c>no</c>.</p></item> - - <tag><c>{time, OsSystemTime}</c></tag> - <item><p><c>OsSystemTime</c> equals current OS - system time in <c>native</c> - <seealso marker="#type_time_unit">time unit</seealso>.</p></item> - </taglist> - </item> - <tag><c>port_parallelism</c></tag> - <item> - <marker id="system_info_port_parallelism"></marker> - <p>Returns the default port parallelism scheduling hint used. - For more information, see command-line argument - <seealso marker="erl#+spp">+spp</seealso> in <c>erl(1)</c>.</p></item> + <seealso marker="erts:time_correction#OS_System_Time">OS + system time</seealso> that is used by the runtime system.</p> + <p>The list contains two-tuples with <c>Key</c>s + as first element, and <c>Value</c>s as second element. The + order if these tuples is undefined. The following + tuples can be part of the list, but more tuples can be + introduced in the future:</p> + <taglist> + <tag><c>{function, Function}</c></tag> + <item><p><c>Function</c> is the name of the funcion used.</p> + </item> + <tag><c>{clock_id, ClockId}</c></tag> + <item><p>Exists only if <c>Function</c> + can be used with different clocks. <c>ClockId</c> + corresponds to the clock identifier used when calling + <c>Function</c>.</p> + </item> + <tag><c>{resolution, OsSystemTimeResolution}</c></tag> + <item><p>Highest possible + <seealso marker="time_correction#Time_Resolution"> + resolution</seealso> + of current OS system time source as parts per + second. If no resolution information can be retrieved + from the OS, <c>OsSystemTimeResolution</c> is + set to the resolution of the time unit of + <c>Function</c>s return value. That is, the actual + resolution can be lower than + <c>OsSystemTimeResolution</c>. Notice that + the resolution does not say anything about the + <seealso marker="time_correction#Time_Accuracy"> + accuracy</seealso> or whether the + <seealso marker="time_correction#Time_Precision"> + precision</seealso> do align with the resolution. You do, + however, know that the precision is not better than + <c>OsSystemTimeResolution</c>.</p> + </item> + <tag><c>{parallel, Parallel}</c></tag> + <item><p><c>Parallel</c> equals <c>yes</c> if + <c>Function</c> is called in parallel from multiple + threads. If it is not called in parallel, because + calls needs to be serialized, <c>Parallel</c> equals + <c>no</c>.</p> + </item> + <tag><c>{time, OsSystemTime}</c></tag> + <item><p><c>OsSystemTime</c> equals current OS + system time in <c>native</c> + <seealso marker="#type_time_unit">time unit</seealso>.</p> + </item> + </taglist> + </item> + <tag><c>port_parallelism</c></tag> + <item> + <marker id="system_info_port_parallelism"></marker> + <p>Returns the default port parallelism scheduling hint used. + For more information, see command-line argument + <seealso marker="erl#+spp"><c>+spp</c></seealso> + in <c>erl(1)</c>.</p> + </item> <tag><marker id="system_info_port_count"/><c>port_count</c></tag> <item> <p>Returns the number of ports currently existing at the @@ -7816,9 +7998,10 @@ ok <p>Returns the maximum number of simultaneously existing ports at the local node as an integer. This limit can be configured at startup by using command-line flag - <seealso marker="erl#+Q">+Q</seealso> in <c>erl(1)</c>.</p> + <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p> </item> - <tag><marker id="system_info_process_count"/><c>process_count</c></tag> + <tag><marker id="system_info_process_count"/> + <c>process_count</c></tag> <item> <p>Returns the number of processes currently existing at the local node. The value is given as an integer. This is @@ -7827,74 +8010,78 @@ ok </item> <tag><c>process_limit</c></tag> <item> - <marker id="system_info_process_limit"></marker> + <marker id="system_info_process_limit"></marker> <p>Returns the maximum number of simultaneously existing processes at the local node. The value is given as an integer. This limit can be configured at startup by using - command-line flag <seealso marker="erl#+P">+P</seealso> - in <c>erl(1)</c>.</p> + command-line flag <seealso marker="erl#+P"><c>+P</c></seealso> + in <c>erl(1)</c>.</p> </item> <tag><c>procs</c></tag> <item> <p>Returns a binary containing a string of process and port information formatted as in Erlang crash dumps. For more - information, see Section - <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso> + information, see section <seealso marker="erts:crash_dump"> + How to interpret the Erlang crash dumps</seealso> in the User's Guide.</p> </item> <tag><c>scheduler_bind_type</c></tag> <item> - <marker id="system_info_scheduler_bind_type"></marker> - <p>Returns information about how the user has requested - schedulers to be bound or not bound.</p> - <p>Notice that even though a user has requested - schedulers to be bound, they can silently have failed - to bind. To inspect the scheduler bindings, call - <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p> - <p>For more information, see command-line argument - <seealso marker="erts:erl#+sbt">+sbt</seealso> - in <c>erl(1)</c> and - <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p> + <marker id="system_info_scheduler_bind_type"></marker> + <p>Returns information about how the user has requested + schedulers to be bound or not bound.</p> + <p>Notice that although a user has requested + schedulers to be bound, they can silently have failed + to bind. To inspect the scheduler bindings, call + <seealso marker="#system_info_scheduler_bindings"> + <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p> + <p>For more information, see command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> + in <c>erl(1)</c> and + <seealso marker="#system_info_scheduler_bindings"> + <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p> </item> <tag><c>scheduler_bindings</c></tag> <item> - <marker id="system_info_scheduler_bindings"></marker> - <p>Returns information about the currently used scheduler - bindings.</p> - <p>A tuple of a size equal to - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso> - is returned. The tuple elements are integers - or the atom <c>unbound</c>. Logical processor identifiers - are represented as integers. The <c>N</c>th - element of the tuple equals the current binding for - the scheduler with the scheduler identifier equal to - <c>N</c>. For example, if the schedulers are bound, - <c>element(erlang:system_info(scheduler_id), - erlang:system_info(scheduler_bindings))</c> returns - the identifier of the logical processor that the calling - process is executing on.</p> - <p>Notice that only schedulers online can be bound to logical - processors.</p> - <p>For more information, see command-line argument - <seealso marker="erts:erl#+sbt">+sbt</seealso> - in <c>erl(1)</c> and - <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>. - </p> + <marker id="system_info_scheduler_bindings"></marker> + <p>Returns information about the currently used scheduler + bindings.</p> + <p>A tuple of a size equal to + <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso> + is returned. The tuple elements are integers + or the atom <c>unbound</c>. Logical processor identifiers + are represented as integers. The <c>N</c>th + element of the tuple equals the current binding for + the scheduler with the scheduler identifier equal to + <c>N</c>. For example, if the schedulers are bound, + <c>element(erlang:system_info(scheduler_id), + erlang:system_info(scheduler_bindings))</c> returns + the identifier of the logical processor that the calling + process is executing on.</p> + <p>Notice that only schedulers online can be bound to logical + processors.</p> + <p>For more information, see command-line argument + <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> + in <c>erl(1)</c> and + <seealso marker="#system_info_schedulers_online"> + <c>erlang:system_info(schedulers_online)</c></seealso>.</p> </item> <tag><c>scheduler_id</c></tag> <item> - <marker id="system_info_scheduler_id"></marker> + <marker id="system_info_scheduler_id"></marker> <p>Returns the scheduler ID (<c>SchedulerId</c>) of the scheduler thread that the calling process is executing on. <c><anno>SchedulerId</anno></c> is a positive integer, - where - <c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers)]]></c>. - See also - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> + where <c><![CDATA[1 <= SchedulerId <= + erlang:system_info(schedulers)]]></c>.</p> + <p>See also + <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso>.</p> </item> <tag><c>schedulers</c></tag> <item> - <marker id="system_info_schedulers"></marker> + <marker id="system_info_schedulers"></marker> <p>Returns the number of scheduler threads used by the emulator. Scheduler threads online schedules Erlang processes and Erlang ports, and execute Erlang code @@ -7902,44 +8089,57 @@ ok <p>The number of scheduler threads is determined at emulator boot time and cannot be changed later. However, the number of schedulers online can - be changed at any time.</p> + be changed at any time.</p> <p>See also - <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>, - <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, - <seealso marker="#system_info_scheduler_id">erlang:system_info(scheduler_id)</seealso>, - <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>, - <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>, - <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso> - and - <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>.</p> + <seealso marker="#system_flag_schedulers_online"> + <c>erlang:system_flag(schedulers_online, + SchedulersOnline)</c></seealso>, + <seealso marker="#system_info_schedulers_online"> + <c>erlang:system_info(schedulers_online)</c></seealso>, + <seealso marker="#system_info_scheduler_id"> + <c>erlang:system_info(scheduler_id)</c></seealso>, + <seealso marker="#system_flag_multi_scheduling"> + <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>, + <seealso marker="#system_info_multi_scheduling"> + <c>erlang:system_info(multi_scheduling)</c></seealso>, + <seealso marker="#system_info_normal_multi_scheduling_blockers"> + <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso> + and <seealso marker="#system_info_multi_scheduling_blockers"> + <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>. + </p> </item> <tag><c>schedulers_online</c></tag> <item> - <marker id="system_info_schedulers_online"></marker> + <marker id="system_info_schedulers_online"></marker> <p>Returns the number of schedulers online. The scheduler - identifiers of schedulers online satisfy the relationship - <c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers_online)]]></c>.</p> - <p>For more information, see - <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso> - and - <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.</p> + identifiers of schedulers online satisfy the relationship + <c><![CDATA[1 <= SchedulerId <= + erlang:system_info(schedulers_online)]]></c>.</p> + <p>For more information, see + <seealso marker="#system_info_schedulers"> + <c>erlang:system_info(schedulers)</c></seealso> and + <seealso marker="#system_flag_schedulers_online"> + <c>erlang:system_flag(schedulers_online, + SchedulersOnline)</c></seealso>.</p> </item> <tag><c>smp_support</c></tag> <item> <p>Returns <c>true</c> if the emulator has been compiled with SMP support, otherwise <c>false</c> is returned.</p> </item> - <tag><marker id="system_info_start_time"/><c>start_time</c></tag> - <item><p>The <seealso marker="#monotonic_time/0">Erlang monotonic - time</seealso> in <c>native</c> - <seealso marker="#type_time_unit">time unit</seealso> at the - time when current Erlang runtime system instance started. See also - <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>. - </p></item> + <tag><marker id="system_info_start_time"/><c>start_time</c></tag> + <item> + <p>The <seealso marker="#monotonic_time/0">Erlang monotonic + time</seealso> in <c>native</c> + <seealso marker="#type_time_unit">time unit</seealso> at the + time when current Erlang runtime system instance started.</p> + <p>See also <seealso marker="#system_info_end_time"> + <c>erlang:system_info(end_time)</c></seealso>.</p> + </item> <tag><c>system_version</c></tag> <item> <p>Returns a string containing version number and - some important properties, such as the number of schedulers.</p> + some important properties, such as the number of schedulers.</p> </item> <tag><c>system_architecture</c></tag> <item> @@ -7953,105 +8153,114 @@ ok </item> <tag><c>thread_pool_size</c></tag> <item> - <marker id="system_info_thread_pool_size"></marker> + <marker id="system_info_thread_pool_size"></marker> <p>Returns the number of async threads in the async thread pool used for asynchronous driver calls - (<seealso marker="erts:erl_driver#driver_async">driver_async()</seealso>). + (<seealso marker="erts:erl_driver#driver_async"> + <c>erl_driver:driver_async()</c></seealso>). The value is given as an integer.</p> </item> - - <tag><c>time_correction</c></tag> - <item> - <marker id="system_info_time_correction"></marker> - <p>Returns a boolean value indicating whether - <seealso marker="time_correction#Time_Correction">time correction</seealso> - is enabled or not. - </p></item> - <tag><c>time_offset</c></tag> - <item> - <marker id="system_info_time_offset"></marker> - <p>Returns the state of the time offset:</p> - <taglist> - <tag><c>preliminary</c></tag> - <item><p>The time offset is preliminary, and will be changed - at a later time when being finalized. The preliminary time offset - is used during the preliminary phase of the - <seealso marker="time_correction#Single_Time_Warp_Mode">single - time warp mode</seealso>.</p></item> - - <tag><c>final</c></tag> - <item><p>The time offset is final. This either because - <seealso marker="time_correction#No_Time_Warp_Mode">no - time warp mode</seealso> is used, or because the time - offset have been finalized when - <seealso marker="time_correction#Single_Time_Warp_Mode">single - time warp mode</seealso> is used.</p></item> - - <tag><c>volatile</c></tag> - <item><p>The time offset is volatile. That is, it can - change at any time. This is because - <seealso marker="time_correction#Multi_Time_Warp_Mode">multi - time warp mode</seealso> is used.</p></item> - </taglist> - </item> - <tag><marker id="system_info_time_warp_mode"/><c>time_warp_mode</c></tag> - <item><p>Returns a value identifying the - <seealso marker="time_correction#Time_Warp_Modes">time warp - mode</seealso> being used:</p> - <taglist> - <tag><c>no_time_warp</c></tag> - <item><p>The <seealso marker="time_correction#No_Time_Warp_Mode">no - time warp mode</seealso> is used.</p></item> - - <tag><c>single_time_warp</c></tag> - <item><p>The <seealso marker="time_correction#Single_Time_Warp_Mode">single - time warp mode</seealso> is used.</p></item> - - <tag><c>multi_time_warp</c></tag> - <item><p>The <seealso marker="time_correction#Multi_Time_Warp_Mode">multi - time warp mode</seealso> is used.</p></item> - </taglist> - </item> + <tag><c>time_correction</c></tag> + <item> + <marker id="system_info_time_correction"></marker> + <p>Returns a boolean value indicating whether + <seealso marker="time_correction#Time_Correction"> + time correction</seealso> is enabled or not.</p> + </item> + <tag><c>time_offset</c></tag> + <item> + <marker id="system_info_time_offset"></marker> + <p>Returns the state of the time offset:</p> + <taglist> + <tag><c>preliminary</c></tag> + <item> + <p>The time offset is preliminary, and will be changed + and finalized later. The preliminary time offset + is used during the preliminary phase of the + <seealso marker="time_correction#Single_Time_Warp_Mode"> + single time warp mode</seealso>.</p> + </item> + <tag><c>final</c></tag> + <item> + <p>The time offset is final. This either because + <seealso marker="time_correction#No_Time_Warp_Mode"> + no time warp mode</seealso> is used, or because the time + offset have been finalized when + <seealso marker="time_correction#Single_Time_Warp_Mode"> + single time warp mode</seealso> is used.</p> + </item> + <tag><c>volatile</c></tag> + <item> + <p>The time offset is volatile. That is, it can + change at any time. This is because + <seealso marker="time_correction#Multi_Time_Warp_Mode"> + multi-time warp mode</seealso> is used.</p> + </item> + </taglist> + </item> + <tag><marker id="system_info_time_warp_mode"/> + <c>time_warp_mode</c></tag> + <item> + <p>Returns a value identifying the + <seealso marker="time_correction#Time_Warp_Modes"> + time warp mode</seealso> that is used:</p> + <taglist> + <tag><c>no_time_warp</c></tag> + <item>The <seealso marker="time_correction#No_Time_Warp_Mode"> + no time warp mode</seealso> is used. + </item> + <tag><c>single_time_warp</c></tag> + <item>The <seealso marker="time_correction#Single_Time_Warp_Mode"> + single time warp mode</seealso> is used. + </item> + <tag><c>multi_time_warp</c></tag> + <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode"> + multi-time warp mode</seealso> is used. + </item> + </taglist> + </item> <tag><c>tolerant_timeofday</c></tag> <item> - <marker id="system_info_tolerant_timeofday"></marker> - <p>Returns whether a pre erts-7.0 backwards compatible compensation - for sudden changes of system time is <c>enabled</c> or <c>disabled</c>. - Such compensation is <c>enabled</c> when the - <seealso marker="#system_info_time_offset">time offset</seealso> is - <c>final</c>, and - <seealso marker="#system_info_time_correction">time correction</seealso> - is enabled.</p> + <marker id="system_info_tolerant_timeofday"></marker> + <p>Returns whether a pre ERTS 7.0 backwards compatible + compensation for sudden changes of system time is <c>enabled</c> + or <c>disabled</c>. Such compensation is <c>enabled</c> when the + <seealso marker="#system_info_time_offset">time offset</seealso> + is <c>final</c>, and + <seealso marker="#system_info_time_correction"> + time correction</seealso> is enabled.</p> </item> <tag><c>trace_control_word</c></tag> <item> <p>Returns the value of the node trace control word. For - more information, see function <c>get_tcw</c> in Section - <seealso marker="erts:match_spec#get_tcw">Match Specifications in Erlang</seealso> in the User's Guide.</p> + more information, see function <c>get_tcw</c> in section + <seealso marker="erts:match_spec#get_tcw"> + Match Specifications in Erlang</seealso> in the User's Guide.</p> </item> <tag><c>update_cpu_info</c></tag> <item> - <marker id="update_cpu_info"></marker> + <marker id="update_cpu_info"></marker> <p>The runtime system rereads the CPU information available - and updates its internally stored information about the - <seealso marker="#system_info_cpu_topology_detected">detected - CPU topology</seealso> and the number of logical processors - <seealso marker="#logical_processors">configured</seealso>, - <seealso marker="#logical_processors_online">online</seealso>, and - <seealso marker="#logical_processors_available">available</seealso>.</p> - <p>If the CPU information has changed since the last time - it was read, the atom <c>changed</c> is returned, otherwise - the atom <c>unchanged</c>. If the CPU information has changed, - you probably want to - <seealso marker="#system_flag_schedulers_online">adjust the - number of schedulers online</seealso>. You typically want - to have as many schedulers online as - <seealso marker="#logical_processors_available">logical - processors available</seealso>.</p> + and updates its internally stored information about the + <seealso marker="#system_info_cpu_topology_detected">detected + CPU topology</seealso> and the number of logical processors + <seealso marker="#logical_processors">configured</seealso>, + <seealso marker="#logical_processors_online">online</seealso>, + and <seealso marker="#logical_processors_available"> + available</seealso>.</p> + <p>If the CPU information has changed since the last time + it was read, the atom <c>changed</c> is returned, otherwise + the atom <c>unchanged</c>. If the CPU information has changed, + you probably want to + <seealso marker="#system_flag_schedulers_online">adjust the + number of schedulers online</seealso>. You typically want + to have as many schedulers online as + <seealso marker="#logical_processors_available">logical + processors available</seealso>.</p> </item> <tag><c>version</c></tag> <item> - <marker id="system_info_version"></marker> + <marker id="system_info_version"></marker> <p>Returns a string containing the version number of the emulator.</p> </item> @@ -8070,19 +8279,19 @@ ok </item> <tag><c>{wordsize, external}</c></tag> <item> - <p>Returns the true word size of the emulator, that is, - the size of a pointer. The value is given in bytes - as an integer. On a pure 32-bit architecture, 4 is - returned. On both a half word and on a pure - 64-bit architecture, 8 is returned.</p> + <p>Returns the true word size of the emulator, that is, + the size of a pointer. The value is given in bytes + as an integer. On a pure 32-bit architecture, 4 is + returned. On both a half word and on a pure + 64-bit architecture, 8 is returned.</p> </item> </taglist> <note> <p>Argument <c>scheduler</c> has changed name to <c>scheduler_id</c> to avoid mix up with argument <c>schedulers</c>. Argument <c>scheduler</c> was - introduced in <c>ERTS</c> 5.5 and renamed in - <c>ERTS</c> 5.5.1.</p> + introduced in ERTS 5.5 and renamed in + ERTS 5.5.1.</p> </note> </desc> </func> @@ -8093,32 +8302,35 @@ ok <type name="system_monitor_option"/> <desc> <p>Returns the current system monitoring settings set by - <seealso marker="#system_monitor/2">erlang:system_monitor/2</seealso> + <seealso marker="#system_monitor/2"> + <c>erlang:system_monitor/2</c></seealso> as <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c>, - or <c>undefined</c> if there - are no settings. The order of the options can be different - from the one that was set.</p> + or <c>undefined</c> if no settings exist. The order of the + options can be different from the one that was set.</p> </desc> </func> <func> <name name="system_monitor" arity="1"/> - <fsummary>Sets or clears system performance monitoring options.</fsummary> + <fsummary>Set or clear system performance monitoring options.</fsummary> <type name="system_monitor_option"/> <desc> <p>When called with argument <c>undefined</c>, all system performance monitoring settings are cleared.</p> <p>Calling the function with <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c> as argument is the same as calling - <seealso marker="#system_monitor/2"><c>erlang:system_monitor(<anno>MonitorPid</anno>, <anno>Options</anno>)</c></seealso>.</p> + <seealso marker="#system_monitor/2"> + <c>erlang:system_monitor(<anno>MonitorPid</anno>, + <anno>Options</anno>)</c></seealso>.</p> <p>Returns the previous system monitor settings just like - <seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p> + <seealso marker="#system_monitor/0"> + <c>erlang:system_monitor/0</c></seealso>.</p> </desc> </func> <func> <name name="system_monitor" arity="2"/> - <fsummary>Sets system performance monitoring options.</fsummary> + <fsummary>Set system performance monitoring options.</fsummary> <type name="system_monitor_option"/> <desc> <p>Sets the system performance monitoring options. @@ -8137,12 +8349,12 @@ ok <p>One of the tuples is <c>{timeout, GcTime}</c>, where <c>GcTime</c> is the time for the garbage collection in milliseconds. The other tuples are - tagged with <c>heap_size</c>, <c>heap_block_size</c> + tagged with <c>heap_size</c>, <c>heap_block_size</c>, <c>stack_size</c>, <c>mbuf_size</c>, <c>old_heap_size</c>, and <c>old_heap_block_size</c>. These tuples are explained in the description of trace message - <seealso marker="#gc_minor_start">gc_minor_start</seealso> (see - <seealso marker="#trace/3">erlang:trace/3</seealso>). + <seealso marker="#gc_minor_start"><c>gc_minor_start</c></seealso> + (see <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>). New tuples can be added, and the order of the tuples in the <c>Info</c> list can be changed at any time without prior notice.</p> @@ -8173,7 +8385,7 @@ ok <c>timeout</c>, <c>ready_input</c>, <c>ready_output</c>, <c>event</c>, and <c>outputv</c> (when the port is used by distribution). Value <c>Millis</c> in - the <c>timeout</c> tuple informs about the + tuple <c>timeout</c> informs about the uninterrupted execution time of the process or port, which always is equal to or higher than the <c>Time</c> value supplied when starting the trace. New tuples can be @@ -8184,7 +8396,7 @@ ok drivers that take too long to execute. 1 ms is considered a good maximum time for a driver callback or a NIF. However, a time-sharing system is usually to - consider everything below 100 ms as "possible" and + consider everything < 100 ms as "possible" and fairly "normal". However, longer schedule times can indicate swapping or a misbehaving NIF/driver. Misbehaving NIFs and drivers can cause bad resource @@ -8202,10 +8414,11 @@ ok <p>The monitor message is sent if the sum of the sizes of all memory blocks allocated for all heap generations after a garbage collection is equal to or higher than <c>Size</c>.</p> - <p>When a process is killed by <seealso marker="#process_flag_max_heap_size"> + <p>When a process is killed by + <seealso marker="#process_flag_max_heap_size"> <c>max_heap_size</c></seealso>, it is killed before the garbage collection is complete and thus no large heap message - will be sent.</p> + is sent.</p> </item> <tag><c>busy_port</c></tag> <item> @@ -8227,7 +8440,8 @@ ok </item> </taglist> <p>Returns the previous system monitor settings just like - <seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p> + <seealso marker="#system_monitor/0"> + <c>erlang:system_monitor/0</c></seealso>.</p> <note> <p>If a monitoring process gets so large that it itself starts to cause system monitor messages when garbage @@ -8252,7 +8466,8 @@ ok <type name="system_profile_option"/> <desc> <p>Returns the current system profiling settings set by - <seealso marker="#system_profile/2">erlang:system_profile/2</seealso> + <seealso marker="#system_profile/2"> + <c>erlang:system_profile/2</c></seealso> as <c>{<anno>ProfilerPid</anno>, <anno>Options</anno>}</c>, or <c>undefined</c> if there are no settings. The order of the options can be different @@ -8272,106 +8487,116 @@ ok <taglist> <tag><c>exclusive</c></tag> <item> - <p>If a synchronous call to a port from a process is done, the - calling process is considered not runnable during the call - runtime to the port. The calling process is notified as - <c>inactive</c>, and later <c>active</c> when the port - callback returns.</p> + <p>If a synchronous call to a port from a process is done, the + calling process is considered not runnable during the call + runtime to the port. The calling process is notified as + <c>inactive</c>, and later <c>active</c> when the port + callback returns.</p> </item> <tag><c>monotonic_timestamp</c></tag> <item> - <p>Timestamps in profile messages will use - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso>. The time-stamp (Ts) has the same - format and value as produced by - <c>erlang:monotonic_time(nano_seconds)</c>.</p> + <p>Time stamps in profile messages use + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso>. The time stamp (Ts) has the same + format and value as produced by + <c>erlang:monotonic_time(nanosecond)</c>.</p> </item> <tag><c>runnable_procs</c></tag> <item> - <p>If a process is put into or removed from the run queue, a - message, <c>{profile, Pid, State, Mfa, Ts}</c>, is sent to - <c><anno>ProfilerPid</anno></c>. Running processes that - are reinserted into the run queue after having been - preempted do not trigger this message.</p> + <p>If a process is put into or removed from the run queue, a + message, <c>{profile, Pid, State, Mfa, Ts}</c>, is sent to + <c><anno>ProfilerPid</anno></c>. Running processes that + are reinserted into the run queue after having been + pre-empted do not trigger this message.</p> </item> <tag><c>runnable_ports</c></tag> <item> - <p>If a port is put into or removed from the run queue, a - message, <c>{profile, Port, State, 0, Ts}</c>, is sent to - <c><anno>ProfilerPid</anno></c>.</p> + <p>If a port is put into or removed from the run queue, a + message, <c>{profile, Port, State, 0, Ts}</c>, is sent to + <c><anno>ProfilerPid</anno></c>.</p> </item> <tag><c>scheduler</c></tag> <item> - <p>If a scheduler is put to sleep or awoken, a message, - <c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is - sent to <c><anno>ProfilerPid</anno></c>.</p> + <p>If a scheduler is put to sleep or awoken, a message, + <c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is + sent to <c><anno>ProfilerPid</anno></c>.</p> </item> <tag><c>strict_monotonic_timestamp</c></tag> <item> - <p>Timestamps in profile messages will consisting of - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso> and a monotonically increasing - integer. The time-stamp (Ts) has the same format and value - as produced by <c>{erlang:monotonic_time(nano_seconds), - erlang:unique_integer([monotonic])}</c>.</p> + <p>Time stamps in profile messages consist of + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso> and a monotonically increasing + integer. The time stamp (Ts) has the same format and value + as produced by <c>{erlang:monotonic_time(nanosecond), + erlang:unique_integer([monotonic])}</c>.</p> </item> <tag><c>timestamp</c></tag> <item> - <p>Timestamps in profile messages will include a - time-stamp (Ts) that has the same form as returned by - <c>erlang:now()</c>. This is also the default if no - timestamp flag is given. If <c>cpu_timestamp</c> has - been enabled via <c>erlang:trace/3</c>, this will also - effect the timestamp produced in profiling messages - when <c>timestamp</c> flag is enabled.</p> + <p>Time stamps in profile messages include a + time stamp (Ts) that has the same form as returned by + <c>erlang:now()</c>. This is also the default if no + time stamp flag is specified. If <c>cpu_timestamp</c> has + been enabled through + <seealso marker="erlang:trace/3"><c>erlang:trace/3</c></seealso>, + this also effects the time stamp produced in profiling messages + when flag <c>timestamp</c> is enabled.</p> </item> </taglist> - <note><p><c>erlang:system_profile</c> is considered experimental - and its behavior can change in a future release.</p> + <note> + <p><c>erlang:system_profile</c> behavior can change + in a future release.</p> </note> </desc> </func> + <func> <name name="system_time" arity="0"/> - <fsummary>Current Erlang system time</fsummary> - <desc> - <p>Returns current - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> - in <c>native</c> - <seealso marker="#type_time_unit">time unit</seealso>.</p> - - <p>Calling <c>erlang:system_time()</c> is equivalent to: - <seealso marker="#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso><c> - + - </c><seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso>.</p> - - <note><p>This time is <em>not</em> a monotonically increasing time - in the general case. For more information, see the documentation of - <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the - ERTS User's Guide.</p></note> + <fsummary>Current Erlang system time.</fsummary> + <desc> + <p>Returns current + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> in <c>native</c> + <seealso marker="#type_time_unit">time unit</seealso>.</p> + <p>Calling <c>erlang:system_time()</c> is equivalent to + <seealso marker="#monotonic_time/0"> + <c>erlang:monotonic_time()</c></seealso><c> + + </c><seealso marker="#time_offset/0"> + <c>erlang:time_offset()</c></seealso>.</p> + <note> + <p>This time is <em>not</em> a monotonically increasing time + in the general case. For more information, see the documentation of + <seealso marker="time_correction#Time_Warp_Modes"> + time warp modes</seealso> in the User's Guide.</p> + </note> </desc> </func> + <func> <name name="system_time" arity="1"/> - <fsummary>Current Erlang system time</fsummary> - <desc> - <p>Returns current - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> - converted into the <c><anno>Unit</anno></c> passed as argument.</p> - - <p>Calling <c>erlang:system_time(<anno>Unit</anno>)</c> is equivalent to: - <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#system_time/0"><c>erlang:system_time()</c></seealso><c>, - native, <anno>Unit</anno>)</c>.</p> - - <note><p>This time is <em>not</em> a monotonically increasing time - in the general case. For more information, see the documentation of - <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the - ERTS User's Guide.</p></note> + <fsummary>Current Erlang system time.</fsummary> + <desc> + <p>Returns current + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> + converted into the <c><anno>Unit</anno></c> passed as argument.</p> + <p>Calling <c>erlang:system_time(<anno>Unit</anno>)</c> is equivalent + to <seealso marker="#convert_time_unit/3"> + <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso + marker="#system_time/0"><c>erlang:system_time()</c></seealso><c>, + native, <anno>Unit</anno>)</c>.</p> + <note> + <p>This time is <em>not</em> a monotonically increasing time + in the general case. For more information, see the documentation of + <seealso marker="time_correction#Time_Warp_Modes"> + time warp modes</seealso> in the User's Guide.</p> + </note> </desc> </func> + <func> <name name="term_to_binary" arity="1"/> - <fsummary>Encodes a term to an Erlang external term format binary.</fsummary> + <fsummary>Encode a term to an Erlang external term format binary. + </fsummary> <desc> <p>Returns a binary data object that is the result of encoding <c><anno>Term</anno></c> according to the Erlang external @@ -8380,64 +8605,65 @@ ok writing a term to a file in an efficient way, or sending an Erlang term to some type of communications channel not supported by distributed Erlang.</p> - <p>See also - <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>.</p> + <p>See also <seealso marker="#binary_to_term/1"> + <c>binary_to_term/1</c></seealso>.</p> </desc> </func> <func> <name name="term_to_binary" arity="2"/> - <fsummary>Encodes a term to en Erlang external term format binary.</fsummary> + <fsummary>Encode a term to en Erlang external term format binary. + </fsummary> <desc> <p>Returns a binary data object that is the result of encoding <c><anno>Term</anno></c> according to the Erlang external term format.</p> <p>If option <c>compressed</c> is provided, the external term format is compressed. The compressed format is automatically - recognized by <c>binary_to_term/1</c> as from Erlang R7B.</p> + recognized by <c>binary_to_term/1</c> as from Erlang/OTP R7B.</p> <p>A compression level can be specified by giving option <c>{compressed, <anno>Level</anno>}</c>. <c><anno>Level</anno></c> is an integer with range 0..9, where:</p> <list type="bulleted"> - <item><c>0</c> - No compression is done (it is the same as - giving no <c>compressed</c> option).</item> - <item><c>1</c> - Takes least time but may not compress - as well as the higher levels.</item> - <item><c>6</c> - Default level when option <c>compressed</c> - is provided.</item> - <item><c>9</c> - Takes most time and tries to produce a smaller + <item><p><c>0</c> - No compression is done (it is the same as + giving no <c>compressed</c> option).</p></item> + <item><p><c>1</c> - Takes least time but may not compress + as well as the higher levels.</p></item> + <item><p><c>6</c> - Default level when option <c>compressed</c> + is provided.</p></item> + <item><p><c>9</c> - Takes most time and tries to produce a smaller result. Notice "tries" in the preceding sentence; depending on the input term, level 9 compression either does or does - not produce a smaller result than level 1 compression.</item> + not produce a smaller result than level 1 compression.</p></item> </list> <p>Option <c>{minor_version, <anno>Version</anno>}</c> - can be used to control - some encoding details. This option was introduced in OTP R11B-4. + can be used to control some + encoding details. This option was introduced in Erlang/OTP R11B-4. The valid values for <c><anno>Version</anno></c> are <c>0</c> and <c>1</c>.</p> - <p>As from OTP 17.0, <c>{minor_version, 1}</c> is the default. It + <p>As from Erlang/OTP 17.0, <c>{minor_version, 1}</c> is the default. It forces any floats in the term to be encoded in a more space-efficient and exact way (namely in the 64-bit IEEE format, rather than converted to a textual representation).</p> - <p>As from OTP R11B-4, <c>binary_to_term/1</c> can decode this + <p>As from Erlang/OTP R11B-4, <c>binary_to_term/1</c> can decode this representation.</p> <p><c>{minor_version, 0}</c> means that floats are encoded using a textual representation. This option is useful to - ensure that releases before OTP R11B-4 can decode resulting + ensure that releases before Erlang/OTP R11B-4 can decode resulting binary.</p> - <p>See also - <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>.</p> + <p>See also <seealso marker="#binary_to_term/1"> + <c>binary_to_term/1</c></seealso>.</p> </desc> </func> <func> <name name="throw" arity="1"/> - <fsummary>Throws an exception.</fsummary> + <fsummary>Throw an exception.</fsummary> <desc> <p>A non-local return from a function. If evaluated within a - <c>catch</c>, <c>catch</c> returns value <c><anno>Any</anno></c>.</p> - <p>Example:</p> + <c>catch</c>, <c>catch</c> returns value <c><anno>Any</anno></c>. + Example:</p> <pre> > <input>catch throw({hello, there}).</input> {hello,there}</pre> @@ -8451,8 +8677,7 @@ ok <desc> <p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p> <p>The time zone and Daylight Saving Time correction depend on - the underlying OS.</p> - <p>Example:</p> + the underlying OS. Example:</p> <pre> > <input>time().</input> {9,42,44}</pre> @@ -8461,86 +8686,94 @@ ok <func> <name name="time_offset" arity="0"/> - <fsummary>Current time offset</fsummary> - <desc> - <p>Returns the current time offset between - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso> - and - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> in - <c>native</c> <seealso marker="#type_time_unit">time unit</seealso>. - Current time offset added to an Erlang monotonic time gives - corresponding Erlang system time.</p> - - <p>The time offset may or may not change during operation depending - on the <seealso marker="time_correction#Time_Warp_Modes">time - warp mode</seealso> used.</p> - - <note> - <p>A change in time offset may be observed at slightly - different points in time by different processes.</p> - - <p>If the runtime system is in - <seealso marker="time_correction#Multi_Time_Warp_Mode">multi - time warp mode</seealso>, the time offset will be changed when - the runtime system detects that the - <seealso marker="time_correction#OS_System_Time">OS system - time</seealso> has changed. The runtime system will, however, - not detect this immediately when it happens. A task checking - the time offset is scheduled to execute at least once a minute, - so under normal operation this should be detected within a - minute, but during heavy load it might take longer time.</p> - </note> + <fsummary>Current time offset.</fsummary> + <desc> + <p>Returns the current time offset between + <seealso marker="time_correction#Erlang_Monotonic_Time"> + Erlang monotonic time</seealso> and + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> in + <c>native</c> <seealso marker="#type_time_unit">time unit</seealso>. + Current time offset added to an Erlang monotonic time gives + corresponding Erlang system time.</p> + <p>The time offset may or may not change during operation depending + on the <seealso marker="time_correction#Time_Warp_Modes">time + warp mode</seealso> used.</p> + <note> + <p>A change in time offset can be observed at slightly + different points in time by different processes.</p> + <p>If the runtime system is in + <seealso marker="time_correction#Multi_Time_Warp_Mode">multi-time + warp mode</seealso>, the time offset is changed when + the runtime system detects that the + <seealso marker="time_correction#OS_System_Time">OS system + time</seealso> has changed. The runtime system will, however, + not detect this immediately when it occurs. A task checking + the time offset is scheduled to execute at least once a minute; + so, under normal operation this is to be detected within a + minute, but during heavy load it can take longer time.</p> + </note> </desc> </func> + <func> <name name="time_offset" arity="1"/> - <fsummary>Current time offset</fsummary> + <fsummary>Current time offset.</fsummary> <desc> - <p>Returns the current time offset between - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso> - and - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> - converted into the <c><anno>Unit</anno></c> passed as argument.</p> - - <p>Same as calling - <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso><c>, native, <anno>Unit</anno>)</c> - however optimized for commonly used <c><anno>Unit</anno></c>s.</p> + <p>Returns the current time offset between + <seealso marker="time_correction#Erlang_Monotonic_Time"> + Erlang monotonic time</seealso> and + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> + converted into the <c><anno>Unit</anno></c> passed as argument.</p> + <p>Same as calling + <seealso marker="#convert_time_unit/3"> + <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#time_offset/0"> + <c>erlang:time_offset()</c></seealso><c>, native, + <anno>Unit</anno>)</c> + however optimized for commonly used <c><anno>Unit</anno></c>s.</p> </desc> </func> + <func> <name name="timestamp" arity="0"/> - <fsummary>Current Erlang System time</fsummary> + <fsummary>Current Erlang System time.</fsummary> <type name="timestamp"/> <desc> - <p>Returns current - <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> - on the format <c>{MegaSecs, Secs, MicroSecs}</c>. This format is - the same as <seealso marker="kernel:os#timestamp/0"><c>os:timestamp/0</c></seealso> - and the deprecated <seealso marker="#now/0"><c>erlang:now/0</c></seealso> - uses. The reason for the existence of <c>erlang:timestamp()</c> is - purely to simplify usage for existing code that assumes this timestamp - format. Current Erlang system time can more efficiently be retrieved in - the time unit of your choice using - <seealso marker="#system_time/1"><c>erlang:system_time/1</c></seealso>.</p> - - <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p><code type="none"> + <p>Returns current + <seealso marker="time_correction#Erlang_System_Time"> + Erlang system time</seealso> + on the format <c>{MegaSecs, Secs, MicroSecs}</c>. This format is + the same as <seealso marker="kernel:os#timestamp/0"> + <c>os:timestamp/0</c></seealso> + and the deprecated <seealso marker="#now/0"> + <c>erlang:now/0</c></seealso> + use. The reason for the existence of <c>erlang:timestamp()</c> is + purely to simplify use for existing code that assumes this time stamp + format. Current Erlang system time can more efficiently be retrieved + in the time unit of your choice using + <seealso marker="#system_time/1"> + <c>erlang:system_time/1</c></seealso>.</p> + <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p> +<code type="none"> timestamp() -> - ErlangSystemTime = erlang:system_time(micro_seconds), + ErlangSystemTime = erlang:system_time(microsecond), MegaSecs = ErlangSystemTime div 1000000000000, Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000, MicroSecs = ErlangSystemTime rem 1000000, {MegaSecs, Secs, MicroSecs}.</code> - <p>It, however, uses a native implementation which does - not build garbage on the heap and with slightly better - performance.</p> - - <note><p>This time is <em>not</em> a monotonically increasing time - in the general case. For more information, see the documentation of - <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the - ERTS User's Guide.</p></note> + <p>It, however, uses a native implementation that does + not build garbage on the heap and with slightly better + performance.</p> + <note> + <p>This time is <em>not</em> a monotonically increasing time + in the general case. For more information, see the documentation of + <seealso marker="time_correction#Time_Warp_Modes"> + time warp modes</seealso> in the User's Guide.</p> + </note> </desc> - </func> + <func> <name name="tl" arity="1"/> <fsummary>Tail of a list.</fsummary> @@ -8558,7 +8791,7 @@ timestamp() -> <func> <name name="trace" arity="3"/> - <fsummary>Sets trace flags for a process or processes.</fsummary> + <fsummary>Set trace flags for a process or processes.</fsummary> <type name="trace_flag"/> <desc> <p>Turns on (if <c><anno>How</anno> == true</c>) or off (if @@ -8568,49 +8801,43 @@ timestamp() -> <c><anno>PidPortSpec</anno></c>.</p> <p><c><anno>PidPortSpec</anno></c> is either a process identifier (pid) for a local process, a port identifier, - or one of the following atoms:</p> + or one of the following atoms:</p> <taglist> <tag><c>all</c></tag> - <item> - <p>All currently existing processes and ports and all that - will be created in the future.</p> + <item>All currently existing processes and ports and all that + will be created in the future. </item> <tag><c>processes</c></tag> - <item> - <p>All currently existing processes and all that will be created in the future.</p> + <item>All currently existing processes and all that will be created + in the future. </item> <tag><c>ports</c></tag> - <item> - <p>All currently existing ports and all that will be created in the future.</p> + <item>All currently existing ports and all that will be created in + the future. </item> <tag><c>existing</c></tag> - <item> - <p>All currently existing processes and ports.</p> + <item>All currently existing processes and ports. </item> <tag><c>existing_processes</c></tag> - <item> - <p>All currently existing processes.</p> + <item>All currently existing processes. </item> <tag><c>existing_ports</c></tag> - <item> - <p>All currently existing ports.</p> + <item>All currently existing ports. </item> <tag><c>new</c></tag> - <item> - <p>All processes and ports that will be created in the future.</p> + <item>All processes and ports that will be created in the future. </item> <tag><c>new_processes</c></tag> - <item> - <p>All processes that will be created in the future.</p> + <item>All processes that will be created in the future. </item> <tag><c>new_ports</c></tag> - <item> - <p>All ports that will be created in the future.</p> + <item>All ports that will be created in the future. </item> </taglist> <p><c><anno>FlagList</anno></c> can contain any number of the following flags (the "message tags" refers to the list of - <seealso marker="#trace_3_trace_messages">trace messages</seealso>):</p> + <seealso marker="#trace_3_trace_messages"> + <c>trace messages</c></seealso>):</p> <taglist> <tag><c>all</c></tag> <item> @@ -8621,21 +8848,29 @@ timestamp() -> <tag><c>send</c></tag> <item> <p>Traces sending of messages.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_send"><c>send</c></seealso> and - <seealso marker="#trace_3_trace_messages_send_to_non_existing_process"><c>send_to_non_existing_process</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_send"> + <c>send</c></seealso> and + <seealso marker="#trace_3_trace_messages_send_to_non_existing_process"> + <c>send_to_non_existing_process</c></seealso>.</p> </item> <tag><c>'receive'</c></tag> <item> <p>Traces receiving of messages.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_receive"><c>'receive'</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_receive"> + <c>'receive'</c></seealso>.</p> </item> -<tag><c>call</c></tag> + <tag><c>call</c></tag> <item> <p>Traces certain function calls. Specify which function - calls to trace by calling - <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso> and - <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>.</p> + calls to trace by calling <seealso marker="#trace_pattern/3"> + <c>erlang:trace_pattern/3</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_call"> + <c>call</c></seealso> and + <seealso marker="#trace_3_trace_messages_return_from"> + <c>return_from</c></seealso>.</p> </item> <tag><c>silent</c></tag> <item> @@ -8653,17 +8888,21 @@ timestamp() -> specification function <c>{silent,Bool}</c>, giving a high degree of control of which functions with which arguments that trigger the trace.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso>, - <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>, and - <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>. Or rather, the absence of.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_call"> + <c>call</c></seealso>, + <seealso marker="#trace_3_trace_messages_return_from"> + <c>return_from</c></seealso>, and + <seealso marker="#trace_3_trace_messages_return_to"> + <c>return_to</c></seealso>. Or rather, the absence of.</p> </item> <tag><c>return_to</c></tag> <item> <p>Used with the <c>call</c> trace flag. Traces the return from a traced function back to its caller. Only works for functions traced with - option <c>local</c> to - <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p> + option <c>local</c> to <seealso marker="#trace_pattern/3"> + <c>erlang:trace_pattern/3</c></seealso>.</p> <p>The semantics is that a trace message is sent when a call traced function returns, that is, when a chain of tail recursive calls ends. Only one trace @@ -8676,105 +8915,144 @@ timestamp() -> <p>To get trace messages containing return values from functions, use the <c>{return_trace}</c> match specification action instead.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_return_to"> + <c>return_to</c></seealso>.</p> </item> <tag><c>procs</c></tag> <item> <p>Traces process-related events.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_spawn"><c>spawn</c></seealso>, - <seealso marker="#trace_3_trace_messages_spawned"><c>spawned</c></seealso>, - <seealso marker="#trace_3_trace_messages_exit"><c>exit</c></seealso>, - <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>, - <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>, - <seealso marker="#trace_3_trace_messages_link"><c>link</c></seealso>, - <seealso marker="#trace_3_trace_messages_unlink"><c>unlink</c></seealso>, - <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and - <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_spawn"> + <c>spawn</c></seealso>, + <seealso marker="#trace_3_trace_messages_spawned"> + <c>spawned</c></seealso>, + <seealso marker="#trace_3_trace_messages_exit"> + <c>exit</c></seealso>, + <seealso marker="#trace_3_trace_messages_register"> + <c>register</c></seealso>, + <seealso marker="#trace_3_trace_messages_unregister"> + <c>unregister</c></seealso>, + <seealso marker="#trace_3_trace_messages_link"> + <c>link</c></seealso>, + <seealso marker="#trace_3_trace_messages_unlink"> + <c>unlink</c></seealso>, + <seealso marker="#trace_3_trace_messages_getting_linked"> + <c>getting_linked</c></seealso>, and + <seealso marker="#trace_3_trace_messages_getting_unlinked"> + <c>getting_unlinked</c></seealso>.</p> </item> <tag><c>ports</c></tag> <item> <p>Traces port-related events.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_open"><c>open</c></seealso>, - <seealso marker="#trace_3_trace_messages_closed"><c>closed</c></seealso>, - <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>, - <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>, - <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and - <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_open"> + <c>open</c></seealso>, + <seealso marker="#trace_3_trace_messages_closed"> + <c>closed</c></seealso>, + <seealso marker="#trace_3_trace_messages_register"> + <c>register</c></seealso>, + <seealso marker="#trace_3_trace_messages_unregister"> + <c>unregister</c></seealso>, + <seealso marker="#trace_3_trace_messages_getting_linked"> + <c>getting_linked</c></seealso>, and + <seealso marker="#trace_3_trace_messages_getting_unlinked"> + <c>getting_unlinked</c></seealso>.</p> </item> <tag><c>running</c></tag> <item> <p>Traces scheduling of processes.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and - <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_in_proc"> + <c>in</c></seealso> and + <seealso marker="#trace_3_trace_messages_out_proc"> + <c>out</c></seealso>.</p> </item> <tag><c>exiting</c></tag> <item> <p>Traces scheduling of exiting processes.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_in_exiting_proc"><c>in_exiting</c></seealso>, - <seealso marker="#trace_3_trace_messages_out_exiting_proc"><c>out_exiting</c></seealso>, and - <seealso marker="#trace_3_trace_messages_out_exited_proc"><c>out_exited</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_in_exiting_proc"> + <c>in_exiting</c></seealso>, + <seealso marker="#trace_3_trace_messages_out_exiting_proc"> + <c>out_exiting</c></seealso>, and + <seealso marker="#trace_3_trace_messages_out_exited_proc"> + <c>out_exited</c></seealso>.</p> </item> <tag><c>running_procs</c></tag> <item> <p>Traces scheduling of processes just like <c>running</c>. - However this option also includes schedule events when the - process executes within the context of a port without - being scheduled out itself.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and - <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p> + However, this option also includes schedule events when the + process executes within the context of a port without + being scheduled out itself.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_in_proc"> + <c>in</c></seealso> and + <seealso marker="#trace_3_trace_messages_out_proc"> + <c>out</c></seealso>.</p> </item> <tag><c>running_ports</c></tag> <item> <p>Traces scheduling of ports.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_in_port"><c>in</c></seealso> and - <seealso marker="#trace_3_trace_messages_out_port"><c>out</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_in_port"> + <c>in</c></seealso> and + <seealso marker="#trace_3_trace_messages_out_port"> + <c>out</c></seealso>.</p> </item> <tag><c>garbage_collection</c></tag> <item> <p>Traces garbage collections of processes.</p> - <p>Message tags: <seealso marker="#trace_3_trace_messages_gc_minor_start"><c>gc_minor_start</c></seealso>, - <seealso marker="#trace_3_trace_messages_gc_max_heap_size"><c>gc_max_heap_size</c></seealso> and - <seealso marker="#trace_3_trace_messages_gc_minor_end"><c>gc_minor_end</c></seealso>.</p> + <p>Message tags: + <seealso marker="#trace_3_trace_messages_gc_minor_start"> + <c>gc_minor_start</c></seealso>, + <seealso marker="#trace_3_trace_messages_gc_max_heap_size"> + <c>gc_max_heap_size</c></seealso>, and + <seealso marker="#trace_3_trace_messages_gc_minor_end"> + <c>gc_minor_end</c></seealso>.</p> </item> <tag><c>timestamp</c></tag> <item> - <p>Includes a time-stamp in all trace messages. The - time-stamp (Ts) has the same form as returned by + <p>Includes a time stamp in all trace messages. The + time stamp (Ts) has the same form as returned by <c>erlang:now()</c>.</p> </item> <tag><c>cpu_timestamp</c></tag> <item> <p>A global trace flag for the Erlang node that makes all - trace time-stamps using the <c>timestamp</c> flag to be - in CPU time, not wall clock time. That is, <c>cpu_timestamp</c> - will not be used if <c>monotonic_timestamp</c>, or - <c>strict_monotonic_timestamp</c> is enabled. + trace time stamps using flag <c>timestamp</c> to be + in CPU time, not wall clock time. That is, <c>cpu_timestamp</c> + is not be used if <c>monotonic_timestamp</c> or + <c>strict_monotonic_timestamp</c> is enabled. Only allowed with <c><anno>PidPortSpec</anno>==all</c>. If the host machine OS does not support high-resolution CPU time measurements, <c>trace/3</c> exits with <c>badarg</c>. Notice that most OS do not synchronize this value across cores, so be prepared - that time might seem to go backwards when using this option.</p> + that time can seem to go backwards when using this option.</p> </item> <tag><c>monotonic_timestamp</c></tag> <item> <p>Includes an - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso> time-stamp in all trace messages. The - time-stamp (Ts) has the same format and value as produced by - <seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso>. - This flag overrides the <c>cpu_timestamp</c> flag.</p> + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso> time stamp in all trace messages. The + time stamp (Ts) has the same format and value as produced by + <seealso marker="#monotonic_time-1"> + <c>erlang:monotonic_time(nanosecond)</c></seealso>. + This flag overrides flag <c>cpu_timestamp</c>.</p> </item> <tag><c>strict_monotonic_timestamp</c></tag> <item> - <p>Includes an timestamp consisting of - <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang - monotonic time</seealso> and a monotonically increasing - integer in all trace messages. The time-stamp (Ts) has the - same format and value as produced by - <c>{</c><seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso><c>,</c> - <seealso marker="#unique_integer-1"><c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>. - This flag overrides the <c>cpu_timestamp</c> flag.</p> + <p>Includes an time stamp consisting of + <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang + monotonic time</seealso> and a monotonically increasing + integer in all trace messages. The time stamp (Ts) has the + same format and value as produced by <c>{</c> + <seealso marker="#monotonic_time-1"> + <c>erlang:monotonic_time(nanosecond)</c></seealso><c>,</c> + <seealso marker="#unique_integer-1"> + <c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>. + This flag overrides flag <c>cpu_timestamp</c>.</p> </item> <tag><c>arity</c></tag> <item> @@ -8812,34 +9090,34 @@ timestamp() -> </item> <tag><c>{tracer, TracerModule, TracerState}</c></tag> <item> - <p>Specifies that a tracer module should be called - instead of sending a trace message. The tracer module - can then ignore or change the trace message. For more details - on how to write a tracer module see - <seealso marker="erts:erl_tracer"><c>erl_tracer</c></seealso> - </p> + <p>Specifies that a tracer module is to be called + instead of sending a trace message. The tracer module + can then ignore or change the trace message. For more details + on how to write a tracer module, see + <seealso marker="erts:erl_tracer"><c>erl_tracer(3)</c></seealso>.</p> </item> </taglist> - <p>If no <c>tracer</c> is given, the calling process - will be receiving all of the trace messages</p> + <p>If no <c>tracer</c> is specified, the calling process + receives all the trace messages.</p> <p>The effect of combining <c>set_on_first_link</c> with - <c>set_on_link</c> is the same as having + <c>set_on_link</c> is the same as <c>set_on_first_link</c> alone. Likewise for <c>set_on_spawn</c> and <c>set_on_first_spawn</c>.</p> <p>The tracing process receives the <em>trace messages</em> described - in the following list. <c>Pid</c> is the process identifier of the - traced process in which the traced event has occurred. The - third tuple element is the message tag.</p> + in the following list. <c>Pid</c> is the process identifier of the + traced process in which the traced event has occurred. The + third tuple element is the message tag.</p> <p>If flag <c>timestamp</c>, <c>strict_monotonic_timestamp</c>, or - <c>monotonic_timestamp</c> is given, the first tuple - element is <c>trace_ts</c> instead, and the time-stamp - is added as an extra element last in the message tuple. If - multiple timestamp flags are passed, <c>timestamp</c> has - precedence over <c>strict_monotonic_timestamp</c> which - in turn has precedence over <c>monotonic_timestamp</c>. All - timestamp flags are remembered, so if two are passed - and the one with highest precedence later is disabled - the other one will become active.</p> + <c>monotonic_timestamp</c> is specified, the first tuple + element is <c>trace_ts</c> instead, and the time stamp + is added as an extra element last in the message tuple. If + multiple time stamp flags are passed, <c>timestamp</c> has + precedence over <c>strict_monotonic_timestamp</c>, which + in turn has precedence over <c>monotonic_timestamp</c>. All + time stamp flags are remembered, so if two are passed + and the one with highest precedence later is disabled, + the other one becomes active.</p> + <p>Trace messages:</p> <marker id="trace_3_trace_messages"></marker> <taglist> <tag> @@ -8851,7 +9129,7 @@ timestamp() -> process <c>To</c>.</p> </item> <tag> - <marker id="trace_3_trace_messages_send_to_non_existing_process"></marker> + <marker id="trace_3_trace_messages_send_to_non_existing_process"/> <c>{trace, PidPort, send_to_non_existing_process, Msg, To}</c> </tag> <item> @@ -8864,9 +9142,9 @@ timestamp() -> </tag> <item> <p>When <c>PidPort</c> receives message <c>Msg</c>. - If <c>Msg</c> is set to timeout, then a receive - statement may have timedout, or the process received - a message with the payload <c>timeout</c>.</p> + If <c>Msg</c> is set to time-out, a receive + statement can have timed out, or the process received + a message with the payload <c>timeout</c>.</p> </item> <tag> <marker id="trace_3_trace_messages_call"></marker> @@ -9000,7 +9278,7 @@ timestamp() -> </tag> <item> <p>When <c>Pid</c> opens a new port <c>Port</c> with - the running the <c>Driver</c>.</p> + the running <c>Driver</c>.</p> <p><c>Driver</c> is the name of the driver as an atom.</p> </item> <tag> @@ -9008,7 +9286,7 @@ timestamp() -> <c>{trace, Port, closed, Reason}</c> </tag> <item> - <p>When <c>Port</c> closed with <c>Reason</c>.</p> + <p>When <c>Port</c> closes with <c>Reason</c>.</p> </item> <tag> <marker id="trace_3_trace_messages_in_proc"></marker> @@ -9025,7 +9303,8 @@ timestamp() -> <marker id="trace_3_trace_messages_out_proc"></marker> <marker id="trace_3_trace_messages_out_exiting_proc"></marker> <marker id="trace_3_trace_messages_out_exited_proc"></marker> - <c>{trace, Pid, out | out_exiting | out_exited, {M, F, Arity} | 0}</c> + <c>{trace, Pid, out | out_exiting | out_exited, {M, F, Arity} + | 0}</c> </tag> <item> <p>When <c>Pid</c> is scheduled out. The process was @@ -9039,11 +9318,13 @@ timestamp() -> </tag> <item> <p>When <c>Port</c> is scheduled to run. <c>Command</c> is the - first thing the port will execute, it may however run several - commands before being scheduled out. On some rare - occasions, the current function cannot be determined, - then the last element is <c>0</c>.</p> - <p>The possible commands are: <c>call | close | command | connect | control | flush | info | link | open | unlink</c></p> + first thing the port will execute, it can however run several + commands before being scheduled out. On some rare + occasions, the current function cannot be determined, + then the last element is <c>0</c>.</p> + <p>The possible commands are <c>call</c>, <c>close</c>, + <c>command</c>, <c>connect</c>, <c>control</c>, <c>flush</c>, + <c>info</c>, <c>link</c>, <c>open</c>, and <c>unlink</c>.</p> </item> <tag> <marker id="trace_3_trace_messages_out_port"></marker> @@ -9054,8 +9335,7 @@ timestamp() -> was <c>Command</c>. On some rare occasions, the current function cannot be determined, then the last element is <c>0</c>. <c>Command</c> can contain the same - commands as <c>in</c> - </p> + commands as <c>in</c></p> </item> <tag> <marker id="trace_3_trace_messages_gc_minor_start"></marker> @@ -9071,13 +9351,13 @@ timestamp() -> <taglist> <tag><c>heap_size</c></tag> <item>The size of the used part of the heap.</item> - <tag><c>heap_block_size</c></tag> - <item>The size of the memory block used for storing - the heap and the stack.</item> + <tag><c>heap_block_size</c></tag> + <item>The size of the memory block used for storing + the heap and the stack.</item> <tag><c>old_heap_size</c></tag> <item>The size of the used part of the old heap.</item> - <tag><c>old_heap_block_size</c></tag> - <item>The size of the memory block used for storing + <tag><c>old_heap_block_size</c></tag> + <item>The size of the memory block used for storing the old heap.</item> <tag><c>stack_size</c></tag> <item>The size of the stack.</item> @@ -9091,14 +9371,15 @@ timestamp() -> <item>The total size of unique off-heap binaries referenced from the process heap.</item> <tag><c>bin_vheap_block_size</c></tag> - <item>The total size of binaries allowed in the virtual - heap in the process before doing a garbage collection.</item> + <item>The total size of binaries allowed in the virtual + heap in the process before doing a garbage collection.</item> <tag><c>bin_old_vheap_size</c></tag> <item>The total size of unique off-heap binaries referenced from the process old heap.</item> <tag><c>bin_old_vheap_block_size</c></tag> - <item>The total size of binaries allowed in the virtual - old heap in the process before doing a garbage collection.</item> + <item>The total size of binaries allowed in the virtual + old heap in the process before doing a garbage + collection.</item> </taglist> <p>All sizes are in words.</p> </item> @@ -9107,13 +9388,12 @@ timestamp() -> <c>{trace, Pid, gc_max_heap_size, Info}</c> </tag> <item> - <p> - Sent when the <seealso marker="#process_flag_max_heap_size"><c>max_heap_size</c></seealso> + <p>Sent when the <seealso marker="#process_flag_max_heap_size"> + <c>max_heap_size</c></seealso> is reached during garbage collection. <c>Info</c> contains the same kind of list as in message <c>gc_start</c>, - but the sizes reflect the sizes that triggered max_heap_size to - be reached. - </p> + but the sizes reflect the sizes that triggered + <c>max_heap_size</c> to be reached.</p> </item> <tag> <marker id="trace_3_trace_messages_gc_minor_end"></marker> @@ -9121,7 +9401,8 @@ timestamp() -> </tag> <item> <p>Sent when young garbage collection is finished. <c>Info</c> - contains the same kind of list as in message <c>gc_minor_start</c>, + contains the same kind of list as in message + <c>gc_minor_start</c>, but the sizes reflect the new sizes after garbage collection.</p> </item> @@ -9130,8 +9411,9 @@ timestamp() -> <c>{trace, Pid, gc_major_start, Info}</c> </tag> <item> - <p>Sent when fullsweep garbage collection is about to be started. <c>Info</c> - contains the same kind of list as in message <c>gc_minor_start</c>.</p> + <p>Sent when fullsweep garbage collection is about to be started. + <c>Info</c> contains the same kind of list as in message + <c>gc_minor_start</c>.</p> </item> <tag> <marker id="trace_3_trace_messages_gc_major_end"></marker> @@ -9139,15 +9421,16 @@ timestamp() -> </tag> <item> <p>Sent when fullsweep garbage collection is finished. <c>Info</c> - contains the same kind of list as in message <c>gc_minor_start</c> - but the sizes reflect the new sizes after a fullsweep garbage collection.</p> + contains the same kind of list as in message + <c>gc_minor_start</c>, but the sizes reflect the new sizes after + a fullsweep garbage collection.</p> </item> </taglist> <p>If the tracing process/port dies or the tracer module returns - <c>remove</c>, the flags are silently removed.</p> + <c>remove</c>, the flags are silently removed.</p> <p>Each process can only be traced by one tracer. Therefore, attempts to trace an already traced process fail.</p> - <p>Returns: A number indicating the number of processes that + <p>Returns a number indicating the number of processes that matched <c><anno>PidPortSpec</anno></c>. If <c><anno>PidPortSpec</anno></c> is a process identifier, the return value is <c>1</c>. @@ -9167,22 +9450,24 @@ timestamp() -> <fsummary>Notification when trace has been delivered.</fsummary> <desc> <p>The delivery of trace messages (generated by - <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>, - <seealso marker="kernel:seq_trace"><c>seq_trace</c></seealso> or - <seealso marker="#system_profile/2"><c>erlang:system_profile/2</c></seealso>) + <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>, + <seealso marker="kernel:seq_trace"><c>seq_trace(3)</c></seealso>, + or <seealso marker="#system_profile/2"> + <c>erlang:system_profile/2</c></seealso>) is dislocated on the time-line compared to other events in the system. If you know that <c><anno>Tracee</anno></c> has passed some specific point in its execution, and you want to know when at least all trace messages corresponding to events up to this point have reached the - tracer, use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>. A - <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c> message is sent to - the caller of <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> when it - is guaranteed that all trace messages are delivered to + tracer, use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p> + <p>When it is guaranteed that all trace messages are delivered to the tracer up to the point that <c><anno>Tracee</anno></c> reached at the time of the call to - <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p> + <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>, then a + <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c> + message is sent to the caller of + <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> .</p> <p>Notice that message <c>trace_delivered</c> does <em>not</em> imply that trace messages have been delivered. Instead it implies that all trace messages that @@ -9192,22 +9477,22 @@ timestamp() -> <em>no</em> trace messages have been delivered when the <c>trace_delivered</c> message arrives.</p> <p>Notice that <c><anno>Tracee</anno></c> must refer - to a process currently, + to a process currently or previously existing on the same node as the caller of <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> resides on. The special <c><anno>Tracee</anno></c> atom <c>all</c> denotes all processes that currently are traced in the node.</p> - <p>When used together with an <seealso marker="erts:erl_tracer"> - Tracer Module</seealso> any message sent in the trace callback - is guaranteed to have reached it's recipient before the + <p>When used together with a <seealso marker="erts:erl_tracer"> + Tracer Module</seealso>, any message sent in the trace callback + is guaranteed to have reached its recipient before the <c>trace_delivered</c> message is sent.</p> <p>Example: Process <c>A</c> is <c><anno>Tracee</anno></c>, - port <c>B</c> is tracer, and process <c>C</c> is the port - owner of <c>B</c>. <c>C</c> wants to close <c>B</c> when - <c>A</c> exits. To ensure that the trace is not truncated, - <c>C</c> can call <c>erlang:trace_delivered(A)</c>, when - <c>A</c> exits, and wait for message <c>{trace_delivered, A, - <anno>Ref</anno>}</c> before closing <c>B</c>.</p> + port <c>B</c> is tracer, and process <c>C</c> is the port + owner of <c>B</c>. <c>C</c> wants to close <c>B</c> when + <c>A</c> exits. To ensure that the trace is not truncated, + <c>C</c> can call <c>erlang:trace_delivered(A)</c> when + <c>A</c> exits, and wait for message <c>{trace_delivered, A, + <anno>Ref</anno>}</c> before closing <c>B</c>.</p> <p>Failure: <c>badarg</c> if <c><anno>Tracee</anno></c> does not refer to a process (dead or alive) on the same node as the caller of @@ -9217,22 +9502,23 @@ timestamp() -> <func> <name name="trace_info" arity="2"/> - <fsummary>Trace information about a process or function.</fsummary> + <fsummary>Trace information about a process or function.</fsummary> <type name="trace_info_return"/> <type name="trace_info_item_result"/> <type name="trace_info_flag"/> <type name="trace_match_spec"/> <desc> - <p>Returns trace information about a port, process, function or event.</p> + <p>Returns trace information about a port, process, function, or + event.</p> <p><em>To get information about a port or process</em>, - <c><anno>PidPortFuncEvent</anno></c> is to - be a process identifier (pid), port identifier or one of - the atoms <c>new</c>, <c>new_processes</c>, <c>new_ports</c>. - The atom <c>new</c> or <c>new_processes</c> means that the default trace - state for processes to be created is returned. The atom <c>new_ports</c> - means that the default trace state for ports to be created is returned. - </p> - <p>The following <c>Item</c>s are valid for ports and processes:</p> + <c><anno>PidPortFuncEvent</anno></c> is to + be a process identifier (pid), port identifier, or one of + the atoms <c>new</c>, <c>new_processes</c>, or <c>new_ports</c>. The + atom <c>new</c> or <c>new_processes</c> means that the default trace + state for processes to be created is returned. The atom + <c>new_ports</c> means that the default trace state for ports to be + created is returned.</p> + <p>Valid <c>Item</c>s for ports and processes:</p> <taglist> <tag><c>flags</c></tag> <item> @@ -9241,30 +9527,32 @@ timestamp() -> traces are enabled, and one or more of the followings atoms if traces are enabled: <c>send</c>, <c>'receive'</c>, <c>set_on_spawn</c>, <c>call</c>, - <c>return_to</c>, <c>procs</c>, <c>ports</c>, <c>set_on_first_spawn</c>, + <c>return_to</c>, <c>procs</c>, <c>ports</c>, + <c>set_on_first_spawn</c>, <c>set_on_link</c>, <c>running</c>, <c>running_procs</c>, - <c>running_ports</c>, <c>silent</c>, <c>exiting</c> + <c>running_ports</c>, <c>silent</c>, <c>exiting</c>, <c>monotonic_timestamp</c>, <c>strict_monotonic_timestamp</c>, <c>garbage_collection</c>, <c>timestamp</c>, and <c>arity</c>. The order is arbitrary.</p> </item> <tag><c>tracer</c></tag> <item> - <p>Returns the identifier for process, port or a tuple containing + <p>Returns the identifier for process, port, or a tuple containing the tracer module and tracer state tracing this - process. If this process is not being traced, the return + process. If this process is not traced, the return value is <c>[]</c>.</p> </item> </taglist> - <p><em>To get information about a function</em>, <c><anno>PidPortFuncEvent</anno></c> is to + <p><em>To get information about a function</em>, + <c><anno>PidPortFuncEvent</anno></c> is to be the three-element tuple <c>{Module, Function, Arity}</c> or - the atom <c>on_load</c>. No wild cards are allowed. Returns + the atom <c>on_load</c>. No wildcards are allowed. Returns <c>undefined</c> if the function does not exist, or - <c>false</c> if the function is not traced. If <c><anno>PidPortFuncEvent</anno></c> - is <c>on_load</c>, the information returned refers to - the default value for code that will be loaded.</p> - - <p>The following <c>Item</c>s are valid for functions:</p> + <c>false</c> if the function is not traced. + If <c><anno>PidPortFuncEvent</anno></c> + is <c>on_load</c>, the information returned refers to + the default value for code that will be loaded.</p> + <p>Valid <c>Item</c>s for functions:</p> <taglist> <tag><c>traced</c></tag> <item> @@ -9283,11 +9571,12 @@ timestamp() -> </item> <tag><c>meta</c></tag> <item> - <p>Returns the meta-trace tracer process, port or trace module + <p>Returns the meta-trace tracer process, port, or trace module for this function, if it has one. If the function is not meta-traced, the returned value is <c>false</c>. If the function is meta-traced but has once detected that - the tracer process is invalid, the returned value is [].</p> + the tracer process is invalid, the returned value is + <c>[]</c>.</p> </item> <tag><c>meta_match_spec</c></tag> <item> @@ -9300,21 +9589,22 @@ timestamp() -> <item> <p>Returns the call count value for this function or <c>true</c> for the pseudo function <c>on_load</c> if call - count tracing is active. Otherwise <c>false</c> is returned. - See also - <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p> + count tracing is active. Otherwise <c>false</c> is returned.</p> + <p>See also <seealso marker="#trace_pattern/3"> + <c>erlang:trace_pattern/3</c></seealso>.</p> </item> <tag><c>call_time</c></tag> <item> - <p>Returns the call time values for this function or + <p>Returns the call time values for this function or <c>true</c> for the pseudo function <c>on_load</c> if call - time tracing is active. Otherwise <c>false</c> is returned. - The call time values returned, <c>[{Pid, Count, S, Us}]</c>, - is a list of each process that executed the function - and its specific counters. See also - <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p> + time tracing is active. Otherwise <c>false</c> is returned. + The call time values returned, <c>[{Pid, Count, S, Us}]</c>, + is a list of each process that executed the function + and its specific counters.</p> + <p>See also + <seealso marker="#trace_pattern/3"> + <c>erlang:trace_pattern/3</c></seealso>.</p> </item> - <tag><c>all</c></tag> <item> <p>Returns a list containing the @@ -9323,80 +9613,84 @@ timestamp() -> is active for this function.</p> </item> </taglist> - <p><em>To get information about an event</em>, <c><anno>PidPortFuncEvent</anno></c> is to + <p><em>To get information about an event</em>, + <c><anno>PidPortFuncEvent</anno></c> is to be one of the atoms <c>send</c> or <c>'receive'</c>.</p> - <p>The only valid <c>Item</c> for events is:</p> + <p>One valid <c>Item</c> for events exists:</p> <taglist> <tag><c>match_spec</c></tag> <item> <p>Returns the match specification for this event, if it has one, or <c>true</c> if no match specification has been - set.</p> + set.</p> </item> </taglist> <p>The return value is <c>{<anno>Item</anno>, Value}</c>, where <c>Value</c> is the requested information as described earlier. - If a pid for a dead process was given, or the name of a + If a pid for a dead process was specified, or the name of a non-existing function, <c>Value</c> is <c>undefined</c>.</p> </desc> </func> <func> <name name="trace_pattern" arity="2" clause_i="1"/> - <fsummary>Sets trace patterns for call, send or 'receive' tracing.</fsummary> + <fsummary>Set trace patterns for call, send, or 'receive' tracing. + </fsummary> <type name="trace_pattern_mfa"/> <type name="trace_match_spec"/> <desc> <p>The same as - <seealso marker="#trace_pattern/3">erlang:trace_pattern(Event, MatchSpec, [])</seealso>, + <seealso marker="#trace_pattern/3"> + <c>erlang:trace_pattern(Event, MatchSpec, [])</c></seealso>, retained for backward compatibility.</p> </desc> </func> <func> <name name="trace_pattern" arity="3" clause_i="1"/> - <fsummary>Sets trace pattern for message sending.</fsummary> + <fsummary>Set trace pattern for message sending.</fsummary> <type name="trace_match_spec"/> <desc> <p>Sets trace pattern for <em>message sending</em>. - Must be combined with - <seealso marker="#trace/3">erlang:trace/3</seealso> + Must be combined with + <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso> to set the <c>send</c> trace flag for one or more processes. - By default all messages, sent from <c>send</c> traced processes, - are traced. Use <c>erlang:trace_pattern/3</c> to limit - traced send events based on the message content, the sender - and/or the receiver.</p> + By default all messages sent from <c>send</c> traced processes + are traced. To limit + traced send events based on the message content, the sender + and/or the receiver, use <c>erlang:trace_pattern/3</c>.</p> <p>Argument <c><anno>MatchSpec</anno></c> can take the following forms:</p> <taglist> <tag><c><anno>MatchSpecList</anno></c></tag> <item> <p>A list of match specifications. The matching is done - on the list <c>[Receiver, Msg]</c>. <c>Receiver</c> - is the process or port identity of the receiver and - <c>Msg</c> is the message term. The pid of the sending - process can be accessed with the guard function - <c>self/0</c>. An empty list is the same as <c>true</c>. - See the users guide section - <seealso marker="erts:match_spec">Match Specifications in Erlang</seealso> - for more information.</p> + on the list <c>[Receiver, Msg]</c>. <c>Receiver</c> + is the process or port identity of the receiver and + <c>Msg</c> is the message term. The pid of the sending + process can be accessed with the guard function + <c>self/0</c>. An empty list is the same as <c>true</c>. + For more information, see section + <seealso marker="erts:match_spec"> + Match Specifications in Erlang</seealso> in the User's Guide.</p> </item> <tag><c>true</c></tag> <item> <p>Enables tracing for all sent messages (from <c>send</c> - traced processes). Any match specification is - removed. <em>This is the default</em>.</p> + traced processes). Any match specification is + removed. <em>This is the default</em>.</p> </item> <tag><c>false</c></tag> <item> <p>Disables tracing for all sent messages. - Any match specification is removed.</p> + Any match specification is removed.</p> </item> </taglist> - <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c> - for send tracing.</p> - <p>The return value is always <c>1</c>.</p> - <p>Example; only trace messages to a specific process <c>Pid</c>:</p> + <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c> + for send tracing.</p> + <p>The return value is always <c>1</c>.</p> + <p>Examples:</p> + <p>Only trace messages to a specific process <c>Pid</c>:</p> <pre> > <input>erlang:trace_pattern(send, [{[Pid, '_'],[],[]}], []).</input> 1</pre> @@ -9412,58 +9706,60 @@ timestamp() -> <pre> > <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=/=',{node,'$1'},{node}}],[]}], []).</input> 1</pre> - <note><p>A match specification for <c>send</c> trace can use - all guard and body functions except <c>caller</c>.</p></note> + <note> + <p>A match specification for <c>send</c> trace can use + all guard and body functions except <c>caller</c>.</p> + </note> </desc> </func> <func> <name name="trace_pattern" arity="3" clause_i="2"/> - <fsummary>Sets trace pattern for tracing of message receiving.</fsummary> + <fsummary>Set trace pattern for tracing of message receiving.</fsummary> <type name="trace_match_spec"/> <desc> - <p></p> <p>Sets trace pattern for <em>message receiving</em>. - Must be combined with - <seealso marker="#trace/3">erlang:trace/3</seealso> + Must be combined with + <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso> to set the <c>'receive'</c> trace flag for one or more processes. - By default all messages, received by <c>'receive'</c> traced processes, - are traced. Use <c>erlang:trace_pattern/3</c> to limit - traced receive events based on the message content, the sender - and/or the receiver.</p> + By default all messages received by <c>'receive'</c> traced + processes are traced. To limit + traced receive events based on the message content, the sender + and/or the receiver, use <c>erlang:trace_pattern/3</c>.</p> <p>Argument <c><anno>MatchSpec</anno></c> can take the following forms:</p> <taglist> <tag><c><anno>MatchSpecList</anno></c></tag> <item> <p>A list of match specifications. The matching is done - on the list <c>[Node, Sender, Msg]</c>. <c>Node</c> - is the node name of the sender. <c>Sender</c> is the - process or port identity of the sender, or the atom - <c>undefined</c> if the sender is not known (which may - be the case for remote senders). <c>Msg</c> is the - message term. The pid of the receiving process can be - accessed with the guard function <c>self/0</c>. An empty - list is the same as <c>true</c>. See the users guide section - <seealso marker="erts:match_spec">Match Specifications in Erlang</seealso> - for more information.</p> + on the list <c>[Node, Sender, Msg]</c>. <c>Node</c> + is the node name of the sender. <c>Sender</c> is the + process or port identity of the sender, or the atom + <c>undefined</c> if the sender is not known (which can + be the case for remote senders). <c>Msg</c> is the + message term. The pid of the receiving process can be + accessed with the guard function <c>self/0</c>. An empty + list is the same as <c>true</c>. For more information, see + section <seealso marker="erts:match_spec"> + Match Specifications in Erlang</seealso> in the User's Guide.</p> </item> <tag><c>true</c></tag> <item> <p>Enables tracing for all received messages (to <c>'receive'</c> - traced processes). Any match specification is - removed. <em>This is the default</em>.</p> + traced processes). Any match specification is + removed. <em>This is the default</em>.</p> </item> <tag><c>false</c></tag> <item> <p>Disables tracing for all received messages. - Any match specification is removed.</p> + Any match specification is removed.</p> </item> </taglist> - <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c> - for receive tracing.</p> - <p>The return value is always <c>1</c>.</p> - <p>Example; only trace messages from a specific process <c>Pid</c>:</p> + <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c> + for receive tracing.</p> + <p>The return value is always <c>1</c>.</p> + <p>Examples:</p> + <p>Only trace messages from a specific process <c>Pid</c>:</p> <pre> > <input>erlang:trace_pattern('receive', [{['_',Pid, '_'],[],[]}], []).</input> 1</pre> @@ -9475,33 +9771,36 @@ timestamp() -> <pre> > <input>erlang:trace_pattern('receive', [{['$1', '_', '_'],[{'=/=','$1',{node}}],[]}], []).</input> 1</pre> - <note><p>A match specification for <c>'receive'</c> trace can - use all guard and body functions except <c>caller, - is_seq_trace, get_seq_token, set_seq_token, enable_trace, - disable_trace, trace, silent</c> and <c>process_dump</c>.</p></note> + <note> + <p>A match specification for <c>'receive'</c> trace can + use all guard and body functions except <c>caller</c>, + <c>is_seq_trace</c>, <c>get_seq_token</c>, <c>set_seq_token</c>, + <c>enable_trace</c>, <c>disable_trace</c>, <c>trace</c>, + <c>silent</c>, and <c>process_dump</c>.</p> + </note> </desc> </func> <func> <name name="trace_pattern" arity="3" clause_i="3"/> - <fsummary>Sets trace patterns for tracing of function calls.</fsummary> + <fsummary>Set trace patterns for tracing of function calls.</fsummary> <type name="trace_pattern_mfa"/> <type name="trace_match_spec"/> <type name="trace_pattern_flag"/> <desc> <p>Enables or disables <em>call tracing</em> for one or more functions. - Must be combined with - <seealso marker="#trace/3">erlang:trace/3</seealso> + Must be combined with + <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso> to set the <c>call</c> trace flag - for one or more processes.</p> + for one or more processes.</p> <p>Conceptually, call tracing works as follows. Inside - the Erlang Virtual Machine, a set of processes and + the Erlang virtual machine, a set of processes and a set of functions are to be traced. If a traced process calls a traced function, the trace action is taken. Otherwise, nothing happens.</p> <p>To add or remove one or more processes to the set of traced processes, use - <seealso marker="#trace/3">erlang:trace/3</seealso>.</p> + <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>.</p> <p>To add or remove functions to the set of traced functions, use <c>erlang:trace_pattern/3</c>.</p> <p>The BIF <c>erlang:trace_pattern/3</c> can also add match @@ -9513,10 +9812,10 @@ timestamp() -> fails, the action is not executed.</p> <p>Argument <c><anno>MFA</anno></c> is to be a tuple, such as <c>{Module, Function, Arity}</c>, or the atom <c>on_load</c> - (described in the following). It can be the module, function, + (described below). It can be the module, function, and arity for a function (or a BIF in any module). - The atom <c>'_'</c> can be used as a wild card in any of the - following ways:</p> + The atom <c>'_'</c> can be used as a wildcard in any of the + following ways:</p> <taglist> <tag><c>{Module,Function,'_'}</c></tag> <item> @@ -9533,7 +9832,7 @@ timestamp() -> </item> </taglist> <p>Other combinations, such as <c>{Module,'_',Arity}</c>, are - not allowed. Local functions match wild cards only if + not allowed. Local functions match wildcards only if option <c>local</c> is in <c><anno>FlagList</anno></c>.</p> <p>If argument <c><anno>MFA</anno></c> is the atom <c>on_load</c>, the match specification and flag list are used on all @@ -9549,13 +9848,14 @@ timestamp() -> <tag><c>true</c></tag> <item> <p>Enables tracing for the matching functions. - Any match specification is removed.</p> + Any match specification is removed.</p> </item> <tag><c><anno>MatchSpecList</anno></c></tag> <item> <p>A list of match specifications. An empty list is equivalent to <c>true</c>. For a description of match - specifications, see the User's Guide.</p> + specifications, see section <seealso marker="erts:match_spec"> + Match Specifications in Erlang</seealso> in the User's Guide.</p> </item> <tag><c>restart</c></tag> <item> @@ -9566,7 +9866,7 @@ timestamp() -> </item> <tag><c>pause</c></tag> <item> - <p>For the <c><anno>FlagList</anno></c> options + <p>For the <c><anno>FlagList</anno></c> options <c>call_count</c> and <c>call_time</c>: pauses the existing counters. The behavior is undefined for other <c><anno>FlagList</anno></c> options.</p> @@ -9591,7 +9891,8 @@ timestamp() -> the process, a <c>return_to</c> message is also sent when this function returns to its caller.</p> </item> - <tag><c>meta | {meta, <anno>Pid</anno>} | {meta, <anno>TracerModule</anno>, <anno>TracerState</anno>}</c> + <tag><c>meta | {meta, <anno>Pid</anno>} | + {meta, <anno>TracerModule</anno>, <anno>TracerState</anno>}</c> </tag> <item> <p>Turns on or off meta-tracing for all types of function @@ -9599,7 +9900,7 @@ timestamp() -> the specified functions are called. If no tracer is specified, <c>self()</c> is used as a default tracer process.</p> <p>Meta-tracing traces all processes and does not care - about the process trace flags set by <c>trace/3</c>, + about the process trace flags set by <c>erlang:trace/3</c>, the trace flags are instead fixed to <c>[call, timestamp]</c>.</p> <p>The match specification function <c>{return_trace}</c> @@ -9620,7 +9921,8 @@ timestamp() -> Paused and running counters can be restarted from zero with <c><anno>MatchSpec</anno> == restart</c>.</p> <p>To read the counter value, use - <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p> + <seealso marker="#trace_info/2"> + <c>erlang:trace_info/2</c></seealso>.</p> </item> <tag><c>call_time</c></tag> <item> @@ -9628,17 +9930,18 @@ timestamp() -> (<c><anno>MatchSpec</anno> == false</c>) call time tracing for all types of function calls. For every function, a counter is - incremented when the function is called. + incremented when the function is called. Time spent in the function is accumulated in two other counters, seconds and microseconds. - The counters are stored for each call traced process.</p> + The counters are stored for each call traced process.</p> <p>If call time tracing is started while already running, - the count and time is restarted from zero. To pause + the count and time restart from zero. To pause running counters, use <c><anno>MatchSpec</anno> == pause</c>. Paused and running counters can be restarted from zero with <c><anno>MatchSpec</anno> == restart</c>.</p> <p>To read the counter value, use - <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p> + <seealso marker="#trace_info/2"> + <c>erlang:trace_info/2</c></seealso>.</p> </item> </taglist> <p>The options <c>global</c> and <c>local</c> are mutually @@ -9653,12 +9956,12 @@ timestamp() -> <p>When disabling trace, the option must match the type of trace set on the function. That is, local tracing must be disabled with option <c>local</c> and global tracing with - option <c>global</c> (or no option), and so forth.</p> + option <c>global</c> (or no option), and so on.</p> <p>Part of a match specification list cannot be changed directly. If a function has a match specification, it can be replaced with a new one. To change an existing match specification, use the BIF - <seealso marker="#trace_info/2">erlang:trace_info/2</seealso> + <seealso marker="#trace_info/2"><c>erlang:trace_info/2</c></seealso> to retrieve the existing match specification.</p> <p>Returns the number of functions matching argument <c><anno>MFA</anno></c>. This is zero if none matched.</p> @@ -9667,7 +9970,7 @@ timestamp() -> <func> <name name="trunc" arity="1"/> - <fsummary>Returns an integer by truncating a number</fsummary> + <fsummary>Return an integer by truncating a number.</fsummary> <desc> <p>Returns an integer by truncating <c><anno>Number</anno></c>, for example:</p> @@ -9680,7 +9983,7 @@ timestamp() -> <func> <name name="tuple_size" arity="1"/> - <fsummary>Returns the size of a tuple.</fsummary> + <fsummary>Return the size of a tuple.</fsummary> <desc> <p>Returns an integer that is the number of elements in <c><anno>Tuple</anno></c>, for example:</p> @@ -9693,11 +9996,11 @@ timestamp() -> <func> <name name="tuple_to_list" arity="1"/> - <fsummary>Converts a tuple to a list.</fsummary> + <fsummary>Convert a tuple to a list.</fsummary> <desc> <p>Returns a list corresponding to <c><anno>Tuple</anno></c>. - <c><anno>Tuple</anno></c> can contain any Erlang terms.</p> - <p>Example:</p> + <c><anno>Tuple</anno></c> can contain any Erlang terms. + Example:</p> <pre> > <input>tuple_to_list({share, {'Ericsson_B', 163}}).</input> [share,{'Ericsson_B',163}]</pre> @@ -9705,16 +10008,111 @@ timestamp() -> </func> <func> + <name name="unique_integer" arity="0"/> + <fsummary>Get a unique integer value.</fsummary> + <desc> + <p>Generates and returns an + <seealso marker="doc/efficiency_guide:advanced#unique_integers"> + integer unique on current runtime system instance</seealso>. + The same as calling + <seealso marker="#unique_integer/1"> + <c>erlang:unique_integer([])</c></seealso>.</p> + </desc> + </func> + + <func> + <name name="unique_integer" arity="1"/> + <fsummary>Get a unique integer value.</fsummary> + <desc> + <p>Generates and returns an + <seealso marker="doc/efficiency_guide:advanced#unique_integers"> + integer unique on current runtime system + instance</seealso>. The integer is unique in the + sense that this BIF, using the same set of + modifiers, does not return the same integer more + than once on the current runtime system instance. + Each integer value can of course be constructed + by other means.</p> + <p>By default, when <c>[]</c> is passed as + <c><anno>ModifierList</anno></c>, both negative and + positive integers can be returned. This + to use the range of integers that do + not need heap memory allocation as much as possible. + By default the returned integers are also only + guaranteed to be unique, that is, any returned integer + can be smaller or larger than previously + returned integers.</p> + <p><c><anno>Modifier</anno></c>s:</p> + <taglist> + <tag>positive</tag> + <item> + <p>Returns only positive integers.</p> + <p>Notice that by passing the <c>positive</c> modifier + you will get heap allocated integers (bignums) quicker.</p> + </item> + <tag>monotonic</tag> + <item> + <p>Returns <seealso + marker="time_correction#Strictly_Monotonically_Increasing"> + strictly monotonically increasing</seealso> integers + corresponding to creation time. That is, the integer + returned is always larger than previously + returned integers on the current runtime system + instance.</p> + <p>These values can be used to determine order between events + on the runtime system instance. That is, if both + <c>X = erlang:unique_integer([monotonic])</c> and + <c>Y = erlang:unique_integer([monotonic])</c> are + executed by different processes (or the same + process) on the same runtime system instance and + <c>X < Y</c>, we know that <c>X</c> was created + before <c>Y</c>.</p> + <warning> + <p>Strictly monotonically increasing values + are inherently quite expensive to generate and scales + poorly. This is because the values need to be synchronized + between CPU cores. That is, do not pass the <c>monotonic</c> + modifier unless you really need strictly monotonically + increasing values.</p> + </warning> + </item> + </taglist> + <p>All valid <c><anno>Modifier</anno></c>s + can be combined. Repeated (valid) + <c><anno>Modifier</anno></c>s in the <c>ModifierList</c> + are ignored.</p> + <note> + <p>The set of integers returned by + <c>erlang:unique_integer/1</c> using different sets of + <c><anno>Modifier</anno></c>s <em>will overlap</em>. + For example, by calling <c>unique_integer([monotonic])</c>, + and <c>unique_integer([positive, monotonic])</c> + repeatedly, you will eventually see some integers that are + returned by both calls.</p> + </note> + <p>Failures:</p> + <taglist> + <tag><c>badarg</c></tag> + <item>if <c><anno>ModifierList</anno></c> is not a + proper list.</item> + <tag><c>badarg</c></tag> + <item>if <c><anno>Modifier</anno></c> is not a + valid modifier.</item> + </taglist> + </desc> + </func> + + <func> <name name="universaltime" arity="0"/> - <fsummary>Current date and time according to Universal Time Coordinated (UTC).</fsummary> + <fsummary>Current date and time according to Universal Time Coordinated + (UTC).</fsummary> <desc> <p>Returns the current date and time according to Universal Time Coordinated (UTC) in the form <c>{{Year, Month, Day}, {Hour, Minute, Second}}</c> if supported by the underlying OS. Otherwise <c>erlang:universaltime()</c> is equivalent to - <c>erlang:localtime()</c>.</p> - <p>Example:</p> + <c>erlang:localtime()</c>. Example:</p> <pre> > <input>erlang:universaltime().</input> {{1996,11,6},{14,18,43}}</pre> @@ -9723,15 +10121,15 @@ timestamp() -> <func> <name name="universaltime_to_localtime" arity="1"/> - <fsummary>Converts from Universal Time Coordinated (UTC) to local date and time.</fsummary> + <fsummary>Convert from Universal Time Coordinated (UTC) to local date + and time.</fsummary> <desc> <p>Converts Universal Time Coordinated (UTC) date and time to local date and time in the form <c>{{Year, Month, Day}, {Hour, Minute, Second}}</c> if supported by the underlying OS. Otherwise no conversion is done, and - <c><anno>Universaltime</anno></c> is returned.</p> - <p>Example:</p> + <c><anno>Universaltime</anno></c> is returned. Example:</p> <pre> > <input>erlang:universaltime_to_localtime({{1996,11,6},{14,18,43}}).</input> {{1996,11,7},{15,18,43}}</pre> @@ -9741,102 +10139,8 @@ timestamp() -> </func> <func> - <name name="unique_integer" arity="0"/> - <fsummary>Get a unique integer value</fsummary> - <desc> - <p>Generates and returns an - <seealso marker="doc/efficiency_guide:advanced#unique_integers">integer - unique on current runtime system instance</seealso>. The same as calling - <seealso marker="#unique_integer/1"><c>erlang:unique_integer([])</c></seealso>.</p> - </desc> - </func> - <func> - <name name="unique_integer" arity="1"/> - <fsummary>Get a unique integer value</fsummary> - <desc> - <p>Generates and returns an - <seealso marker="doc/efficiency_guide:advanced#unique_integers">integer - unique on current runtime system - instance</seealso>. The integer is unique in the - sense that this BIF, using the same set of - modifiers, will not return the same integer more - than once on the current runtime system instance. - Each integer value can of course be constructed - by other means.</p> - - <p>By default, when <c>[]</c> is passed as - <c><anno>ModifierList</anno></c>, both negative and - positive integers can be returned. This in order - to utilize the range of integers that do - not need heap memory allocation as much as possible. - By default the returned integers are also only - guaranteed to be unique, that is, any returned integer - can be smaller or larger than previously - returned integers.</p> - - <p>Valid <c><anno>Modifier</anno></c>s:</p> - <taglist> - - <tag>positive</tag> - <item><p>Return only positive integers.</p> - <p>Note that by passing the <c>positive</c> modifier - you will get heap allocated integers (bignums) - quicker.</p> - </item> - - <tag>monotonic</tag> - <item><p>Return - <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly - monotonically increasing</seealso> integers - corresponding to creation time. That is, the integer - returned will always be larger than previously - returned integers on the current runtime system - instance.</p> - <p>These values can be used to determine order between events - on the runtime system instance. That is, if both - <c>X = erlang:unique_integer([monotonic])</c> and - <c>Y = erlang:unique_integer([monotonic])</c> are - executed by different processes (or the same - process) on the same runtime system instance and - <c>X < Y</c> we know that <c>X</c> was created - before <c>Y</c>.</p> - <warning><p>Strictly monotonically increasing values - are inherently quite expensive to generate and scales - poorly. This is because the values need to be - synchronized between cpu cores. That is, do not pass the <c>monotonic</c> - modifier unless you really need strictly monotonically - increasing values.</p></warning> - </item> - - </taglist> - - <p>All valid <c><anno>Modifier</anno></c>s - can be combined. Repeated (valid) - <c><anno>Modifier</anno></c>s in the <c>ModifierList</c> - are ignored.</p> - - <note><p>Note that the set of integers returned by - <c>unique_integer/1</c> using different sets of - <c><anno>Modifier</anno></c>s <em>will overlap</em>. - For example, by calling <c>unique_integer([monotonic])</c>, - and <c>unique_integer([positive, monotonic])</c> - repeatedly, you will eventually see some integers being - returned by both calls.</p></note> - - <p>Failures:</p> - <taglist> - <tag><c>badarg</c></tag> - <item>if <c><anno>ModifierList</anno></c> is not a - proper list.</item> - <tag><c>badarg</c></tag> - <item>if <c><anno>Modifier</anno></c> is not a - valid modifier.</item> - </taglist> - </desc> - </func> - <func> <name name="unlink" arity="1"/> - <fsummary>Removes a link to another process or port.</fsummary> + <fsummary>Remove a link to another process or port.</fsummary> <desc> <p>Removes the link, if there is one, between the calling process and the process or port referred to by @@ -9851,8 +10155,8 @@ timestamp() -> in the future (unless the link is setup again). If the caller is trapping exits, an <c>{'EXIT', <anno>Id</anno>, _}</c> message from the link - can have been placed in the caller's message queue before - the call.</p> + can have been placed in the caller's message queue before + the call.</p> <p>Notice that the <c>{'EXIT', <anno>Id</anno>, _}</c> message can be the result of the link, but can also be the result of <c>Id</c> @@ -9860,16 +10164,16 @@ timestamp() -> appropriate to clean up the message queue when trapping exits after the call to <c>unlink(<anno>Id</anno>)</c>, as follows:</p> <code type="none"> - unlink(Id), - receive - {'EXIT', Id, _} -> - true - after 0 -> - true - end</code> +unlink(Id), +receive + {'EXIT', Id, _} -> + true +after 0 -> + true +end</code> <note> - <p>Prior to OTP release R11B (ERTS version 5.5) <c>unlink/1</c> - behaved completely asynchronously, i.e., the link was active + <p>Before Erlang/OTP R11B (ERTS 5.5) <c>unlink/1</c> + behaved completely asynchronously, that is, the link was active until the "unlink signal" reached the linked entity. This had an undesirable effect, as you could never know when you were guaranteed <em>not</em> to be effected by the link.</p> @@ -9882,7 +10186,7 @@ timestamp() -> <func> <name name="unregister" arity="1"/> - <fsummary>Removes the registered name for a process (or port).</fsummary> + <fsummary>Remove the registered name for a process (or port).</fsummary> <desc> <p>Removes the registered name <c><anno>RegName</anno></c> associated with a @@ -9898,12 +10202,12 @@ true</pre> <func> <name name="whereis" arity="1"/> - <fsummary>Gets the pid (or port) with a given registered name.</fsummary> + <fsummary>Get the pid (or port) with a specified registered name. + </fsummary> <desc> <p>Returns the process identifier or port identifier with the registered name <c>RegName</c>. Returns <c>undefined</c> - if the name is not registered.</p> - <p>Example:</p> + if the name is not registered. Example:</p> <pre> > <input>whereis(db).</input> <0.43.0></pre> @@ -9912,17 +10216,19 @@ true</pre> <func> <name name="yield" arity="0"/> - <fsummary>Lets other processes get a chance to execute.</fsummary> + <fsummary>Let other processes get a chance to execute.</fsummary> <desc> <p>Voluntarily lets other processes (if any) get a chance to - execute. Using <c>erlang:yield()</c> is similar to + execute. Using this function is similar to <c>receive after 1 -> ok end</c>, except that <c>yield()</c> is faster.</p> - <warning><p>There is seldom or never any need to use this BIF, - especially in the SMP emulator, as other processes have a - chance to run in another scheduler thread anyway. - Using this BIF without a thorough grasp of how the scheduler - works can cause performance degradation.</p></warning> + <warning> + <p>There is seldom or never any need to use this BIF, + especially in the SMP emulator, as other processes have a + chance to run in another scheduler thread anyway. + Using this BIF without a thorough grasp of how the scheduler + works can cause performance degradation.</p> + </warning> </desc> </func> </funcs> diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index a64927fec2..7355be488b 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -28,7 +28,7 @@ <docno>1</docno> <approved>Bjarne Däcker</approved> <checked></checked> - <date>97-03-24</date> + <date>1997-03-24</date> <rev>A</rev> <file>erlc.xml</file> </header> @@ -38,167 +38,162 @@ <p>The <c><![CDATA[erlc]]></c> program provides a common way to run all compilers in the Erlang system. Depending on the extension of each input file, <c><![CDATA[erlc]]></c> - will invoke the appropriate compiler. - Regardless of which compiler is used, the same flags are used to provide parameters such as include paths and output directory.</p> - <p>The current working directory, <c>"."</c>, will not be included - in the code path when running the compiler (to avoid loading - Beam files from the current working directory that could potentially - be in conflict with the compiler or Erlang/OTP system used by the - compiler).</p> + invokes the appropriate compiler. + Regardless of which compiler is used, the same flags are used to provide + parameters, such as include paths and output directory.</p> + <p>The current working directory, <c>"."</c>, is not included + in the code path when running the compiler. This to avoid loading + Beam files from the current working directory that could potentially + be in conflict with the compiler or the Erlang/OTP system used by the + compiler.</p> </description> + <funcs> <func> <name>erlc flags file1.ext file2.ext...</name> - <fsummary>Compile files</fsummary> + <fsummary>Compile files.</fsummary> <desc> - <p><c><![CDATA[Erlc]]></c> compiles one or more files. - The files must include the extension, for example <c><![CDATA[.erl]]></c> - for Erlang source code, or <c><![CDATA[.yrl]]></c> for Yecc source code. - <c><![CDATA[Erlc]]></c> uses the extension to invoke the correct compiler.</p> + <p>Compiles one or more files. The files must include the extension, + for example, <c><![CDATA[.erl]]></c> for Erlang source code, or + <c><![CDATA[.yrl]]></c> for Yecc source code. + <c><![CDATA[Erlc]]></c> uses the extension to invoke the correct + compiler.</p> </desc> </func> </funcs> <section> <title>Generally Useful Flags</title> - <p>The following flags are supported: - </p> + <p>The following flags are supported:</p> <taglist> - <tag>-I <em>directory</em></tag> + <tag><c>-I <Directory></c></tag> <item> <p>Instructs the compiler to search for include files in - the specified directory. When encountering an - <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> directive, the - compiler searches for header files in the following + the <c>Directory</c>. When encountering an + <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> + directive, the compiler searches for header files in the following directories:</p> - <list type="ordered"> + <list type="bulleted"> <item> <p><c><![CDATA["."]]></c>, the current working directory of the - file server;</p> + 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><![CDATA[-I]]></c> option. - The directory specified last is searched first.</p> + <p>The directories specified using option <c><![CDATA[-I]]></c>; + the directory specified last is searched first</p> </item> </list> </item> - <tag>-o <em>directory</em></tag> + <tag><c>-o <Directory></c></tag> <item> - <p>The directory where the compiler should place the output files. - If not specified, output files will be placed in the current working - directory.</p> + <p>The directory where the compiler is to place the output files. + Defaults to the current working directory.</p> </item> - <tag>-D<em>name</em></tag> + <tag><c>-D<Name></c></tag> <item> <p>Defines a macro.</p> </item> - <tag>-D<em>name</em>=<em>value</em></tag> + <tag><c>-D<Name>=<Value></c></tag> <item> - <p>Defines a macro with the given value. + <p>Defines a macro with the specified value. The value can be any Erlang term. Depending on the platform, the value may need to be quoted if the shell itself interprets certain characters. - On Unix, terms which contain tuples and list - must be quoted. Terms which contain spaces + On Unix, terms containing tuples and lists + must be quoted. Terms containing spaces must be quoted on all platforms.</p> </item> - <tag>-W<em>error</em></tag> + <tag><c>-W<Error></c></tag> <item> <p>Makes all warnings into errors.</p> </item> - <tag>-W<em>number</em></tag> + <tag><c>-W<Number></c></tag> <item> - <p>Sets warning level to <em>number</em>. Default is <c><![CDATA[1]]></c>. - Use <c><![CDATA[-W0]]></c> to turn off warnings.</p> + <p>Sets warning level to <c>Number</c>. Defaults to + <c><![CDATA[1]]></c>. To turn off warnings, + use <c><![CDATA[-W0]]></c>.</p> </item> - <tag>-W</tag> + <tag><c>-W</c></tag> <item> <p>Same as <c><![CDATA[-W1]]></c>. Default.</p> </item> - <tag>-v</tag> + <tag><c>-v</c></tag> <item> <p>Enables verbose output.</p> </item> - <tag>-b <em>output-type</em></tag> + <tag><c>-b <Output_type></c></tag> <item> <p>Specifies the type of output file. - Generally, <em>output-type</em> is the same as the file extension - of the output file but without the period. - This option will be ignored by compilers that have a + <c>Output_type</c> is the same as the file extension + of the output file, but without the period. + This option is ignored by compilers that have a single output format.</p> </item> - <tag>-smp</tag> + <tag><c>-smp</c></tag> <item> - <p>Compile using the SMP emulator. This is mainly useful - for compiling native code, which needs to be compiled with the same - run-time system that it should be run on.</p> + <p>Compiles using the SMP emulator. This is mainly useful + for compiling native code, which must be compiled with the same + runtime system that it is to be run on.</p> </item> - <tag>-M</tag> + <tag><c>-M</c></tag> <item> - <p>Produces a Makefile rule to track headers dependencies. The - rule is sent to stdout. No object file is produced. - </p> + <p>Produces a Makefile rule to track header dependencies. The + rule is sent to <c>stdout</c>. No object file is produced.</p> </item> - <tag>-MF <em>Makefile</em></tag> + <tag><c>-MF <Makefile></c></tag> <item> - <p>Like the <c><![CDATA[-M]]></c> option above, except that the - Makefile is written to <em>Makefile</em>. No object - file is produced. - </p> + <p>As option <c><![CDATA[-M]]></c>, except that the + Makefile is written to <c>Makefile</c>. No object + file is produced.</p> </item> - <tag>-MD</tag> + <tag><c>-MD</c></tag> <item> - <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>. - </p> + <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.</p> </item> - <tag>-MT <em>Target</em></tag> + <tag><c>-MT <Target></c></tag> <item> - <p>In conjunction with <c><![CDATA[-M]]></c> or - <c><![CDATA[-MF]]></c>, change the name of the rule emitted - to <em>Target</em>. - </p> + <p>In conjunction with option <c><![CDATA[-M]]></c> or + <c><![CDATA[-MF]]></c>, changes the name of the rule emitted + to <c>Target</c>.</p> </item> - <tag>-MQ <em>Target</em></tag> + <tag><c>-MQ <Target></c></tag> <item> - <p>Like the <c><![CDATA[-MT]]></c> option above, except that - characters special to make(1) are quoted. - </p> + <p>As option <c><![CDATA[-MT]]></c>, except that characters special to + <c>make/1</c> are quoted.</p> </item> - <tag>-MP</tag> + <tag><c>-MP</c></tag> <item> - <p>In conjunction with <c><![CDATA[-M]]></c> or - <c><![CDATA[-MF]]></c>, add a phony target for each dependency. - </p> + <p>In conjunction with option <c><![CDATA[-M]]></c> or + <c><![CDATA[-MF]]></c>, adds a phony target for each dependency.</p> </item> - <tag>-MG</tag> + <tag><c>-MG</c></tag> <item> - <p>In conjunction with <c><![CDATA[-M]]></c> or - <c><![CDATA[-MF]]></c>, consider missing headers as generated - files and add them to the dependencies. - </p> + <p>In conjunction with option <c><![CDATA[-M]]></c> or + <c><![CDATA[-MF]]></c>, considers missing headers as generated + files and adds them to the dependencies.</p> </item> - <tag>--</tag> + <tag><c>--</c></tag> <item> <p>Signals that no more options will follow. - The rest of the arguments will be treated as file names, + The rest of the arguments is treated as filenames, even if they start with hyphens.</p> </item> - <tag>+<em>term</em></tag> + <tag><c>+<Term></c></tag> <item> - <p>A flag starting with a plus ('<em>+</em>') rather than a hyphen - will be converted to an Erlang term and passed unchanged to + <p>A flag starting with a plus (<c>+</c>) rather than a hyphen + is converted to an Erlang term and passed unchanged to the compiler. - For instance, the <c><![CDATA[export_all]]></c> option for the Erlang + For example, option <c><![CDATA[export_all]]></c> for the Erlang compiler can be specified as follows:</p> <pre> erlc +export_all file.erl</pre> <p>Depending on the platform, the value may need to be quoted if the shell itself interprets certain characters. - On Unix, terms which contain tuples and list - must be quoted. Terms which contain spaces + On Unix, terms containing tuples and lists + must be quoted. Terms containing spaces must be quoted on all platforms.</p> </item> </taglist> @@ -206,19 +201,19 @@ erlc +export_all file.erl</pre> <section> <title>Special Flags</title> - <p>The flags in this section are useful in special situations - such as re-building the OTP system.</p> + <p>The following flags are useful in special situations, + such as rebuilding the OTP system:</p> <taglist> - <tag>-pa <em>directory</em></tag> + <tag><c>-pa <Directory></c></tag> <item> - <p>Appends <em>directory</em> to the front of the code path in + <p>Appends <c>Directory</c> to the front of the code path in the invoked Erlang emulator. This can be used to invoke another compiler than the default one.</p> </item> - <tag>-pz <em>directory</em></tag> + <tag><c>-pz <Directory></c></tag> <item> - <p>Appends <em>directory</em> to the code path in + <p>Appends <c>Directory</c> to the code path in the invoked Erlang emulator.</p> </item> </taglist> @@ -226,63 +221,70 @@ erlc +export_all file.erl</pre> <section> <title>Supported Compilers</title> + <p>The following compilers are supported:</p> <taglist> - <tag>.erl</tag> + <tag><c>.erl</c></tag> <item> <p>Erlang source code. It generates a <c><![CDATA[.beam]]></c> file.</p> - <p>The options -P, -E, and -S are equivalent to +'P', - +'E', and +'S', except that it is not necessary to include the single quotes to protect them - from the shell.</p> - <p>Supported options: -I, -o, -D, -v, -W, -b.</p> + <p>Options <c>-P</c>, <c>-E</c>, and <c>-S</c> are equivalent to + <c>+'P'</c>, <c>+'E'</c>, and <c>+'S'</c>, except that it is not + necessary to include the single quotes to protect them from the + shell.</p> + <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-D</c>, <c>-v</c>, + <c>-W</c>, <c>-b</c>.</p> </item> - <tag>.S</tag> + <tag><c>.S</c></tag> <item> - <p>Erlang assembler source code. It generates a <c><![CDATA[.beam]]></c> file.</p> - <p>Supported options: same as for .erl.</p> + <p>Erlang assembler source code. It generates a <c><![CDATA[.beam]]></c> + file.</p> + <p>Supported options: same as for <c>.erl</c>.</p> </item> - <tag>.core</tag> + <tag><c>.core</c></tag> <item> - <p>Erlang core source code. It generates a <c><![CDATA[.beam]]></c> file.</p> - <p>Supported options: same as for .erl.</p> + <p>Erlang core source code. It generates a <c><![CDATA[.beam]]></c> + file.</p> + <p>Supported options: same as for <c>.erl</c>.</p> </item> - <tag>.yrl</tag> + <tag><c>.yrl</c></tag> <item> <p>Yecc source code. It generates an <c><![CDATA[.erl]]></c> file.</p> - <p>Use the -I option with the name of a file to use that file - as a customized prologue file (the <c><![CDATA[includefile]]></c> option).</p> - <p>Supported options: -o, -v, -I, -W (see above).</p> + <p>Use option <c>-I</c> with the name of a file to use that file + as a customized prologue file (option + <c><![CDATA[includefile]]></c>).</p> + <p>Supported options: <c>-o</c>, <c>-v</c>, <c>-I</c>, <c>-W</c>.</p> </item> - <tag>.mib</tag> + <tag><c>.mib</c></tag> <item> <p>MIB for SNMP. It generates a <c><![CDATA[.bin]]></c> file.</p> - <p>Supported options: -I, -o, -W.</p> + <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-W</c>.</p> </item> - <tag>.bin</tag> + <tag><c>.bin</c></tag> <item> - <p>A compiled MIB for SNMP. It generates a <c><![CDATA[.hrl]]></c> file.</p> - <p>Supported options: -o, -v.</p> + <p>A compiled MIB for SNMP. It generates a <c><![CDATA[.hrl]]></c> + file.</p> + <p>Supported options: <c>-o</c>, <c>-v</c>.</p> </item> - <tag>.rel</tag> + <tag><c>.rel</c></tag> <item> <p>Script file. It generates a boot file.</p> - <p>Use the -I to name directories to be searched for application - files (equivalent to the <c><![CDATA[path]]></c> in the option list for - <c><![CDATA[systools:make_script/2]]></c>).</p> - <p>Supported options: -o.</p> + <p>Use option <c>-I</c> to name directories to be searched for + application files (equivalent to the <c><![CDATA[path]]></c> in the + option list for <c><![CDATA[systools:make_script/2]]></c>).</p> + <p>Supported option: <c>-o</c>.</p> </item> - <tag>.asn1</tag> + <tag><c>.asn1</c></tag> <item> - <p>ASN1 file.</p> - <p>Creates an <c><![CDATA[.erl]]></c>, <c><![CDATA[.hrl]]></c>, and <c><![CDATA[.asn1db]]></c> file from - an <c><![CDATA[.asn1]]></c> file. Also compiles the <c><![CDATA[.erl]]></c> using the Erlang - compiler unless the <c><![CDATA[+noobj]]></c> options is given.</p> - <p>Supported options: -I, -o, -b, -W.</p> + <p>ASN1 file. It creates an <c><![CDATA[.erl]]></c>, + <c><![CDATA[.hrl]]></c>, and <c><![CDATA[.asn1db]]></c> file from + an <c><![CDATA[.asn1]]></c> file. Also compiles the + <c><![CDATA[.erl]]></c> using the Erlang compiler unless option + <c><![CDATA[+noobj]]></c> is specified.</p> + <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-b</c>, <c>-W</c>.</p> </item> - <tag>.idl</tag> + <tag><c>.idl</c></tag> <item> - <p>IC file.</p> - <p>Runs the IDL compiler.</p> - <p>Supported options: -I, -o.</p> + <p>IC file. It runs the IDL compiler.</p> + <p>Supported options: <c>-I</c>, <c>-o</c>.</p> </item> </taglist> </section> @@ -290,20 +292,20 @@ erlc +export_all file.erl</pre> <section> <title>Environment Variables</title> <taglist> - <tag>ERLC_EMULATOR</tag> - <item>The command for starting the emulator. - Default is <em>erl</em> in the same directory as the <em>erlc</em> program - itself, or if it doesn't exist, <em>erl</em> in any of the directories - given in the <em>PATH</em> environment variable.</item> + <tag><c>ERLC_EMULATOR</c></tag> + <item>The command for starting the emulator. Defaults to <c>erl</c> + in the same directory as the <c>erlc</c> program itself, + or, if it does not exist, <c>erl</c> in any of the directories + specified in environment variable <c>PATH</c>.</item> </taglist> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="erl">erl(1)</seealso>, - <seealso marker="compiler:compile">compile(3)</seealso>, - <seealso marker="parsetools:yecc">yecc(3)</seealso>, - <seealso marker="snmp:snmp">snmp(3)</seealso></p> + <title>See Also</title> + <p><seealso marker="erl"><c>erl(1)</c></seealso>, + <seealso marker="compiler:compile"><c>compile(3)</c></seealso>, + <seealso marker="parsetools:yecc"><c>yecc(3)</c></seealso>, + <seealso marker="snmp:snmp"><c>snmp(3)</c></seealso></p> </section> </comref> diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml index fb00444aa4..6c08b25220 100644 --- a/erts/doc/src/erlsrv.xml +++ b/erts/doc/src/erlsrv.xml @@ -28,343 +28,447 @@ <docno></docno> <approved></approved> <checked></checked> - <date>98-04-29</date> + <date>1998-04-29</date> <rev></rev> <file>erlsrv.xml</file> </header> <com>erlsrv</com> - <comsummary>Run the Erlang emulator as a service on Windows NT®</comsummary> + <comsummary>Run the Erlang emulator as a service on Windows</comsummary> <description> - <p>This utility is specific to Windows NT/2000/XP® (and subsequent versions of Windows) It allows Erlang + <p>This utility is specific to Windows NT/2000/XP (and later + versions of Windows). It allows Erlang emulators to run as services on the Windows system, allowing embedded - systems to start without any user needing to log in. The + systems to start without any user needing to log on. The emulator started in this way can be manipulated through the - Windows® services applet in a manner similar to other - services.</p> - <p>Note that erlsrv is not a general service utility for Windows, but designed for embedded Erlang systems.</p> - <p>As well as being the actual service, erlsrv also provides a - command line interface for registering, changing, starting and - stopping services.</p> - <p>To manipulate services, the logged in user should have - Administrator privileges on the machine. The Erlang machine + Windows services applet in a manner similar to other services.</p> + + <p>Notice that <c>erlsrv</c> is not a general service utility for Windows, + but designed for embedded Erlang systems.</p> + + <p><c>erlsrv</c> also provides a command-line interface for registering, + changing, starting, and stopping services.</p> + + <p>To manipulate services, the logged on user is to have + administrator privileges on the machine. The Erlang machine itself is (default) run as the local administrator. This can be - changed with the Services applet in Windows ®.</p> + changed with the Services applet in Windows.</p> + <p>The processes created by the service can, as opposed to normal - services, be "killed" with the task manager. Killing a emulator - that is started by a service will trigger the "OnFail" action - specified for that service, which may be a reboot.</p> - <p>The following parameters may be specified for each Erlang - service:</p> - <list type="bulleted"> + services, be "killed" with the task manager. Killing an emulator + that is started by a service triggers the "OnFail" action + specified for that service, which can be a reboot.</p> + + <p>The following parameters can be specified for each Erlang service:</p> + + <taglist> + <tag><c><![CDATA[StopAction]]></c></tag> <item> - <p><c><![CDATA[StopAction]]></c>: This tells <c><![CDATA[erlsrv]]></c> how to stop + <p>Tells <c><![CDATA[erlsrv]]></c> how to stop the Erlang emulator. Default is to kill it (Win32 TerminateProcess), but this action can specify any Erlang shell command that will be executed in the emulator to make it stop. The emulator is expected to stop within 30 seconds after the command is issued in the shell. If the emulator is - not stopped, it will report a running state to the service + not stopped, it reports a running state to the service manager.</p> </item> + <tag><c><![CDATA[OnFail]]></c></tag> <item> - <p><c><![CDATA[OnFail]]></c>: This can be either of <c><![CDATA[reboot]]></c>, - <c><![CDATA[restart]]></c>, <c><![CDATA[restart_always]]></c> or <c><![CDATA[ignore]]></c> (the - default). In case of <c><![CDATA[reboot]]></c>, the NT system is - rebooted whenever the emulator stops (a more simple form of - watchdog), this could be useful for less critical systems, - otherwise use the heart functionality to accomplish - this. The restart value makes the Erlang emulator be - restarted (with whatever parameters are registered for the - service at the occasion) when it stops. If the emulator - stops again within 10 seconds, it is not restarted to avoid - an infinite loop which could completely hang the NT - system. <c><![CDATA[restart_always]]></c> is similar to restart, but - does not try to detect cyclic restarts, it is expected that - some other mechanism is present to avoid the problem. The - default (ignore) just reports the service as stopped to the - service manager whenever it fails, it has to be manually - restarted.</p> - <p>On a system where release handling is - used, this should always be set to <c><![CDATA[ignore]]></c>. Use - <c><![CDATA[heart]]></c> to restart the service on failure instead.</p> + <p>Can be one of the following:</p> + <taglist> + <tag><c><![CDATA[reboot]]></c></tag> + <item> + <p>The Windows system is rebooted whenever the emulator stops + (a more simple form of watchdog). This can be useful for + less critical systems, otherwise use the heart functionality + to accomplish this.</p> + </item> + <tag><c><![CDATA[restart]]></c></tag> + <item> + <p>Makes the Erlang emulator be + restarted (with whatever parameters are registered for the + service at the occasion) when it stops. If the emulator + stops again within 10 seconds, it is not restarted to avoid + an infinite loop, which could hang the Windows system.</p> + </item> + <tag><c><![CDATA[restart_always]]></c></tag> + <item> + <p>Similar to <c><![CDATA[restart]]></c>, but does + not try to detect cyclic restarts; it is expected that + some other mechanism is present to avoid the problem.</p> + </item> + <tag><c><![CDATA[ignore]]></c> (the default)</tag> + <item> + <p>Reports the service as stopped to the service manager + whenever it fails; it must be manually restarted.</p> + </item> + </taglist> + <p>On a system where release handling is used, + this is always to be set to <c><![CDATA[ignore]]></c>. Use + <c><![CDATA[heart]]></c> to restart the service on failure + instead.</p> </item> + <tag><c><![CDATA[Machine]]></c></tag> <item> - <p><c><![CDATA[Machine]]></c>: The location of the Erlang - emulator. The default is the <c><![CDATA[erl.exe]]></c> located in the - same directory as erlsrv.exe. Do not specify <c><![CDATA[werl.exe]]></c> - as this emulator, it will not work.</p> - <p>If the system - uses release handling, this should be set to a program - similar to <c><![CDATA[start_erl.exe]]></c>.</p> + <p>The location of the Erlang emulator. + The default is the <c><![CDATA[erl.exe]]></c> located in the same + directory as <c>erlsrv.exe</c>. Do not specify + <c><![CDATA[werl.exe]]></c> as this emulator, it will not work.</p> + <p>If the system uses release handling, this is to be set to a + program similar to <c><![CDATA[start_erl.exe]]></c>.</p> </item> + <tag><c><![CDATA[Env]]></c></tag> <item> - <p><c><![CDATA[Env]]></c>: Specifies an <em>additional</em> environment + <p>Specifies an <em>extra</em> environment for the emulator. The environment variables specified - here are added to the system wide environment block that is + here are added to the system-wide environment block that is normally present when a service starts up. Variables present - in both the system wide environment and in the service + in both the system-wide environment and in the service environment specification will be set to the value specified in the service.</p> </item> + <tag><c><![CDATA[WorkDir]]></c></tag> <item> - <p><c><![CDATA[WorkDir]]></c>: The working directory for the Erlang - emulator, has to be on a local drive (there are no network - drives mounted when a service starts). Default working - directory for services is <c><![CDATA[%SystemDrive%%SystemPath%]]></c>. + <p>The working directory for the Erlang emulator. + Must be on a local drive (no network drives are mounted when a + service starts). Default working directory for services is + <c><![CDATA[%SystemDrive%%SystemPath%]]></c>. Debug log files will be placed in this directory.</p> </item> + <tag><c><![CDATA[Priority]]></c></tag> <item> - <p><c><![CDATA[Priority]]></c>: The process priority of the emulator, - this can be one of <c><![CDATA[realtime]]></c>, <c><![CDATA[high]]></c>, <c><![CDATA[low]]></c> - or <c><![CDATA[default]]></c> (the default). Real-time priority is not - recommended, the machine will possibly be inaccessible to - interactive users. High priority could be used if two Erlang - nodes should reside on one dedicated system and one should - have precedence over the other. Low process priority may be - used if interactive performance should not be affected by - the emulator process.</p> + <p>The process priority of the emulator. Can be one of the + following:</p> + <taglist> + <tag><c><![CDATA[realtime]]></c></tag> + <item> + <p>Not recommended, as the machine will possibly be + inaccessible to interactive users.</p> + </item> + <tag><c><![CDATA[high]]></c></tag> + <item> + <p>Can be used if two Erlang nodes are to reside on one dedicated + system and one is to have precedence over the other.</p> + </item> + <tag><c><![CDATA[low]]></c></tag> + <item> + <p>Can be used if interactive performance is not to be affected + by the emulator process.</p> + </item> + <tag><c><![CDATA[default]]></c> (the default></tag> + <item> + </item> + </taglist> </item> + <tag><c><![CDATA[SName or Name]]></c></tag> <item> - <p><c><![CDATA[SName or Name]]></c>: Specifies the short or long - node-name of the Erlang emulator. The Erlang services are - always distributed, default is to use the service name as - (short) node-name.</p> + <p>Specifies the short or long + node name of the Erlang emulator. The Erlang services are + always distributed. Default is to use the service name as + (short) nodename.</p> </item> + <tag><c><![CDATA[DebugType]]></c></tag> <item> - <p><c><![CDATA[DebugType]]></c>: Can be one of <c><![CDATA[none]]></c> (default), - <c><![CDATA[new]]></c>, <c><![CDATA[reuse]]></c> or <c><![CDATA[console]]></c>. - Specifies that output from the Erlang shell should be + <p>Specifies that output from the Erlang shell is to be sent to a "debug log". The log file is named <servicename><c><![CDATA[.debug]]></c> or - <servicename><c><![CDATA[.debug.]]></c><N>, where <N> is - an integer between 1 and 99. The log-file is placed in the - working directory of the service (as specified in WorkDir). The - <c><![CDATA[reuse]]></c> option always reuses the same log file - (<servicename><c><![CDATA[.debug]]></c>) and the <c><![CDATA[new]]></c> option - uses a separate log file for every invocation of the service - (<servicename><c><![CDATA[.debug.]]></c><N>). The <c><![CDATA[console]]></c> - option opens an interactive Windows® console window for - the Erlang shell of the service. The <c><![CDATA[console]]></c> option - automatically - disables the <c><![CDATA[StopAction]]></c> and a service started with an - interactive console window will not survive logouts, - <c><![CDATA[OnFail]]></c> actions do not work with debug-consoles either. - If no <c><![CDATA[DebugType]]></c> is specified (<c><![CDATA[none]]></c>), the - output of the Erlang shell is discarded.</p> - <p>The <c><![CDATA[console]]></c><c><![CDATA[DebugType]]></c> is <em>not in any way</em> - intended for production. It is <em>only</em> a convenient way to - debug Erlang services during development. The <c><![CDATA[new]]></c> and - <c><![CDATA[reuse]]></c> options might seem convenient to have in a - production system, but one has to take into account that the - logs will grow indefinitely during the systems lifetime and - there is no way, short of restarting the service, to - truncate those logs. In short, the <c><![CDATA[DebugType]]></c> is - intended for debugging only. Logs during production are - better produced with the standard Erlang logging - facilities.</p> + <servicename><c><![CDATA[.debug.]]></c><N>, + where <N> is an integer from 1 through 99. + The log file is placed in the working directory of the + service (as specified in <c>WorkDir</c>).</p> + <p>Can be one of the following:</p> + <taglist> + <tag><c><![CDATA[new]]></c></tag> + <item> + <p>Uses a separate log file for every invocation of the service + (<servicename><c><![CDATA[.debug.]]></c><N>).</p> + </item> + <tag><c><![CDATA[reuse]]></c></tag> + <item> + <p>Reuses the same log file + (<servicename><c><![CDATA[.debug]]></c>).</p> + </item> + <tag><c><![CDATA[console]]></c></tag> + <item> + <p>Opens an interactive Windows console window for the Erlang + shell of the service. Automatically disables the + <c><![CDATA[StopAction]]></c>. A service started with an + interactive console window does not survive logouts. + <c><![CDATA[OnFail]]></c> actions do not work with + debug consoles either.</p> + </item> + <tag><c><![CDATA[none]]></c> (the default)</tag> + <item> + <p>The output of the Erlang shell is discarded.</p> + </item> + </taglist> + <note> + <p>The <c><![CDATA[console]]></c> option is <em>not</em> intended + for production. It is <em>only</em> a convenient way to debug + Erlang services during development.</p> + <p>The <c><![CDATA[new]]></c> and <c><![CDATA[reuse]]></c> options + might seem convenient in a production system, but consider that + the logs grow indefinitely during the system lifetime and cannot + be truncated, except if the service is restarted.</p> + <p>In short, the <c><![CDATA[DebugType]]></c> is + intended for debugging only. Logs during production are + better produced with the standard Erlang logging facilities.</p> + </note> </item> + <tag><c><![CDATA[Args]]></c></tag> <item> - <p><c><![CDATA[Args]]></c>: Additional arguments passed to the - emulator startup program <c><![CDATA[erl.exe]]></c> (or - <c><![CDATA[start_erl.exe]]></c>). Arguments that cannot be specified - here are <c><![CDATA[-noinput]]></c> (StopActions would not work), - <c><![CDATA[-name]]></c> and <c><![CDATA[-sname]]></c> (they are specified in any - way. The most common use is for specifying cookies and flags - to be passed to init:boot() (<c><![CDATA[-s]]></c>).</p> + <p>Passes extra arguments to the emulator startup program + <c><![CDATA[erl.exe]]></c> (or <c><![CDATA[start_erl.exe]]></c>). + Arguments that cannot be specified here are + <c><![CDATA[-noinput]]></c> (<c>StopActions</c> would not work), + <c><![CDATA[-name]]></c>, and <c><![CDATA[-sname]]></c> (they are + specified in any way). The most common use is for specifying cookies + and flags to be passed to <c>init:boot()</c> + (<c><![CDATA[-s]]></c>).</p> </item> + <tag><c><![CDATA[InternalServiceName]]></c></tag> <item> - <p><c><![CDATA[InternalServiceName]]></c>: Specifies the Windows® internal service name (not the display name, which is the one <c>erlsrv</c> uses to identify the service).</p> - <p>This internal name can not be changed, it is fixed even if the service is renamed. <c>Erlsrv</c> generates a unique internal name when a service is created, it is recommended to keep to the defaut if release-handling is to be used for the application.</p> - <p>The internal service name can be seen in the Windows® service manager if viewing <c>Properties</c> for an erlang service.</p> + <p>Specifies the Windows-internal service name (not the display name, + which is the one <c>erlsrv</c> uses to identify the service).</p> + <p>This internal name cannot be changed, it is fixed even if the + service is renamed. <c>erlsrv</c> generates a unique internal name + when a service is created. It is recommended to keep to the default + if release handling is to be used for the application.</p> + <p>The internal service name can be seen in the Windows service + manager if viewing <c>Properties</c> for an Erlang service.</p> </item> + <tag><c><![CDATA[Comment]]></c></tag> <item> - <p><c><![CDATA[Comment]]></c>: A textual comment describing the service. Not mandatory, but shows up as the service description in the Windows® service manager.</p> + <p>A textual comment describing the service. Not mandatory, but shows + up as the service description in the Windows service manager.</p> </item> - </list> - <p> <marker id="001"></marker> - The naming of the service in a system that - uses release handling has to follow the convention + </taglist> + + <p><marker id="001"></marker> + The naming of the service in a system that + uses release handling must follow the convention <em>NodeName</em>_<em>Release</em>, where <em>NodeName</em> is - the first part of the Erlang nodename (up to, but not including + the first part of the Erlang node name (up to, but not including the "@") and <em>Release</em> is the current release of the application.</p> </description> + <funcs> <func> <name>erlsrv {set | add} <service-name> [<service options>]</name> - <fsummary>Add or modify an Erlang service</fsummary> + <fsummary>Add or modify an Erlang service.</fsummary> <desc> - <p>The set and add commands adds or modifies a Erlang service - respectively. The simplest form of an add command would be - completely without options in which case all default values + <p>The <c>set</c> and <c>add</c> commands modifies or adds an Erlang + service, respectively. The simplest form of an <c>add</c> command is + without any options in which case all default values (described above) apply. The service name is mandatory.</p> - <p>Every option can be given without parameters, in which case - the default value is applied. Values to the options are - supplied <em>only</em> when the default should not be used - (i.e. <c><![CDATA[erlsrv set myservice -prio -arg]]></c> sets the - default priority and removes all arguments).</p> - <p>The following service options are currently available:</p> + <p>Every option can be specified without parameters, the + default value is then applied. Values to the options are + supplied <em>only</em> when the default is not to be used. + For example, <c><![CDATA[erlsrv set myservice -prio -arg]]></c> + sets the default priority and removes all arguments.</p> + <p>Service options:</p> <taglist> - <tag>-st[opaction] [<erlang shell command>]</tag> - <item>Defines the StopAction, the command given to the Erlang - shell when the service is stopped. Default is none.</item> - <tag>-on[fail] [{reboot | restart | restart_always}]</tag> - <item>Specifies the action to take when the Erlang emulator - stops unexpectedly. Default is to ignore.</item> - <tag>-m[achine] [<erl-command>]</tag> - <item>The complete path to the Erlang emulator, never use the - werl program for this. Default is the <c><![CDATA[erl.exe]]></c> in the - same directory as <c><![CDATA[erlsrv.exe]]></c>. When release handling - is used, this should be set to a program similar to - <c><![CDATA[start_erl.exe]]></c>.</item> - <tag>-e[nv] [<variable>[=<value>]] ...</tag> - <item>Edits the environment block for the service. Every - environment variable specified will add to the system - environment block. If a variable specified here has the same - name as a system wide environment variable, the specified - value overrides the system wide. Environment variables are - added to this list by specifying - <variable>=<value> and deleted from the list by - specifying <variable> alone. The environment block is - automatically sorted. Any number of <c><![CDATA[-env]]></c> options can - be specified in one command. Default is to use the system - environment block unmodified (except for two additions, see - <seealso marker="#002">below</seealso>).</item> - <tag>-w[orkdir] [<directory>]</tag> - <item>The initial working directory of the Erlang - emulator. Default is the system directory.</item> - <tag>-p[riority] [{low|high|realtime}]</tag> - <item>The priority of the Erlang emulator. The default is the - Windows® default priority.</item> - <tag>{-sn[ame] | -n[ame]} [<node-name>]</tag> - <item>The node-name of the Erlang machine, distribution is - mandatory. Default is <c><![CDATA[-sname <service name>]]></c>. + <tag><c>-st[opaction] [<erlang shell command>]</c></tag> + <item> + <p>Defines the <c><![CDATA[StopAction]]></c>, the command given + to the Erlang shell when the service is stopped. + Default is none.</p> + </item> + <tag><c>-on[fail] [{reboot | restart | restart_always}]</c></tag> + <item> + <p>The action to take when the Erlang emulator + stops unexpectedly. Default is to ignore.</p> + </item> + <tag><c>-m[achine] [<erl-command>]</c></tag> + <item> + <p>The complete path to the Erlang emulator. Never use the + <c>werl</c> program for this. Defaults to the + <c><![CDATA[erl.exe]]></c> in the same directory as + <c><![CDATA[erlsrv.exe]]></c>. When release handling + is used, this is to be set to a program similar to + <c><![CDATA[start_erl.exe]]></c>.</p> + </item> + <tag><c>-e[nv] [<variable>[=<value>]] ...</c></tag> + <item> + <p>Edits the environment block for the service. Every + environment variable specified is added to the system + environment block. If a variable specified here has the same + name as a system-wide environment variable, the specified + value overrides the system-wide. Environment variables are + added to this list by specifying + <variable>=<value> and deleted from the list by + specifying <variable> alone. The environment block is + automatically sorted. Any number of <c><![CDATA[-env]]></c> + options can be specified in one command. Default is to use the + system environment block unmodified (except for two additions, + see section <seealso marker="#002">Environment</seealso> + below).</p> + </item> + <tag><c>-w[orkdir] [<directory>]</c></tag> + <item> + <p>The initial working directory of the Erlang + emulator. Defaults to the system directory.</p> + </item> + <tag><c>-p[riority] [{low|high|realtime}]</c></tag> + <item> + <p>The priority of the Erlang emulator. Default to the + Windows default priority.</p> + </item> + <tag><c>{-sn[ame] | -n[ame]} [<node-name>]</c></tag> + <item> + <p>The node name of the Erlang machine. Distribution is mandatory. + Defaults to <c><![CDATA[-sname <service name>]]></c>.</p> + </item> + <tag><c>-d[ebugtype] [{new|reuse|console}]</c></tag> + <item> + <p>Specifies where shell output is to be sent. + Default is that shell output is discarded. + To be used only for debugging.</p> + </item> + <tag><c>-ar[gs] [<limited erl arguments>]</c></tag> + <item> + <p>Extra arguments to the Erlang emulator. Avoid + <c><![CDATA[-noinput]]></c>, <c><![CDATA[-noshell]]></c>, and + <c><![CDATA[-sname]]></c>/<c><![CDATA[-name]]></c>. Default is + no extra arguments. Remember that the services cookie file is not + necessarily the same as the interactive users. The service + runs as the local administrator. Specify all arguments + together in one string, use double quotes (") to specify an + argument string containing spaces, and use quoted quotes (\") + to specify a quote within the argument string if necessary.</p> + </item> + <tag><c>-i[nternalservicename] [<internal name>]</c></tag> + <item> + <p><em>Only</em> allowed for <c>add</c>. Specifies a + Windows-internal service name for the service, which by + default is set to something unique (prefixed with the + original service name) by <c>erlsrv</c> when adding a new + service. Specifying this is a purely cosmethic action and is + <em>not</em> recommended if release handling is to be + performed. The internal service name cannot be changed once + the service is created. The internal name is <em>not</em> to + be confused with the ordinary service name, which is the name + used to identify a service to <c>erlsrv</c>.</p> + </item> + <tag><c>-c[omment] [<short description>]</c></tag> + <item> + <p>Specifies a textual comment describing the + service. This comment shows up as the service description + in the Windows service manager.</p> </item> - <tag>-d[ebugtype] [{new|reuse|console}]</tag> - <item>Specifies where shell output should be sent, - default is that shell output is discarded. - To be used only for debugging.</item> - <tag>-ar[gs] [<limited erl arguments>]</tag> - <item>Additional arguments to the Erlang emulator, avoid - <c><![CDATA[-noinput]]></c>, <c><![CDATA[-noshell]]></c> and - <c><![CDATA[-sname]]></c>/<c><![CDATA[-name]]></c>. Default is no additional - arguments. Remember that the services cookie file is not - necessarily the same as the interactive users. The service - runs as the local administrator. All arguments should be given - together in one string, use double quotes (") to give an - argument string containing spaces and use quoted quotes (\") - to give an quote within the argument string if - necessary.</item> - <tag>-i[nternalservicename] [<internal name>]</tag> - <item><em>Only</em> allowed for <c>add</c>. Specifies a - Windows® internal service name for the service, which by - default is set to something unique (prefixed with the - original service name) by erlsrv when adding a new - service. Specifying this is a purely cosmethic action and is - <em>not</em> recommended if release handling is to be - performed. The internal service name cannot be changed once - the service is created. The internal name is <em>not</em> to - be confused with the ordinary service name, which is the name - used to identify a service to erlsrv.</item> - <tag>-c[omment] [<short description>]</tag> - <item>Specifies a textual comment describing the - service. This comment will show upp as the service description - in the Windows® service manager.</item> </taglist> </desc> </func> + <func> - <name>erlsrv {start | start_disabled | stop | disable | enable} <service-name></name> + <name>erlsrv {start | start_disabled | stop | disable | + enable} <service-name></name> <fsummary>Manipulate the current service status.</fsummary> <desc> <p>These commands are only added for convenience, the normal way to manipulate the state of a service is through the - control panels services applet. The <c><![CDATA[start]]></c> and + control panels services applet.</p> + <p>The <c><![CDATA[start]]></c> and <c><![CDATA[stop]]></c> commands communicates - with the service manager for stopping and starting a - service. The commands wait until the service is actually - stopped or started. When disabling a service, it is not - stopped, the disabled state will not take effect until the - service actually is stopped. Enabling a service sets it in - automatic mode, that is started at boot. This command cannot - set the service to manual. </p> - - <p>The <c>start_disabled</c> command operates on a service - regardless of if it's enabled/disabled or started/stopped. It - does this by first enabling it (regardless of if it's enabled - or not), then starting it (if it's not already started) and - then disabling it. The result will be a disabled but started - service, regardless of its earlier state. This is useful for - starting services temporarily during a release upgrade. The - difference between using <c>start_disabled</c> and the - sequence <c>enable</c>, <c>start</c> and <c>disable</c> is - that all other <c>erlsrv</c> commands are locked out during - the sequence of operations in <c>start_disable</c>, making the - operation atomic from an <c>erlsrv</c> user's point of - view.</p> - + with the service manager for starting and stopping a + service. The commands wait until the service is + started or stopped. When disabling a service, it is not + stopped, the disabled state does not take effect until the + service is stopped. Enabling a service sets it in + automatic mode, which is started at boot. This command cannot + set the service to manual.</p> + <p>The <c>start_disabled</c> command operates on a service + regardless of if it is enabled/disabled or started/stopped. It + does this by first enabling it (regardless of if it is enabled + or not), then starting it (if not already started), and + then disabling it. The result is a disabled but started + service, regardless of its earlier state. This is useful for + starting services temporarily during a release upgrade. The + difference between using <c>start_disabled</c> and the + sequence <c>enable</c>, <c>start</c>, and <c>disable</c> is + that all other <c>erlsrv</c> commands are locked out during + the sequence of operations in <c>start_disable</c>, making the + operation atomic from an <c>erlsrv</c> user's point of view.</p> </desc> </func> + <func> <name>erlsrv remove <service-name></name> <fsummary>Remove the service.</fsummary> <desc> - <p>This command removes the service completely with all its registered - options. It will be stopped before it is removed.</p> + <p>Removes the service completely with all its registered + options. It is stopped before it is removed.</p> </desc> </func> + <func> <name>erlsrv list [<service-name>]</name> - <fsummary>List all Erlang services or all options for one service.</fsummary> + <fsummary>List all Erlang services or all options for one service. + </fsummary> <desc> - <p>If no service name is supplied, a brief listing of all Erlang services - is presented. If a service-name is supplied, all options for that - service are presented.</p> + <p>If no service name is specified, a brief listing of all Erlang + services is presented. If a service name is supplied, all options + for that service are presented.</p> </desc> </func> + <func> <name>erlsrv help</name> - <fsummary>Display a brief help text</fsummary> + <fsummary>Display a brief help text.</fsummary> + <desc> + <p>Displays a brief help text.</p> + </desc> </func> </funcs> <section> - <title>ENVIRONMENT</title> - <p> <marker id="002"></marker> -The environment of an Erlang machine started - as a service will contain two special variables, - <c><![CDATA[ERLSRV_SERVICE_NAME]]></c>, which is the name of the service that - started the machine and <c><![CDATA[ERLSRV_EXECUTABLE]]></c> which is the - full path to the <c><![CDATA[erlsrv.exe]]></c> that can be used to manipulate - the service. This will come in handy when defining a heart command for - your service. A command file for restarting a service will - simply look like this:</p> + <title>Environment</title> + <p><marker id="002"></marker> + The environment of an Erlang machine started + as a service contains two special variables:</p> + + <taglist> + <tag><c><![CDATA[ERLSRV_SERVICE_NAME]]></c></tag> + <item>The name of the service that started the machine.</item> + <tag><c><![CDATA[ERLSRV_EXECUTABLE]]></c></tag> + <item>The full path to the <c><![CDATA[erlsrv.exe]]></c>, which can be + used to manipulate the service. This comes in handy when defining a + heart command for your service.</item> + </taglist> + + <p>A command file for restarting a service looks as follows:</p> + <code type="none"><![CDATA[ @echo off %ERLSRV_EXECUTABLE% stop %ERLSRV_SERVICE_NAME% %ERLSRV_EXECUTABLE% start %ERLSRV_SERVICE_NAME% ]]></code> + <p>This command file is then set as heart command.</p> + <p>The environment variables can also be used to detect that we are running as a service and make port programs react correctly - to the control events generated on logout (see below).</p> + to the control events generated on logout (see the next section).</p> </section> <section> - <title>PORT PROGRAMS</title> + <title>Port Programs</title> <p>When a program runs in - the service context, it has to handle the control events that is + the service context, it must handle the control events that are sent to every program in the system when the interactive user logs off. This is done in different ways for programs running in the console subsystem and programs running as window - applications. An application which runs in the console subsystem + applications. An application running in the console subsystem (normal for port programs) uses the win32 function <c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler - that returns TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c> + that returns <c>true</c> in answer to the + <c><![CDATA[CTRL_LOGOFF_EVENT]]></c> and <c><![CDATA[CTRL_SHUTDOWN_EVENT]]></c> events. Other applications - just forward <c><![CDATA[WM_ENDSESSION]]></c> and - <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure. - Here is a brief example in C of how to set the console control - handler:</p> + only forward <c><![CDATA[WM_ENDSESSION]]></c> and + <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.</p> + + <p>A brief example in C of how to set the console control handler:</p> + <code type="none"><![CDATA[ #include <windows.h> /* @@ -383,7 +487,7 @@ void initialize_handler(void){ char buffer[2]; /* * We assume we are running as a service if this - * environment variable is defined + * environment variable is defined. */ if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer, (DWORD) 2)){ @@ -396,29 +500,34 @@ void initialize_handler(void){ </section> <section> - <title>NOTES</title> - <p>Even though the options are described in a Unix-like format, the case of - the options or commands is not relevant, and the "/" character for options - can be used as well as the "-" character. </p> - <p>Note that the program resides in the emulators - <c><![CDATA[bin]]></c>-directory, not in the <c><![CDATA[bin]]></c>-directory directly under + <title>Notes</title> + <p>Although the options are described in a Unix-like format, the case of + the options or commands is not relevant, and both character "/" and "-" + can be used for options.</p> + + <p>Notice that the program resides in the emulator's <c><![CDATA[bin]]></c> + directory, not in the <c><![CDATA[bin]]></c> directory directly under the Erlang root. The reasons for this are the subtle problem of upgrading the emulator on a running system, where a new version of the runtime system should not need to overwrite existing (and probably used) executables.</p> - <p>To easily manipulate the Erlang services, put + + <p>To manipulate the Erlang services easily, put the <c><![CDATA[<erlang_root>\erts-<version>\bin]]></c> directory in - the path instead of <c><![CDATA[<erlang_root>\bin]]></c>. The erlsrv program - can be found from inside Erlang by using the + the path instead of <c><![CDATA[<erlang_root>\bin]]></c>. The + <c>erlsrv</c> program can be found from inside Erlang by using the <c><![CDATA[os:find_executable/1]]></c> Erlang function.</p> - <p>For release handling to work, use <c><![CDATA[start_erl]]></c> as the Erlang - machine. It is also worth mentioning again that the name of the - service is significant (see <seealso marker="#001">above</seealso>).</p> + + <p>For release handling to work, use <c><![CDATA[start_erl]]></c> as the + Erlang machine. As stated <seealso marker="#001">above</seealso>, + the service name is significant.</p> </section> <section> - <title>SEE ALSO</title> - <p>start_erl(1), release_handler(3)</p> + <title>See Also</title> + <p><seealso marker="start_erl"><c>start_erl(1)</c></seealso>, + <seealso marker="sasl:release_handler"> + <c>release_handler(3)</c></seealso></p> </section> </comref> diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 9aef1c0b1f..8ab35851c1 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -25,63 +25,68 @@ <title>erts_alloc</title> <prepared>Rickard Green</prepared> <docno>1</docno> - <date>03-06-11</date> + <date>2003-06-11</date> <rev>1</rev> <file>erts_alloc.xml</file> </header> <lib>erts_alloc</lib> - <libsummary>An Erlang Run-Time System internal memory allocator library.</libsummary> + <libsummary>An Erlang runtime system internal memory allocator library. + </libsummary> <description> - <p><c>erts_alloc</c> is an Erlang Run-Time System internal memory + <p><c>erts_alloc</c> is an Erlang runtime system internal memory allocator library. <c>erts_alloc</c> provides the Erlang - Run-Time System with a number of memory allocators.</p> + runtime system with a number of memory allocators.</p> </description> <section> <title>Allocators</title> <marker id="allocators"></marker> - <p>Currently the following allocators are present:</p> + <p>The following allocators are present:</p> + <taglist> <tag><c>temp_alloc</c></tag> <item>Allocator used for temporary allocations.</item> <tag><c>eheap_alloc</c></tag> - <item>Allocator used for Erlang heap data, such as Erlang process heaps.</item> + <item>Allocator used for Erlang heap data, such as Erlang process heaps. + </item> <tag><c>binary_alloc</c></tag> <item>Allocator used for Erlang binary data.</item> <tag><c>ets_alloc</c></tag> - <item>Allocator used for ETS data.</item> + <item>Allocator used for <c>ets</c> data.</item> <tag><c>driver_alloc</c></tag> <item>Allocator used for driver data.</item> <tag><c>literal_alloc</c></tag> <item>Allocator used for constant terms in Erlang code.</item> <tag><c>sl_alloc</c></tag> <item>Allocator used for memory blocks that are expected to be - short-lived.</item> + short-lived.</item> <tag><c>ll_alloc</c></tag> <item>Allocator used for memory blocks that are expected to be - long-lived, for example Erlang code.</item> + long-lived, for example, Erlang code.</item> <tag><c>fix_alloc</c></tag> <item>A fast allocator used for some frequently used fixed size data types.</item> <tag><c>exec_alloc</c></tag> - <item>Allocator used by hipe for native executable code - on specific architectures (x86_64).</item> + <item>Allocator used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> + application for native executable code on specific architectures + (x86_64).</item> <tag><c>std_alloc</c></tag> - <item>Allocator used for most memory blocks not allocated via any of - the other allocators described above.</item> + <item>Allocator used for most memory blocks not allocated through any of + the other allocators described above.</item> <tag><c>sys_alloc</c></tag> <item>This is normally the default <c>malloc</c> implementation - used on the specific OS.</item> + used on the specific OS.</item> <tag><c>mseg_alloc</c></tag> - <item>A memory segment allocator. <c>mseg_alloc</c> is used by other - allocators for allocating memory segments and is currently only - available on systems that have the <c>mmap</c> system - call. Memory segments that are deallocated are kept for a - while in a segment cache before they are destroyed. When - segments are allocated, cached segments are used if possible - instead of creating new segments. This in order to reduce - the number of system calls made.</item> + <item>A memory segment allocator. It is used by other + allocators for allocating memory segments and is only + available on systems that have the <c>mmap</c> system + call. Memory segments that are deallocated are kept for a + while in a segment cache before they are destroyed. When + segments are allocated, cached segments are used if possible + instead of creating new segments. This to reduce + the number of system calls made.</item> </taglist> + <p><c>sys_alloc</c> and <c>literal_alloc</c> are always enabled and cannot be disabled. <c>exec_alloc</c> is only available if it is needed and cannot be disabled. <c>mseg_alloc</c> is always enabled if it is @@ -90,9 +95,10 @@ By default all allocators are enabled. When an allocator is disabled, <c>sys_alloc</c> is used instead of the disabled allocator.</p> + <p>The main idea with the <c>erts_alloc</c> library is to separate memory blocks that are used differently into different memory - areas, and by this achieving less memory fragmentation. By + areas, to achieve less memory fragmentation. By putting less effort in finding a good fit for memory blocks that are frequently allocated than for those less frequently allocated, a performance gain can be achieved.</p> @@ -100,61 +106,85 @@ <section> <marker id="alloc_util"></marker> - <title>The alloc_util framework</title> + <title>The alloc_util Framework</title> <p>Internally a framework called <c>alloc_util</c> is used for - implementing allocators. <c>sys_alloc</c>, and - <c>mseg_alloc</c> do not use this framework; hence, the + implementing allocators. <c>sys_alloc</c> and + <c>mseg_alloc</c> do not use this framework, so the following does <em>not</em> apply to them.</p> + <p>An allocator manages multiple areas, called carriers, in which memory blocks are placed. A carrier is either placed in a - separate memory segment (allocated via <c>mseg_alloc</c>), or in - the heap segment (allocated via <c>sys_alloc</c>). Multiblock - carriers are used for storage of several blocks. Singleblock - carriers are used for storage of one block. Blocks that are - larger than the value of the singleblock carrier threshold - (<seealso marker="#M_sbct">sbct</seealso>) parameter are placed - in singleblock carriers. Blocks that are smaller than the value - of the <c>sbct</c> parameter are placed in multiblock - carriers. Normally an allocator creates a "main multiblock + separate memory segment (allocated through <c>mseg_alloc</c>), or in + the heap segment (allocated through <c>sys_alloc</c>).</p> + + <list type="bulleted"> + <item> + <p>Multiblock carriers are used for storage of several blocks.</p> + </item> + <item> + <p>Singleblock carriers are used for storage of one block.</p> + </item> + <item> + <p>Blocks that are larger than the value of the singleblock carrier + threshold (<seealso marker="#M_sbct"><c>sbct</c></seealso>) parameter + are placed in singleblock carriers.</p> + </item> + <item> + <p>Blocks that are smaller than the value of parameter <c>sbct</c> + are placed in multiblock carriers.</p></item> + </list> + + <p>Normally an allocator creates a "main multiblock carrier". Main multiblock carriers are never deallocated. The - size of the main multiblock carrier is determined by the value - of the <seealso marker="#M_mmbcs">mmbcs</seealso> parameter.</p> + size of the main multiblock carrier is determined by the value of + parameter <seealso marker="#M_mmbcs"><c>mmbcs</c></seealso>.</p> + <p><marker id="mseg_mbc_sizes"></marker>Sizes of multiblock carriers - allocated via <c>mseg_alloc</c> are - decided based on the values of the largest multiblock carrier - size (<seealso marker="#M_lmbcs">lmbcs</seealso>), the smallest - multiblock carrier size (<seealso marker="#M_smbcs">smbcs</seealso>), - and the multiblock carrier growth stages - (<seealso marker="#M_mbcgs">mbcgs</seealso>) parameters. If - <c>nc</c> is the current number of multiblock carriers (the main + allocated through <c>mseg_alloc</c> are decided based on the + following parameters:</p> + + <list type="bulleted"> + <item>The values of the largest multiblock carrier size + (<seealso marker="#M_lmbcs"><c>lmbcs</c></seealso>)</item> + <item>The smallest multiblock carrier size + (<seealso marker="#M_smbcs"><c>smbcs</c></seealso>)</item> + <item>The multiblock carrier growth stages + (<seealso marker="#M_mbcgs"><c>mbcgs</c></seealso>)</item> + </list> + + <p>If <c>nc</c> is the current number of multiblock carriers (the main multiblock carrier excluded) managed by an allocator, the size of the next <c>mseg_alloc</c> multiblock carrier allocated by - this allocator will roughly be + this allocator is roughly <c><![CDATA[smbcs+nc*(lmbcs-smbcs)/mbcgs]]></c> when <c><![CDATA[nc <= mbcgs]]></c>, - and <c>lmbcs</c> when <c><![CDATA[nc > mbcgs]]></c>. If the value of the - <c>sbct</c> parameter should be larger than the value of the - <c>lmbcs</c> parameter, the allocator may have to create - multiblock carriers that are larger than the value of the - <c>lmbcs</c> parameter, though. - Singleblock carriers allocated via <c>mseg_alloc</c> are sized + and <c>lmbcs</c> when <c><![CDATA[nc > mbcgs]]></c>. If the value of + parameter <c>sbct</c> is larger than the value of parameter + <c>lmbcs</c>, the allocator may have to create + multiblock carriers that are larger than the value of + parameter <c>lmbcs</c>, though. + Singleblock carriers allocated through <c>mseg_alloc</c> are sized to whole pages.</p> - <p>Sizes of carriers allocated via <c>sys_alloc</c> are + + <p>Sizes of carriers allocated through <c>sys_alloc</c> are decided based on the value of the <c>sys_alloc</c> carrier size - (<seealso marker="#Muycs">ycs</seealso>) parameter. The size of - a carrier is the least number of multiples of the value of the - <c>ycs</c> parameter that satisfies the request.</p> + (<seealso marker="#Muycs"><c>ycs</c></seealso>) parameter. The size of + a carrier is the least number of multiples of the value of + parameter <c>ycs</c> satisfying the request.</p> + <p>Coalescing of free blocks are always performed immediately. - Boundary tags (headers and footers) in free blocks are used + Boundary tags (headers and footers) in free blocks are used, which makes the time complexity for coalescing constant.</p> + <p><marker id="strategy"></marker>The memory allocation strategy - used for multiblock carriers by an - allocator is configurable via the <seealso marker="#M_as">as</seealso> - parameter. Currently the following strategies are available:</p> + used for multiblock carriers by an allocator can be + configured using parameter <seealso marker="#M_as"><c>as</c></seealso>. + The following strategies are available:</p> + <taglist> <tag>Best fit</tag> <item> - <p>Strategy: Find the smallest block that satisfies the + <p>Strategy: Find the smallest block satisfying the requested block size.</p> <p>Implementation: A balanced binary search tree is used. The time complexity is proportional to log N, where @@ -162,7 +192,7 @@ </item> <tag>Address order best fit</tag> <item> - <p>Strategy: Find the smallest block that satisfies the + <p>Strategy: Find the smallest block satisfying the requested block size. If multiple blocks are found, choose the one with the lowest address.</p> <p>Implementation: A balanced binary search tree is @@ -171,7 +201,7 @@ </item> <tag>Address order first fit</tag> <item> - <p>Strategy: Find the block with the lowest address that satisfies the + <p>Strategy: Find the block with the lowest address satisfying the requested block size.</p> <p>Implementation: A balanced binary search tree is used. The time complexity is proportional to log N, where @@ -180,8 +210,8 @@ <tag>Address order first fit carrier best fit</tag> <item> <p>Strategy: Find the <em>carrier</em> with the lowest address that - can satisfy the requested block size, then find a block within - that carrier using the "best fit" strategy.</p> + can satisfy the requested block size, then find a block within + that carrier using the "best fit" strategy.</p> <p>Implementation: Balanced binary search trees are used. The time complexity is proportional to log N, where N is the number of free blocks.</p> @@ -189,8 +219,8 @@ <tag>Address order first fit carrier address order best fit</tag> <item> <p>Strategy: Find the <em>carrier</em> with the lowest address that - can satisfy the requested block size, then find a block within - that carrier using the "adress order best fit" strategy.</p> + can satisfy the requested block size, then find a block within + that carrier using the "address order best fit" strategy.</p> <p>Implementation: Balanced binary search trees are used. The time complexity is proportional to log N, where N is the number of free blocks.</p> @@ -200,12 +230,12 @@ <p>Strategy: Try to find the best fit, but settle for the best fit found during a limited search.</p> <p>Implementation: The implementation uses segregated free - lists with a maximum block search depth (in each list) in - order to find a good fit fast. When the maximum block - search depth is small (by default 3) this implementation + lists with a maximum block search depth (in each list) + to find a good fit fast. When the maximum block + search depth is small (by default 3), this implementation has a time complexity that is constant. The maximum block - search depth is configurable via the - <seealso marker="#M_mbsd">mbsd</seealso> parameter.</p> + search depth can be configured using parameter + <seealso marker="#M_mbsd"><c>mbsd</c></seealso>.</p> </item> <tag>A fit</tag> <item> @@ -213,43 +243,47 @@ block to see if it satisfies the request. This strategy is only intended to be used for temporary allocations.</p> <p>Implementation: Inspect the first block in a free-list. - If it satisfies the request, it is used; otherwise, a new + If it satisfies the request, it is used, otherwise a new carrier is created. The implementation has a time complexity that is constant.</p> - <p>As of erts version 5.6.1 the emulator will refuse to - use this strategy on other allocators than <c>temp_alloc</c>. - This since it will only cause problems for other allocators.</p> + <p>As from ERTS 5.6.1 the emulator refuses to + use this strategy on other allocators than <c>temp_alloc</c>. + This because it only causes problems for other allocators.</p> </item> </taglist> - <p>Apart from the ordinary allocators described above a number of - pre-allocators are used for some specific data types. These - pre-allocators pre-allocate a fixed amount of memory for certain data - types when the run-time system starts. As long as pre-allocated memory - is available, it will be used. When no pre-allocated memory is - available, memory will be allocated in ordinary allocators. These - pre-allocators are typically much faster than the ordinary allocators, - but can only satisfy a limited amount of requests.</p> + + <p>Apart from the ordinary allocators described above, some + pre-allocators are used for some specific data types. These + pre-allocators pre-allocate a fixed amount of memory for certain data + types when the runtime system starts. As long as pre-allocated memory + is available, it is used. When no pre-allocated memory is + available, memory is allocated in ordinary allocators. These + pre-allocators are typically much faster than the ordinary allocators, + but can only satisfy a limited number of requests.</p> </section> <section> <marker id="flags"></marker> <title>System Flags Effecting erts_alloc</title> <warning> - <p>Only use these flags if you are absolutely sure what you are - doing. Unsuitable settings may cause serious performance + <p>Only use these flags if you are sure what you are + doing. Unsuitable settings can cause serious performance degradation and even a system crash at any time during operation.</p> </warning> + <p>Memory allocator system flags have the following syntax: - <c><![CDATA[+M<S><P> <V>]]></c> + <c><![CDATA[+M<S><P> <V>]]></c>, where <c><![CDATA[<S>]]></c> is a letter identifying a subsystem, <c><![CDATA[<P>]]></c> is a parameter, and <c><![CDATA[<V>]]></c> is the value to use. The flags can be passed to the Erlang emulator - (<seealso marker="erl">erl</seealso>) as command line + (<seealso marker="erl"><c>erl(1)</c></seealso>) as command-line arguments.</p> - <p>System flags effecting specific allocators have an upper-case + + <p>System flags effecting specific allocators have an uppercase letter as <c><![CDATA[<S>]]></c>. The following letters are used for - the currently present allocators:</p> + the allocators:</p> + <list type="bulleted"> <item><c>B: binary_alloc</c></item> <item><c>D: std_alloc</c></item> @@ -265,421 +299,501 @@ <item><c>X: exec_alloc</c></item> <item><c>Y: sys_alloc</c></item> </list> - <p>The following flags are available for configuration of - <c>mseg_alloc</c>:</p> - <taglist> - <tag><marker id="MMamcbf"/><c><![CDATA[+MMamcbf <size>]]></c></tag> - <item> - Absolute max cache bad fit (in kilobytes). A segment in the - memory segment cache is not reused if its size exceeds the - requested size with more than the value of this - parameter. Default value is 4096. </item> - <tag><marker id="MMrmcbf"/><c><![CDATA[+MMrmcbf <ratio>]]></c></tag> - <item> - Relative max cache bad fit (in percent). A segment in the - memory segment cache is not reused if its size exceeds the - requested size with more than relative max cache bad fit - percent of the requested size. Default value is 20.</item> - <tag><marker id="MMsco"/><c><![CDATA[+MMsco true|false]]></c></tag> - <item> - Set <seealso marker="#MMscs">super carrier</seealso> only flag. This - flag defaults to <c>true</c>. When a super carrier is used and this - flag is <c>true</c>, <c>mseg_alloc</c> will only create carriers - in the super carrier. Note that the <c>alloc_util</c> framework may - create <c>sys_alloc</c> carriers, so if you want all carriers to - be created in the super carrier, you therefore want to disable use - of <c>sys_alloc</c> carriers by also passing - <seealso marker="#Musac"><c>+Musac false</c></seealso>. When the flag - is <c>false</c>, <c>mseg_alloc</c> will try to create carriers outside - of the super carrier when the super carrier is full. - <br/><br/> - <em>NOTE</em>: Setting this flag to <c>false</c> may not be supported - on all systems. This flag will in that case be ignored. - <br/><br/> - <em>NOTE</em>: The super carrier cannot be enabled nor - disabled on halfword heap systems. This flag will be - ignored on halfword heap systems. - </item> - <tag><marker id="MMscrfsd"/><c><![CDATA[+MMscrfsd <amount>]]></c></tag> - <item> - Set <seealso marker="#MMscs">super carrier</seealso> reserved - free segment descriptors. This parameter defaults to <c>65536</c>. - This parameter determines the amount of memory to reserve for - free segment descriptors used by the super carrier. If the system - runs out of reserved memory for free segment descriptors, other - memory will be used. This may however cause fragmentation issues, - so you want to ensure that this never happens. The maximum amount - of free segment descriptors used can be retrieved from the - <c>erts_mmap</c> tuple part of the result from calling - <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, mseg_alloc})</seealso>. - </item> - <tag><marker id="MMscrpm"/><c><![CDATA[+MMscrpm true|false]]></c></tag> - <item> - Set <seealso marker="#MMscs">super carrier</seealso> reserve physical - memory flag. This flag defaults to <c>true</c>. When this flag is - <c>true</c>, physical memory will be reserved for the whole super - carrier at once when it is created. The reservation will after that - be left unchanged. When this flag is set to <c>false</c> only virtual - address space will be reserved for the super carrier upon creation. - The system will attempt to reserve physical memory upon carrier - creations in the super carrier, and attempt to unreserve physical - memory upon carrier destructions in the super carrier. - <br/><br/> - <em>NOTE</em>: What reservation of physical memory actually means - highly depends on the operating system, and how it is configured. For - example, different memory overcommit settings on Linux drastically - change the behaviour. Also note, setting this flag to <c>false</c> - may not be supported on all systems. This flag will in that case - be ignored. - <br/><br/> - <em>NOTE</em>: The super carrier cannot be enabled nor - disabled on halfword heap systems. This flag will be - ignored on halfword heap systems. - </item> - <tag><marker id="MMscs"/><c><![CDATA[+MMscs <size in MB>]]></c></tag> - <item> - Set super carrier size (in MB). The super carrier size defaults to - zero; i.e, the super carrier is by default disabled. The super - carrier is a large continuous area in the virtual address space. - <c>mseg_alloc</c> will always try to create new carriers in the super - carrier if it exists. Note that the <c>alloc_util</c> framework may - create <c>sys_alloc</c> carriers. For more information on this, see the - documentation of the <seealso marker="#MMsco"><c>+MMsco</c></seealso> - flag. - <br/><br/> - <em>NOTE</em>: The super carrier cannot be enabled nor - disabled on halfword heap systems. This flag will be - ignored on halfword heap systems. - </item> - <tag><marker id="MMmcs"/><c><![CDATA[+MMmcs <amount>]]></c></tag> - <item> - Max cached segments. The maximum number of memory segments - stored in the memory segment cache. Valid range is - 0-30. Default value is 10.</item> - </taglist> - <p>The following flags are available for configuration of - <c>sys_alloc</c>:</p> - <taglist> - <tag><marker id="MYe"/><c>+MYe true</c></tag> - <item> - Enable <c>sys_alloc</c>. Note: <c>sys_alloc</c> cannot be disabled.</item> - <tag><marker id="MYm"/><c>+MYm libc</c></tag> - <item> - <c>malloc</c> library to use. Currently only - <c>libc</c> is available. <c>libc</c> enables the standard - <c>libc</c> malloc implementation. By default <c>libc</c> is used.</item> - <tag><marker id="MYtt"/><c><![CDATA[+MYtt <size>]]></c></tag> - <item> - Trim threshold size (in kilobytes). This is the maximum amount - of free memory at the top of the heap (allocated by - <c>sbrk</c>) that will be kept by <c>malloc</c> (not - released to the operating system). When the amount of free - memory at the top of the heap exceeds the trim threshold, - <c>malloc</c> will release it (by calling - <c>sbrk</c>). Trim threshold is given in kilobytes. Default - trim threshold is 128. <em>Note:</em> This flag will - only have any effect when the emulator has been linked with - the GNU C library, and uses its <c>malloc</c> implementation.</item> - <tag><marker id="MYtp"/><c><![CDATA[+MYtp <size>]]></c></tag> - <item> - Top pad size (in kilobytes). This is the amount of extra - memory that will be allocated by <c>malloc</c> when - <c>sbrk</c> is called to get more memory from the operating - system. Default top pad size is 0. <em>Note:</em> This flag - will only have any effect when the emulator has been linked - with the GNU C library, and uses its <c>malloc</c> - implementation.</item> - </taglist> - <p>The following flags are available for configuration of allocators - based on <c>alloc_util</c>. If <c>u</c> is used as subsystem - identifier (i.e., <c><![CDATA[<S> = u]]></c>) all allocators based on - <c>alloc_util</c> will be effected. If <c>B</c>, <c>D</c>, <c>E</c>, - <c>F</c>, <c>H</c>, <c>L</c>, <c>R</c>, <c>S</c>, or <c>T</c> is used as - subsystem identifier, only the specific allocator identified will be - effected:</p> - <taglist> - <tag><marker id="M_acul"/><c><![CDATA[+M<S>acul <utilization>|de]]></c></tag> - <item> - Abandon carrier utilization limit. A valid - <c><![CDATA[<utilization>]]></c> is an integer in the range - <c>[0, 100]</c> representing utilization in percent. When a - utilization value larger than zero is used, allocator instances - are allowed to abandon multiblock carriers. If <c>de</c> (default - enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>, - a recomended non zero utilization value will be used. The actual - value chosen depend on allocator type and may be changed between - ERTS versions. Currently the default equals <c>de</c>, but this - may be changed in the future. Carriers will be abandoned when - memory utilization in the allocator instance falls below the - utilization value used. Once a carrier has been abandoned, no new - allocations will be made in it. When an allocator instance gets an - increased multiblock carrier need, it will first try to fetch an - abandoned carrier from an allocator instances of the same - allocator type. If no abandoned carrier could be fetched, it will - create a new empty carrier. When an abandoned carrier has been - fetched it will function as an ordinary carrier. This feature has - special requirements on the - <seealso marker="#M_as">allocation strategy</seealso> used. Currently - only the strategies <c>aoff</c>, <c>aoffcbf</c> and <c>aoffcaobf</c> support - abandoned carriers. This feature also requires - <seealso marker="#M_t">multiple thread specific instances</seealso> - to be enabled. When enabling this feature, multiple thread specific - instances will be enabled if not already enabled, and the - <c>aoffcbf</c> strategy will be enabled if current strategy does not - support abandoned carriers. This feature can be enabled on all - allocators based on the <c>alloc_util</c> framework with the - exception of <c>temp_alloc</c> (which would be pointless). - </item> - <tag><marker id="M_as"/><c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag> - <item> - Allocation strategy. Valid strategies are <c>bf</c> (best fit), - <c>aobf</c> (address order best fit), <c>aoff</c> (address order first fit), - <c>aoffcbf</c> (address order first fit carrier best fit), - <c>aoffcaobf</c> (address order first fit carrier address order best fit), - <c>gf</c> (good fit), and <c>af</c> (a fit). See - <seealso marker="#strategy">the description of allocation strategies</seealso> in "the <c>alloc_util</c> framework" section.</item> - <tag><marker id="M_asbcst"/><c><![CDATA[+M<S>asbcst <size>]]></c></tag> - <item> - Absolute singleblock carrier shrink threshold (in - kilobytes). When a block located in an - <c>mseg_alloc</c> singleblock carrier is shrunk, the carrier - will be left unchanged if the amount of unused memory is less - than this threshold; otherwise, the carrier will be shrunk. - See also <seealso marker="#M_rsbcst">rsbcst</seealso>.</item> - <tag><marker id="M_e"/><c><![CDATA[+M<S>e true|false]]></c></tag> - <item> - Enable allocator <c><![CDATA[<S>]]></c>.</item> - <tag><marker id="M_lmbcs"/><c><![CDATA[+M<S>lmbcs <size>]]></c></tag> - <item> - Largest (<c>mseg_alloc</c>) multiblock carrier size (in - kilobytes). See <seealso marker="#mseg_mbc_sizes">the description - on how sizes for mseg_alloc multiblock carriers are decided</seealso> - in "the <c>alloc_util</c> framework" section. On 32-bit Unix style OS - this limit can not be set higher than 128 megabyte.</item> - <tag><marker id="M_mbcgs"/><c><![CDATA[+M<S>mbcgs <ratio>]]></c></tag> - <item> - (<c>mseg_alloc</c>) multiblock carrier growth stages. See - <seealso marker="#mseg_mbc_sizes">the description on how sizes for - mseg_alloc multiblock carriers are decided</seealso> - in "the <c>alloc_util</c> framework" section.</item> - <tag><marker id="M_mbsd"/><c><![CDATA[+M<S>mbsd <depth>]]></c></tag> - <item> - Max block search depth. This flag has effect only if the - good fit strategy has been selected for allocator - <c><![CDATA[<S>]]></c>. When the good fit strategy is used, free - blocks are placed in segregated free-lists. Each free list - contains blocks of sizes in a specific range. The max block - search depth sets a limit on the maximum number of blocks to - inspect in a free list during a search for suitable block - satisfying the request.</item> - <tag><marker id="M_mmbcs"/><c><![CDATA[+M<S>mmbcs <size>]]></c></tag> - <item> - Main multiblock carrier size. Sets the size of the main - multiblock carrier for allocator <c><![CDATA[<S>]]></c>. The main - multiblock carrier is allocated via <c><![CDATA[sys_alloc]]></c> and is - never deallocated.</item> - <tag><marker id="M_mmmbc"/><c><![CDATA[+M<S>mmmbc <amount>]]></c></tag> - <item> - Max <c>mseg_alloc</c> multiblock carriers. Maximum number of - multiblock carriers allocated via <c>mseg_alloc</c> by - allocator <c><![CDATA[<S>]]></c>. When this limit has been reached, - new multiblock carriers will be allocated via - <c>sys_alloc</c>.</item> - <tag><marker id="M_mmsbc"/><c><![CDATA[+M<S>mmsbc <amount>]]></c></tag> - <item> - Max <c>mseg_alloc</c> singleblock carriers. Maximum number of - singleblock carriers allocated via <c>mseg_alloc</c> by - allocator <c><![CDATA[<S>]]></c>. When this limit has been reached, - new singleblock carriers will be allocated via - <c>sys_alloc</c>.</item> - <tag><marker id="M_ramv"/><c><![CDATA[+M<S>ramv <bool>]]></c></tag> - <item> - Realloc always moves. When enabled, reallocate operations will - more or less be translated into an allocate, copy, free sequence. - This often reduce memory fragmentation, but costs performance. - </item> - <tag><marker id="M_rmbcmt"/><c><![CDATA[+M<S>rmbcmt <ratio>]]></c></tag> - <item> - Relative multiblock carrier move threshold (in percent). When - a block located in a multiblock carrier is shrunk, - the block will be moved if the ratio of the size of the returned - memory compared to the previous size is more than this threshold; - otherwise, the block will be shrunk at current location.</item> - <tag><marker id="M_rsbcmt"/><c><![CDATA[+M<S>rsbcmt <ratio>]]></c></tag> - <item> - Relative singleblock carrier move threshold (in percent). When - a block located in a singleblock carrier is shrunk to - a size smaller than the value of the - <seealso marker="#M_sbct">sbct</seealso> parameter, - the block will be left unchanged in the singleblock carrier if - the ratio of unused memory is less than this threshold; - otherwise, it will be moved into a multiblock carrier. </item> - <tag><marker id="M_rsbcst"/><c><![CDATA[+M<S>rsbcst <ratio>]]></c></tag> - <item> - Relative singleblock carrier shrink threshold (in - percent). When a block located in an <c>mseg_alloc</c> - singleblock carrier is shrunk, the carrier will be left - unchanged if the ratio of unused memory is less than this - threshold; otherwise, the carrier will be shrunk. - See also <seealso marker="#M_asbcst">asbcst</seealso>.</item> - <tag><marker id="M_sbct"/><c><![CDATA[+M<S>sbct <size>]]></c></tag> - <item> - Singleblock carrier threshold. Blocks larger than this - threshold will be placed in singleblock carriers. Blocks - smaller than this threshold will be placed in multiblock - carriers. On 32-bit Unix style OS this threshold can not be set higher - than 8 megabytes.</item> - <tag><marker id="M_smbcs"/><c><![CDATA[+M<S>smbcs <size>]]></c></tag> - <item> - Smallest (<c>mseg_alloc</c>) multiblock carrier size (in - kilobytes). See <seealso marker="#mseg_mbc_sizes">the description - on how sizes for mseg_alloc multiblock carriers are decided</seealso> - in "the <c>alloc_util</c> framework" section.</item> - <tag><marker id="M_t"/><c><![CDATA[+M<S>t true|false]]></c></tag> - <item> - <p>Multiple, thread specific instances of the allocator. - This option will only have any effect on the runtime system - with SMP support. Default behaviour on the runtime system with - SMP support is <c>NoSchedulers+1</c> instances. Each scheduler will use - a lock-free instance of its own and other threads will use - a common instance.</p> - <p>It was previously (before ERTS version 5.9) possible to configure - a smaller amount of thread specific instances than schedulers. - This is, however, not possible any more.</p> - </item> - </taglist> - <p>Currently the following flags are available for configuration of - <c>alloc_util</c>, i.e. all allocators based on <c>alloc_util</c> - will be effected:</p> - <taglist> - <tag><marker id="Muycs"/><c><![CDATA[+Muycs <size>]]></c></tag> - <item> - <c>sys_alloc</c> carrier size. Carriers allocated via - <c>sys_alloc</c> will be allocated in sizes which are - multiples of the <c>sys_alloc</c> carrier size. This is not - true for main multiblock carriers and carriers allocated - during a memory shortage, though.</item> - <tag><marker id="Mummc"/><c><![CDATA[+Mummc <amount>]]></c></tag> - <item> - Max <c>mseg_alloc</c> carriers. Maximum number of carriers - placed in separate memory segments. When this limit has been - reached, new carriers will be placed in memory retrieved from - <c>sys_alloc</c>.</item> - <tag><marker id="Musac"/><c><![CDATA[+Musac <bool>]]></c></tag> - <item> - Allow <c>sys_alloc</c> carriers. By default <c>true</c>. If - set to <c>false</c>, <c>sys_alloc</c> carriers will never be - created by allocators using the <c>alloc_util</c> framework.</item> - </taglist> - <p>The following flag is special for <c>literal_alloc</c>:</p> - <taglist> - <tag><marker id="MIscs"/><c><![CDATA[+MIscs <size in MB>]]></c></tag> - <item> - <c>literal_alloc</c> super carrier size (in MB). The amount of - <em>virtual</em> address space reserved for literal terms in - Erlang code on 64-bit architectures. The default is 1024 (1GB) - and is usually sufficient. The flag is ignored on 32-bit - architectures.</item> - </taglist> - <p>The following flag is special for <c>exec_alloc</c>:</p> - <taglist> - <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag> - <item> - <c>exec_alloc</c> super carrier size (in MB). The amount of - <em>virtual</em> address space reserved for native executable code - used by hipe on specific architectures (x86_64). The default is 512 MB. - </item> - </taglist> - <p>Instrumentation flags:</p> - <taglist> - <tag><marker id="Mim"/><c>+Mim true|false</c></tag> - <item> - A map over current allocations is kept by the emulator. The - allocation map can be retrieved via the <c>instrument</c> - module. <c>+Mim true</c> implies <c>+Mis true</c>. - <c>+Mim true</c> is the same as - <seealso marker="erl#instr">-instr</seealso>.</item> - <tag><marker id="Mis"/><c>+Mis true|false</c></tag> - <item> - Status over allocated memory is kept by the emulator. The - allocation status can be retrieved via the <c>instrument</c> - module.</item> - <tag><marker id="Mit"/><c>+Mit X</c></tag> - <item> - Reserved for future use. Do <em>not</em> use this flag.</item> - </taglist> - <note> - <p>When instrumentation of the emulator is enabled, the emulator - uses more memory and runs slower.</p> - </note> - <p>Other flags:</p> - <taglist> - <tag><marker id="Mea"/><c>+Mea min|max|r9c|r10b|r11b|config</c></tag> - <item> + + <section> + <title>Flags for Configuration of mseg_alloc</title> + <taglist> + <tag><marker id="MMamcbf"/><c><![CDATA[+MMamcbf <size>]]></c></tag> + <item> + <p>Absolute maximum cache bad fit (in kilobytes). A segment in the + memory segment cache is not reused if its size exceeds the + requested size with more than the value of this + parameter. Defaults to <c>4096</c>.</p> + </item> + <tag><marker id="MMrmcbf"/><c><![CDATA[+MMrmcbf <ratio>]]></c></tag> + <item> + <p>Relative maximum cache bad fit (in percent). A segment in the + memory segment cache is not reused if its size exceeds the + requested size with more than relative maximum cache bad fit + percent of the requested size. Defaults to <c>20</c>.</p> + </item> + <tag><marker id="MMsco"/><c><![CDATA[+MMsco true|false]]></c></tag> + <item> + <p>Sets <seealso marker="#MMscs">super carrier</seealso> only flag. + Defaults to <c>true</c>. When a super carrier is used and this + flag is <c>true</c>, <c>mseg_alloc</c> only creates carriers in + the super carrier. Notice that the <c>alloc_util</c> framework can + create <c>sys_alloc</c> carriers, so if you want all carriers to + be created in the super carrier, you therefore want to disable use + of <c>sys_alloc</c> carriers by also passing + <seealso marker="#Musac"><c>+Musac false</c></seealso>. When + the flag is <c>false</c>, <c>mseg_alloc</c> tries to create carriers + outside of the super carrier when the super carrier is full.</p> + <note> + <p>Setting this flag to <c>false</c> is not supported + on all systems. The flag is then ignored.</p> + </note> + </item> + <tag><marker id="MMscrfsd"/><c><![CDATA[+MMscrfsd <amount>]]></c></tag> + <item> + <p>Sets <seealso marker="#MMscs">super carrier</seealso> reserved + free segment descriptors. Defaults to <c>65536</c>. + This parameter determines the amount of memory to reserve for + free segment descriptors used by the super carrier. If the system + runs out of reserved memory for free segment descriptors, other + memory is used. This can however cause fragmentation issues, + so you want to ensure that this never happens. The maximum amount + of free segment descriptors used can be retrieved from the + <c>erts_mmap</c> tuple part of the result from calling + <seealso marker="erts:erlang#system_info_allocator_tuple"> + <c>erlang:system_info({allocator, mseg_alloc})</c></seealso>.</p> + </item> + <tag><marker id="MMscrpm"/><c><![CDATA[+MMscrpm true|false]]></c></tag> + <item> + <p>Sets <seealso marker="#MMscs">super carrier</seealso> reserve + physical memory flag. Defaults to <c>true</c>. When this flag is + <c>true</c>, physical memory is reserved for the whole super + carrier at once when it is created. The reservation is after that + left unchanged. When this flag is set to <c>false</c>, only virtual + address space is reserved for the super carrier upon creation. + The system attempts to reserve physical memory upon carrier + creations in the super carrier, and attempt to unreserve physical + memory upon carrier destructions in the super carrier.</p> + <note> + <p>What reservation of physical memory means, highly + depends on the operating system, and how it is configured. For + example, different memory overcommit settings on Linux drastically + change the behavior.</p> + <p>Setting this flag to <c>false</c> is possibly not supported on + all systems. The flag is then ignored.</p> + </note> + </item> + <tag><marker id="MMscs"/><c><![CDATA[+MMscs <size in MB>]]></c></tag> + <item> + <p>Sets super carrier size (in MB). Defaults to <c>0</c>, that is, + the super carrier is by default disabled. The super + carrier is a large continuous area in the virtual address space. + <c>mseg_alloc</c> always tries to create new carriers in the super + carrier if it exists. Notice that the <c>alloc_util</c> framework + can create <c>sys_alloc</c> carriers. For more information, see + <seealso marker="#MMsco"><c>+MMsco</c></seealso>.</p> + </item> + <tag><marker id="MMmcs"/><c><![CDATA[+MMmcs <amount>]]></c></tag> + <item> + <p>Maximum cached segments. The maximum number of memory segments + stored in the memory segment cache. Valid range is <c>[0, 30]</c>. + Defaults to <c>10</c>.</p> + </item> + </taglist> + </section> + + <section> + <title>Flags for Configuration of sys_alloc</title> + <taglist> + <tag><marker id="MYe"/><c>+MYe true</c></tag> + <item> + <p>Enables <c>sys_alloc</c>.</p> + <note> + <p><c>sys_alloc</c> cannot be disabled.</p> + </note> + </item> + <tag><marker id="MYm"/><c>+MYm libc</c></tag> + <item> + <p><c>malloc</c> library to use. Only + <c>libc</c> is available. <c>libc</c> enables the standard + <c>libc</c> <c>malloc</c> implementation. By default <c>libc</c> + is used.</p> + </item> + <tag><marker id="MYtt"/><c><![CDATA[+MYtt <size>]]></c></tag> + <item> + <p>Trim threshold size (in kilobytes). This is the maximum amount + of free memory at the top of the heap (allocated by + <c>sbrk</c>) that is kept by <c>malloc</c> (not + released to the operating system). When the amount of free + memory at the top of the heap exceeds the trim threshold, + <c>malloc</c> releases it (by calling <c>sbrk</c>). + Trim threshold is specified in kilobytes. + Defaults to <c>128</c>.</p> + <note> + <p>This flag has effect only when the emulator is linked with + the GNU C library, and uses its <c>malloc</c> implementation.</p> + </note> + </item> + <tag><marker id="MYtp"/><c><![CDATA[+MYtp <size>]]></c></tag> + <item> + <p>Top pad size (in kilobytes). This is the amount of extra + memory that is allocated by <c>malloc</c> when + <c>sbrk</c> is called to get more memory from the operating + system. Defaults to <c>0</c>.</p> + <note> + <p>This flag has effect only when the emulator is linked with + the GNU C library, and uses its <c>malloc</c> implementation.</p> + </note> + </item> + </taglist> + </section> + + <section> + <title>Flags for Configuration of Allocators Based on alloc_util</title> + <p>If <c>u</c> is used as subsystem identifier (that is, + <c><![CDATA[<S> = u]]></c>), all allocators based on + <c>alloc_util</c> are effected. If <c>B</c>, <c>D</c>, <c>E</c>, + <c>F</c>, <c>H</c>, <c>L</c>, <c>R</c>, <c>S</c>, or <c>T</c> is used + as subsystem identifier, only the specific allocator identifier is + effected.</p> + + <taglist> + <tag><marker id="M_acul"/><c><![CDATA[+M<S>acul <utilization>|de]]></c> + </tag> + <item> + <p>Abandon carrier utilization limit. A valid + <c><![CDATA[<utilization>]]></c> is an integer in the range + <c>[0, 100]</c> representing utilization in percent. When a + utilization value > 0 is used, allocator instances + are allowed to abandon multiblock carriers. If <c>de</c> (default + enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>, + a recomended non-zero utilization value is used. The value + chosen depends on the allocator type and can be changed between + ERTS versions. Defaults to <c>de</c>, but this + can be changed in the future.</p> + <p>Carriers are abandoned when + memory utilization in the allocator instance falls below the + utilization value used. Once a carrier is abandoned, no new + allocations are made in it. When an allocator instance gets an + increased multiblock carrier need, it first tries to fetch an + abandoned carrier from an allocator instance of the same + allocator type. If no abandoned carrier can be fetched, it + creates a new empty carrier. When an abandoned carrier has been + fetched, it will function as an ordinary carrier. This feature has + special requirements on the + <seealso marker="#M_as">allocation strategy</seealso> used. Only + the strategies <c>aoff</c>, <c>aoffcbf</c>, and <c>aoffcaobf</c> + support abandoned carriers.</p> + <p>This feature also requires + <seealso marker="#M_t">multiple thread specific instances</seealso> + to be enabled. When enabling this feature, multiple thread-specific + instances are enabled if not already enabled, and the + <c>aoffcbf</c> strategy is enabled if the current strategy does not + support abandoned carriers. This feature can be enabled on all + allocators based on the <c>alloc_util</c> framework, except + <c>temp_alloc</c> (which would be pointless).</p> + </item> + <tag><marker id="M_as"/> + <c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag> + <item> + <p>Allocation strategy. The following strategies are valid:</p> + <list type="bulleted"> + <item><c>bf</c> (best fit)</item> + <item><c>aobf</c> (address order best fit)</item> + <item><c>aoff</c> (address order first fit)</item> + <item><c>aoffcbf</c> (address order first fit carrier best fit) + </item> + <item><c>aoffcaobf</c> (address order first fit carrier address + order best fit)</item> + <item><c>gf</c> (good fit)</item> + <item><c>af</c> (a fit)</item> + </list> + <p>See the description of allocation strategies in section + <seealso marker="#strategy">The alloc_util Framework</seealso>.</p> + </item> + <tag><marker id="M_asbcst"/><c><![CDATA[+M<S>asbcst <size>]]></c></tag> + <item> + <p>Absolute singleblock carrier shrink threshold (in + kilobytes). When a block located in an + <c>mseg_alloc</c> singleblock carrier is shrunk, the carrier + is left unchanged if the amount of unused memory is less + than this threshold, otherwise the carrier is shrunk. + See also <seealso marker="#M_rsbcst"><c>rsbcst</c></seealso>.</p> + </item> + <tag><marker id="M_e"/><c><![CDATA[+M<S>e true|false]]></c></tag> + <item> + <p>Enables allocator <c><![CDATA[<S>]]></c>.</p> + </item> + <tag><marker id="M_lmbcs"/><c><![CDATA[+M<S>lmbcs <size>]]></c></tag> + <item> + <p>Largest (<c>mseg_alloc</c>) multiblock carrier size (in kilobytes). + See the description on how sizes for <c>mseg_alloc</c> multiblock + carriers are decided in section + <seealso marker="#mseg_mbc_sizes"> + The alloc_util Framework</seealso>. On + 32-bit Unix style OS this limit cannot be set > 128 MB.</p> + </item> + <tag><marker id="M_mbcgs"/><c><![CDATA[+M<S>mbcgs <ratio>]]></c></tag> + <item> + <p>(<c>mseg_alloc</c>) multiblock carrier growth stages. + See the description on how sizes for <c>mseg_alloc</c> multiblock + carriers are decided in section + <seealso marker="#mseg_mbc_sizes"> + The alloc_util Framework</seealso>.</p> + </item> + <tag><marker id="M_mbsd"/><c><![CDATA[+M<S>mbsd <depth>]]></c></tag> + <item> + <p>Maximum block search depth. This flag has effect only if the + good fit strategy is selected for allocator + <c><![CDATA[<S>]]></c>. When the good fit strategy is used, free + blocks are placed in segregated free-lists. Each free-list + contains blocks of sizes in a specific range. The maxiumum block + search depth sets a limit on the maximum number of blocks to + inspect in a free-list during a search for suitable block + satisfying the request.</p> + </item> + <tag><marker id="M_mmbcs"/><c><![CDATA[+M<S>mmbcs <size>]]></c></tag> + <item> + <p>Main multiblock carrier size. Sets the size of the main + multiblock carrier for allocator <c><![CDATA[<S>]]></c>. The main + multiblock carrier is allocated through <c><![CDATA[sys_alloc]]></c> + and is never deallocated.</p> + </item> + <tag><marker id="M_mmmbc"/><c><![CDATA[+M<S>mmmbc <amount>]]></c></tag> + <item> + <p>Maximum <c>mseg_alloc</c> multiblock carriers. Maximum number of + multiblock carriers allocated through <c>mseg_alloc</c> by + allocator <c><![CDATA[<S>]]></c>. When this limit is reached, + new multiblock carriers are allocated through + <c>sys_alloc</c>.</p> + </item> + <tag><marker id="M_mmsbc"/><c><![CDATA[+M<S>mmsbc <amount>]]></c></tag> + <item> + <p>Maximum <c>mseg_alloc</c> singleblock carriers. Maximum number of + singleblock carriers allocated through <c>mseg_alloc</c> by + allocator <c><![CDATA[<S>]]></c>. When this limit is reached, + new singleblock carriers are allocated through + <c>sys_alloc</c>.</p> + </item> + <tag><marker id="M_ramv"/><c><![CDATA[+M<S>ramv <bool>]]></c></tag> + <item> + <p>Realloc always moves. When enabled, reallocate operations are + more or less translated into an allocate, copy, free sequence. + This often reduces memory fragmentation, but costs performance.</p> + </item> + <tag><marker id="M_rmbcmt"/><c><![CDATA[+M<S>rmbcmt <ratio>]]></c></tag> + <item> + <p>Relative multiblock carrier move threshold (in percent). When + a block located in a multiblock carrier is shrunk, + the block is moved if the ratio of the size of the returned + memory compared to the previous size is more than this threshold, + otherwise the block is shrunk at the current location.</p> + </item> + <tag><marker id="M_rsbcmt"/><c><![CDATA[+M<S>rsbcmt <ratio>]]></c></tag> + <item> + <p>Relative singleblock carrier move threshold (in percent). When + a block located in a singleblock carrier is shrunk to + a size smaller than the value of parameter + <seealso marker="#M_sbct"><c>sbct</c></seealso>, + the block is left unchanged in the singleblock carrier if + the ratio of unused memory is less than this threshold, + otherwise it is moved into a multiblock carrier.</p> + </item> + <tag><marker id="M_rsbcst"/><c><![CDATA[+M<S>rsbcst <ratio>]]></c></tag> + <item> + <p>Relative singleblock carrier shrink threshold (in + percent). When a block located in an <c>mseg_alloc</c> + singleblock carrier is shrunk, the carrier is left + unchanged if the ratio of unused memory is less than this + threshold, otherwise the carrier is shrunk. + See also <seealso marker="#M_asbcst"><c>asbcst</c></seealso>.</p> + </item> + <tag><marker id="M_sbct"/><c><![CDATA[+M<S>sbct <size>]]></c></tag> + <item> + <p>Singleblock carrier threshold. Blocks larger than this + threshold are placed in singleblock carriers. Blocks + smaller than this threshold are placed in multiblock + carriers. On 32-bit Unix style OS this threshold cannot be set + > 8 MB.</p> + </item> + <tag><marker id="M_smbcs"/><c><![CDATA[+M<S>smbcs <size>]]></c></tag> + <item> + <p>Smallest (<c>mseg_alloc</c>) multiblock carrier size (in + kilobytes). See the description on how sizes for <c>mseg_alloc</c> + multiblock carriers are decided in section + <seealso marker="#mseg_mbc_sizes"> + The alloc_util Framework</seealso>.</p> + </item> + <tag><marker id="M_t"/><c><![CDATA[+M<S>t true|false]]></c></tag> + <item> + <p>Multiple, thread-specific instances of the allocator. + This option has only effect on the runtime system + with SMP support. Default behavior on the runtime system with + SMP support is <c>NoSchedulers+1</c> instances. Each scheduler + uses a lock-free instance of its own and other threads use + a common instance.</p> + <p>Before ERTS 5.9 it was possible to configure + a smaller number of thread-specific instances than schedulers. + This is, however, not possible anymore.</p> + </item> + </taglist> + </section> + + <section> + <title>Flags for Configuration of alloc_util</title> + <p>All allocators based on <c>alloc_util</c> are effected.</p> + + <taglist> + <tag><marker id="Muycs"/><c><![CDATA[+Muycs <size>]]></c></tag> + <item> + <p><c>sys_alloc</c> carrier size. Carriers allocated through + <c>sys_alloc</c> are allocated in sizes that are + multiples of the <c>sys_alloc</c> carrier size. This is not + true for main multiblock carriers and carriers allocated + during a memory shortage, though.</p> + </item> + <tag><marker id="Mummc"/><c><![CDATA[+Mummc <amount>]]></c></tag> + <item> + <p>Maximum <c>mseg_alloc</c> carriers. Maximum number of carriers + placed in separate memory segments. When this limit is + reached, new carriers are placed in memory retrieved from + <c>sys_alloc</c>.</p> + </item> + <tag><marker id="Musac"/><c><![CDATA[+Musac <bool>]]></c></tag> + <item> + <p>Allow <c>sys_alloc</c> carriers. Defaults to <c>true</c>. + If set to <c>false</c>, <c>sys_alloc</c> carriers are never + created by allocators using the <c>alloc_util</c> framework.</p> + </item> + </taglist> + </section> + + <section> + <title>Special Flag for literal_alloc</title> <taglist> - <tag><c>min</c></tag> + <tag><marker id="MIscs"/><c><![CDATA[+MIscs <size in MB>]]></c></tag> <item> - Disables all allocators that can be disabled. - </item> + <p><c>literal_alloc</c> super carrier size (in MB). The amount of + <em>virtual</em> address space reserved for literal terms in + Erlang code on 64-bit architectures. Defaults to <c>1024</c> + (that is, 1 GB), which is usually sufficient. + The flag is ignored on 32-bit architectures.</p> + </item> + </taglist> + </section> - <tag><c>max</c></tag> + <section> + <title>Special Flag for exec_alloc</title> + <taglist> + <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag> <item> - Enables all allocators (currently default). - </item> + <p><c>exec_alloc</c> super carrier size (in MB). The amount of + <em>virtual</em> address space reserved for native executable code + used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> application + on specific architectures (x86_64). Defaults to <c>512</c>.</p> + </item> + </taglist> + </section> - <tag><c>r9c|r10b|r11b</c></tag> + <section> + <title>Instrumentation Flags</title> + <taglist> + <tag><marker id="Mim"/><c>+Mim true|false</c></tag> <item> - Configures all allocators as they were configured in respective - OTP release. These will eventually be removed. - </item> + <p>A map over current allocations is kept by the emulator. + The allocation map can be retrieved through module + <seealso marker="tools:instrument"> + <c>instrument(3)</c></seealso>. <c>+Mim true</c> + implies <c>+Mis true</c>. <c>+Mim true</c> is the same as flag + <seealso marker="erl#instr"><c>-instr</c></seealso> in + <c>erl(1)</c>.</p> + </item> + <tag><marker id="Mis"/><c>+Mis true|false</c></tag> + <item> + <p>Status over allocated memory is kept by the emulator. + The allocation status can be retrieved through module + <seealso marker="tools:instrument"> + <c>instrument(3)</c></seealso>.</p> + </item> + <tag><marker id="Mit"/><c>+Mit X</c></tag> + <item> + <p>Reserved for future use. Do <em>not</em> use this flag.</p> + </item> + </taglist> - <tag><c>config</c></tag> + <note> + <p>When instrumentation of the emulator is enabled, the emulator + uses more memory and runs slower.</p> + </note> + </section> + + <section> + <title>Other Flags</title> + <taglist> + <tag><marker id="Mea"/><c>+Mea min|max|r9c|r10b|r11b|config</c></tag> + <item> + <p>Options:</p> + <taglist> + <tag><c>min</c></tag> + <item> + <p>Disables all allocators that can be disabled.</p> + </item> + <tag><c>max</c></tag> + <item> + <p>Enables all allocators (default).</p> + </item> + <tag><c>r9c|r10b|r11b</c></tag> + <item> + <p>Configures all allocators as they were configured in respective + Erlang/OTP release. These will eventually be removed.</p> + </item> + <tag><c>config</c></tag> + <item> + <p>Disables features that cannot be enabled while creating an + allocator configuration with + <seealso marker="runtime_tools:erts_alloc_config"> + <c>erts_alloc_config(3)</c></seealso>.</p> + <note> + <p>This option is to be used only while running + <c>erts_alloc_config(3)</c>, <em>not</em> when + using the created configuration.</p> + </note> + </item> + </taglist> + </item> + <tag><marker id="Mlpm"/><c>+Mlpm all|no</c></tag> <item> - Disables features that cannot be enabled while creating an - allocator configuration with - <seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>. - Note, this option should only be used while running - <c>erts_alloc_config</c>, <em>not</em> when using the created - configuration. + <p>Lock physical memory. Defaults to <c>no</c>, that is, + no physical memory is locked. If set to <c>all</c>, all + memory mappings made by the runtime system are locked into + physical memory. If set to <c>all</c>, the runtime system fails to + start if this feature is not supported, the user has not got enough + privileges, or the user is not allowed to lock enough physical + memory. The runtime system also fails with an out of memory + condition if the user limit on the amount of locked memory is + reached.</p> </item> </taglist> - </item> - <tag><marker id="Mlpm"/><c>+Mlpm all|no</c></tag> - <item>Lock physical memory. The default value is <c>no</c>, i.e., - no physical memory will be locked. If set to <c>all</c>, all - memory mappings made by the runtime system, will be locked into - physical memory. If set to <c>all</c>, the runtime system will fail - to start if this feature is not supported, the user has not got enough - privileges, or the user is not allowed to lock enough physical memory. - The runtime system will also fail with an out of memory condition - if the user limit on the amount of locked memory is reached. - </item> - </taglist> - <p>Only some default values have been presented - here. - <seealso marker="erts:erlang#system_info_allocator">erlang:system_info(allocator)</seealso>, - and - <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, Alloc})</seealso> - can be used in order to obtain currently used settings and current - status of the allocators.</p> + </section> + </section> + + <section> + <title>Notes</title> + <p>Only some default values have been presented here. For information + about the currently used settings and the current status of the + allocators, see + <seealso marker="erts:erlang#system_info_allocator"> + <c>erlang:system_info(allocator)</c></seealso> and + <seealso marker="erts:erlang#system_info_allocator_tuple"> + <c>erlang:system_info({allocator, Alloc})</c></seealso>.</p> + <note> - <p>Most of these flags are highly implementation dependent, and they - may be changed or removed without prior notice.</p> + <p>Most of these flags are highly implementation-dependent and + can be changed or removed without prior notice.</p> <p><c>erts_alloc</c> is not obliged to strictly use the settings that - have been passed to it (it may even ignore them).</p> + have been passed to it (it can even ignore them).</p> </note> - <p><seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso> - is a tool that can be used to aid creation of an + + <p>The <seealso marker="runtime_tools:erts_alloc_config"> + <c>erts_alloc_config(3)</c></seealso> + tool can be used to aid creation of an <c>erts_alloc</c> configuration that is suitable for a limited number of runtime scenarios.</p> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>, - <seealso marker="erl">erl(1)</seealso>, - <seealso marker="tools:instrument">instrument(3)</seealso>, - <seealso marker="erts:erlang">erlang(3)</seealso></p> + <title>See Also</title> + <p><seealso marker="erl"><c>erl(1)</c></seealso>, + <seealso marker="erlang"><c>erlang(3)</c></seealso>, + <seealso marker="runtime_tools:erts_alloc_config"> + <c>erts_alloc_config(3)</c></seealso>, + <seealso marker="tools:instrument"> + <c>instrument(3)</c></seealso></p> </section> </cref> diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index f12f76890c..1d5d280338 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -4,7 +4,7 @@ <comref> <header> <copyright> - <year>2007</year><year>2015</year> + <year>2007</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,17 +33,18 @@ <comsummary>Erlang scripting support</comsummary> <description> <p><c>escript</c> provides support for running short Erlang programs - without having to compile them first and an easy way to retrieve the - command line arguments.</p> + without having to compile them first, and an easy way to retrieve the + command-line arguments.</p> </description> + <funcs> <func> <name>script-name script-arg1 script-arg2...</name> <name>escript escript-flags script-name script-arg1 script-arg2...</name> - <fsummary>Run a script written in Erlang</fsummary> + <fsummary>Run a script written in Erlang.</fsummary> <desc> <p><c>escript</c> runs a script written in Erlang.</p> - <p>Here follows an example.</p> + <p>Example:</p> <pre> $ <input>chmod u+x factorial</input> $ <input>cat factorial</input> @@ -73,195 +74,183 @@ factorial 5 = 120 $ <input>./factorial</input> usage: factorial integer $ <input>./factorial five</input> -usage: factorial integer - </pre> +usage: factorial integer</pre> <p>The header of the Erlang script in the example differs from - a normal Erlang module. The first line is intended to be the - interpreter line, which invokes <c>escript</c>. However if you - invoke the <c>escript</c> like this</p> + a normal Erlang module. The first line is intended to be the + interpreter line, which invokes <c>escript</c>.</p> + <p>However, if you invoke the <c>escript</c> as follows, + the contents of the first line does not matter, but it + cannot contain Erlang code as it will be ignored:</p> <pre> -$ <input>escript factorial 5</input> </pre> - <p>the contents of the first line does not matter, but it - cannot contain Erlang code as it will be ignored.</p> - <p>The second line in the example, contains an optional - directive to the <c>Emacs</c> editor which causes it to +$ <input>escript factorial 5</input></pre> + <p>The second line in the example contains an optional + directive to the <c>Emacs</c> editor, which causes it to enter the major mode for editing Erlang source files. If the - directive is present it must be located on the second + directive is present, it must be located on the second line.</p> - - <p>If there is a comment selecting the <seealso - marker="stdlib:epp#encoding">encoding</seealso> it can be + <p>If a comment selecting the <seealso + marker="stdlib:epp#encoding">encoding</seealso> exists, it can be located on the second line.</p> - - <note><p> - The encoding specified by the above mentioned comment - applies to the script itself. The encoding of the - I/O-server, however, has to be set explicitly like this:</p> -<code>io:setopts([{encoding, unicode}])</code> + <note> + <p>The encoding specified by the above mentioned comment + applies to the script itself. The encoding of the + I/O-server, however, must be set explicitly as follows:</p> + <code> +io:setopts([{encoding, unicode}])</code> <p>The default encoding of the I/O-server for <c>standard_io</c> - is <c>latin1</c> - since the script runs in a non-interactive terminal - (see <seealso marker="stdlib:unicode_usage#unicode_options_summary"> - Using Unicode in Erlang</seealso>). - </p></note> - - <p>On the third line (or second line depending on the presence - of the Emacs directive), it is possible to give arguments to - the emulator, such as </p> + is <c>latin1</c>, as the script runs in a non-interactive terminal + (see section + <seealso marker="stdlib:unicode_usage#unicode_options_summary"> + Summary of Options</seealso>) in the STDLIB User's Guide.</p> + </note> + <p>On the third line (or second line depending on the presence + of the Emacs directive), arguments can be specified to + the emulator, for example:</p> <pre> %%! -smp enable -sname factorial -mnesia debug verbose</pre> <p>Such an argument line must start with <c>%%!</c> and the - rest of the line will interpreted as arguments to the emulator.</p> + remaining line is interpreted as arguments to the emulator.</p> <p>If you know the location of the <c>escript</c> executable, the first - line can directly give the path to <c>escript</c>. For instance:</p> + line can directly give the path to <c>escript</c>, for example:</p> <pre> -#!/usr/local/bin/escript </pre> - <p>As any other kind of scripts, Erlang scripts will not work on +#!/usr/local/bin/escript</pre> + <p>As any other type of scripts, Erlang scripts do not work on Unix platforms if the execution bit for the script file is not set. - (Use <c>chmod +x script-name</c> to turn on the execution bit.) - </p> - - <p>The rest of the Erlang script file may either contain - Erlang <c>source code</c>, an <c>inlined beam file</c> or an - <c>inlined archive file</c>.</p> - - <p>An Erlang script file must always contain the function - <em>main/1</em>. When the script is run, the - <c>main/1</c> function will be called with a list - of strings representing the arguments given to the script (not - changed or interpreted in any way).</p> - + (To turn on the execution bit, use <c>chmod +x script-name</c>.)</p> + <p>The remaining Erlang script file can either contain + Erlang <em>source code</em>, an <em>inlined beam file</em>, or an + <em>inlined archive file</em>.</p> + <p>An Erlang script file must always contain the <c>main/1</c> + function. When the script is run, the + <c>main/1</c> function is called with a list + of strings representing the arguments specified to the script (not + changed or interpreted in any way).</p> <p>If the <c>main/1</c> function in the script returns successfully, - the exit status for the script will be 0. If an exception is generated - during execution, a short message will be printed and the script terminated - with exit status 127.</p> - - <p>To return your own non-zero exit code, call <c>halt(ExitCode)</c>; - for instance:</p> + the exit status for the script is <c>0</c>. If an exception is + generated during execution, a short message is printed and the script + terminates with exit status <c>127</c>.</p> + <p>To return your own non-zero exit code, call <c>halt(ExitCode)</c>, + for example:</p> <pre> halt(1).</pre> - - <p>Call <seealso marker="#script_name_0">escript:script_name()</seealso> - from your to script to retrieve the pathname of the script - (the pathname is usually, but not always, absolute).</p> - + <p>To retrieve the pathname of the script, call + <seealso marker="#script_name_0"> + <c>escript:script_name()</c></seealso> from your script + (the pathname is usually, but not always, absolute).</p> <p>If the file contains source code (as in the example above), - it will be processed by the preprocessor <c>epp</c>. This - means that you for example may use pre-defined macros (such as - <c>?MODULE</c>) as well as include directives like - the <c>-include_lib</c> directive. For instance, use</p> + it is processed by the + <seealso marker="stdlib:epp"><c>epp</c></seealso> preprocessor. + This means that you, for example, can use predefined macros + (such as <c>?MODULE</c>) and include directives like + the <c>-include_lib</c> directive. For example, use</p> <pre> -include_lib("kernel/include/file.hrl").</pre> - <p>to include the record definitions for the records used by the - <c>file:read_link_info/1</c> function. You can also select - encoding by including a encoding comment here, but if there - is a valid encoding comment on the second line it takes + <p>to include the record definitions for the records used by function + <seealso marker="kernel:file#read_link_info/1"> + <c>file:read_link_info/1</c></seealso>. You can also select + encoding by including an encoding comment here, but if + a valid encoding comment exists on the second line, it takes precedence.</p> - - <p>The script will be checked for syntactic and semantic - correctness before being run. If there are warnings (such as - unused variables), they will be printed and the script will - still be run. If there are errors, they will be printed and - the script will not be run and its exit status will be - 127.</p> - + <p>The script is checked for syntactic and semantic + correctness before it is run. If there are warnings (such as + unused variables), they are printed and the script will + still be run. If there are errors, they are printed and + the script will not be run and its exit status is + <c>127</c>.</p> <p>Both the module declaration and the export declaration of the <c>main/1</c> function are optional.</p> - <p>By default, the script will be interpreted. You can force it to be compiled by including the following line somewhere - in the script file:</p><pre> + in the script file:</p> + <pre> -mode(compile).</pre> - <p>Execution of interpreted code is slower than compiled code. - If much of the execution takes place in interpreted code it - may be worthwhile to compile it, even though the compilation - itself will take a little while. It is also possible to supply - <c>native</c> instead of <c>compile</c>, this will compile the script - using the native flag, again depending on the characteristics - of the escript this could or could not be worth while.</p> - - <p>As mentioned earlier, it is possible to have a script which + If much of the execution takes place in interpreted code, it + can be worthwhile to compile it, although the compilation + itself takes a little while. Also, <c>native</c> can be supplied + instead of <c>compile</c>. This compiles the script + using the native flag and may or may not be worthwhile + depending on the escript characteristics.</p> + <p>As mentioned earlier, a script can contains precompiled <c>beam</c> code. In a precompiled - script, the interpretation of the script header is exactly - the same as in a script containing source code. That means + script, the interpretation of the script header is + the same as in a script containing source code. This means that you can make a <c>beam</c> file executable by prepending the file with the lines starting with <c>#!</c> and <c>%%!</c> mentioned above. In a precompiled script, the - function - <c>main/1</c> must be exported.</p> - - <p>As yet another option it is possible to have an entire - Erlang archive in the script. In a archive script, the - interpretation of the script header is exactly the same as - in a script containing source code. That means that you can + <c>main/1</c> function must be exported.</p> + <p>Another option is to have an entire + Erlang archive in the script. In an archive script, the + interpretation of the script header is the same as + in a script containing source code. This means that you can make an archive file executable by prepending the file with the lines starting with <c>#!</c> and <c>%%!</c> mentioned - above. In an archive script, the function <c>main/1</c> must + above. In an archive script, the <c>main/1</c> function must be exported. By default the <c>main/1</c> function in the module with the same name as the basename of the - <c>escript</c> file will be invoked. This behavior can be - overridden by setting the flag <c>-escript main Module</c> - as one of the emulator flags. The <c>Module</c> must be the - name of a module which has an exported <c>main/1</c> - function. See <seealso marker="kernel:code">code(3)</seealso> - for more information about archives and code loading.</p> - - <p>In many cases it is very convenient to have a header in - the escript, especially on Unix platforms. But the header is - in fact optional. This means that you directly can "execute" - an Erlang module, beam file or archive file without adding - any header to them. But then you have to invoke the script - like this:</p> - <pre> + <c>escript</c> file is invoked. This behavior can be + overridden by setting flag <c>-escript main Module</c> + as one of the emulator flags. <c>Module</c> must be the + name of a module that has an exported <c>main/1</c> + function. For more information about archives and code loading, see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> + <p>It is often very convenient to have a header in + the escript, especially on Unix platforms. However, the header + is optional, so you directly can "execute" + an Erlang module, Beam file, or archive file without adding + any header to them. But then you have to invoke the script + as follows:</p> + <pre> $ <input>escript factorial.erl 5</input> factorial 5 = 120 $ <input>escript factorial.beam 5</input> factorial 5 = 120 $ <input>escript factorial.zip 5</input> -factorial 5 = 120 -</pre> +factorial 5 = 120</pre> </desc> </func> + <func> - <name>escript:create(FileOrBin, Sections) -> ok | {ok, binary()} | {error, term()}</name> - <fsummary>Create an escript</fsummary> + <name>escript:create(FileOrBin, Sections) -> ok | {ok, binary()} | + {error, term()}</name> + <fsummary>Create an escript.</fsummary> <type> <v>FileOrBin = filename() | 'binary'</v> <v>Sections = [Header] Body | Body</v> <v>Header = shebang | {shebang, Shebang} - | comment | {comment, Comment} - | {emu_args, EmuArgs}</v> + | comment | {comment, Comment} + | {emu_args, EmuArgs}</v> <v>Shebang = string() | 'default' | 'undefined'</v> <v>Comment = string() | 'default' | 'undefined'</v> <v>EmuArgs = string() | 'undefined'</v> - <v>Body = {source, SourceCode} - | {beam, BeamCode} - | {archive, ZipArchive} - | {archive, ZipFiles, ZipOptions}</v> + <v>Body = {source, SourceCode} | {beam, BeamCode} + | {archive, ZipArchive} + | {archive, ZipFiles, ZipOptions}</v> <v>SourceCode = BeamCode = file:filename() | binary()</v> - <v>ZipArchive = <seealso marker="stdlib:zip#type-filename">zip:filename()</seealso> | binary()</v> + <v>ZipArchive = <seealso marker="stdlib:zip#type-filename"> + zip:filename()</seealso> | binary()</v> <v>ZipFiles = [ZipFile]</v> - <v>ZipFile = file:filename() | {file:filename(), binary()} | {file:filename(), binary(), file:file_info()}</v> - <v>ZipOptions = [<seealso marker="stdlib:zip#type-create_option">zip:create_option()</seealso>]</v> + <v>ZipFile = file:filename() + | {file:filename(), binary()} + | {file:filename(), binary(), file:file_info()}</v> + <v>ZipOptions = [<seealso marker="stdlib:zip#type-create_option"> + zip:create_option()</seealso>]</v> </type> <desc> - <p>The <marker id="create_2"></marker> <c>create/2</c> - function creates an escript from a list of sections. The - sections can be given in any order. An escript begins with an - optional <c>Header</c> followed by a mandatory <c>Body</c>. If - the header is present, it does always begin with a - <c>shebang</c>, possibly followed by a <c>comment</c> and - <c>emu_args</c>. The <c>shebang</c> defaults to - <c>"/usr/bin/env escript"</c>. The comment defaults to - <c>"This is an -*- erlang -*- file"</c>. The created escript - can either be returned as a binary or written to file.</p> - - <p>As an example of how the function can be used, we create an - interpreted escript which uses <c>emu_args</c> to set some emulator - flag. In this case it happens to disable the smp_support. We - do also extract the different sections from the newly created - script:</p> + <p><marker id="create_2"></marker> + Creates an escript from a list of sections. The + sections can be specified in any order. An escript begins with an + optional <c>Header</c> followed by a mandatory <c>Body</c>. If + the header is present, it does always begin with a + <c>shebang</c>, possibly followed by a <c>comment</c> and + <c>emu_args</c>. The <c>shebang</c> defaults to + <c>"/usr/bin/env escript"</c>. The <c>comment</c> defaults to + <c>"This is an -*- erlang -*- file"</c>. The created escript + can either be returned as a binary or written to file.</p> + <p>As an example of how the function can be used, we create an + interpreted escript that uses <c>emu_args</c> to set some emulator + flag. In this case, it happens to disable the <c>smp_support</c>. We + also extract the different sections from the newly created script:</p> <pre> > <input>Source = "%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_support)).\n".</input> "%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_support)).\n" @@ -280,11 +269,9 @@ ok "false" > <input>escript:extract("demo.escript", []).</input> {ok,[{shebang,default}, {comment,default}, {emu_args,"-smp disable"}, - {source,<<"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_su"...>>}]} - </pre> - - <p>An escript without header can be created like this:</p> -<pre> + {source,<<"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_su"...>>}]}</pre> + <p>An escript without header can be created as follows:</p> + <pre> > <input>file:write_file("demo.erl", ["%% demo.erl\n-module(demo).\n-export([main/1]).\n\n", Source]).</input> ok @@ -299,14 +286,12 @@ ok {beam,<<70,79,82,49,0,0,3,68,66,69,65,77,65,116, 111,109,0,0,0,83,0,0,0,9,...>>}]} > <input>os:cmd("escript demo.beam").</input> -"true" -</pre> - <p>Here we create an archive script containing both Erlang - code as well as beam code. Then we iterate over all files in - the archive and collect their contents and some info about - them. - </p> -<pre> +"true"</pre> + <p>Here we create an archive script containing both Erlang + code and Beam code, then we iterate over all files in + the archive and collect their contents and some information about + them:</p> + <pre> > <input>{ok, SourceCode} = file:read_file("demo.erl").</input> {ok,<<"%% demo.erl\n-module(demo).\n-export([main/1]).\n\n%% Demo\nmain(_Arg"...>>} > <input>escript:create("demo.escript", @@ -339,43 +324,43 @@ ok <<"%% demo.erl\n-module(demo).\n-export([main/1]).\n\n%% Demo\nmain(_Arg"...>>}]}</pre> </desc> </func> + <func> - <name>escript:extract(File, Options) -> {ok, Sections} | {error, term()}</name> - <fsummary>Parses an escript and extracts its sections</fsummary> + <name>escript:extract(File, Options) -> {ok, Sections} | + {error, term()}</name> + <fsummary>Parse an escript and extract its sections.</fsummary> <type> <v>File = filename()</v> <v>Options = [] | [compile_source]</v> <v>Sections = Headers Body</v> <v>Headers = {shebang, Shebang} - {comment, Comment} - {emu_args, EmuArgs}</v> + {comment, Comment} + {emu_args, EmuArgs}</v> <v>Shebang = string() | 'default' | 'undefined'</v> <v>Comment = string() | 'default' | 'undefined'</v> <v>EmuArgs = string() | 'undefined'</v> <v>Body = {source, SourceCode} - | {source, BeamCode} - | {beam, BeamCode} - | {archive, ZipArchive}</v> + | {source, BeamCode} + | {beam, BeamCode} + | {archive, ZipArchive}</v> <v>SourceCode = BeamCode = ZipArchive = binary()</v> </type> <desc> - <p>The <marker id="extract_2"></marker> <c>extract/2</c> - function parses an escript and extracts its sections. This is - the reverse of <c>create/2</c>.</p> - - <p>All sections are returned even if they do not exist in the - escript. If a particular section happens to have the same - value as the default value, the extracted value is set to the - atom <c>default</c>. If a section is missing, the extracted - value is set to the atom <c>undefined</c>. </p> - - <p>The <c>compile_source</c> option only affects the result if - the escript contains <c>source</c> code. In that case the - Erlang code is automatically compiled and <c>{source, - BeamCode}</c> is returned instead of <c>{source, - SourceCode}</c>.</p> - - <pre> + <p><marker id="extract_2"></marker> + Parses an escript and extracts its sections. This is the reverse + of <seealso marker="#create_2"><c>create/2</c></seealso>.</p> + <p>All sections are returned even if they do not exist in the + escript. If a particular section happens to have the same + value as the default value, the extracted value is set to the + atom <c>default</c>. If a section is missing, the extracted + value is set to the atom <c>undefined</c>.</p> + <p>Option <c>compile_source</c> only affects the result if + the escript contains <c>source</c> code. In this case the + Erlang code is automatically compiled and <c>{source, + BeamCode}</c> is returned instead of <c>{source, + SourceCode}</c>.</p> + <p>Example:</p> + <pre> > <input>escript:create("demo.escript", [shebang, {archive, [{"demo.erl", SourceCode}, {"demo.beam", BeamCode}], []}]).</input> @@ -385,52 +370,51 @@ ok escript:extract("demo.escript", []).</input> {ok,[{{archive,<<80,75,3,4,20,0,0,0,8,0,118,7,98,60,105, 152,61,93,107,0,0,0,118,0,...>>} - {emu_args,undefined}]} - </pre> + {emu_args,undefined}]}</pre> </desc> </func> + <func> <name>escript:script_name() -> File</name> - <fsummary>Returns the name of an escript</fsummary> + <fsummary>Return the name of an escript.</fsummary> <type> <v>File = filename()</v> </type> <desc> - <p>The <marker id="script_name_0"></marker> - <c>script_name/0</c> function returns the name of the escript - being executed. If the function is invoked outside the context - of an escript, the behavior is undefined.</p> + <p><marker id="script_name_0"></marker> + Returns the name of the escript that is executed. + If the function is invoked outside the context + of an escript, the behavior is undefined.</p> </desc> </func> </funcs> <section> - <title>Options accepted by escript</title> + <title>Options Accepted By escript</title> <taglist> - <tag>-c</tag> - <item>Compile the escript regardless of the value of the mode attribute. + <tag><c>-c</c></tag> + <item>Compiles the escript regardless of the value of the mode attribute. </item> - - <tag>-d</tag> - <item>Debug the escript. Starts the debugger, loads the module - containing the <c>main/1</c> function into the debugger, sets a - breakpoint in <c>main/1</c> and invokes <c>main/1</c>. If the - module is precompiled, it must be explicitly compiled with the - <c>debug_info</c> option. + <tag><c>-d</c></tag> + <item>Debugs the escript. Starts the debugger, loads the module + containing the <c>main/1</c> function into the debugger, sets a + breakpoint in <c>main/1</c>, and invokes <c>main/1</c>. If the + module is precompiled, it must be explicitly compiled with option + <c>debug_info</c>. </item> - - <tag>-i</tag> - <item>Interpret the escript regardless of the value of the mode attribute. + <tag><c>-i</c></tag> + <item>Interprets the escript regardless of the value of the mode + attribute. + </item> + <tag><c>-s</c></tag> + <item>Performs a syntactic and semantic check of the script file. + Warnings and errors (if any) are written to the standard output, but + the script will not be run. The exit status is <c>0</c> if any errors + are found, otherwise <c>127</c>. + </item> + <tag><c>-n</c></tag> + <item>Compiles the escript using flag <c>+native</c>. </item> - - <tag>-s</tag> - <item>Only perform a syntactic and semantic check of the script file. - Warnings and errors (if any) are written to the standard output, but - the script will not be run. The exit status will be 0 if there were - no errors, and 127 otherwise.</item> - - <tag>-n</tag> - <item>Compile the escript using the +native flag.</item> </taglist> </section> </comref> diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml index 027fe600d7..0cfcc7905d 100644 --- a/erts/doc/src/inet_cfg.xml +++ b/erts/doc/src/inet_cfg.xml @@ -22,7 +22,7 @@ </legalnotice> - <title>Inet configuration</title> + <title>Inet Configuration</title> <prepared>Peter Andersson</prepared> <docno></docno> <date>2004-03-02</date> @@ -32,374 +32,341 @@ <section> <title>Introduction</title> - <p>This chapter tells you how the Erlang runtime system is configured - for IP communication. It also explains how you may configure it - for your own particular needs by means of a configuration file. - The information here is mainly intended for users with special - configuration needs or problems. There should normally be no need - for specific settings for Erlang to function properly on a correctly - IP configured platform. </p> - <p>When Erlang starts up it will read the kernel variable - <c><![CDATA[inetrc]]></c> which, if defined, should specify the location and - name of a user configuration file. Example:</p> - <p><c><![CDATA[% erl -kernel inetrc '"./cfg_files/erl_inetrc"']]></c></p> - <p>Note that the usage of a <c><![CDATA[.inetrc]]></c> file, which was - supported in earlier Erlang versions, is now obsolete.</p> - <p>A second way to specify the configuration file is to set the - environment variable <c><![CDATA[ERL_INETRC]]></c> to the full name of the file. Example (bash):</p> - <p><c><![CDATA[% export ERL_INETRC=./cfg_files/erl_inetrc]]></c></p> - <p>Note that the kernel variable <c><![CDATA[inetrc]]></c> overrides this environment variable.</p> + <p>This section describes how the Erlang runtime system is configured + for IP communication. It also explains how you can configure it + for your needs by a configuration file. + The information is primarily intended for users with special + configuration needs or problems. There is normally no need + for specific settings for Erlang to function properly on a correctly + IP-configured platform.</p> + + <p>When Erlang starts up it reads the Kernel variable + <c><![CDATA[inetrc]]></c>, which, if defined, is to specify the location + and name of a user configuration file. Example:</p> + + <code type="none"><![CDATA[ +% erl -kernel inetrc '"./cfg_files/erl_inetrc"']]></code> + + <p>Notice that the use of an <c><![CDATA[.inetrc]]></c> file, which was + supported in earlier Erlang/OTP versions, is now obsolete.</p> + + <p>A second way to specify the configuration file is to set + environment variable <c><![CDATA[ERL_INETRC]]></c> to the full name of + the file. Example (bash):</p> + + <code type="none"><![CDATA[ +% export ERL_INETRC=./cfg_files/erl_inetrc]]></code> + + <p>Notice that the Kernel variable <c><![CDATA[inetrc]]></c> + overrides this environment variable.</p> + <p>If no user configuration file is specified and Erlang is started - in non-distributed or short name distributed mode, Erlang will use - default configuration settings and a native lookup method that should - work correctly under most circumstances. Erlang - will not read any information from system inet configuration files - (like /etc/host.conf, /etc/nsswitch.conf, etc) in these modes, - except for /etc/resolv.conf and /etc/hosts that is read and monitored - for changes on Unix platforms for the internal DNS client - <seealso marker="kernel:inet_res">inet_res</seealso>.</p> + in non-distributed or short name distributed mode, Erlang uses + default configuration settings and a native lookup method that + works correctly under most circumstances. Erlang reads no + information from system <c>inet</c> configuration files (such as + <c>/etc/host.conf</c> and <c>/etc/nsswitch.conf</c>) in these modes, + except for <c>/etc/resolv.conf</c> and <c>/etc/hosts</c> that is read and + monitored for changes on Unix platforms for the internal DNS client + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.</p> + <p>If Erlang is started in long name distributed mode, it needs to - get the domain name from somewhere and will read system inet + get the domain name from somewhere and reads system <c>inet</c> configuration files for this information. Any hosts and resolver - information found then is also recorded, but not - used as long as Erlang is configured for native lookups. (The + information found is also recorded, but not + used as long as Erlang is configured for native lookups. The information becomes useful if the lookup method is changed to - <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c>, see below).</p> - <p>Native lookup (system calls) is always the default resolver method. This - is true for all platforms except VxWorks and OSE Delta where <c><![CDATA['file']]></c> - or <c><![CDATA['dns']]></c> is used (in that order of priority).</p> - <p>On Windows platforms, Erlang will search the system registry rather than - look for configuration files when started in long name distributed mode. </p> + <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c>, see below.</p> + + <p>Native lookup (system calls) is always the default resolver method. + This is true for all platforms, except VxWorks and OSE Delta where + <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c> is used (in that + priority order).</p> + + <p>On Windows platforms, Erlang searches the system registry rather than + looks for configuration files when started in long name distributed + mode.</p> </section> <section> <title>Configuration Data</title> <p>Erlang records the following data in a local database if found in system - inet configuration files (or system registry):</p> - <list type="bulleted"> - <item>Host names and addresses</item> + <c>inet</c> configuration files (or system registry):</p> + + <list type="bulleted"> + <item>Hostnames and host addresses</item> <item>Domain name</item> <item>Nameservers</item> <item>Search domains</item> <item>Lookup method</item> </list> - <p>This data may also be specified explicitly in the user - configuration file. The configuration file should contain lines - of configuration parameters (each terminated with a full - stop). Some parameters add data to the configuration (e.g. host + + <p>This data can also be specified explicitly in the user + configuration file. This file is to contain lines + of configuration parameters (each terminated with a full stop). + Some parameters add data to the configuration (such as host and nameserver), others overwrite any previous settings - (e.g. domain and lookup). The user configuration file is always + (such as domain and lookup). The user configuration file is always examined last in the configuration process, making it possible for the user to override any default values or previously made settings. Call <c><![CDATA[inet:get_rc()]]></c> to view the state of the - inet configuration database.</p> - <p>These are the valid configuration parameters:</p> - <p></p> + <c>inet</c> configuration database.</p> + + <p>The valid configuration parameters are as follows:</p> + <taglist> - <tag><em><c><![CDATA[{file, Format, File}.]]></c></em></tag> + <tag><c><![CDATA[{file, Format, File}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Format = atom()]]></c></p> <p><c><![CDATA[File = string()]]></c></p> - <p></p> - <p>Specify a system file that Erlang should read configuration - data from. <c><![CDATA[Format]]></c> tells the parser how the file should be - interpreted: <c><![CDATA[resolv]]></c> (Unix resolv.conf), <c><![CDATA[host_conf_freebsd]]></c> - (FreeBSD host.conf), <c><![CDATA[host_conf_bsdos]]></c> (BSDOS host.conf), - <c><![CDATA[host_conf_linux]]></c> (Linux host.conf), <c><![CDATA[nsswitch_conf]]></c> - (Unix nsswitch.conf) or <c><![CDATA[hosts]]></c> (Unix hosts). <c><![CDATA[File]]></c> should - specify the name of the file with full path.</p> - <p></p> + <p>Specify a system file that Erlang is to read configuration data from. + <c><![CDATA[Format]]></c> tells the parser how the file is to be + interpreted:</p> + <list type="bulleted"> + <item><c><![CDATA[resolv]]></c> (Unix resolv.conf)</item> + <item><c><![CDATA[host_conf_freebsd]]></c> (FreeBSD host.conf)</item> + <item><c><![CDATA[host_conf_bsdos]]></c> (BSDOS host.conf)</item> + <item><c><![CDATA[host_conf_linux]]></c> (Linux host.conf)</item> + <item><c><![CDATA[nsswitch_conf]]></c> (Unix nsswitch.conf)</item> + <item><c><![CDATA[hosts]]></c> (Unix hosts)</item> + </list> + <p><c><![CDATA[File]]></c> is to specify the filename with full + path.</p> </item> - <tag><em><c><![CDATA[{resolv_conf, File}.]]></c></em></tag> + <tag><c><![CDATA[{resolv_conf, File}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[File = string()]]></c></p> - <p></p> - <p>Specify a system file that Erlang should read resolver + <p>Specify a system file that Erlang is to read resolver configuration from for the internal DNS client - <seealso marker="kernel:inet_res">inet_res</seealso>, + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>, and monitor for changes, even if it does not exist. The path must be absolute.</p> - <p>This may override the configuration parameters + <p>This can override the configuration parameters <c><![CDATA[nameserver]]></c> and <c><![CDATA[search]]></c> depending on the contents - of the specified file. They may also change any time in the future + of the specified file. They can also change any time in the future reflecting the file contents.</p> - <p>If the file is specified as an empty string "", - no file is read nor monitored in the future. This emulates - the old behaviour of not configuring the DNS client when + <p>If the file is specified as an empty string <c>""</c>, + no file is read or monitored in the future. This emulates + the old behavior of not configuring the DNS client when the node is started in short name distributed mode.</p> - <p>If this parameter is not specified it defaults to - <c><![CDATA[/etc/resolv.conf]]></c> unless the environment variable - <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set which defines + <p>If this parameter is not specified, it defaults to + <c><![CDATA[/etc/resolv.conf]]></c> unless environment variable + <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set, which defines the directory for this file to some maybe other than <c><![CDATA[/etc]]></c>.</p> - <p></p> </item> - <tag><em><c><![CDATA[{hosts_file, File}.]]></c></em></tag> + <tag><c><![CDATA[{hosts_file, File}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[File = string()]]></c></p> - <p></p> - <p>Specify a system file that Erlang should read resolver - configuration from for the internal hosts file resolver + <p>Specify a system file that Erlang is to read resolver + configuration from for the internal hosts file resolver, and monitor for changes, even if it does not exist. The path must be absolute.</p> <p>These host entries are searched after all added with <c>{file, hosts, File}</c> above or - <c>{host, IP, Aliases}</c> below when the lookup option + <c>{host, IP, Aliases}</c> below when lookup option <c>file</c> is used.</p> - <p>If the file is specified as an empty string "", - no file is read nor monitored in the future. This emulates - the old behaviour of not configuring the DNS client when + <p>If the file is specified as an empty string <c>""</c>, + no file is read or monitored in the future. This emulates + the old behavior of not configuring the DNS client when the node is started in short name distributed mode.</p> - <p>If this parameter is not specified it defaults to - <c><![CDATA[/etc/hosts]]></c> unless the environment variable - <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set which defines + <p>If this parameter is not specified, it defaults to + <c><![CDATA[/etc/hosts]]></c> unless environment variable + <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set, which defines the directory for this file to some maybe other than <c><![CDATA[/etc]]></c>.</p> - <p></p> </item> - <tag><em><c><![CDATA[{registry, Type}.]]></c></em></tag> + <tag><c><![CDATA[{registry, Type}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Type = atom()]]></c></p> - <p></p> - <p>Specify a system registry that Erlang should read configuration - data from. Currently, <c><![CDATA[win32]]></c> is the only valid option.</p> - <p></p> + <p>Specify a system registry that Erlang is to read configuration + data from. <c><![CDATA[win32]]></c> is the only valid option.</p> </item> - <tag><em><c><![CDATA[{host, IP, Aliases}.]]></c></em></tag> + <tag><c><![CDATA[{host, IP, Aliases}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[IP = tuple()]]></c></p> <p><c><![CDATA[Aliases = [string()]]]></c></p> - <p></p> <p>Add host entry to the hosts table.</p> - <p></p> </item> - <tag><em><c><![CDATA[{domain, Domain}.]]></c></em></tag> + <tag><c><![CDATA[{domain, Domain}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Domain = string()]]></c></p> - <p></p> <p>Set domain name.</p> - <p></p> </item> - <tag><em><c><![CDATA[{nameserver, IP [,Port]}.]]></c></em></tag> + <tag><c><![CDATA[{nameserver, IP [,Port]}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[IP = tuple()]]></c></p> <p><c><![CDATA[Port = integer()]]></c></p> - <p></p> - <p>Add address (and port, if other than default) of primary + <p>Add address (and port, if other than default) of the primary nameserver to use for - <seealso marker="kernel:inet_res">inet_res</seealso>.</p> - <p></p> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>. + </p> </item> - <tag><em><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></em></tag> + <tag><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[IP = tuple()]]></c></p> <p><c><![CDATA[Port = integer()]]></c></p> - <p></p> - <p>Add address (and port, if other than default) of secondary + <p>Add address (and port, if other than default) of the secondary nameserver for - <seealso marker="kernel:inet_res">inet_res</seealso>.</p> - <p></p> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>. + </p> </item> - <tag><em><c><![CDATA[{search, Domains}.]]></c></em></tag> + <tag><c><![CDATA[{search, Domains}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Domains = [string()]]]></c></p> - <p></p> <p>Add search domains for - <seealso marker="kernel:inet_res">inet_res</seealso>.</p> - <p></p> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>. + </p> </item> - <tag><em><c><![CDATA[{lookup, Methods}.]]></c></em></tag> + <tag><c><![CDATA[{lookup, Methods}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Methods = [atom()]]]></c></p> - <p></p> - <p>Specify lookup methods and in which order to try them. - The valid methods are: <c><![CDATA[native]]></c> (use system calls), - <c><![CDATA[file]]></c> (use host data retrieved from - system configuration files and/or - the user configuration file) or <c><![CDATA[dns]]></c> - (use the Erlang DNS client - <seealso marker="kernel:inet_res">inet_res</seealso> - for nameserver queries).</p> + <p>Specify lookup methods and in which order to try them. + The valid methods are as follows:</p> + <list type="bulleted"> + <item><c><![CDATA[native]]></c> (use system calls)</item> + <item><c><![CDATA[file]]></c> (use host data retrieved from system + configuration files and/or the user configuration file)</item> + <item><c><![CDATA[dns]]></c> (use the Erlang DNS client + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> + for nameserver queries)</item> + </list> <p>The lookup method <c><![CDATA[string]]></c> tries to - parse the hostname as a IPv4 or IPv6 string and return + parse the hostname as an IPv4 or IPv6 string and return the resulting IP address. It is automatically tried first when <c><![CDATA[native]]></c> is <em>not</em> - in the <c><![CDATA[Methods]]></c> list. To skip it in this case + in the <c><![CDATA[Methods]]></c> list. To skip it in this case, the pseudo lookup method <c><![CDATA[nostring]]></c> can be inserted anywhere in the <c><![CDATA[Methods]]></c> list.</p> - <p></p> </item> - <tag><em><c><![CDATA[{cache_size, Size}.]]></c></em></tag> + <tag><c><![CDATA[{cache_size, Size}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Size = integer()]]></c></p> - <p></p> - <p>Set size of resolver cache. Default is 100 DNS records.</p> - <p></p> + <p>Set the resolver cache size. Defaults to 100 DNS records.</p> </item> - <tag><em><c><![CDATA[{cache_refresh, Time}.]]></c></em></tag> - <item> - <p></p> + <tag><c><![CDATA[{cache_refresh, Time}.]]></c></tag> + <item> <p><c><![CDATA[Time = integer()]]></c></p> - <p></p> - <p>Set how often (in millisec) - the resolver cache for - <seealso marker="kernel:inet_res">inet_res</seealso>. - is refreshed (i.e. expired DNS records are deleted). - Default is 1 h.</p> - <p></p> + <p>Set how often (in milliseconds) the resolver cache for + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> + is refreshed (that is, expired DNS records are deleted). + Defaults to 1 hour.</p> </item> - <tag><em><c><![CDATA[{timeout, Time}.]]></c></em></tag> + <tag><c><![CDATA[{timeout, Time}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Time = integer()]]></c></p> - <p></p> - <p>Set the time to wait until retry (in millisec) for DNS queries + <p>Set the time to wait until retry (in milliseconds) for DNS queries made by - <seealso marker="kernel:inet_res">inet_res</seealso>. - Default is 2 sec.</p> - <p></p> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>. + Defaults to 2 seconds.</p> </item> - <tag><em><c><![CDATA[{retry, N}.]]></c></em></tag> + <tag><c><![CDATA[{retry, N}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[N = integer()]]></c></p> - <p></p> <p>Set the number of DNS queries - <seealso marker="kernel:inet_res">inet_res</seealso> - will try before giving up. - Default is 3.</p> - <p></p> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> + will try before giving up. Defaults to 3.</p> </item> - <tag><em><c><![CDATA[{inet6, Bool}.]]></c></em></tag> + <tag><c><![CDATA[{inet6, Bool}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Bool = true | false]]></c></p> - <p></p> - <p>Tells the DNS client - <seealso marker="kernel:inet_res">inet_res</seealso> - to look up IPv6 addresses. Default is false.</p> - <p></p> + <p>Tells the DNS client + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> + to look up IPv6 addresses. Defaults to <c>false</c>.</p> </item> - <tag><em><c><![CDATA[{usevc, Bool}.]]></c></em></tag> + <tag><c><![CDATA[{usevc, Bool}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Bool = true | false]]></c></p> - <p></p> - <p>Tells the DNS client - <seealso marker="kernel:inet_res">inet_res</seealso> - to use TCP (Virtual Circuit) instead of UDP. Default is false.</p> - <p></p> + <p>Tells the DNS client + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> + to use TCP (Virtual Circuit) instead of UDP. Defaults to + <c>false</c>.</p> </item> - <tag><em><c><![CDATA[{edns, Version}.]]></c></em></tag> + <tag><c><![CDATA[{edns, Version}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Version = false | 0]]></c></p> - <p></p> <p>Sets the EDNS version that - <seealso marker="kernel:inet_res">inet_res</seealso> - will use. The only allowed is zero. Default is false - which means to not use EDNS.</p> - <p></p> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> + will use. The only allowed version is zero. Defaults to <c>false</c>, + which means not to use EDNS.</p> </item> - <tag><em><c><![CDATA[{udp_payload_size, Size}.]]></c></em></tag> + <tag><c><![CDATA[{udp_payload_size, Size}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[N = integer()]]></c></p> - <p></p> <p>Sets the allowed UDP payload size - <seealso marker="kernel:inet_res">inet_res</seealso> + <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso> will advertise in EDNS queries. Also sets the limit when the DNS query will be deemed too large for UDP - forcing a TCP query instead, which is not entirely - correct since the advertised UDP payload size of the - individual nameserver is what should be used, + forcing a TCP query instead; this is not entirely + correct, as the advertised UDP payload size of the + individual nameserver is what is to be used, but this simple strategy will do until a more intelligent - (probing, caching) algorithm need be implemented. - The default is 1280 which stems from the - standard Ethernet MTU size.</p> - <p></p> + (probing, caching) algorithm needs to be implemented. + Default to 1280, which stems from the standard Ethernet MTU size.</p> </item> - <tag><em><c><![CDATA[{udp, Module}.]]></c></em></tag> + <tag><c><![CDATA[{udp, Module}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Module = atom()]]></c></p> - <p></p> - <p>Tell Erlang to use other primitive UDP module than inet_udp.</p> - <p></p> + <p>Tell Erlang to use another primitive UDP module than + <c>inet_udp</c>.</p> </item> - <tag><em><c><![CDATA[{tcp, Module}.]]></c></em></tag> + <tag><c><![CDATA[{tcp, Module}.]]></c></tag> <item> - <p></p> <p><c><![CDATA[Module = atom()]]></c></p> - <p></p> - <p>Tell Erlang to use other primitive TCP module than inet_tcp.</p> - <p></p> + <p>Tell Erlang to use another primitive TCP module than + <c>inet_tcp</c>.</p> </item> - <tag><em><c><![CDATA[clear_hosts.]]></c></em></tag> + <tag><c><![CDATA[clear_hosts.]]></c></tag> <item> - <p></p> <p>Clear the hosts table.</p> - <p></p> </item> - <tag><em><c><![CDATA[clear_ns.]]></c></em></tag> + <tag><c><![CDATA[clear_ns.]]></c></tag> <item> - <p></p> <p>Clear the list of recorded nameservers (primary and secondary).</p> - <p></p> </item> - <tag><em><c><![CDATA[clear_search.]]></c></em></tag> + <tag><c><![CDATA[clear_search.]]></c></tag> <item> - <p></p> <p>Clear the list of search domains.</p> - <p></p> </item> </taglist> </section> <section> <title>User Configuration Example</title> - <p>Here follows a user configuration example.</p> - <p>Assume a user does not want Erlang to use the native lookup method, - but wants Erlang to read all information necessary from start and use - that for resolving names and addresses. In case lookup fails, Erlang - should request the data from a nameserver (using the Erlang + <p>Assume that a user does not want Erlang to use the native lookup method, + but wants Erlang to read all information necessary from start and use + that for resolving names and addresses. If lookup fails, Erlang + is to request the data from a nameserver (using the Erlang DNS client, set to use EDNS allowing larger responses). - The resolver configuration will be updated when - its configuration file changes, furthermore, DNS records - should never be cached. The user configuration file + The resolver configuration is updated when + its configuration file changes. Also, DNS records + are never to be cached. The user configuration file (in this example named <c><![CDATA[erl_inetrc]]></c>, stored - in directory <c><![CDATA[./cfg_files]]></c>) could then look like this + in directory <c><![CDATA[./cfg_files]]></c>) can then look as follows (Unix):</p> + <pre> - %% -- ERLANG INET CONFIGURATION FILE -- - %% read the hosts file - {file, hosts, "/etc/hosts"}. - %% add a particular host - {host, {134,138,177,105}, ["finwe"]}. - %% do not monitor the hosts file - {hosts_file, ""}. - %% read and monitor nameserver config from here - {resolv_conf, "/usr/local/etc/resolv.conf"}. - %% enable EDNS - {edns,0}. - %% disable caching - {cache_size, 0}. - %% specify lookup method - {lookup, [file, dns]}.</pre> - <p>And Erlang could, for example, be started like this:</p> - <p><c><![CDATA[% erl -sname my_node -kernel inetrc '"./cfg_files/erl_inetrc"']]></c></p> +%% -- ERLANG INET CONFIGURATION FILE -- +%% read the hosts file +{file, hosts, "/etc/hosts"}. +%% add a particular host +{host, {134,138,177,105}, ["finwe"]}. +%% do not monitor the hosts file +{hosts_file, ""}. +%% read and monitor nameserver config from here +{resolv_conf, "/usr/local/etc/resolv.conf"}. +%% enable EDNS +{edns,0}. +%% disable caching +{cache_size, 0}. +%% specify lookup method +{lookup, [file, dns]}.</pre> + + <p>And Erlang can, for example, be started as follows:</p> + + <code type="none"><![CDATA[ +% erl -sname my_node -kernel inetrc '"./cfg_files/erl_inetrc"']]></code> </section> </chapter> diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index a88a815ef6..c14f0a558d 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + </legalnotice> <title>init</title> @@ -30,128 +30,140 @@ <file>init.xml</file> </header> <module>init</module> - <modulesummary>Coordination of System Startup</modulesummary> + <modulesummary>Coordination of system startup.</modulesummary> <description> - <p>The <c>init</c> module is pre-loaded and contains the code for - the <c>init</c> system process which coordinates the start-up of - the system. The first function evaluated at start-up is - <c>boot(BootArgs)</c>, where <c>BootArgs</c> is a list of command - line arguments supplied to the Erlang runtime system from - the local operating system. See - <seealso marker="erts:erl">erl(1)</seealso>.</p> - <p><c>init</c> reads the boot script which contains instructions on - how to initiate the system. See - <seealso marker="sasl:script">script(4)</seealso> for more - information about boot scripts.</p> + <p>This module is preloaded and contains the code for + the <c>init</c> system process that coordinates the startup of + the system. The first function evaluated at startup is + <c>boot(BootArgs)</c>, where <c>BootArgs</c> is a list of + command-line arguments supplied to the Erlang runtime system from + the local operating system; see + <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p> + + <p><c>init</c> reads the boot script, which contains instructions on + how to initiate the system. For more information about boot scripts, see + <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p> + <p><c>init</c> also contains functions to restart, reboot, and stop the system.</p> </description> + <funcs> <func> <name name="boot" arity="1"/> - <fsummary>Start the Erlang runtime system</fsummary> + <fsummary>Start the Erlang runtime system.</fsummary> <desc> <p>Starts the Erlang runtime system. This function is called - when the emulator is started and coordinates system start-up.</p> - <p><c><anno>BootArgs</anno></c> are all command line arguments except - the emulator flags, that is, flags and plain arguments. See - <seealso marker="erts:erl">erl(1)</seealso>.</p> - <p><c>init</c> itself interprets some of the flags, see - <seealso marker="#flags">Command Line Flags</seealso> below. + when the emulator is started and coordinates system startup.</p> + <p><c><anno>BootArgs</anno></c> are all command-line arguments except + the emulator flags, that is, flags and plain arguments; see + <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p> + <p><c>init</c> interprets some of the flags, see section + <seealso marker="#flags">Command-Line Flags</seealso> below. The remaining flags ("user flags") and plain arguments are passed to the <c>init</c> loop and can be retrieved by calling - <c>get_arguments/0</c> and <c>get_plain_arguments/0</c>, - respectively.</p> + <seealso marker="#get_arguments/0"><c>get_arguments/0</c></seealso> + and <seealso marker="#get_plain_arguments/0"> + <c>get_plain_arguments/0</c></seealso>, respectively.</p> </desc> </func> + <func> <name name="get_argument" arity="1"/> - <fsummary>Get the values associated with a command line user flag</fsummary> + <fsummary>Get the values associated with a command-line user flag. + </fsummary> <desc> - <p>Returns all values associated with the command line user flag - <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided several times, each - <c><anno>Values</anno></c> is returned in preserved order.</p> + <p>Returns all values associated with the command-line user flag + <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided + several times, each <c><anno>Values</anno></c> is returned in + preserved order. Example:</p> <pre> % <input>erl -a b c -a d</input> ... 1> <input>init:get_argument(a).</input> {ok,[["b","c"],["d"]]}</pre> - <p>There are also a number of flags, which are defined + <p>The following flags are defined automatically and can be retrieved using this function:</p> <taglist> <tag><c>root</c></tag> <item> - <p>The installation directory of Erlang/OTP, <c>$ROOT</c>.</p> + <p>The installation directory of Erlang/OTP, <c>$ROOT</c>:</p> <pre> 2> <input>init:get_argument(root).</input> {ok,[["/usr/local/otp/releases/otp_beam_solaris8_r10b_patched"]]}</pre> </item> <tag><c>progname</c></tag> <item> - <p>The name of the program which started Erlang.</p> + <p>The name of the program which started Erlang:</p> <pre> 3> <input>init:get_argument(progname).</input> {ok,[["erl"]]}</pre> </item> <tag><c>home</c></tag> <item> - <p>The home directory.</p> + <p>The home directory:</p> <pre> 4> <input>init:get_argument(home).</input> {ok,[["/home/harry"]]}</pre> </item> </taglist> - <p>Returns <c>error</c> if there is no value associated with - <c>Flag</c>.</p> + <p>Returns <c>error</c> if no value is associated with <c>Flag</c>.</p> </desc> </func> + <func> <name name="get_arguments" arity="0"/> - <fsummary>Get all command line user flags</fsummary> + <fsummary>Get all command-line user flags.</fsummary> <desc> - <p>Returns all command line flags, as well as the system - defined flags, see <c>get_argument/1</c>.</p> + <p>Returns all command-line flags and the system-defined flags, see + <seealso marker="#get_argument/1"><c>get_argument/1</c></seealso>.</p> </desc> </func> + <func> <name name="get_plain_arguments" arity="0"/> - <fsummary>Get all non-flag command line arguments</fsummary> + <fsummary>Get all non-flag command-line arguments.</fsummary> <desc> - <p>Returns any plain command line arguments as a list of strings + <p>Returns any plain command-line arguments as a list of strings (possibly empty).</p> </desc> </func> + <func> <name name="get_status" arity="0"/> - <fsummary>Get system status information</fsummary> + <fsummary>Get system status information.</fsummary> <type name="internal_status"/> <desc> <p>The current status of the <c>init</c> process can be inspected. During system startup (initialization), <c><anno>InternalStatus</anno></c> is <c>starting</c>, and - <c><anno>ProvidedStatus</anno></c> indicates how far the boot script has - been interpreted. Each <c>{progress, Info}</c> term - interpreted in the boot script affects <c><anno>ProvidedStatus</anno></c>, - that is, <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p> + <c><anno>ProvidedStatus</anno></c> indicates how far the boot + script has been interpreted. Each <c>{progress, Info}</c> term + interpreted in the boot script affects + <c><anno>ProvidedStatus</anno></c>, that is, + <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p> </desc> </func> + <func> <name name="reboot" arity="0"/> - <fsummary>Take down and restart an Erlang node smoothly</fsummary> + <fsummary>Take down and restart an Erlang node smoothly.</fsummary> <desc> <p>All applications are taken down smoothly, all code is unloaded, and all ports are closed before the system - terminates. If the <c>-heart</c> command line flag was given, - the <c>heart</c> program will try to reboot the system. Refer - to <c>heart(3)</c> for more information.</p> + terminates. If command-line flag <c>-heart</c> was specified, + the <c>heart</c> program tries to reboot the system. For more + information, see + <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p> <p>To limit the shutdown time, the time <c>init</c> is allowed - to spend taking down applications, the <c>-shutdown_time</c> - command line flag should be used.</p> + to spend taking down applications, command-line flag + <c>-shutdown_time</c> is to be used.</p> </desc> </func> + <func> <name name="restart" arity="0"/> - <fsummary>Restart the running Erlang node</fsummary> + <fsummary>Restart the running Erlang node.</fsummary> <desc> <p>The system is restarted <em>inside</em> the running Erlang node, which means that the emulator is not restarted. All @@ -160,80 +172,91 @@ the same way as initially started. The same <c>BootArgs</c> are used again.</p> <p>To limit the shutdown time, the time <c>init</c> is allowed - to spend taking down applications, the <c>-shutdown_time</c> - command line flag should be used.</p> + to spend taking down applications, command-line flag + <c>-shutdown_time</c> is to be used.</p> </desc> </func> + <func> <name name="script_id" arity="0"/> - <fsummary>Get the identity of the used boot script</fsummary> + <fsummary>Get the identity of the used boot script.</fsummary> <desc> - <p>Get the identity of the boot script used to boot the system. + <p>Gets the identity of the boot script used to boot the system. <c><anno>Id</anno></c> can be any Erlang term. In the delivered boot - scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c> and - <c>Vsn</c> are strings.</p> + scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c> + and <c>Vsn</c> are strings.</p> </desc> </func> + <func> <name name="stop" arity="0"/> - <fsummary>Take down an Erlang node smoothly</fsummary> + <fsummary>Take down an Erlang node smoothly.</fsummary> <desc> <p>The same as <seealso marker="#stop/1"><c>stop(0)</c></seealso>.</p> </desc> </func> + <func> <name name="stop" arity="1"/> - <fsummary>Take down an Erlang node smoothly</fsummary> + <fsummary>Take down an Erlang node smoothly.</fsummary> <desc> <p>All applications are taken down smoothly, all code is unloaded, and all ports are closed before the system - terminates by calling <c>halt(<anno>Status</anno>)</c>. If the - <c>-heart</c> command line flag was given, the <c>heart</c> - program is terminated before the Erlang node - terminates. Refer to <c>heart(3)</c> for more - information.</p> + terminates by calling <c>halt(<anno>Status</anno>)</c>. If + command-line flag <c>-heart</c> was specified, the <c>heart</c> + program is terminated before the Erlang node terminates. + For more information, see + <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p> <p>To limit the shutdown time, the time <c>init</c> is allowed - to spend taking down applications, the <c>-shutdown_time</c> - command line flag should be used.</p> + to spend taking down applications, command-line flag + <c>-shutdown_time</c> is to be used.</p> </desc> </func> </funcs> <section> <marker id="flags"></marker> - <title>Command Line Flags</title> - <warning><p>The support for loading of code from archive files is - experimental. The sole purpose of releasing it before it is ready - is to obtain early feedback. The file format, semantics, - interfaces etc. may be changed in a future release. The - <c>-code_path_choice</c> flag is also experimental.</p></warning> + <title>Command-Line Flags</title> + <warning> + <p>The support for loading of code from archive files is + experimental. The only purpose of releasing it before it is ready + is to obtain early feedback. The file format, semantics, + interfaces, and so on, can be changed in a future release. The + <c>-code_path_choice</c> flag is also experimental.</p> + </warning> - <p>The <c>init</c> module interprets the following command line - flags:</p> + <p>The <c>init</c> module interprets the following command-line flags:</p> <taglist> <tag><c>--</c></tag> <item> <p>Everything following <c>--</c> up to the next flag is considered plain arguments and can be retrieved using - <c>get_plain_arguments/0</c>.</p> + <seealso marker="#get_plain_arguments/0"> + <c>get_plain_arguments/0</c></seealso>.</p> </item> <tag><c>-code_path_choice Choice</c></tag> <item> - <p>This flag can be set to <c>strict</c> or <c>relaxed</c>. It - controls whether each directory in the code path should be - interpreted strictly as it appears in the <c>boot script</c> or if - <c>init</c> should be more relaxed and try to find a suitable - directory if it can choose from a regular ebin directory and - an ebin directory in an archive file. This flag is particular - useful when you want to elaborate with code loading from - archives without editing the <c>boot script</c>. See <seealso - marker="sasl:script">script(4)</seealso> for more information - about interpretation of boot scripts. The flag does also have - a similar affect on how the code server works. See <seealso - marker="kernel:code">code(3)</seealso>.</p> - + <p>Can be set to <c>strict</c> or <c>relaxed</c>. It controls how each + directory in the code path is to be interpreted:</p> + <list type="bulleted"> + <item> + <p>Strictly as it appears in the <c>boot script</c>, or</p> + </item> + <item> + <p><c>init</c> is to be more relaxed and try to find a suitable + directory if it can choose from a regular <c>ebin</c> directory + and an <c>ebin</c> directory in an archive file.</p> + </item> + </list> + <p>This flag is particular + useful when you want to elaborate with code loading from + archives without editing the <c>boot script</c>. For more + information about interpretation of boot scripts, see + <seealso marker="sasl:script"><c>script(4)</c></seealso>. + The flag has also a similar effect on how the code server works; see + <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p> </item> <tag><c>-epmd_module Module</c></tag> <item> @@ -242,11 +265,11 @@ </item> <tag><c>-eval Expr</c></tag> <item> - <p>Scans, parses and evaluates an arbitrary expression + <p>Scans, parses, and evaluates an arbitrary expression <c>Expr</c> during system initialization. If any of these - steps fail (syntax error, parse error or exception during - evaluation), Erlang stops with an error message. Here is an - example that uses Erlang as a hexadecimal calculator:</p> + steps fail (syntax error, parse error, or exception during + evaluation), Erlang stops with an error message. In the following + example Erlang is used as a hexadecimal calculator:</p> <pre> % <input>erl -noshell -eval 'R = 16#1F+16#A0, io:format("~.16B~n", [R])' \\</input> <input>-s erlang halt</input> @@ -256,14 +279,15 @@ BF</pre> <c>-eval</c> expressions are evaluated sequentially with <c>-s</c> and <c>-run</c> function calls (this also in the order specified). As with <c>-s</c> and <c>-run</c>, an - evaluation that does not terminate, blocks the system + evaluation that does not terminate blocks the system initialization process.</p> </item> <tag><c>-extra</c></tag> <item> <p>Everything following <c>-extra</c> is considered plain arguments and can be retrieved using - <c>get_plain_arguments/0</c>.</p> + <seealso marker="#get_plain_arguments/0"> + <c>get_plain_arguments/0</c></seealso>.</p> </item> <tag><c>-run Mod [Func [Arg1, Arg2, ...]]</c></tag> <item> @@ -285,8 +309,8 @@ foo:bar() foo:bar(["baz", "1", "2"]).</code> <p>The functions are executed sequentially in an initialization process, which then terminates normally and passes control to - the user. This means that a <c>-run</c> call which does not - return will block further processing; to avoid this, use + the user. This means that a <c>-run</c> call that does not + return blocks further processing; to avoid this, use some variant of <c>spawn</c> in such cases.</p> </item> <tag><c>-s Mod [Func [Arg1, Arg2, ...]]</c></tag> @@ -309,11 +333,11 @@ foo:bar() foo:bar([baz, '1', '2']).</code> <p>The functions are executed sequentially in an initialization process, which then terminates normally and passes control to - the user. This means that a <c>-s</c> call which does not - return will block further processing; to avoid this, use + the user. This means that a <c>-s</c> call that does not + return blocks further processing; to avoid this, use some variant of <c>spawn</c> in such cases.</p> - <p>Due to the limited length of atoms, it is recommended that - <c>-run</c> be used instead.</p> + <p>Because of the limited length of atoms, it is recommended to + use <c>-run</c> instead.</p> </item> </taglist> </section> @@ -335,9 +359,9 @@ error</pre> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>, - <seealso marker="kernel:heart">heart(3)</seealso></p> + <title>See Also</title> + <p><seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>, + <seealso marker="kernel:heart"><c>heart(3)</c></seealso></p> </section> </erlref> diff --git a/erts/doc/src/introduction.xml b/erts/doc/src/introduction.xml new file mode 100644 index 0000000000..790e24f9f3 --- /dev/null +++ b/erts/doc/src/introduction.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2012</year><year>2016</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + </legalnotice> + + <title>Introduction</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>2016-05-22</date> + <rev>PA1</rev> + <file>introduction.xml</file> + </header> + <section> + <title>Scope</title> + <p>The Erlang Runtime System Application, ERTS, contains + functionality necessary to run the Erlang system.</p> + <note> + <p>By default, <c><![CDATA[ERTS]]></c> is only guaranteed to be + compatible with other Erlang/OTP components from the same release as + <c><![CDATA[ERTS]]></c> itself.</p> + <p>For information on how to communicate with Erlang/OTP components + from earlier releases, see the documentation of system flag + <seealso marker="erl#compat_rel"><c>+R</c></seealso> in <c>erl(1)</c>. + </p> + </note> + </section> + + <section> + <title>Prerequisites</title> + <p>It is assumed that the reader is familiar with the Erlang programming + language.</p> + </section> +</chapter> + diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index 7be3d15de6..2a14f1e47b 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -22,7 +22,7 @@ </legalnotice> - <title>Match specifications in Erlang</title> + <title>Match Specifications in Erlang</title> <prepared>Patrik Nyblom</prepared> <responsible></responsible> <docno></docno> @@ -32,43 +32,54 @@ <rev>PA1</rev> <file>match_spec.xml</file> </header> - <p>A "match specification" (match_spec) is an Erlang term describing a - small "program" that will try to match something. It can be used + <p>A "match specification" (<c>match_spec</c>) is an Erlang term describing a + small "program" that tries to match something. It can be used to either control tracing with <seealso marker="erlang#trace_pattern/3">erlang:trace_pattern/3</seealso> or to search for objects in an ETS table with for example <seealso marker="stdlib:ets#select/2">ets:select/2</seealso>. - The match_spec in many ways works like a small function in Erlang, but is - interpreted/compiled by the Erlang runtime system to something much more - efficient than calling an Erlang function. The match_spec is also + The match specification in many ways works like a small function in Erlang, + but is interpreted/compiled by the Erlang runtime system to something much more + efficient than calling an Erlang function. The match specification is also very limited compared to the expressiveness of real Erlang functions.</p> - <p>The most notable difference between a match_spec and an Erlang fun is - of course the syntax. Match specifications are Erlang terms, not - Erlang code. A match_spec also has a somewhat strange concept of - exceptions. An exception (e.g., <c><![CDATA[badarg]]></c>) in the <c><![CDATA[MatchCondition]]></c> - part, - which resembles an Erlang guard, will generate immediate failure, - while an exception in the <c><![CDATA[MatchBody]]></c> part, which resembles the body of an - Erlang function, is implicitly caught and results in the single atom - <c><![CDATA['EXIT']]></c>. - </p> + <p>The most notable difference between a match specification and an Erlang + fun is the syntax. Match specifications are Erlang terms, not Erlang code. + Also, a match specification has a strange concept of exceptions:</p> + + <list type="bulleted"> + <item> + <p>An exception (such as <c><![CDATA[badarg]]></c>) in the + <c><![CDATA[MatchCondition]]></c> part, which resembles an Erlang guard, + generates immediate failure.</p> + </item> + <item> + <p>An exception in the <c><![CDATA[MatchBody]]></c> part, which resembles + the body of an Erlang function, is implicitly caught and results in the + single atom <c><![CDATA['EXIT']]></c>.</p> + </item> + </list> <section> <title>Grammar</title> - <p>A match_spec used in tracing can be described in this <em>informal</em> grammar:</p> + <p>A match specification used in tracing can be described in the following + <em>informal</em> grammar:</p> + <list type="bulleted"> <item>MatchExpression ::= [ MatchFunction, ... ] </item> <item>MatchFunction ::= { MatchHead, MatchConditions, MatchBody } </item> - <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | [ MatchHeadPart, ... ] + <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | + [ MatchHeadPart, ... ] + </item> + <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c> </item> - <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c></item> <item>MatchVariable ::= '$<number>' </item> - <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c></item> - <item>MatchCondition ::= { GuardFunction } | - { GuardFunction, ConditionExpression, ... } + <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c> + </item> + <item>MatchCondition ::= { GuardFunction } | { GuardFunction, + ConditionExpression, ... } </item> <item>BoolFunction ::= <c><![CDATA[is_atom]]></c> | <c><![CDATA[is_float]]></c> | <c><![CDATA[is_integer]]></c> | @@ -79,58 +90,77 @@ <c><![CDATA[is_function]]></c> | <c><![CDATA[is_record]]></c> | <c><![CDATA[is_seq_trace]]></c> | <c><![CDATA['and']]></c> | <c><![CDATA['or']]></c> | <c><![CDATA['not']]></c> | - <c><![CDATA['xor']]></c> | <c><![CDATA[andalso]]></c> | - <c><![CDATA[orelse]]></c></item> + <c><![CDATA['xor']]></c> | <c><![CDATA['andalso']]></c> | + <c><![CDATA['orelse']]></c> + </item> <item>ConditionExpression ::= ExprMatchVariable | { GuardFunction } | - { GuardFunction, ConditionExpression, ... } | TermConstruct + { GuardFunction, ConditionExpression, ... } | TermConstruct + </item> + <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) | + <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c> </item> - <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) | - <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c></item> <item>TermConstruct = {{}} | {{ ConditionExpression, ... }} | <c><![CDATA[[]]]></c> | [ConditionExpression, ...] | <c><![CDATA[#{}]]></c> | #{term() => ConditionExpression, ...} | - NonCompositeTerm | Constant</item> - <item>NonCompositeTerm ::= term() (not list or tuple or map)</item> + NonCompositeTerm | Constant + </item> + <item>NonCompositeTerm ::= term() (not list or tuple or map) + </item> <item>Constant ::= {<c><![CDATA[const]]></c>, term()} </item> <item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> | - <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> | - <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> | - <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> | - <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> | - <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> | - <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> | - <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> | - <c><![CDATA[get_tcw]]></c></item> + <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | + <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> | + <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | + <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> | + <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | + <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> | + <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | + <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> | + <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | + <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> | + <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | + <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> | + <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | + <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> | + <c><![CDATA[get_tcw]]></c> + </item> <item>MatchBody ::= [ ActionTerm ] </item> <item>ActionTerm ::= ConditionExpression | ActionCall </item> - <item>ActionCall ::= {ActionFunction} | - {ActionFunction, ActionTerm, ...} + <item>ActionCall ::= {ActionFunction} | {ActionFunction, ActionTerm, ...} </item> <item>ActionFunction ::= <c><![CDATA[set_seq_token]]></c> | - <c><![CDATA[get_seq_token]]></c> | <c><![CDATA[message]]></c> | - <c><![CDATA[return_trace]]></c> | <c><![CDATA[exception_trace]]></c> | <c><![CDATA[process_dump]]></c> | - <c><![CDATA[enable_trace]]></c> | <c><![CDATA[disable_trace]]></c> | <c><![CDATA[trace]]></c> | - <c><![CDATA[display]]></c> | <c><![CDATA[caller]]></c> | <c><![CDATA[set_tcw]]></c> | - <c><![CDATA[silent]]></c></item> + <c><![CDATA[get_seq_token]]></c> | <c><![CDATA[message]]></c> | + <c><![CDATA[return_trace]]></c> | <c><![CDATA[exception_trace]]></c> | + <c><![CDATA[process_dump]]></c> | <c><![CDATA[enable_trace]]></c> | + <c><![CDATA[disable_trace]]></c> | <c><![CDATA[trace]]></c> | + <c><![CDATA[display]]></c> | <c><![CDATA[caller]]></c> | + <c><![CDATA[set_tcw]]></c> | <c><![CDATA[silent]]></c> + </item> </list> - <p>A match_spec used in ets can be described in this <em>informal</em> grammar:</p> + <p>A match specification used in + <seealso marker="stdlib:ets"><c>ets(3)</c></seealso> + can be described in the following <em>informal</em> grammar:</p> + <list type="bulleted"> <item>MatchExpression ::= [ MatchFunction, ... ] </item> <item>MatchFunction ::= { MatchHead, MatchConditions, MatchBody } </item> - <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | { MatchHeadPart, ... } + <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | + { MatchHeadPart, ... } + </item> + <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c> </item> - <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c></item> <item>MatchVariable ::= '$<number>' </item> - <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c></item> + <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c> + </item> <item>MatchCondition ::= { GuardFunction } | - { GuardFunction, ConditionExpression, ... } + { GuardFunction, ConditionExpression, ... } </item> <item>BoolFunction ::= <c><![CDATA[is_atom]]></c> | <c><![CDATA[is_float]]></c> | <c><![CDATA[is_integer]]></c> | @@ -141,243 +171,322 @@ <c><![CDATA[is_function]]></c> | <c><![CDATA[is_record]]></c> | <c><![CDATA[is_seq_trace]]></c> | <c><![CDATA['and']]></c> | <c><![CDATA['or']]></c> | <c><![CDATA['not']]></c> | - <c><![CDATA['xor']]></c> | <c><![CDATA[andalso]]></c> | - <c><![CDATA[orelse]]></c></item> + <c><![CDATA['xor']]></c> | <c><![CDATA['andalso']]></c> | + <c><![CDATA['orelse']]></c> + </item> <item>ConditionExpression ::= ExprMatchVariable | { GuardFunction } | - { GuardFunction, ConditionExpression, ... } | TermConstruct + { GuardFunction, ConditionExpression, ... } | TermConstruct </item> <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) | - <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c></item> + <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c> + </item> <item>TermConstruct = {{}} | {{ ConditionExpression, ... }} | <c><![CDATA[[]]]></c> | [ConditionExpression, ...] | #{} | - #{term() => ConditionExpression, ...} | NonCompositeTerm | - Constant</item> - <item>NonCompositeTerm ::= term() (not list or tuple or map)</item> + #{term() => ConditionExpression, ...} | NonCompositeTerm | Constant + </item> + <item>NonCompositeTerm ::= term() (not list or tuple or map) + </item> <item>Constant ::= {<c><![CDATA[const]]></c>, term()} </item> <item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> | - <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> | - <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> | - <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> | - <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> | - <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> | - <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> | - <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> | - <c><![CDATA[get_tcw]]></c></item> - <item>MatchBody ::= [ ConditionExpression, ... ]</item> + <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | + <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> | + <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | + <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> | + <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | + <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> | + <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | + <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> | + <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | + <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> | + <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | + <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> | + <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | + <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> | + <c><![CDATA[get_tcw]]></c> + </item> + <item>MatchBody ::= [ ConditionExpression, ... ] + </item> </list> </section> <section> - <title>Function descriptions</title> - + <title>Function Descriptions</title> <section> - <title>Functions allowed in all types of match specifications</title> - <p>The different functions allowed in <c><![CDATA[match_spec]]></c> work like this: - </p> - <p><em>is_atom, is_float, is_integer, is_list, is_number, is_pid, is_port, - is_reference, is_tuple, is_map, is_binary, is_function:</em> Like the - corresponding guard tests in Erlang, return <c><![CDATA[true]]></c> or - <c><![CDATA[false]]></c>.</p> - <p><em>is_record: </em>Takes an additional parameter, which SHALL - be the result of <c><![CDATA[record_info(size, <record_type>)]]></c>, - like in <c><![CDATA[{is_record, '$1', rectype, record_info(size, rectype)}]]></c>. - </p> - <p><em>'not': </em>Negates its single argument (anything other - than <c><![CDATA[false]]></c> gives <c><![CDATA[false]]></c>). - </p> - <p><em>'and': </em>Returns <c><![CDATA[true]]></c> if all its arguments - (variable length argument list) evaluate to <c><![CDATA[true]]></c>, else - <c><![CDATA[false]]></c>. Evaluation order is undefined. - </p> - <p><em>'or': </em>Returns <c><![CDATA[true]]></c> if any of its arguments - evaluates to <c><![CDATA[true]]></c>. Variable length argument - list. Evaluation order is undefined. - </p> - <p><em>andalso: </em>Like <c><![CDATA['and']]></c>, but quits evaluating its - arguments as soon as one argument evaluates to something else - than true. Arguments are evaluated left to right. - </p> - <p><em>orelse: </em>Like <c><![CDATA['or']]></c>, but quits evaluating as soon - as one of its arguments evaluates to <c><![CDATA[true]]></c>. Arguments are - evaluated left to right. - </p> - <p><em>'xor': </em>Only two arguments, of which one has to be true - and the other false to return <c><![CDATA[true]]></c>; otherwise - <c><![CDATA['xor']]></c> returns false. - </p> - <p><em>abs, element, hd, length, node, round, size, tl, trunc, '+', '-', '*', 'div', 'rem', 'band', 'bor', 'bxor', 'bnot', 'bsl', 'bsr', '>', '>=', '<', '=<', '=:=', '==', '=/=', '/=', self: </em>Work as the corresponding Erlang bif's (or - operators). In case of bad arguments, the result depends on - the context. In the <c><![CDATA[MatchConditions]]></c> part of the - expression, the test fails immediately (like in an Erlang - guard), but in the <c><![CDATA[MatchBody]]></c>, exceptions are implicitly - caught and the call results in the atom <c><![CDATA['EXIT']]></c>.</p> + <title>Functions Allowed in All Types of Match Specifications</title> + <p>The functions allowed in <c><![CDATA[match_spec]]></c> work as + follows:</p> + + <taglist> + <tag><c>is_atom</c>, <c>is_float</c>, <c>is_integer</c>, <c>is_list</c>, + <c>is_number</c>, <c>is_pid</c>, <c>is_port</c>, <c>is_reference</c>, + <c>is_tuple</c>, <c>is_map</c>, <c>is_binary</c>, <c>is_function</c> + </tag> + <item> + <p>Same as the corresponding guard tests in Erlang, return + <c><![CDATA[true]]></c> or <c><![CDATA[false]]></c>.</p> + </item> + <tag><c>is_record</c></tag> + <item> + <p>Takes an additional parameter, which <em>must</em> be the result + of <c><![CDATA[record_info(size, <record_type>)]]></c>, like in + <c><![CDATA[{is_record, '$1', rectype, record_info(size, + rectype)}]]></c>.</p> + </item> + <tag><c>'not'</c></tag> + <item> + <p>Negates its single argument (anything other + than <c><![CDATA[false]]></c> gives <c><![CDATA[false]]></c>).</p> + </item> + <tag><c>'and'</c></tag> + <item> + <p>Returns <c><![CDATA[true]]></c> if all its arguments (variable + length argument list) evaluate to <c><![CDATA[true]]></c>, otherwise + <c><![CDATA[false]]></c>. Evaluation order is undefined.</p> + </item> + <tag><c>'or'</c></tag> + <item> + <p>Returns <c><![CDATA[true]]></c> if any of its arguments + evaluates to <c><![CDATA[true]]></c>. Variable length argument + list. Evaluation order is undefined.</p> + </item> + <tag><c>'andalso'</c></tag> + <item> + <p>Works as <c><![CDATA['and']]></c>, but quits evaluating its + arguments when one argument evaluates to something else + than <c>true</c>. Arguments are evaluated left to right.</p> + </item> + <tag><c>'orelse'</c></tag> + <item> + <p>Works as <c><![CDATA['or']]></c>, but quits evaluating as soon + as one of its arguments evaluates to <c><![CDATA[true]]></c>. + Arguments are evaluated left to right.</p> + </item> + <tag><c>'xor'</c></tag> + <item> + <p>Only two arguments, of which one must be <c>true</c> and the + other <c>false</c> to return <c><![CDATA[true]]></c>; otherwise + <c><![CDATA['xor']]></c> returns false.</p> + </item> + <tag><c>abs</c>, <c>element</c>, <c>hd</c>, <c>length</c>, <c>node</c>, + <c>round</c>, <c>size</c>, <c>tl</c>, <c>trunc</c>, <c>'+'</c>, + <c>'-'</c>, <c>'*'</c>, <c>'div'</c>, <c>'rem'</c>, <c>'band'</c>, + <c>'bor'</c>, <c>'bxor'</c>, <c>'bnot'</c>, <c>'bsl'</c>, + <c>'bsr'</c>, <c>'>'</c>, <c>'>='</c>, <c>'<'</c>, <c>'=<'</c>, + <c>'=:='</c>, <c>'=='</c>, <c>'=/='</c>, <c>'/='</c>, + <c>self</c></tag> + <item> + <p>Same as the corresponding Erlang BIFs (or operators). In case of + bad arguments, the result depends on the context. In the + <c><![CDATA[MatchConditions]]></c> part of the expression, the test + fails immediately (like in an Erlang guard). In the + <c><![CDATA[MatchBody]]></c> part, exceptions are implicitly caught + and the call results in the atom <c><![CDATA['EXIT']]></c>.</p> + </item> + </taglist> </section> <section> - <title>Functions allowed only for tracing</title> - <p><em>is_seq_trace: </em>Returns <c><![CDATA[true]]></c> if a sequential - trace token is set for the current process, otherwise <c><![CDATA[false]]></c>. - </p> - <p><em>set_seq_token:</em> Works like - <c><![CDATA[seq_trace:set_token/2]]></c>, but returns <c><![CDATA[true]]></c> on success - and <c><![CDATA['EXIT']]></c> on error or bad argument. Only allowed in the - <c><![CDATA[MatchBody]]></c> part and only allowed when tracing. - </p> - <p><em>get_seq_token:</em> Works just like - <c><![CDATA[seq_trace:get_token/0]]></c>, and is only allowed in the - <c><![CDATA[MatchBody]]></c> part when tracing. - </p> - <p><em>message:</em> Sets an additional message appended to the - trace message sent. One can only set one additional message in - the body; subsequent calls will replace the appended message. As - a special case, <c><![CDATA[{message, false}]]></c> disables sending of - trace messages ('call' and 'return_to') - for this function call, just like if the match_spec had not matched, - which can be useful if only the side effects of - the <c><![CDATA[MatchBody]]></c> are desired. - Another special case is <c><![CDATA[{message, true}]]></c> which - sets the default behavior, as if the function had no match_spec, - trace message is sent with no extra - information (if no other calls to <c><![CDATA[message]]></c> are placed - before <c><![CDATA[{message, true}]]></c>, it is in fact a "noop"). - </p> - <p>Takes one argument, the message. Returns <c><![CDATA[true]]></c> and can - only be used in the <c><![CDATA[MatchBody]]></c> part and when tracing. - </p> - <p><em>return_trace:</em> Causes a <c><![CDATA[return_from]]></c> trace - message to be sent upon return from the current function. - Takes no arguments, returns <c><![CDATA[true]]></c> and can only be used - in the <c><![CDATA[MatchBody]]></c> part when tracing. - If the process trace flag <c><![CDATA[silent]]></c> - is active the <c><![CDATA[return_from]]></c> trace message is inhibited. - </p> - <p>NOTE! If the traced function is tail recursive, this match - spec function destroys that property. - Hence, if a match spec executing this function is used on a - perpetual server process, it may only be active for a limited - time, or the emulator will eventually use all memory in the host - machine and crash. If this match_spec function is inhibited - using the <c><![CDATA[silent]]></c> process trace flag - tail recursiveness still remains. - </p> - <p><em>exception_trace:</em> Same as <em>return_trace</em>, - plus; if the traced function exits due to an exception, - an <c><![CDATA[exception_from]]></c> trace message is generated, - whether the exception is caught or not. - </p> - <p><em>process_dump:</em> Returns some textual information about - the current process as a binary. Takes no arguments and is only - allowed in the <c><![CDATA[MatchBody]]></c> part when tracing. - </p> - <p><em>enable_trace:</em> With one parameter this function turns - on tracing like the Erlang call <c><![CDATA[erlang:trace(self(), true, [P2])]]></c>, where <c><![CDATA[P2]]></c> is the parameter to - <c><![CDATA[enable_trace]]></c>. With two parameters, the first parameter - should be either a process identifier or the registered name of - a process. In this case tracing is turned on for the designated - process in the same way as in the Erlang call <c><![CDATA[erlang:trace(P1, true, [P2])]]></c>, where P1 is the first and P2 is the second - argument. The process <c><![CDATA[P1]]></c> gets its trace messages sent to the same - tracer as the process executing the statement uses. <c><![CDATA[P1]]></c> - can <em>not</em> be one of the atoms <c><![CDATA[all]]></c>, <c><![CDATA[new]]></c> or - <c><![CDATA[existing]]></c> (unless, of course, they are registered names). - <c><![CDATA[P2]]></c> can <em>not</em> be <c><![CDATA[cpu_timestamp]]></c> nor - <c><![CDATA[tracer]]></c>. - Returns <c><![CDATA[true]]></c> and may only be used in - the <c><![CDATA[MatchBody]]></c> part when tracing. - </p> - <p><em>disable_trace:</em> With one parameter this function - disables tracing like the Erlang call <c><![CDATA[erlang:trace(self(), false, [P2])]]></c>, where <c><![CDATA[P2]]></c> is the parameter to - <c><![CDATA[disable_trace]]></c>. With two parameters it works like the - Erlang call <c><![CDATA[erlang:trace(P1, false, [P2])]]></c>, where P1 can - be either a process identifier or a registered name and is given - as the first argument to the match_spec function. - <c><![CDATA[P2]]></c> can <em>not</em> be <c><![CDATA[cpu_timestamp]]></c> nor - <c><![CDATA[tracer]]></c>. Returns - <c><![CDATA[true]]></c> and may only be used in the <c><![CDATA[MatchBody]]></c> part - when tracing. - </p> - <p><em>trace:</em> With two parameters this function takes a list - of trace flags to disable as first parameter and a list - of trace flags to enable as second parameter. Logically, the - disable list is applied first, but effectively all changes - are applied atomically. The trace flags - are the same as for <c><![CDATA[erlang:trace/3]]></c> not including - <c><![CDATA[cpu_timestamp]]></c> but including <c><![CDATA[tracer]]></c>. If a - tracer is specified in both lists, the tracer in the - enable list takes precedence. If no tracer is specified the - same tracer as the process executing the match spec is - used. When using a <seealso marker="erl_tracer">tracer module</seealso> - the module has to be loaded before the match specification is executed. - If it is not loaded the match will fail. - With three parameters to this function the first is - either a process identifier or the registered name of a - process to set trace flags on, the second is the disable - list, and the third is the enable list. Returns - <c><![CDATA[true]]></c> if any trace property was changed for the - trace target process or <c><![CDATA[false]]></c> if not. It may only - be used in the <c><![CDATA[MatchBody]]></c> part when tracing. - </p> - <p><em>caller:</em> - Returns the calling function as a tuple {Module, - Function, Arity} or the atom <c><![CDATA[undefined]]></c> if the calling - function cannot be determined. May only be used in the - <c><![CDATA[MatchBody]]></c> part when tracing. - </p> - <p>Note that if a "technically built in function" (i.e. a - function not written in Erlang) is traced, the <c><![CDATA[caller]]></c> - function will sometimes return the atom <c><![CDATA[undefined]]></c>. The calling - Erlang function is not available during such calls. - </p> - <p><em>display:</em> For debugging purposes only; displays the - single argument as an Erlang term on stdout, which is seldom - what is wanted. Returns <c><![CDATA[true]]></c> and may only be used in the - <c><![CDATA[MatchBody]]></c> part when tracing. - </p> - <p> <marker id="get_tcw"></marker> -<em>get_tcw:</em> - Takes no argument and returns the value of the node's trace - control word. The same is done by - <c><![CDATA[erlang:system_info(trace_control_word)]]></c>. - </p> - <p>The trace control word is a 32-bit unsigned integer intended for - generic trace control. The trace control word can be tested and - set both from within trace match specifications and with BIFs. - This call is only allowed when tracing. - </p> - <p> <marker id="set_tcw"></marker> -<em>set_tcw:</em> - Takes one unsigned integer argument, sets the value of - the node's trace control word to the value of the argument - and returns the previous value. The same is done by - <c><![CDATA[erlang:system_flag(trace_control_word, Value)]]></c>. It is only - allowed to use <c><![CDATA[set_tcw]]></c> in the <c><![CDATA[MatchBody]]></c> part - when tracing. - </p> - <p><em>silent:</em> - Takes one argument. If the argument is <c><![CDATA[true]]></c>, the call - trace message mode for the current process is set to silent - for this call and all subsequent, i.e call trace messages - are inhibited even if <c><![CDATA[{message, true}]]></c> is called in the - <c><![CDATA[MatchBody]]></c> part for a traced function. - </p> - <p>This mode can also be activated with the <c><![CDATA[silent]]></c> flag - to <c><![CDATA[erlang:trace/3]]></c>. - </p> - <p>If the argument is <c><![CDATA[false]]></c>, the call trace message mode - for the current process is set to normal (non-silent) for - this call and all subsequent. - </p> - <p>If the argument is neither <c><![CDATA[true]]></c> nor <c><![CDATA[false]]></c>, - the call trace message mode is unaffected.</p> + <title>Functions Allowed Only for Tracing</title> + <p>The functions allowed only for tracing work as follows:</p> + + <taglist> + <tag><c>is_seq_trace</c></tag> + <item> + <p>Returns <c><![CDATA[true]]></c> if a sequential trace token is set + for the current process, otherwise <c><![CDATA[false]]></c>.</p> + </item> + <tag><c>set_seq_token</c></tag> + <item> + <p>Works as <c><![CDATA[seq_trace:set_token/2]]></c>, but returns + <c><![CDATA[true]]></c> on success, and <c><![CDATA['EXIT']]></c> + on error or bad argument. Only allowed in the + <c><![CDATA[MatchBody]]></c> part and only allowed when tracing.</p> + </item> + <tag><c>get_seq_token</c></tag> + <item> + <p>Same as <c><![CDATA[seq_trace:get_token/0]]></c> and only + allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.</p> + </item> + <tag><c>message</c></tag> + <item> + <p>Sets an additional message appended to the + trace message sent. One can only set one additional message in + the body. Later calls replace the appended message.</p> + <p>As a special case, <c><![CDATA[{message, false}]]></c> disables + sending of trace messages ('call' and 'return_to') for this function + call, just like if the match specification had not matched. + This can be useful if only the side effects of + the <c><![CDATA[MatchBody]]></c> part are desired.</p> + <p>Another special case is <c><![CDATA[{message, true}]]></c>, which + sets the default behavior, as if the function had no match + specification; trace message is sent with no extra information + (if no other calls to <c><![CDATA[message]]></c> are placed before + <c><![CDATA[{message, true}]]></c>, it is in fact a "noop").</p> + <p>Takes one argument: the message. Returns <c><![CDATA[true]]></c> + and can only be used in the <c><![CDATA[MatchBody]]></c> part and + when tracing.</p> + </item> + <tag><c>return_trace</c></tag> + <item> + <p>Causes a <c><![CDATA[return_from]]></c> trace message to be sent + upon return from the current function. Takes no arguments, returns + <c><![CDATA[true]]></c> and can only be used in the + <c><![CDATA[MatchBody]]></c> part when tracing. + If the process trace flag <c><![CDATA[silent]]></c> is active, the + <c><![CDATA[return_from]]></c> trace message is inhibited.</p> + <p><em>Warning:</em> If the traced function is tail-recursive, this + match specification function destroys that property. Hence, if a + match specification executing this function is used on a + perpetual server process, it can only be active for a limited + period of time, or the emulator will eventually use all memory in + the host machine and crash. If this match specification function is + inhibited using process trace flag <c><![CDATA[silent]]></c>, + tail-recursiveness still remains.</p> + </item> + <tag><c>exception_trace</c></tag> + <item> + <p>Works as <c>return_trace</c> plus; if the traced function exits + because of an exception, + an <c><![CDATA[exception_from]]></c> trace message is generated, + regardless of the exception is caught or not.</p> + </item> + <tag><c>process_dump</c></tag> + <item> + <p>Returns some textual information about + the current process as a binary. Takes no arguments and is only + allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.</p> + </item> + <tag><c>enable_trace</c></tag> + <item> + <p>With one parameter this function turns on tracing like the Erlang + call <c><![CDATA[erlang:trace(self(), true, [P2])]]></c>, where + <c><![CDATA[P2]]></c> is the parameter to + <c><![CDATA[enable_trace]]></c>.</p> + <p>With two parameters, the first parameter is to be either a process + identifier or the registered name of a process. In this case tracing + is turned on for the designated process in the same way as in the + Erlang call <c><![CDATA[erlang:trace(P1, true, [P2])]]></c>, where + <c>P1</c> is the first and <c>P2</c> is the second argument. The + process <c><![CDATA[P1]]></c> gets its trace messages sent to the + same tracer as the process executing the statement uses. + <c><![CDATA[P1]]></c> <em>cannot</em> be one of the atoms + <c><![CDATA[all]]></c>, <c><![CDATA[new]]></c> or + <c><![CDATA[existing]]></c> (unless they are registered names). + <c><![CDATA[P2]]></c> <em>cannot</em> be + <c><![CDATA[cpu_timestamp]]></c> or <c><![CDATA[tracer]]></c>.</p> + <p>Returns <c><![CDATA[true]]></c> and can only be used in + the <c><![CDATA[MatchBody]]></c> part when tracing.</p> + </item> + <tag><c>disable_trace</c></tag> + <item> + <p>With one parameter this function disables tracing like the Erlang + call <c><![CDATA[erlang:trace(self(), false, [P2])]]></c>, where + <c><![CDATA[P2]]></c> is the parameter to + <c><![CDATA[disable_trace]]></c>.</p> + <p>With two parameters this function works as the Erlang call + <c><![CDATA[erlang:trace(P1, false, [P2])]]></c>, where <c>P1</c> + can be either a process identifier or a registered name and is + specified as the first argument to the match specification function. + <c><![CDATA[P2]]></c> <em>cannot</em> be + <c><![CDATA[cpu_timestamp]]></c> or <c><![CDATA[tracer]]></c>.</p> + <p>Returns <c><![CDATA[true]]></c> and can only be used in the + <c><![CDATA[MatchBody]]></c> part when tracing.</p> + </item> + <tag><c>trace</c></tag> + <item> + <p>With two parameters this function takes a list + of trace flags to disable as first parameter and a list + of trace flags to enable as second parameter. Logically, the + disable list is applied first, but effectively all changes + are applied atomically. The trace flags + are the same as for <c><![CDATA[erlang:trace/3]]></c>, + not including <c><![CDATA[cpu_timestamp]]></c>, but including + <c><![CDATA[tracer]]></c>.</p> + <p>If a tracer is specified in both lists, the tracer in the + enable list takes precedence. If no tracer is specified, the same + tracer as the process executing the match specification is used.</p> + <p>When using a <seealso marker="erl_tracer">tracer module</seealso>, + the module must be loaded before the match specification is + executed. If it is not loaded, the match fails.</p> + <p>With three parameters to this function, the first is + either a process identifier or the registered name of a + process to set trace flags on, the second is the disable + list, and the third is the enable list.</p> + <p>Returns <c><![CDATA[true]]></c> if any trace property was changed + for the trace target process, otherwise <c><![CDATA[false]]></c>. + Can only be used in the <c><![CDATA[MatchBody]]></c> part when + tracing.</p> + </item> + <tag><c>caller</c></tag> + <item> + <p>Returns the calling function as a tuple <c>{Module, Function, + Arity}</c> or the atom <c><![CDATA[undefined]]></c> if the calling + function cannot be determined. Can only be used in the + <c><![CDATA[MatchBody]]></c> part when tracing.</p> + <p>Notice that if a "technically built in function" (that is, a + function not written in Erlang) is traced, the + <c><![CDATA[caller]]></c> function sometimes returns the atom + <c><![CDATA[undefined]]></c>. The calling + Erlang function is not available during such calls.</p> + </item> + <tag><c>display</c></tag> + <item> + <p>For debugging purposes only. Displays the single argument as an + Erlang term on <c>stdout</c>, which is seldom what is wanted. + Returns <c><![CDATA[true]]></c> and can only be used in the + <c><![CDATA[MatchBody]]></c> part when tracing.</p> + </item> + <tag><marker id="get_tcw"/><c>get_tcw</c></tag> + <item> + <p>Takes no argument and returns the value of the node's trace + control word. The same is done by + <c><![CDATA[erlang:system_info(trace_control_word)]]></c>.</p> + <p>The trace control word is a 32-bit unsigned integer intended for + generic trace control. The trace control word can be tested and + set both from within trace match specifications and with BIFs. + This call is only allowed when tracing.</p> + </item> + <tag><marker id="set_tcw"/><c>set_tcw</c></tag> + <item> + <p>Takes one unsigned integer argument, sets the value of + the node's trace control word to the value of the argument, + and returns the previous value. The same is done by + <c><![CDATA[erlang:system_flag(trace_control_word, Value)]]></c>. + It is only allowed to use <c><![CDATA[set_tcw]]></c> in the + <c><![CDATA[MatchBody]]></c> part when tracing.</p> + </item> + <tag><c>silent</c></tag> + <item> + <p>Takes one argument. If the argument is <c><![CDATA[true]]></c>, + the call trace message mode for the current process is set to + silent for this call and all later calls, that is, call trace + messages are inhibited even if + <c><![CDATA[{message, true}]]></c> is called in the + <c><![CDATA[MatchBody]]></c> part for a traced function.</p> + <p>This mode can also be activated with flag + <c><![CDATA[silent]]></c> to + <c><![CDATA[erlang:trace/3]]></c>.</p> + <p>If the argument is <c><![CDATA[false]]></c>, the call trace + message mode for the current process is set to normal + (non-silent) for this call and all later calls.</p> + <p>If the argument is not <c><![CDATA[true]]></c> or + <c><![CDATA[false]]></c>, the call trace message mode is + unaffected.</p> + </item> + </taglist> </section> - <p><em>Note</em> that all "function calls" have to be tuples, - even if they take no arguments. The value of <c><![CDATA[self]]></c> is - the atom() <c><![CDATA[self]]></c>, but the value of <c><![CDATA[{self}]]></c> is - the pid() of the current process.</p> + + <note> + <p>All "function calls" must be tuples, even if they take no arguments. + The value of <c><![CDATA[self]]></c> is the atom() + <c><![CDATA[self]]></c>, but the value of <c><![CDATA[{self}]]></c> is + the pid() of the current process.</p> + </note> </section> - <marker id="match_target"></marker> <section> + <marker id="match_target"/> <title>Match target</title> <p>Each execution of a match specification is done against a match target term. The format and content of the target term @@ -422,217 +531,268 @@ </section> <section> - <title>Variables and literals</title> - <p>Variables take the form <c><![CDATA['$<number>']]></c> where - <c><![CDATA[<number>]]></c> is an integer between 0 (zero) and - 100000000 (1e+8), the behavior if the number is outside these - limits is <em>undefined</em>. In the <c><![CDATA[MatchHead]]></c> part, the special - variable <c><![CDATA['_']]></c> matches anything, and never gets bound (like - <c><![CDATA[_]]></c> in Erlang). In the <c><![CDATA[MatchCondition/MatchBody]]></c> - parts, no unbound variables are allowed, why <c><![CDATA['_']]></c> is - interpreted as itself (an atom). Variables can only be bound in - the <c><![CDATA[MatchHead]]></c> part. In the <c><![CDATA[MatchBody]]></c> and - <c><![CDATA[MatchCondition]]></c> parts, only variables bound previously may - be used. As a special case, in the - <c><![CDATA[MatchCondition/MatchBody]]></c> parts, the variable <c><![CDATA['$_']]></c> - expands to the whole <seealso marker="#match_target">match target</seealso> - term and the variable <c><![CDATA['$$']]></c> expands to a list - of the values of all bound variables in order - (i.e. <c><![CDATA[['$1','$2', ...]]]></c>). - </p> - <p>In the <c><![CDATA[MatchHead]]></c> part, all literals (except the variables - noted above) are interpreted as is. In the - <c><![CDATA[MatchCondition/MatchBody]]></c> parts, however, the - interpretation is in some ways different. Literals in the - <c><![CDATA[MatchCondition/MatchBody]]></c> can either be written as is, - which works for all literals except tuples, or by using the - special form <c><![CDATA[{const, T}]]></c>, where <c><![CDATA[T]]></c> is any Erlang - term. For tuple literals in the match_spec, one can also use - double tuple parentheses, i.e., construct them as a tuple of + <title>Variables and Literals</title> + <p>Variables take the form <c><![CDATA['$<number>']]></c>, where + <c><![CDATA[<number>]]></c> is an integer between 0 and + 100,000,000 (1e+8). The behavior if the number is outside these limits + is <em>undefined</em>. In the <c><![CDATA[MatchHead]]></c> part, the + special variable <c><![CDATA['_']]></c> matches anything, and never gets + bound (like <c><![CDATA[_]]></c> in Erlang).</p> + + <list type="bulleted"> + <item> + <p>In the <c><![CDATA[MatchCondition/MatchBody]]></c> parts, + no unbound variables are allowed, so <c><![CDATA['_']]></c> is + interpreted as itself (an atom). Variables can only be bound in the + <c><![CDATA[MatchHead]]></c> part.</p> + </item> + <item> + <p>In the <c><![CDATA[MatchBody]]></c> and + <c><![CDATA[MatchCondition]]></c> parts, only variables bound + previously can be used.</p> + </item> + <item> + <p>As a special case, the following apply in the + <c><![CDATA[MatchCondition/MatchBody]]></c> parts:</p> + <list type="bulleted"> + <item> + <p>The variable <c><![CDATA['$_']]></c> expands to the whole + <seealso marker="#match_target">match target</seealso> term. + </p> + </item> + <item> + <p>The variable <c><![CDATA['$$']]></c> expands to a list of the + values of all bound variables in order (that is, + <c><![CDATA[['$1','$2', ...]]]></c>).</p> + </item> + </list> + </item> + </list> + + <p>In the <c><![CDATA[MatchHead]]></c> part, all literals (except the + variables above) are interpreted "as is".</p> + + <p>In the <c><![CDATA[MatchCondition/MatchBody]]></c> parts, the + interpretation is in some ways different. Literals in these parts + can either be written "as is", which works for all literals except + tuples, or by using the special form <c><![CDATA[{const, T}]]></c>, + where <c><![CDATA[T]]></c> is any Erlang term.</p> + + <p>For tuple literals in the match specification, double tuple parentheses + can also be used, that is, construct them as a tuple of arity one containing a single tuple, which is the one to be constructed. The "double tuple parenthesis" syntax is useful to construct tuples from already bound variables, like in - <c><![CDATA[{{'$1', [a,b,'$2']}}]]></c>. Some examples may be needed: - </p> + <c><![CDATA[{{'$1', [a,b,'$2']}}]]></c>. Examples:</p> + <table> <row> - <cell align="left" valign="middle">Expression </cell> - <cell align="left" valign="middle">Variable bindings </cell> - <cell align="left" valign="middle">Result </cell> + <cell align="left" valign="middle"><em>Expression</em></cell> + <cell align="left" valign="middle"><em>Variable Bindings</em></cell> + <cell align="left" valign="middle"><em>Result</em></cell> </row> <row> - <cell align="left" valign="middle">{{'$1','$2'}} </cell> + <cell align="left" valign="middle">{{'$1','$2'}}</cell> <cell align="left" valign="middle">'$1' = a, '$2' = b</cell> <cell align="left" valign="middle">{a,b}</cell> </row> <row> - <cell align="left" valign="middle">{const, {'$1', '$2'}} </cell> - <cell align="left" valign="middle">doesn't matter</cell> + <cell align="left" valign="middle">{const, {'$1', '$2'}}</cell> + <cell align="left" valign="middle">Irrelevant</cell> <cell align="left" valign="middle">{'$1', '$2'}</cell> </row> <row> - <cell align="left" valign="middle">a </cell> - <cell align="left" valign="middle">doesn't matter </cell> + <cell align="left" valign="middle">a</cell> + <cell align="left" valign="middle">Irrelevant</cell> <cell align="left" valign="middle">a</cell> </row> <row> - <cell align="left" valign="middle">'$1' </cell> - <cell align="left" valign="middle">'$1' = [] </cell> + <cell align="left" valign="middle">'$1'</cell> + <cell align="left" valign="middle">'$1' = []</cell> <cell align="left" valign="middle">[]</cell> </row> <row> - <cell align="left" valign="middle">['$1'] </cell> - <cell align="left" valign="middle">'$1' = [] </cell> + <cell align="left" valign="middle">['$1']</cell> + <cell align="left" valign="middle">'$1' = []</cell> <cell align="left" valign="middle">[[]]</cell> </row> <row> - <cell align="left" valign="middle">[{{a}}] </cell> - <cell align="left" valign="middle">doesn't matter</cell> + <cell align="left" valign="middle">[{{a}}]</cell> + <cell align="left" valign="middle">Irrelevant</cell> <cell align="left" valign="middle">[{a}]</cell> </row> <row> - <cell align="left" valign="middle">42 </cell> - <cell align="left" valign="middle">doesn't matter</cell> + <cell align="left" valign="middle">42</cell> + <cell align="left" valign="middle">Irrelevant</cell> <cell align="left" valign="middle">42</cell> </row> <row> - <cell align="left" valign="middle">"hello" </cell> - <cell align="left" valign="middle">doesn't matter</cell> + <cell align="left" valign="middle">"hello"</cell> + <cell align="left" valign="middle">Irrelevant</cell> <cell align="left" valign="middle">"hello"</cell> </row> <row> - <cell align="left" valign="middle">$1 </cell> - <cell align="left" valign="middle">doesn't matter</cell> - <cell align="left" valign="middle">49 (the ASCII value for the character '1')</cell> + <cell align="left" valign="middle">$1</cell> + <cell align="left" valign="middle">Irrelevant</cell> + <cell align="left" valign="middle">49 (the ASCII value for + character '1')</cell> </row> - <tcaption>Literals in the MatchCondition/MatchBody parts of a match_spec</tcaption> + <tcaption>Literals in MatchCondition/MatchBody Parts of a Match + Specification</tcaption> </table> </section> <section> - <title>Execution of the match</title> + <title>Execution of the Match</title> <p>The execution of the match expression, when the runtime system - decides whether a trace message should be sent, goes as follows: - </p> - <p>For each tuple in the <c><![CDATA[MatchExpression]]></c> list and while no - match has succeeded:</p> - <list type="bulleted"> - <item>Match the <c><![CDATA[MatchHead]]></c> part against the - match target term, - binding the <c><![CDATA['$<number>']]></c> variables (much like in - <c><![CDATA[ets:match/2]]></c>). - If the <c><![CDATA[MatchHead]]></c> cannot match the arguments, the match fails. - </item> - <item>Evaluate each <c><![CDATA[MatchCondition]]></c> (where only - <c><![CDATA['$<number>']]></c> variables previously bound in the - <c><![CDATA[MatchHead]]></c> can occur) and expect it to return the atom - <c><![CDATA[true]]></c>. As soon as a condition does not evaluate to - <c><![CDATA[true]]></c>, the match fails. If any BIF call generates an - exception, also fail. + decides whether a trace message is to be sent, is as follows:</p> + + <p>For each tuple in the <c><![CDATA[MatchExpression]]></c> list and while + no match has succeeded:</p> + + <list type="ordered"> + <item> + <p>Match the <c><![CDATA[MatchHead]]></c> part against the match target + term, binding the <c><![CDATA['$<number>']]></c> variables + (much like in <c><![CDATA[ets:match/2]]></c>). If the + <c><![CDATA[MatchHead]]></c> part cannot match the arguments, the + match fails.</p> </item> <item> + <p>Evaluate each <c><![CDATA[MatchCondition]]></c> (where only + <c><![CDATA['$<number>']]></c> variables previously bound in the + <c><![CDATA[MatchHead]]></c> part can occur) and expect it to return + the atom <c><![CDATA[true]]></c>. When a condition does not evaluate + to <c><![CDATA[true]]></c>, the match fails. If any BIF call + generates an exception, the match also fails.</p> + </item> + <item> + <p>Two cases can occur:</p> <list type="bulleted"> - <item><em>If the match_spec is executing when tracing:</em><br></br> - Evaluate each <c><![CDATA[ActionTerm]]></c> in the same way as the - <c><![CDATA[MatchConditions]]></c>, but completely ignore the return - values. Regardless of what happens in this part, the match has - succeeded.</item> - <item><em>If the match_spec is executed when selecting objects from an ETS table:</em><br></br> - Evaluate the expressions in order and return the value of - the last expression (typically there is only one expression - in this context)</item> + <item> + <p>If the match specification is executing when tracing:</p> + <p>Evaluate each <c><![CDATA[ActionTerm]]></c> in the same way as + the <c><![CDATA[MatchConditions]]></c>, but ignore the return + values. Regardless of what happens in this part, the match has + succeeded.</p> + </item> + <item> + <p>If the match specification is executed when selecting objects + from an ETS table:</p> + <p>Evaluate the expressions in order and return the value of + the last expression (typically there is only one expression + in this context).</p> + </item> </list> </item> </list> </section> <section> - <title>Differences between match specifications in ETS and tracing</title> - <p>ETS match specifications are there to produce a return - value. Usually the <c><![CDATA[MatchBody]]></c> contains one single - <c><![CDATA[ConditionExpression]]></c> which defines the return value without having - any side effects. Calls with side effects are not allowed in the - ETS context.</p> + <marker id="differences_ets_tracing"/> + <title>Differences between Match Specifications in ETS and Tracing</title> + <p>ETS match specifications produce a return value. + Usually the <c><![CDATA[MatchBody]]></c> contains one single + <c><![CDATA[ConditionExpression]]></c> that defines the return value + without any side effects. Calls with side effects are not allowed in + the ETS context.</p> + <p>When tracing there is no return value to produce, the - match specification either matches or doesn't. The effect when the - expression matches is a trace message rather then a returned - term. The <c><![CDATA[ActionTerm]]></c>'s are executed as in an imperative - language, i.e. for their side effects. Functions with side effects + match specification either matches or does not. The effect when the + expression matches is a trace message rather than a returned + term. The <c><![CDATA[ActionTerm]]></c>s are executed as in an imperative + language, that is, for their side effects. Functions with side effects are also allowed when tracing.</p> </section> <section> <title>Tracing Examples</title> - <p>Match an argument list of three where the first and third arguments + <p>Match an argument list of three, where the first and third arguments are equal:</p> + <code type="none"><![CDATA[ [{['$1', '_', '$1'], [], []}] ]]></code> - <p>Match an argument list of three where the second argument is - a number greater than three:</p> + + <p>Match an argument list of three, where the second argument is + a number > 3:</p> + <code type="none"><![CDATA[ [{['_', '$1', '_'], [{ '>', '$1', 3}], []}] ]]></code> - <p>Match an argument list of three, where the third argument - is a tuple containing argument one and two <em>or</em> a list - beginning with argument one and two (i. e. <c><![CDATA[[a,b,[a,b,c]]]]></c> or - <c><![CDATA[[a,b,{a,b}]]]></c>): - </p> + + <p>Match an argument list of three, where the third argument is + either a tuple containing argument one and two, <em>or</em> a list + beginning with argument one and two (that is, + <c><![CDATA[[a,b,[a,b,c]]]]></c> or <c><![CDATA[[a,b,{a,b}]]]></c>):</p> + <code type="none"><![CDATA[ [{['$1', '$2', '$3'], - [{orelse, + [{'orelse', {'=:=', '$3', {{'$1','$2'}}}, {'and', {'=:=', '$1', {hd, '$3'}}, {'=:=', '$2', {hd, {tl, '$3'}}}}}], []}] ]]></code> - <p>The above problem may also be solved like this:</p> + + <p>The above problem can also be solved as follows:</p> + <code type="none"><![CDATA[ [{['$1', '$2', {'$1', '$2}], [], []}, {['$1', '$2', ['$1', '$2' | '_']], [], []}] ]]></code> - <p>Match two arguments where the first is a tuple beginning with - a list which in turn begins with the second argument times - two (i. e. [{[4,x],y},2] or [{[8], y, z},4])</p> + + <p>Match two arguments, where the first is a tuple beginning with + a list that in turn begins with the second argument times + two (that is, <c>[{[4,x],y},2]</c> or <c>[{[8], y, z},4])</c>:</p> + <code type="none"><![CDATA[ [{['$1', '$2'],[{'=:=', {'*', 2, '$2'}, {hd, {element, 1, '$1'}}}], []}] ]]></code> + <p>Match three arguments. When all three are equal and are - numbers, append the process dump to the trace message, else - let the trace message be as is, but set the sequential trace - token label to 4711.</p> + numbers, append the process dump to the trace message, otherwise + let the trace message be "as is", but set the sequential trace + token label to 4711:</p> + <code type="none"><![CDATA[ [{['$1', '$1', '$1'], [{is_number, '$1'}], [{message, {process_dump}}]}, {'_', [], [{set_seq_token, label, 4711}]}] ]]></code> - <p>As can be noted above, the parameter list can be matched - against a single <c><![CDATA[MatchVariable]]></c> or an <c><![CDATA['_']]></c>. To replace the - whole - parameter list with a single variable is a special case. In all - other cases the <c><![CDATA[MatchHead]]></c> has to be a <em>proper</em> list. - </p> - <p>Only generate trace message if trace control word is set to 1:</p> + + <p>As can be noted above, the parameter list can be matched against a + single <c><![CDATA[MatchVariable]]></c> or an <c><![CDATA['_']]></c>. + To replace the whole parameter list with a single variable is a special + case. In all other cases the <c><![CDATA[MatchHead]]></c> must be a + <em>proper</em> list.</p> + + <p>Generate a trace message only if the trace control word is set to 1:</p> + <code type="none"><![CDATA[ [{'_', [{'==',{get_tcw},{const, 1}}], []}] ]]></code> - <p>Only generate trace message if there is a seq trace token:</p> + + <p>Generate a trace message only if there is a <c>seq_trace</c> token:</p> + <code type="none"><![CDATA[ [{'_', [{'==',{is_seq_trace},{const, 1}}], []}] ]]></code> - <p>Remove 'silent' trace flag when first argument is 'verbose' - and add it when it is 'silent':</p> + + <p>Remove the <c>'silent'</c> trace flag when the first argument is + <c>'verbose'</c>, and add it when it is <c>'silent':</c></p> + <code type="none"><![CDATA[ [{'$1', [{'==',{hd, '$1'},verbose}], @@ -641,14 +801,19 @@ [{'==',{hd, '$1'},silent}], [{trace, [],[silent]}]}] ]]></code> - <p>Add return_trace message if function is of arity 3:</p> + + <p>Add a <c>return_trace</c> message if the function is of arity 3:</p> + <code type="none"><![CDATA[ [{'$1', [{'==',{length, '$1'},3}], [{return_trace}]}, {'_',[],[]}] ]]></code> - <p>Only generate trace message if function is of arity 3 and first argument is 'trace':</p> + + <p>Generate a trace message only if the function is of arity 3 and the + first argument is <c>'trace'</c>:</p> + <code type="none"><![CDATA[ [{['trace','$2','$3'], [], @@ -659,29 +824,35 @@ <section> <title>ETS Examples</title> - <p>Match all objects in an ets table where the first element is - the atom 'strider' and the tuple arity is 3 and return the whole - object.</p> + <p>Match all objects in an ETS table, where the first element is + the atom <c>'strider'</c> and the tuple arity is 3, and return the whole + object:</p> + <code type="none"><![CDATA[ [{{strider,'_','_'}, [], ['$_']}] ]]></code> - <p>Match all objects in an ets table with arity > 1 and the first - element is 'gandalf', return element 2.</p> + + <p>Match all objects in an ETS table with arity > 1 and the first + element is 'gandalf', and return element 2:</p> + <code type="none"><![CDATA[ [{'$1', [{'==', gandalf, {element, 1, '$1'}},{'>=',{size, '$1'},2}], [{element,2,'$1'}]}] ]]></code> - <p>In the above example, if the first element had been the key, - it's much more efficient to match that key in the <c><![CDATA[MatchHead]]></c> - part than in the <c><![CDATA[MatchConditions]]></c> part. The search space of - the tables is restricted with regards to the <c><![CDATA[MatchHead]]></c> so - that only objects with the matching key are searched. - </p> - <p>Match tuples of 3 elements where the second element is either - 'merry' or 'pippin', return the whole objects.</p> + + <p>In this example, if the first element had been the key, it is + much more efficient to match that key in the <c><![CDATA[MatchHead]]></c> + part than in the <c><![CDATA[MatchConditions]]></c> part. + The search space of the tables is restricted with regards to the + <c><![CDATA[MatchHead]]></c> so + that only objects with the matching key are searched.</p> + + <p>Match tuples of three elements, where the second element is either + <c>'merry'</c> or <c>'pippin'</c>, and return the whole objects:</p> + <code type="none"><![CDATA[ [{{'_',merry,'_'}, [], @@ -690,8 +861,9 @@ [], ['$_']}] ]]></code> - <p>The function <c><![CDATA[ets:test_ms/2]]></c> can be useful for testing - complicated ets matches.</p> + + <p>Function <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2></c></seealso> + can be useful for testing complicated ETS matches.</p> </section> </chapter> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 3c3129d543..2a36e5568c 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,72 @@ <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 8.0.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed a race that could cause a lost wakeup of a process + that timed out in a <c>receive ... after</c>. This bug + was introduced in ERTS version 7.0.</p> + <p> + Own Id: OTP-13798 Aux Id: OTP-11997 </p> + </item> + <item> + <p> + Fixed segfault after writing an erl crash dump.</p> + <p> + Own Id: OTP-13799</p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 8.0.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix scheduler deadlock bug in <c>ets:update_counter/4</c> + when key is not found and inserting the default object + causes the table to grow.</p> + <p> + Own Id: OTP-13731 Aux Id: ERL-188 </p> + </item> + <item> + <p> + Fix VM abort "Overrun stack and heap" in garbage + collection triggered by a <c>bsl</c> operation and some + very specific heap conditions.</p> + <p> + Own Id: OTP-13732 Aux Id: seq13142 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 8.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A memory allocation bug in <c>group_leader/2</c> could + cause an emulator crash when garbage collecting a process + that had been assigned a remote group leader. This bug + was introduced in ERTS version 8.0.</p> + <p> + Own Id: OTP-13716</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 8.0</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -523,9 +589,7 @@ Own Id: OTP-13440</p> </item> <item> - <p> - Add the following NIF API functions:</p> - <p> + <p>Add the following NIF API functions:</p> <list> <item><seealso marker="erl_nif#enif_cpu_time"><c>enif_cpu_time</c></seealso></item> <item><seealso @@ -542,9 +606,9 @@ marker="erl_nif#enif_binary_to_term"><c>enif_binary_to_term</c></seealso></item> <item><seealso marker="erl_nif#enif_port_command"><c>enif_port_command</c></seealso></item> - </list></p> + </list> <p> - for details of what each function does, see the erl_nif + For details of what each function does, see the erl_nif documentation.</p> <p> Own Id: OTP-13442</p> @@ -9316,9 +9380,9 @@ dynamically linking against <c>libssl.so</c> and <c>libcrypto.so</c>. The runtime library search path has also been extended. </item><item> The <c>configure</c> - scripts of <c>erl_interface</c> and <c>odbc</c> now + scripts of Erl_interface and ODBC now search for thread libraries and thread library quirks the - same way as <c>erts</c> do. </item><item> The + same way as ERTS do. </item><item> The <c>configure</c> script of the <c>odbc</c> application now also looks for odbc libraries in <c>lib64</c> and <c>lib/64</c> directories when building on a 64-bit @@ -10434,7 +10498,7 @@ </item> <item> <p> - A corrected bug in <c>ets</c> for <c>bag</c> and + A corrected bug in ETS for <c>bag</c> and <c>duplicate_bag</c>. A <c>delete/2</c> or <c>lookup_element/3</c> could miss objects in a fixed table if one or more objects with the same key had @@ -10876,7 +10940,7 @@ <list> <item> <p> - A corrected bug in <c>ets</c> for <c>bag</c> and + A corrected bug in ETS for <c>bag</c> and <c>duplicate_bag</c>. A <c>delete/2</c> or <c>lookup_element/3</c> could miss objects in a fixed table if one or more objects with the same key had diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml index b2abfc62ca..d583b873a0 100644 --- a/erts/doc/src/part.xml +++ b/erts/doc/src/part.xml @@ -30,8 +30,8 @@ <file>part.xml</file> </header> <description> - <p>The Erlang Runtime System Application <em>ERTS</em>.</p> </description> + <xi:include href="introduction.xml"/> <xi:include href="communication.xml"/> <xi:include href="time_correction.xml"/> <xi:include href="match_spec.xml"/> diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index e45402a397..0617463a7b 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -30,14 +30,6 @@ <file>application.xml</file> </header> <description> - <p>The Erlang Runtime System Application <em>ERTS</em>.</p> - <note> - <p>By default, the <c><![CDATA[erts]]></c> is only guaranteed to be compatible - with other Erlang/OTP components from the same release as - the <c><![CDATA[erts]]></c> itself. See the documentation of the system flag - <seealso marker="erl#compat_rel">+R</seealso> on how to communicate - with Erlang/OTP components from earlier releases.</p> - </note> </description> <xi:include href="erl_prim_loader.xml"/> <xi:include href="erlang.xml"/> diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml index 6b0fef7c0a..ad7b2c5b85 100644 --- a/erts/doc/src/run_erl.xml +++ b/erts/doc/src/run_erl.xml @@ -28,137 +28,184 @@ <docno></docno> <approved></approved> <checked></checked> - <date>99-12-15</date> + <date>1999-12-15</date> <rev></rev> <file>run_erl.xml</file> </header> <com>run_erl</com> - <comsummary>Redirect Erlang input and output streams on Solaris®</comsummary> + <comsummary>Redirect Erlang input and output streams on Unix systems.</comsummary> <description> - <p>This describes the <c><![CDATA[run_erl]]></c> program specific to - Solaris/Linux. This program redirect the standard input and standard - output streams so that all output can be logged. It also let the - program <c><![CDATA[to_erl]]></c> connect to the Erlang console making it - possible to monitor and debug an embedded system remotely.</p> - <p>You can read more about the use in the <c><![CDATA[Embedded System User's Guide]]></c>.</p> + <p>The <c><![CDATA[run_erl]]></c> program is specific to Unix systems. + This program redirects the standard input and standard + output streams so that all output can be logged. It also lets the + program <c><![CDATA[to_erl]]></c> connect to the Erlang console, making + it possible to monitor and debug an embedded system remotely.</p> + + <p>For more information about the use, see the + <seealso marker="doc/embedded:embedded_solaris"> + Embedded System User's Guide</seealso> in System Documentation.</p> </description> + <funcs> <func> - <name>run_erl [-daemon] pipe_dir/ log_dir "exec command [command_arguments]"</name> - <fsummary>Start the Erlang emulator without attached terminal</fsummary> + <name>run_erl [-daemon] pipe_dir/ log_dir "exec command + [command_arguments]"</name> + <fsummary>Start the Erlang emulator without attached terminal.</fsummary> <desc> - <p>The <c><![CDATA[run_erl]]></c> program arguments are:</p> + <p>Arguments:</p> <taglist> - <tag>-daemon</tag> - <item>This option is highly recommended. It makes run_erl run in - the background completely detached from any controlling - terminal and the command returns to the caller immediately. - Without this option, run_erl must be started using several - tricks in the shell to detach it completely from the - terminal in use when starting it. The option must be the - first argument to run_erl on the command line.</item> - <tag>pipe_dir</tag> - <item>This is where to put the named pipe, usually - <c><![CDATA[/tmp/]]></c>. It shall be suffixed by a <c><![CDATA[/]]></c> (slash), - i.e. not <c><![CDATA[/tmp/epipies]]></c>, but <c><![CDATA[/tmp/epipes/]]></c>. </item> - <tag>log_dir</tag> - <item>This is where the log files are written. There will be one - log file, <c><![CDATA[run_erl.log]]></c> that log progress and - warnings from the <c><![CDATA[run_erl]]></c> program itself and there - will be up to five log files at maximum 100KB each (both - number of logs and sizes can be - changed by environment variables, see below) with - the content of the standard streams from and to the - command. When the logs are full <c><![CDATA[run_erl]]></c> will delete - and reuse the oldest log file.</item> - <tag>"exec command [command_arguments]"</tag> - <item>In the third argument <c><![CDATA[command]]></c> is the to execute - where everything written to stdin and stdout is logged to - <c><![CDATA[log_dir]]></c>.</item> + <tag><c>-daemon</c></tag> + <item> + <p>This option is highly recommended. It makes <c>run_erl</c> run + in the background completely detached from any controlling + terminal and the command returns to the caller immediately. + Without this option, <c>run_erl</c> must be started using several + tricks in the shell to detach it completely from the + terminal in use when starting it. The option must be the + first argument to <c>run_erl</c> on the command line.</p> + </item> + <tag><c>pipe_dir</c></tag> + <item> + <p>The named pipe, usually <c><![CDATA[/tmp/]]></c>. It must be + suffixed by a <c><![CDATA[/]]></c> (slash), that is, + <c><![CDATA[/tmp/epipes/]]></c>, not + <c><![CDATA[/tmp/epipes]]></c>.</p> + </item> + <tag><c>log_dir</c></tag> + <item> + <p>The log files, that is:</p> + <list type="bulleted"> + <item> + <p>One log file, <c><![CDATA[run_erl.log]]></c>, which logs + progress and warnings from the <c><![CDATA[run_erl]]></c> + program itself.</p> + </item> + <item> + <p>Up to five log files at maximum 100 KB each with the content + of the standard streams from and to the command. (Both the + number of logs and sizes can be changed by environment + variables, see section <seealso + marker="#environment_variables">Environment Variables</seealso> + below.)</p> + <p>When the logs are full, <c><![CDATA[run_erl]]></c> deletes + and reuses the oldest log file.</p> + </item> + </list> + </item> + <tag><c>"exec command [command_arguments]"</c></tag> + <item> + <p>In the third argument, <c><![CDATA[command]]></c> is the + executable to execute where everything written to <c>stdin</c> + and <c>stdout</c> is logged to <c><![CDATA[log_dir]]></c>.</p> + </item> </taglist> </desc> </func> </funcs> <section> - <title>Notes concerning the log files</title> - <p>While running, run_erl (as stated earlier) sends all output, - uninterpreted, to a log file. The file is called - <c><![CDATA[erlang.log.N]]></c>, where N is a number. When the log is "full", - default after 100KB, run_erl starts to log in file - <c><![CDATA[erlang.log.(N+1)]]></c>, until N reaches a certain number (default - 5), where after N starts at 1 again and the oldest files start - getting overwritten. If no output comes from the erlang shell, but - the erlang machine still seems to be alive, an "ALIVE" message is - written to the log, it is a timestamp and is written, by default, - after 15 minutes of inactivity. Also, if output from erlang is - logged but it's been more than 5 minutes (default) since last time - we got anything from erlang, a timestamp is written in the - log. The "ALIVE" messages look like this:</p> + <title>Notes concerning the Log Files</title> + <p>While running, <c>run_erl</c> (as stated earlier) sends all output, + uninterpreted, to a log file. The file is named + <c><![CDATA[erlang.log.N]]></c>, where <c>N</c> is an integer. When the + log is "full" (default log size is 100 KB), <c>run_erl</c> starts to log + in file <c><![CDATA[erlang.log.(N+1)]]></c>, until <c>N</c> reaches a + certain number (default 5), whereupon <c>N</c> starts at 1 again and + the oldest files start getting overwritten.</p> + + <p>If no output comes from the Erlang shell, but + the Erlang machine still seems to be alive, an "ALIVE" message is + written to the log; it is a time stamp and is written, by default, + after 15 minutes of inactivity. Also, if output from Erlang is + logged, but more than 5 minutes (default) has passed since last time + we got anything from Erlang, a time stamp is written in the + log. The "ALIVE" messages look as follows:</p> + <code type="none"><![CDATA[ - ===== ALIVE <date-time-string> - ]]></code> - <p>while the other timestamps look like this:</p> +===== ALIVE <date-time-string> ]]></code> + + <p>The other time stamps look as follows:</p> + <code type="none"><![CDATA[ - ===== <date-time-string> - ]]></code> - <p>The <c><![CDATA[date-time-string]]></c> is the date and time the message is - written, default in local time (can be changed to GMT if one wants - to) and is formatted with the ANSI-C function <c><![CDATA[strftime]]></c> - using the format string <c><![CDATA[%a %b %e %T %Z %Y]]></c>, which produces - messages on the line of <c><![CDATA[===== ALIVE Thu May 15 10:13:36 MEST 2003]]></c>, this can be changed, see below.</p> +===== <date-time-string> ]]></code> + + <p><c><![CDATA[date-time-string]]></c> is the date and time the message is + written, default in local time (can be changed to UTC if needed). + It is formatted with the ANSI-C function <c><![CDATA[strftime]]></c> + using the format string <c><![CDATA[%a %b %e %T %Z %Y]]></c>, which + produces messages like + <c><![CDATA[===== ALIVE Thu May 15 10:13:36 MEST 2003]]></c>; this can + be changed, see the next section.</p> </section> <section> - <title>Environment variables</title> - <p>The following environment variables are recognized by run_erl - and change the logging behavior. Also see the notes above to get - more info on how the log behaves.</p> + <marker id="environment_variables"/> + <title>Environment Variables</title> + <p>The following environment variables are recognized by <c>run_erl</c> + and change the logging behavior. For more information, see the previous + section.</p> + <taglist> - <tag>RUN_ERL_LOG_ALIVE_MINUTES</tag> - <item>How long to wait for output (in minutes) before writing an - "ALIVE" message to the log. Default is 15, can never be less - than 1.</item> - <tag>RUN_ERL_LOG_ACTIVITY_MINUTES</tag> - <item>How long erlang need to be inactive before output will be - preceded with a timestamp. Default is - RUN_ERL_LOG_ALIVE_MINUTES div 3, but never less than 1.</item> - <tag>RUN_ERL_LOG_ALIVE_FORMAT</tag> - <item>Specifies another format string to be used in the strftime - C library call. i.e specifying this to <c><![CDATA["%e-%b-%Y, %T %Z"]]></c> - will give log messages with timestamps looking like - <c><![CDATA[15-May-2003, 10:23:04 MET]]></c> etc. See the documentation - for the C library function strftime for more - information. Default is <c><![CDATA["%a %b %e %T %Z %Y"]]></c>.</item> - <tag>RUN_ERL_LOG_ALIVE_IN_UTC</tag> - <item>If set to anything else than "0", it will make all - times displayed by run_erl to be in UTC (GMT,CET,MET, without - DST), rather than - in local time. This does not affect data coming from erlang, - only the logs output directly by run_erl. The application - <c><![CDATA[sasl]]></c> can be modified accordingly by setting the erlang - application variable <c><![CDATA[utc_log]]></c> to <c><![CDATA[true]]></c>.</item> - <tag>RUN_ERL_LOG_GENERATIONS</tag> - <item>Controls the number of log files written before older - files are being reused. Default is 5, minimum is 2, maximum is 1000.</item> - <tag>RUN_ERL_LOG_MAXSIZE</tag> - <item>The size (in bytes) of a log file before switching to a - new log file. Default is 100000, minimum is 1000 and maximum is - approximately 2^30.</item> - <tag>RUN_ERL_DISABLE_FLOWCNTRL</tag> - <item>If defined, disables input and output flow control for the pty opend by run_erl. - Useful if you want to remove any risk of accidentally blocking the flow control - by hit Ctrl-S (instead of Ctrl-D to detach). - Which may result in blocking of the entire beam process - and in the case of running heart as supervisor - even the heart process will be blocked when writing log message to terminal. - Leaving the heart process unable to do its work.</item> + <tag><c>RUN_ERL_LOG_ALIVE_MINUTES</c></tag> + <item> + <p>How long to wait for output (in minutes) before writing an + "ALIVE" message to the log. Defaults to 15, minimum is 1.</p> + </item> + <tag><c>RUN_ERL_LOG_ACTIVITY_MINUTES</c></tag> + <item> + <p>How long Erlang needs to be inactive before output is + preceded with a time stamp. Defaults to + <c>RUN_ERL_LOG_ALIVE_MINUTES div 3</c>, minimum is 1.</p> + </item> + <tag><c>RUN_ERL_LOG_ALIVE_FORMAT</c></tag> + <item> + <p>Specifies another format string to be used in the <c>strftime</c> + C library call. That is, specifying this to + <c><![CDATA["%e-%b-%Y, %T %Z"]]></c> gives + log messages with time stamps like + <c><![CDATA[15-May-2003, 10:23:04 MET]]></c>. For more information, + see the documentation for the C library function <c>strftime</c>. + Defaults to <c><![CDATA["%a %b %e %T %Z %Y"]]></c>.</p> + </item> + <tag><c>RUN_ERL_LOG_ALIVE_IN_UTC</c></tag> + <item> + <p>If set to anything else than <c>0</c>, it makes all + times displayed by <c>run_erl</c> to be in UTC (GMT, CET, MET, + without Daylight Saving Time), rather than in local time. + This does not affect data coming from Erlang, + only the logs output directly by <c>run_erl</c>. Application + SASL can be modified accordingly by setting the Erlang + application variable <c><![CDATA[utc_log]]></c> to + <c><![CDATA[true]]></c>.</p> + </item> + <tag><c>RUN_ERL_LOG_GENERATIONS</c></tag> + <item> + <p>Controls the number of log files written before older + files are reused. Defaults to 5, minimum is 2, maximum is 1000.</p> + </item> + <tag><c>RUN_ERL_LOG_MAXSIZE</c></tag> + <item> + <p>The size, in bytes, of a log file before switching to a + new log file. Defaults to 100000, minimum is 1000, maximum is + about 2^30.</p> + </item> + <tag><c>RUN_ERL_DISABLE_FLOWCNTRL</c></tag> + <item> + <p>If defined, disables input and output flow control for the pty + opend by <c>run_erl</c>. Useful if you want to remove any risk of + accidentally blocking the flow control by using Ctrl-S (instead of + Ctrl-D to detach), which can result in blocking of the entire Beam + process, and in the case of running heart as supervisor even the + heart process becomes blocked when writing log message to terminal, + leaving the heart process unable to do its work.</p> + </item> </taglist> </section> <section> - <title>SEE ALSO</title> - <p>start(1), start_erl(1)</p> + <title>See Also</title> + <p><seealso marker="start"><c>start(1)</c></seealso>, + <seealso marker="start_erl"><c>start_erl(1)</c></seealso></p> </section> </comref> diff --git a/erts/doc/src/start.xml b/erts/doc/src/start.xml index adacf5b98d..6eac47fe94 100644 --- a/erts/doc/src/start.xml +++ b/erts/doc/src/start.xml @@ -28,38 +28,46 @@ <docno></docno> <approved></approved> <checked></checked> - <date>99-12-15</date> + <date>1999-12-15</date> <rev></rev> <file>start.xml</file> </header> <com>start</com> - <comsummary>OTP start script example for Unix</comsummary> + <comsummary>OTP start script example for Unix.</comsummary> <description> - <p>This describes the <c><![CDATA[start]]></c> script that is an example script on - how to startup the Erlang system in embedded mode on Unix.</p> - <p>You can read more about the use in the <c><![CDATA[Embedded System User's Guide]]></c>.</p> + <p>The <c><![CDATA[start]]></c> script is an example script on + how to start up the Erlang system in embedded mode on Unix.</p> + + <p>For more information about the use, see the + <seealso marker="doc/embedded:embedded_solaris"> + Embedded System User's Guide</seealso> in System Documentation.</p> </description> + <funcs> <func> <name>start [ data_file ]</name> - <fsummary>This is an example script on how to startup the Erlang system in embedded mode on Unix.</fsummary> + <fsummary>Example script on how to start up the Erlang system in embedded + mode on Unix.</fsummary> <desc> - <p>In the example there is one argument</p> + <p>Argument:</p> <taglist> - <tag>data_file</tag> - <item>Optional, specifies what <c><![CDATA[start_erl.data]]></c> file - to use.</item> + <tag><c>data_file</c></tag> + <item> + <p>Optional. Specifies what <c><![CDATA[start_erl.data]]></c> file + to use.</p> + </item> </taglist> - <p>There is also an environment variable <c><![CDATA[RELDIR]]></c> that can - be set prior to calling this example that set the directory + <p>Environment variable <c><![CDATA[RELDIR]]></c> can + be set before calling this example, which sets the directory where to find the release files.</p> </desc> </func> </funcs> <section> - <title>SEE ALSO</title> - <p>run_erl(1), start_erl(1)</p> + <title>See Also</title> + <p><seealso marker="run_erl"><c>run_erl(1)</c></seealso>, + <seealso marker="start_erl"><c>start_erl(1)</c></seealso></p> </section> </comref> diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml index 243aeaa717..4887d4606e 100644 --- a/erts/doc/src/start_erl.xml +++ b/erts/doc/src/start_erl.xml @@ -28,117 +28,142 @@ <docno></docno> <approved></approved> <checked></checked> - <date>98-08-05</date> + <date>1998-08-05</date> <rev></rev> <file>start_erl.xml</file> </header> <com>start_erl</com> - <comsummary>Start Erlang for embedded systems on Windows NT®</comsummary> + <comsummary>Start Erlang for embedded systems on Windows systems.</comsummary> <description> - <p>This describes the <c><![CDATA[start_erl]]></c> program specific to Windows - NT. Although there exists programs with the same name on other - platforms, their functionality is not the same.</p> - <p>The <c><![CDATA[start_erl]]></c> program is distributed both in compiled + <p>The <c><![CDATA[start_erl]]></c> program is specific to + Windows NT/2000/XP (and later versions of Windows). + Although there are programs with the same name on other + platforms, their functionality is different.</p> + + <p>This program is distributed both in compiled form (under <Erlang root>\\erts-<version>\\bin) and - in source form (under <Erlang - root>\\erts-<version>\\src). - The purpose of the source code is to make it possible to easily - customize the program for local needs, such as cyclic restart - detection etc. There is also a "make"-file, written for the - <c><![CDATA[nmake]]></c> program distributed with Microsoft® Visual - C++®. The program can however be compiled with - any Win32 C compiler (possibly with slight modifications).</p> - <p>The purpose of the program is to aid release handling on - Windows NT®. The program should be called by the + in source form (under <Erlang root>\\erts-<version>\\src). + The purpose of the source code is to ease customization of the + program for local needs, such as cyclic restart + detection. There is also a "make"-file, written for the + <c><![CDATA[nmake]]></c> program distributed with Microsoft Visual + C++. This program can, however, be compiled with + any Win32 C compiler (possibly with minor modifications).</p> + + <p>This program aids release handling on Windows systems. + The program is to be called by the <c><![CDATA[erlsrv]]></c> program, read up the release data file - start_erl.data and start Erlang. Certain options to start_erl - are added and removed by the release handler during upgrade with - emulator restart (more specifically the <c><![CDATA[-data]]></c> option).</p> + <c>start_erl.data</c>, and start Erlang. Some options to + <c>start_erl</c> are added and removed by the release handler + during upgrade with emulator restart (more specifically option + <c><![CDATA[-data]]></c>).</p> </description> + <funcs> <func> <name>start_erl [<erl options>] ++ [<start_erl options>]</name> - <fsummary>Start the Erlang emulator with the correct release data</fsummary> + <fsummary>Start the Erlang emulator with the correct release data. + </fsummary> <desc> - <p>The <c><![CDATA[start_erl]]></c> program in its original form + <p>The <c><![CDATA[start_erl]]></c> program in its original form recognizes the following options:</p> <taglist> - <tag>++</tag> - <item>Mandatory, delimits start_erl options from normal Erlang - options. Everything on the command line <em>before</em> the - <c><![CDATA[++]]></c> is interpreted as options to be sent to the - <c><![CDATA[erl]]></c> program. Everything <em>after</em><c><![CDATA[++]]></c> is - interpreted as options to <c><![CDATA[start_erl]]></c> itself.</item> - <tag>-reldir <release root></tag> - - <item>Mandatory if the environment variable - <c><![CDATA[RELDIR]]></c> is not specified and no - <c>-rootdir</c> option is given. Tells start_erl where the - root of the release tree is placed in the file-system (typically - <Erlang root>\\releases). The - <c><![CDATA[start_erl.data]]></c> file is expected to be - placed in this directory (if not otherwise specified). If - only the <c>-rootdir</c> option is given, the directory is - assumed to be <Erlang root>\\releases.</item> - - <tag>-rootdir <Erlang root directory></tag> - - <item>Mandatory if <c>-reldir</c> is not given and there is - no <c><![CDATA[RELDIR]]></c> in the environment. This - specifies the Erlang installation root directory (under - which the <c>lib</c>, <c>releases</c> and - <c>erts-<Version></c> directories are placed). If only - <c>-reldir</c> (or the environment variable - <c><![CDATA[RELDIR]]></c>) is given, the Erlang root is assumed to - be the directory exactly one level above the release - directory.</item> - - <tag>-data <data file name></tag> - <item>Optional, specifies another data file than start_erl.data - in the <release root>. It is specified relative to the - <release root> or absolute (including drive letter - etc.). This option is used by the release handler during - upgrade and should not be used during normal - operation. The release data file should not normally be - named differently.</item> - <tag>-bootflags <boot flags file name></tag> - <item>Optional, specifies a file name relative to actual release - directory (that is the subdirectory of <release - root> where the <c><![CDATA[.boot]]></c> file etc. are placed). - The contents of this file is appended to the command line - when Erlang is started. This makes it easy to start the - emulator with different options for different releases.</item> + <tag><c>++</c></tag> + <item> + <p>Mandatory. Delimits <c>start_erl</c> options from normal Erlang + options. Everything on the command line <em>before</em> + <c><![CDATA[++]]></c> is interpreted as options to be sent to the + <c><![CDATA[erl]]></c> program. Everything <em>after</em> + <c><![CDATA[++]]></c> is interpreted as options to + <c><![CDATA[start_erl]]></c> itself.</p> + </item> + <tag><c>-reldir <release root></c></tag> + <item> + <p>Mandatory if environment variable + <c><![CDATA[RELDIR]]></c> is not specified and no + <c>-rootdir</c> option is specified. Tells <c>start_erl</c> where + the root of the release tree is located in the file system + (typically <Erlang root>\\releases). The + <c><![CDATA[start_erl.data]]></c> file is expected to be + located in this directory (unless otherwise specified). If + only option <c>-rootdir</c> is specified, the directory is + assumed to be <Erlang root>\\releases.</p> + </item> + <tag><c>-rootdir <Erlang root directory></c></tag> + <item> + <p>Mandatory if <c>-reldir</c> is not specified and no + <c><![CDATA[RELDIR]]></c> exists in the environment. This + specifies the Erlang installation root directory (under + which the <c>lib</c>, <c>releases</c>, and + <c>erts-<Version></c> directories are located). If only + <c>-reldir</c> (or environment variable <c><![CDATA[RELDIR]]></c>) + is specified, the Erlang root is assumed to + be the directory exactly one level above the release + directory.</p> + </item> + <tag><c>-data <data file name></c></tag> + <item> + <p>Optional. Specifies another data file than <c>start_erl.data</c> + in the <release root>. It is specified relative to the + <release root> or absolute (including drive letter, and so + on). This option is used by the release handler during + upgrade and is not to be used during normal + operation. Normally the release data file is not to be + named differently.</p> + </item> + <tag><c>-bootflags <boot flags file name></c></tag> + <item> + <p>Optional. Specifies a file name relative to the release + directory (that is, the subdirectory of <release root> + where the <c><![CDATA[.boot]]></c> file and others are located). + The contents of this file is appended to the command line + when Erlang is started. This makes it easy to start the + emulator with different options for different releases.</p> + </item> </taglist> </desc> </func> </funcs> <section> - <title>NOTES</title> - <p>As the source code is distributed, it can easily be modified to - accept other options. The program must still accept the - <c><![CDATA[-data]]></c> option with the semantics described above for the - release handler to work correctly.</p> - <p>The Erlang emulator is found by examining the registry keys for - the emulator version specified in the release data file. The new - emulator needs to be properly installed before the upgrade for - this to work.</p> - <p>Although the program is located together with files specific to - emulator version, it is not expected to be specific to the - emulator version. The release handler does <em>not</em> change the - <c><![CDATA[-machine]]></c> option to <c><![CDATA[erlsrv]]></c> during emulator restart. - Place the (possibly customized) <c><![CDATA[start_erl]]></c> program so that - it is not overwritten during upgrade. </p> - <p>The <c><![CDATA[erlsrv]]></c> program's default options are not - sufficient for release handling. The machine <c><![CDATA[erlsrv]]></c> - starts should be specified as the <c><![CDATA[start_erl]]></c> program and - the arguments should contain the <c><![CDATA[++]]></c> followed by desired - options.</p> + <title>Notes</title> + <list type="bulleted"> + <item> + <p>As the source code is distributed, it can easily be modified to + accept other options. The program must still accept option + <c><![CDATA[-data]]></c> with the semantics described above for the + release handler to work correctly.</p> + </item> + <item> + <p>The Erlang emulator is found by examining the registry keys for + the emulator version specified in the release data file. The new + emulator must be properly installed before the upgrade for + this to work.</p> + </item> + <item> + <p>Although the program is located together with files specific to the + emulator version, it is not expected to be specific to the + emulator version. The release handler does <em>not</em> change option + <c><![CDATA[-machine]]></c> to <c><![CDATA[erlsrv]]></c> during + emulator restart. Locate the (possibly customized) + <c><![CDATA[start_erl]]></c> program so that it is not overwritten + during upgrade.</p> + </item> + <item> + <p>The default options of the <c><![CDATA[erlsrv]]></c> program are not + sufficient for release handling. The machine started by + <c><![CDATA[erlsrv]]></c> is be specified as the + <c><![CDATA[start_erl]]></c> program and the arguments are to contain + <c><![CDATA[++]]></c> followed by the desired options.</p> + </item> + </list> </section> <section> - <title>SEE ALSO</title> - <p>erlsrv(1), release_handler(3)</p> + <title>See Also</title> + <p><seealso marker="erlsrv"><c>erlsrv(1)</c></seealso>, + <seealso marker="sasl:release_handler"> + <c>release_handler(3)</c></seealso></p> </section> </comref> diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 236fe679cb..77e7a40529 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1999</year><year>2015</year> + <year>1999</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -35,12 +35,12 @@ <section> <title>New Extended Time Functionality</title> - <note><p>As of OTP 18 (<c>ERTS</c> version 7.0) the time functionality of - Erlang has been extended. This includes a + <note><p>As from Erlang/OTP 18 (ERTS 7.0) the time functionality + has been extended. This includes a <seealso marker="#The_New_Time_API">new API</seealso> for time and <seealso marker="#Time_Warp_Modes">time warp - modes</seealso> that alter the system behavior when + modes</seealso> that change the system behavior when system time changes.</p> <p>The <seealso marker="#No_Time_Warp_Mode">default @@ -106,14 +106,18 @@ <section> <title>POSIX Time</title> <p>Time since - <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_17">Epoch</url>. + <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_17"> + Epoch</url>. Epoch is defined to be 00:00:00 <seealso marker="#UTC">UTC</seealso>, 1970-01-01. - <url href="http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap04.html#tag_04_14">A day in POSIX time</url> - is defined to be exactly 86400 seconds long. Strangely enough + <url href="http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap04.html#tag_04_14"> + A day in POSIX time</url> + is defined to be exactly 86400 seconds long. Strangely enough, Epoch is defined to be a time in UTC, and UTC has another definition of how long a day is. Quoting the Open Group - <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_15">"POSIX time is therefore not necessarily UTC, despite its appearance"</url>. + <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_15"> + "POSIX time is therefore not necessarily UTC, despite its + appearance"</url>. The effect of this is that when an UTC leap second is inserted, POSIX time either stops for a second, or repeats the last second. If an UTC leap second would be deleted (which has not @@ -157,7 +161,8 @@ <p>The operating systems view of <seealso marker="#POSIX_Time">POSIX time</seealso>. To retrieve it, call - <seealso marker="kernel:os#system_time/0"><c>os:system_time()</c></seealso>. + <seealso marker="kernel:os#system_time/0"> + <c>os:system_time()</c></seealso>. This may or may not be an accurate view of POSIX time. This time may typically be adjusted both backwards and forwards without limitation. That is, <seealso marker="#Time_Warp">time warps</seealso> @@ -165,25 +170,26 @@ <p>To get information about the Erlang runtime system's source of OS system time, call - <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>.</p> + <seealso marker="erlang#system_info_os_system_time_source"> + <c>erlang:system_info(os_system_time_source)</c></seealso>.</p> </section> <marker id="OS_Monotonic_Time"/> <section> <title>OS Monotonic Time</title> - <p>A monotonically increasing time provided by the operating - system. This time does not leap and has a relatively steady + <p>A monotonically increasing time provided by the OS. + This time does not leap and has a relatively steady frequency although not completely correct. However, it is not uncommon that OS monotonic time stops if the system is suspended. This time typically increases since some unspecified point in time that is not connected to <seealso marker="#OS_System_Time">OS system time</seealso>. - This type of time is not necessarily provided by all - operating systems.</p> + This type of time is not necessarily provided by all OSs.</p> <p>To get information about the Erlang runtime system's source of OS monotonic time, call - <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p> + <seealso marker="erlang#system_info_os_monotonic_time_source"> + <c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p> </section> <marker id="Erlang_System_Time"/> @@ -192,7 +198,8 @@ <p>The Erlang runtime systems view of <seealso marker="#POSIX_Time">POSIX time</seealso>. To retrieve it, call - <seealso marker="erlang#system_time/0"><c>erlang:system_time()</c></seealso>.</p> + <seealso marker="erlang#system_time/0"> + <c>erlang:system_time()</c></seealso>.</p> <p>This time may or may not be an accurate view of POSIX time, and may @@ -211,8 +218,8 @@ <p>A monotonically increasing time provided by the Erlang runtime system. Erlang monotonic time increases since some unspecified point in time. To retrieve it, call - <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso>. - </p> + <seealso marker="erlang#monotonic_time/0"> + <c>erlang:monotonic_time()</c></seealso>.</p> <p>The <seealso marker="#Time_Accuracy">accuracy</seealso> and <seealso marker="#Time_Precision">precision</seealso> of Erlang @@ -225,7 +232,8 @@ <item>Accuracy and precision of <seealso marker="#OS_System_Time">OS system time</seealso> </item> - <item><seealso marker="#Time_Warp_Modes">time warp mode</seealso> used + <item><seealso marker="#Time_Warp_Modes"> + time warp mode</seealso> used </item> </list> @@ -238,16 +246,17 @@ time is the "time engine" that is used for more or less everything that has anything to do with time. All timers, regardless of it is a <c>receive ... after</c> timer, BIF timer, - or a timer in the <c>timer</c> module, are triggered - relative Erlang monotonic time. Even + or a timer in the + <seealso marker="stdlib:timer"><c>timer(3)</c></seealso> + module, are triggered relative Erlang monotonic time. Even <seealso marker="#Erlang_System_Time">Erlang system time</seealso> is based on Erlang monotonic time. By adding current Erlang monotonic time with current time offset, you get current Erlang system time.</p> - <p>To retrieve current time offset, call - <seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso>. - </p> + <p>To retrieve the current time offset, call + <seealso marker="erlang#time_offset/0"> + <c>erlang:time_offset/0</c></seealso>.</p> </section> </section> @@ -278,7 +287,7 @@ less potent time-keeper than an atomic clock is used.</p> <p>However, NTP is not fail-safe. The NTP server can be unavailable, - <c>ntp.conf</c> can be wrongly configured, or your computer may + <c>ntp.conf</c> can be wrongly configured, or your computer can sometimes be disconnected from Internet. Furthermore, you can have a user (or even system administrator) who thinks the correct way to handle Daylight Saving Time is to adjust the clock one @@ -327,16 +336,18 @@ implementation in the Erlang runtime system using OS monotonic time. To check if your system has support for OS monotonic time, call - <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>. + <seealso marker="erlang#system_info_os_monotonic_time_source"> + <c>erlang:system_info(os_monotonic_time_source)</c></seealso>. To check if time correction is enabled on your system, call - <seealso marker="erlang#system_info_time_correction"><c>erlang:system_info(time_correction)</c></seealso>.</p> + <seealso marker="erlang#system_info_time_correction"> + <c>erlang:system_info(time_correction)</c></seealso>.</p> <p>To enable or disable time correction, pass command-line argument - <seealso marker="erl#+c"><c>+c [true|false]</c></seealso> - to <c>erl</c>.</p> + <seealso marker="erl#+c"><c>+c [true|false]</c></seealso> to + <seealso marker="erl"><c>erl(1)</c></seealso>.</p> <p>If time correction is disabled, Erlang monotonic time - may warp forwards or stop, or even freeze for extended + can warp forwards or stop, or even freeze for extended periods of time. There are then no guarantees that the frequency of the Erlang monotonic clock is accurate or stable.</p> @@ -369,7 +380,7 @@ <c>erlang:now/0</c> are suboptimal</em> from a performance and scalability perspective. So you really want to replace the use of it with other functionality. For examples - of how to replace the use of <c>erlang:now/0</c>, see Section + of how to replace the use of <c>erlang:now/0</c>, see section <seealso marker="#Dos_and_Donts">How to Work with the New API</seealso>.</p> </section> @@ -378,7 +389,7 @@ <title>Time Warp Modes</title> <marker id="Time_Warp_Modes"/> <p>Current <seealso marker="#Erlang_System_Time">Erlang system - time</seealso> is determined by adding current + time</seealso> is determined by adding the current <seealso marker="erlang#monotonic_time/0">Erlang monotonic time</seealso> with current <seealso marker="erlang#time_offset/0">time offset</seealso>. The @@ -387,8 +398,8 @@ <p>To set the time warp mode, pass command-line argument <seealso marker="erl#+C_"><c>+C - [no_time_warp|single_time_warp|multi_time_warp]</c></seealso> - to <c>erl</c>.</p> + [no_time_warp|single_time_warp|multi_time_warp]</c></seealso> to + <seealso marker="erl"><c>erl(1)</c></seealso>.</p> <marker id="No_Time_Warp_Mode"/> <section> @@ -397,8 +408,8 @@ and does not change later. This is the default behavior, but not because it is the best mode (which it is not). It is default <em>only</em> because this is how the runtime system - behaved until <c>ERTS</c> 7.0. - Ensure that your Erlang code that may execute during a time + behaved until ERTS 7.0. + Ensure that your Erlang code that can execute during a time warp is <seealso marker="#Time_Warp_Safe_Code">time warp safe</seealso> before enabling other modes.</p> @@ -422,8 +433,8 @@ <marker id="Single_Time_Warp_Mode"/> <section> <title>Single Time Warp Mode</title> - <p>This mode is more or less a backwards compatibility mode - as of its introduction.</p> + <p>This mode is more or less a backward compatibility mode + as from its introduction.</p> <p>On an embedded system it is not uncommon that the system has no power supply, not even a battery, when it is @@ -475,11 +486,12 @@ <item> <p>This phase begins when the user finalizes the time offset by calling - <seealso marker="erlang#system_flag_time_offset"><c>erlang:system_flag(time_offset, finalize)</c></seealso>. + <seealso marker="erlang#system_flag_time_offset"> + <c>erlang:system_flag(time_offset, finalize)</c></seealso>. The finalization can only be performed once.</p> <p>During finalization, the time offset is adjusted and - fixated so that current Erlang system time aligns with + fixed so that current Erlang system time aligns with the current OS system time. As the time offset can change during the finalization, Erlang system time can do a time warp at this point. The time offset is @@ -488,7 +500,7 @@ correction from now on also makes adjustments to align Erlang system time with OS system time. When the system is in the final phase, it behaves - exactly as in the <seealso marker="#No_Time_Warp_Mode">no + exactly as in <seealso marker="#No_Time_Warp_Mode">no time warp mode</seealso>.</p> </item> </taglist> @@ -520,7 +532,7 @@ may behave very bad.</p> <p>Assuming that these requirements are fulfilled, - time correction is enabled, and that OS system time + time correction is enabled, and OS system time is adjusted using a time adjustment protocol such as NTP, only small adjustments of Erlang monotonic time are needed to keep system times @@ -529,9 +541,10 @@ inserted (or deleted) leap seconds.</p> <warning><p>To use this mode, ensure that - all Erlang code that will execute in both phases are + all Erlang code that will execute in both phases is <seealso marker="#Time_Warp_Safe_Code">time warp safe</seealso>.</p> + <p>Code executing only in the final phase does not have to be able to cope with the time warp.</p></warning> </section> @@ -542,13 +555,13 @@ <p><em>Multi-time warp mode in combination with time correction is the preferred configuration</em>. This as the Erlang runtime system have better performance, scale - better, and behave better on almost all platforms. In - addition, the accuracy and precision of time measurements + better, and behave better on almost all platforms. + Also, the accuracy and precision of time measurements are better. Only Erlang runtime systems executing on ancient platforms benefit from another configuration.</p> - <p>The time offset may change at any time without limitations. - That is, Erlang system time may perform time warps both + <p>The time offset can change at any time without limitations. + That is, Erlang system time can perform time warps both forwards and backwards at <em>any</em> time. As we align Erlang system time with OS system time by changing the time offset, we can enable a time correction that tries @@ -582,8 +595,8 @@ such issues. To improve this, the new API spreads different functionality over multiple functions.</p> - <p>To be backwards compatible, <c>erlang:now/0</c> - remains as is, but <em>you are strongly discouraged from using + <p>To be backward compatible, <c>erlang:now/0</c> + remains "as is", but <em>you are strongly discouraged from using it</em>. Many use cases of <c>erlang:now/0</c> prevents you from using the new <seealso marker="#Multi_Time_Warp_Mode">multi-time warp @@ -597,38 +610,60 @@ <p>The new API consists of the following new BIFs:</p> <list> - <item><p><seealso marker="erlang#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso></p></item> - <item><p><seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time/0</c></seealso></p></item> - <item><p><seealso marker="erlang#monotonic_time/1"><c>erlang:monotonic_time/1</c></seealso></p></item> - <item><p><seealso marker="erlang#system_time/0"><c>erlang:system_time/0</c></seealso></p></item> - <item><p><seealso marker="erlang#system_time/1"><c>erlang:system_time/1</c></seealso></p></item> - <item><p><seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso></p></item> - <item><p><seealso marker="erlang#time_offset/1"><c>erlang:time_offset/1</c></seealso></p></item> - <item><p><seealso marker="erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso></p></item> - <item><p><seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer/0</c></seealso></p></item> - <item><p><seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer/1</c></seealso></p></item> - <item><p><seealso marker="kernel:os#system_time/0"><c>os:system_time/0</c></seealso></p></item> - <item><p><seealso marker="kernel:os#system_time/1"><c>os:system_time/1</c></seealso></p></item> + <item><p><seealso marker="erlang#convert_time_unit/3"> + <c>erlang:convert_time_unit/3</c></seealso></p></item> + <item><p><seealso marker="erlang#monotonic_time/0"> + <c>erlang:monotonic_time/0</c></seealso></p></item> + <item><p><seealso marker="erlang#monotonic_time/1"> + <c>erlang:monotonic_time/1</c></seealso></p></item> + <item><p><seealso marker="erlang#system_time/0"> + <c>erlang:system_time/0</c></seealso></p></item> + <item><p><seealso marker="erlang#system_time/1"> + <c>erlang:system_time/1</c></seealso></p></item> + <item><p><seealso marker="erlang#time_offset/0"> + <c>erlang:time_offset/0</c></seealso></p></item> + <item><p><seealso marker="erlang#time_offset/1"> + <c>erlang:time_offset/1</c></seealso></p></item> + <item><p><seealso marker="erlang#timestamp/0"> + <c>erlang:timestamp/0</c></seealso></p></item> + <item><p><seealso marker="erlang#unique_integer/0"> + <c>erlang:unique_integer/0</c></seealso></p></item> + <item><p><seealso marker="erlang#unique_integer/1"> + <c>erlang:unique_integer/1</c></seealso></p></item> + <item><p><seealso marker="kernel:os#system_time/0"> + <c>os:system_time/0</c></seealso></p></item> + <item><p><seealso marker="kernel:os#system_time/1"> + <c>os:system_time/1</c></seealso></p></item> </list> - <p>The new API also consists of extensions of the following existing BIFs:</p> + <p>The new API also consists of extensions of the following existing BIFs: + </p> <list> - <item><p><seealso marker="erlang#monitor/2"><c>erlang:monitor(time_offset, clock_service)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_flag_time_offset"><c>erlang:system_flag(time_offset, finalize)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_time_offset"><c>erlang:system_info(time_offset)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_time_warp_mode"><c>erlang:system_info(time_warp_mode)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_time_correction"><c>erlang:system_info(time_correction)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso></p></item> - <item><p><seealso marker="erlang#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso></p></item> + <item><p><seealso marker="erlang#monitor/2"> + <c>erlang:monitor(time_offset, clock_service)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_flag_time_offset"> + <c>erlang:system_flag(time_offset, finalize)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_os_monotonic_time_source"> + <c>erlang:system_info(os_monotonic_time_source)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_os_system_time_source"> + <c>erlang:system_info(os_system_time_source)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_time_offset"> + <c>erlang:system_info(time_offset)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_time_warp_mode"> + <c>erlang:system_info(time_warp_mode)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_time_correction"> + <c>erlang:system_info(time_correction)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_start_time"> + <c>erlang:system_info(start_time)</c></seealso></p></item> + <item><p><seealso marker="erlang#system_info_end_time"> + <c>erlang:system_info(end_time)</c></seealso></p></item> </list> <marker id="The_New_Erlang_Monotonic_Time"/> <section> <title>New Erlang Monotonic Time</title> - <p>Erlang monotonic time as such is new as of <c>ERTS</c> 7.0. + <p>Erlang monotonic time as such is new as from ERTS 7.0. It is introduced to detach time measurements, such as elapsed time from calendar time. In many use cases there is a need to measure elapsed time or specify a time relative to another point @@ -649,7 +684,7 @@ modes, and only fully separated in the <seealso marker="#Multi_Time_Warp_Mode">multi-time warp mode</seealso>. All other modes than the - multi-time warp mode are for backwards + multi-time warp mode are for backward compatibility reasons. When using these modes, the accuracy of Erlang monotonic time suffer, as the adjustments of Erlang monotonic time in these @@ -672,15 +707,17 @@ <p>To be able to react to a change in Erlang system time, you must be able to detect that it - happened. The change in Erlang system time occurs when + happened. The change in Erlang system time occurs when the current time offset is changed. We have therefore introduced the possibility to monitor the time offset using - <seealso marker="erlang#monitor/2"><c>erlang:monitor(time_offset, clock_service)</c></seealso>. + <seealso marker="erlang#monitor/2"> + <c>erlang:monitor(time_offset, clock_service)</c></seealso>. A process monitoring the time offset is sent a message on the following format when the time offset is changed:</p> - <code type="none">{'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}</code> + <code type="none"> +{'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}</code> </section> <marker id="Unique_Values"/> @@ -690,8 +727,8 @@ produces unique and strictly monotonically increasing values. To detach this functionality from time measurements, we have introduced - <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer()</c></seealso>. - </p> + <seealso marker="erlang#unique_integer/1"> + <c>erlang:unique_integer()</c></seealso>.</p> </section> <marker id="Dos_and_Donts"/> @@ -699,27 +736,28 @@ <title>How to Work with the New API</title> <p>Previously <c>erlang:now/0</c> was the only option for doing many things. This section deals with some things that - <c>erlang:now/0</c> can be used for, and how you are to - these using the new API.</p> + <c>erlang:now/0</c> can be used for, and how you use the new API.</p> <marker id="Dos_and_Donts_Retrieve_Erlang_System_Time"/> <section> <title>Retrieve Erlang System Time</title> <dont> <p> - Use <c>erlang:now/0</c> to retrieve current Erlang system time. + Use <c>erlang:now/0</c> to retrieve the current Erlang system time. </p> </dont> <do> <p> Use - <seealso marker="erlang#system_time/1"><c>erlang:system_time/1</c></seealso> - to retrieve current Erlang system time on the + <seealso marker="erlang#system_time/1"> + <c>erlang:system_time/1</c></seealso> + to retrieve the current Erlang system time on the <seealso marker="erlang#type_time_unit">time unit</seealso> of your choice.</p> - <p>If you want the same format as returned by <c>erlang:now/0</c>, use - <seealso marker="erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>. - </p> + <p>If you want the same format as returned by <c>erlang:now/0</c>, + use <seealso marker="erlang#timestamp/0"> + <c>erlang:timestamp/0</c></seealso>. + </p> </do> </section> @@ -728,28 +766,31 @@ <title>Measure Elapsed Time</title> <dont> <p> - Take timestamps with <c>erlang:now/0</c> and calculate + Take time stamps with <c>erlang:now/0</c> and calculate the difference in time with - <seealso marker="stdlib:timer#now_diff/2"><c>timer:now_diff/2</c></seealso>. + <seealso marker="stdlib:timer#now_diff/2"> + <c>timer:now_diff/2</c></seealso>. </p> </dont> <do> <p> - Take timestamps with - <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time/0</c></seealso> + Take time stamps with + <seealso marker="erlang#monotonic_time/0"> + <c>erlang:monotonic_time/0</c></seealso> and calculate the time difference using ordinary subtraction. - The result will be in <c>native</c> + The result is in <c>native</c> <seealso marker="erlang#type_time_unit">time unit</seealso>. If you want to convert the result to another time unit, you can use - <seealso marker="erlang#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso>. - </p> - - <p>An easier way to do this is to use - <seealso marker="erlang#monotonic_time/1"><c>erlang:monotonic_time/1</c></seealso> + <seealso marker="erlang#convert_time_unit/3"> + <c>erlang:convert_time_unit/3</c></seealso>. + </p> + <p>An easier way to do this is to use + <seealso marker="erlang#monotonic_time/1"> + <c>erlang:monotonic_time/1</c></seealso> with the desired time unit. However, you can then lose accuracy and precision. - </p> + </p> </do> </section> @@ -758,7 +799,7 @@ <title>Determine Order of Events</title> <dont> <p> - Determine the order of events by saving a timestamp + Determine the order of events by saving a time stamp with <c>erlang:now/0</c> when the event occurs. </p> </dont> @@ -766,8 +807,9 @@ <p> Determine the order of events by saving the integer returned by - <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer([monotonic])</c></seealso> - when the event occurs. These integers will be strictly + <seealso marker="erlang#unique_integer/1"> + <c>erlang:unique_integer([monotonic])</c></seealso> + when the event occurs. These integers are strictly monotonically ordered on current runtime system instance corresponding to creation time. </p> @@ -779,7 +821,7 @@ <title>Determine Order of Events with Time of the Event</title> <dont> <p> - Determine the order of events by saving a timestamp + Determine the order of events by saving a time stamp with <c>erlang:now/0</c> when the event occurs. </p> </dont> @@ -795,18 +837,19 @@ Time = erlang:monotonic_time(), UMI = erlang:unique_integer([monotonic]), EventTag = {Time, UMI}</code> - <p>These tuples will be strictly monotonically ordered - on current runtime system instance according to + <p>These tuples are strictly monotonically ordered + on the current runtime system instance according to creation time. It is important that the monotonic time is in the first element (the most - significant element when comparing 2-tuples). Using + significant element when comparing two-tuples). Using the monotonic time in the tuples, you can calculate time between events.</p> <p>If you are interested in Erlang system time at the time when the event occurred, you can also save the time offset before or after saving the events using - <seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso>. + <seealso marker="erlang#time_offset/0"> + <c>erlang:time_offset/0</c></seealso>. Erlang monotonic time added with the time offset corresponds to Erlang system time.</p> @@ -814,7 +857,7 @@ EventTag = {Time, UMI}</code> can change, and you want to get the actual Erlang system time when the event occurred, you can save the time offset as a third element in the tuple - (the least significant element when comparing 3-tuples).</p> + (the least significant element when comparing three-tuples).</p> </do> </section> @@ -830,10 +873,12 @@ EventTag = {Time, UMI}</code> <do> <p> Use the value returned from - <seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer/0</c></seealso> + <seealso marker="erlang#unique_integer/0"> + <c>erlang:unique_integer/0</c></seealso> to create a name unique on the current runtime system instance. If you only want positive integers, you can use - <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer([positive])</c></seealso>. + <seealso marker="erlang#unique_integer/1"> + <c>erlang:unique_integer([positive])</c></seealso>. </p> </do> </section> @@ -849,12 +894,15 @@ EventTag = {Time, UMI}</code> <do> <p> Seed random number generation using a combination of - <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso>, - <seealso marker="erlang#time_offset/0"><c>erlang:time_offset()</c></seealso>, - <seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer()</c></seealso>, + <seealso marker="erlang#monotonic_time/0"> + <c>erlang:monotonic_time()</c></seealso>, + <seealso marker="erlang#time_offset/0"> + <c>erlang:time_offset()</c></seealso>, + <seealso marker="erlang#unique_integer/0"> + <c>erlang:unique_integer()</c></seealso>, and other functionality. </p> - </do> + </do> </section> <p>To sum up this section: <em>Do not use <c>erlang:now/0</c>.</em></p> @@ -867,9 +915,9 @@ EventTag = {Time, UMI}</code> <p>It can be required that your code must run on a variety of OTP installations of different OTP releases. If so, you cannot use the new API out of the box, as it will - not be available on old pre OTP 18 releases. The solution + not be available on releases before OTP 18. The solution is <em>not</em> to avoid using the new API, as your - code then would not benefit from the scalability + code would then not benefit from the scalability and accuracy improvements made. Instead, use the new API when available, and fall back on <c>erlang:now/0</c> when the new API is unavailable.</p> @@ -879,16 +927,20 @@ EventTag = {Time, UMI}</code> <list type="bulleted"> <item> - <seealso marker="erlang#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso> + <seealso marker="erlang#system_info_start_time"> + <c>erlang:system_info(start_time)</c></seealso> </item> <item> - <seealso marker="erlang#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso> + <seealso marker="erlang#system_info_end_time"> + <c>erlang:system_info(end_time)</c></seealso> </item> <item> - <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso> + <seealso marker="erlang#system_info_os_monotonic_time_source"> + <c>erlang:system_info(os_monotonic_time_source)</c></seealso> </item> <item> - <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>) + <seealso marker="erlang#system_info_os_system_time_source"> + <c>erlang:system_info(os_system_time_source)</c></seealso>) </item> </list> diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml index b2866c82cf..51db1ba8e2 100644 --- a/erts/doc/src/tty.xml +++ b/erts/doc/src/tty.xml @@ -22,7 +22,7 @@ </legalnotice> - <title>tty - A command line interface</title> + <title>tty - A Command-Line Interface</title> <prepared>ETX/B/SFP C. Granbom</prepared> <responsible></responsible> <docno></docno> @@ -32,24 +32,48 @@ <rev>A</rev> <file>tty.xml</file> </header> - <p><c><![CDATA[tty]]></c> is a simple command line interface program where keystrokes are collected and interpreted. Completed lines are sent to the shell for interpretation. There is a simple history mechanism, which saves previous lines. These can be edited before sending them to the shell. - <c><![CDATA[tty]]></c> is started when Erlang is started with the command:<br></br></p> - <p><em>erl</em></p> - <p><c><![CDATA[tty]]></c> operates in one of two modes:<br></br></p> + <p><c><![CDATA[tty]]></c> is a simple command-line interface program where + keystrokes are collected and interpreted. Completed lines are sent to the + shell for interpretation. A simple history mechanism saves previous lines, + which can be edited before sending them to the shell. <c><![CDATA[tty]]></c> + is started when Erlang is started with the following command:</p> + + <pre> +erl</pre> + + <p><c><![CDATA[tty]]></c> operates in one of two modes:</p> + <list type="bulleted"> <item> - <p><em>normal mode</em>, in which lines of text can be edited and sent to the shell.</p> + <p>Normal mode, in which text lines can be edited and sent to the + shell.</p> </item> <item> - <p><em>shell break</em> mode, which allows the user to kill the current shell, start multiple shells etc. Shell break mode is started by typing <em>Control G</em>.</p> + <p>Shell break mode, which allows the user to kill the current shell, + start multiple shells, and so on.</p> </item> </list> <section> <title>Normal Mode</title> - <p>In normal mode keystrokes from the user are collected and interpreted by <c><![CDATA[tty]]></c>. Most of the <em>emacs</em> line editing commands are supported. The following is a complete list of the supported line editing commands.<br></br></p> - <p><em>Note:</em> The notation <c><![CDATA[C-a]]></c> means pressing the control key and the letter <c><![CDATA[a]]></c> simultaneously. <c><![CDATA[M-f]]></c> means pressing the <c><![CDATA[ESC]]></c> key followed by the letter <c><![CDATA[f]]></c>. <c><![CDATA[Home]]></c> and <c><![CDATA[End]]></c> represent the keys with the same name on the keyboard, whereas <c><![CDATA[Left]]></c> and <c><![CDATA[Right]]></c> represent the corresponding arrow keys. - </p> + <p>In normal mode keystrokes from the user are collected and interpreted by + <c><![CDATA[tty]]></c>. Most of the <em>Emacs</em> line-editing commands + are supported. The following is a complete list of the supported + line-editing commands.</p> + + <p>Typographic conventions:</p> + + <list type="bulleted"> + <item><c>C-a</c> means pressing the <em>Ctrl</em> key and the letter + <c>a</c> simultaneously.</item> + <item><c>M-f</c> means pressing the <em>Esc</em> key and the letter + <c>f</c> in sequence.</item> + <item><c>Home</c> and <c>End</c> represent the keys with the same + name on the keyboard.</item> + <item><c>Left</c> and <c>Right</c> represent the corresponding arrow + keys.</item> + </list> + <table> <row> <cell align="left" valign="middle"><em>Key Sequence</em></cell> @@ -121,11 +145,13 @@ </row> <row> <cell align="left" valign="middle">C-n</cell> - <cell align="left" valign="middle">Fetch next line from the history buffer</cell> + <cell align="left" valign="middle">Fetch next line from the history + buffer</cell> </row> <row> <cell align="left" valign="middle">C-p</cell> - <cell align="left" valign="middle">Fetch previous line from the history buffer</cell> + <cell align="left" valign="middle">Fetch previous line from the history + buffer</cell> </row> <row> <cell align="left" valign="middle">C-t</cell> @@ -139,23 +165,18 @@ <cell align="left" valign="middle">C-y</cell> <cell align="left" valign="middle">Insert previously killed text</cell> </row> - <tcaption>tty text editing</tcaption> + <tcaption>tty Text Editing</tcaption> </table> </section> <section> <title>Shell Break Mode</title> - <p><em>tty</em> enters <em>shell</em> break mode when you type <em>Control G</em>. In this mode you can:<br></br></p> + <p>In this mode the following can be done:</p> + <list type="bulleted"> - <item> - <p>Kill or suspend the current shell</p> - </item> - <item> - <p>Connect to a suspended shell</p> - </item> - <item> - <p>Start a new shell</p> - </item> + <item>Kill or suspend the current shell</item> + <item>Connect to a suspended shell</item> + <item>Start a new shell</item> </list> </section> </chapter> diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl.xml index 1a3cb6f502..792fe204e8 100644 --- a/erts/doc/src/werl.xml +++ b/erts/doc/src/werl.xml @@ -28,62 +28,91 @@ <docno>1</docno> <approved>Bjarne Däcker</approved> <checked></checked> - <date>98-01-26</date> + <date>1998-01-26</date> <rev>A</rev> <file>werl.xml</file> </header> <com>werl</com> <comsummary>The Erlang Emulator</comsummary> <description> - <p>On Windows, the preferred way to start the Erlang system for interactive use is:</p> + <p>On Windows, the preferred way to start the Erlang system for interactive + use is as follows:</p> + <p><c><![CDATA[werl <arguments>]]></c></p> - <p>This will start Erlang in its own window, with fully - functioning command-line editing and scrollbars. All flags + <p>This starts Erlang in its own window, with fully + functioning command-line editing and scrollbars. All flags except <c><![CDATA[-oldshell]]></c> work as they do for - the <seealso marker="erl">erl</seealso> command.</p> + <seealso marker="erl"><c>erl(1)</c></seealso>.</p> + + <list type="bulleted"> + <item> + <p>To copy text to the clipboard, use <c>Ctrl-C</c>.</p> + </item> + <item> + <p>To paste text, use <c>Ctrl-V</c>.</p> + </item> + <item> + <p>To interrupt the runtime system or the shell process (depending + on what has been specified with system flag <c>+B</c>), use + <c>Ctrl-Break</c>.</p> + </item> + </list> - <p>Ctrl-C is reserved for copying text to the clipboard (Ctrl-V to paste). - To interrupt the runtime system or the shell process (depending on what - has been specified with the +B system flag), you should use Ctrl-Break.</p> <p>In cases where you want to redirect standard input and/or - standard output or use Erlang in a pipeline, the <c>werl</c> is - not suitable, and the <c>erl</c> program should be used instead.</p> + standard output or use Erlang in a pipeline, <c>werl</c> is + not suitable, and the <c>erl</c> program is to be used instead.</p> - <p>The <c>werl</c> window is in many ways modelled after the <c>xterm</c> + <p>The <c>werl</c> window is in many ways modeled after the <c>xterm</c> window present on other platforms, as the <c>xterm</c> model - fits well with line oriented command based interaction. This - means that selecting text is line oriented rather than rectangle - oriented.</p> - - <p>To select text in the <c>werl</c> window , simply press and hold - the left mouse button and drag the mouse over the text you want - to select. If the selection crosses line boundaries, the - selected text will consist of complete lines where applicable - (just like in a word processor). To select more text than fits - in the window, start by selecting a small portion in the - beginning of the text you want, then use the scrollbar - to view the end of the desired selection, point to it and press - the <em>right</em> mouse-button. The whole area between your - first selection and the point where you right-clicked will be - included in the selection.</p> + fits well with line-oriented command-based interaction. This + means that selecting text is line-oriented rather than + rectangle-oriented.</p> - <p>The selected text is copied to the clipboard by either - pressing <c>Ctrl-C</c>, using the menu or pressing the copy - button in the toolbar.</p> + <list type="bulleted"> + <item> + <p>To select text in the <c>werl</c> window, press and hold + the left mouse button and drag the mouse over the text you want + to select. If the selection crosses line boundaries, the + selected text consists of complete lines where applicable + (just like in a word processor).</p> + </item> + <item> + <p>To select more text than fits + in the window, start by selecting a small part in the + beginning of the text you want, then use the scrollbar + to view the end of the desired selection, point to it, and press + the <em>right</em> mouse button. The whole area between your + first selection and the point where you right-clicked is + included in the selection.</p> + </item> + <item> + <p>To copy the selected text to the clipboard, either + use <c>Ctrl-C</c>, use the menu, or press the copy + button in the toolbar.</p> + </item> + </list> - <p>Pasted text is always inserted at the current prompt position - and will be interpreted by Erlang as usual keyboard input.</p> + <p>Pasted text is inserted at the current prompt position + and is interpreted by Erlang as usual keyboard input.</p> - <p>Previous command lines can be retrieved by pressing the <c>Up - arrow</c> or by pressing <c>Ctrl-P</c>. There is also a drop - down box in the toolbar containing the command - history. Selecting a command in the drop down box will insert it - at the prompt, just as if you used the keyboard to retrieve the + <list type="bulleted"> + <item> + <p>To retrieve previous command lines, press the <c>Up arrow</c> or + use <c>Ctrl-P</c>.</p> + </item> + </list> + + <p>A drop-down box in the toolbar contains the command + history. Selecting a command in the drop-down box inserts the command + at the prompt, as if you used the keyboard to retrieve the command.</p> - - <p>Closing the <c>werl</c> window will stop the Erlang emulator.</p> + <list type="bulleted"> + <item> + <p>To stop the Erlang emulator, close the <c>werl</c> window.</p> + </item> + </list> </description> </comref> diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 861661043f..138414a880 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -30,13 +30,18 @@ <file>zlib.xml</file> </header> <module>zlib</module> - <modulesummary>Zlib Compression interface.</modulesummary> + <modulesummary>zlib compression interface.</modulesummary> <description> - <p>The zlib module provides an API for the zlib library - (http://www.zlib.org). - It is used to compress and decompress data. The - data format is described by RFCs 1950 to 1952.</p> - <p>A typical (compress) usage looks like:</p> + <p>This module provides an API for the zlib library + (<url href="http://www.zlib.net">www.zlib.net</url>). + It is used to compress and decompress data. + The data format is described by + <url href="https://www.ietf.org/rfc/rfc1950.txt">RFC 1950</url>, + <url href="https://www.ietf.org/rfc/rfc1951.txt">RFC 1951</url>, and + <url href="https://www.ietf.org/rfc/rfc1952.txt">RFC 1952</url>.</p> + + <p>A typical (compress) usage is as follows:</p> + <pre> Z = zlib:open(), ok = zlib:deflateInit(Z,default), @@ -50,29 +55,25 @@ Last = zlib:deflate(Z, [], finish), ok = zlib:deflateEnd(Z), zlib:close(Z), list_to_binary([Compressed|Last])</pre> + <p>In all functions errors, <c>{'EXIT',{Reason,Backtrace}}</c>, - might be thrown, where <c>Reason</c> describes the - error. Typical reasons are:</p> + can be thrown, where <c>Reason</c> describes the error.</p> + + <p>Typical <c>Reasons</c>s:</p> + <taglist> <tag><c>badarg</c></tag> - <item> - <p>Bad argument</p> + <item>Bad argument. </item> <tag><c>data_error</c></tag> - <item> - <p>The data contains errors</p> + <item>The data contains errors. </item> <tag><c>stream_error</c></tag> - <item> - <p>Inconsistent stream state</p> - </item> + <item>Inconsistent stream state.</item> <tag><c>einval</c></tag> - <item> - <p>Bad value or wrong function called</p> - </item> + <item>Bad value or wrong function called.</item> <tag><c>{need_dictionary,Adler32}</c></tag> - <item> - <p>See <c>inflate/2</c></p> + <item>See <seealso marker="#inflate/2"><c>inflate/2</c></seealso>. </item> </taglist> </description> @@ -81,7 +82,7 @@ list_to_binary([Compressed|Last])</pre> <datatype> <name name="zstream"/> <desc> - <p>A zlib stream, see <seealso marker="#open/0">open/0</seealso>. + <p>A zlib stream, see <seealso marker="#open/0"><c>open/0</c></seealso>. </p> </desc> </datatype> @@ -104,116 +105,144 @@ list_to_binary([Compressed|Last])</pre> </desc> </datatype> </datatypes> + <funcs> <func> - <name name="open" arity="0"/> - <fsummary>Open a stream and return a stream reference</fsummary> + <name name="adler32" arity="2"/> + <fsummary>Calculate the Adler checksum.</fsummary> + <desc> + <p>Calculates the Adler-32 checksum for <c><anno>Data</anno></c>.</p> + </desc> + </func> + + <func> + <name name="adler32" arity="3"/> + <fsummary>Calculate the Adler checksum.</fsummary> + <desc> + <p>Updates a running Adler-32 checksum for <c><anno>Data</anno></c>. + If <c><anno>Data</anno></c> is the empty binary or the empty iolist, + this function returns the required initial value for the checksum.</p> + <p>Example:</p> + <pre> +Crc = lists:foldl(fun(Data,Crc0) -> + zlib:adler32(Z, Crc0, Data), + end, zlib:adler32(Z,<< >>), Datas)</pre> + </desc> + </func> + + <func> + <name name="adler32_combine" arity="4"/> + <fsummary>Combine two Adler-32 checksums.</fsummary> <desc> - <p>Open a zlib stream.</p> + <p>Combines two Adler-32 checksums into one. For two binaries or + iolists, <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> + and <c><anno>Size2</anno></c>, with Adler-32 checksums + <c><anno>Adler1</anno></c> and <c><anno>Adler2</anno></c>.</p> + <p>This function returns the <c><anno>Adler</anno></c> checksum of + <c>[Data1,Data2]</c>, requiring only <c><anno>Adler1</anno></c>, + <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>.</p> </desc> </func> + <func> <name name="close" arity="1"/> - <fsummary>Close a stream</fsummary> + <fsummary>Close a stream.</fsummary> <desc> <p>Closes the stream referenced by <c><anno>Z</anno></c>.</p> </desc> </func> + <func> - <name name="deflateInit" arity="1"/> - <fsummary>Initialize a session for compression</fsummary> + <name name="compress" arity="1"/> + <fsummary>Compress data with standard zlib functionality.</fsummary> <desc> - <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p> + <p>Compresses data with zlib headers and checksum.</p> </desc> </func> + <func> - <name name="deflateInit" arity="2"/> - <fsummary>Initialize a session for compression</fsummary> + <name name="crc32" arity="1"/> + <fsummary>Get current CRC.</fsummary> <desc> - <p>Initialize a zlib stream for compression.</p> - <p><c><anno>Level</anno></c> decides the compression level to be used, 0 - (<c>none</c>), gives no compression at all, 1 - (<c>best_speed</c>) gives best speed and 9 - (<c>best_compression</c>) gives best compression.</p> + <p>Gets the current calculated CRC checksum.</p> </desc> </func> + <func> - <name name="deflateInit" arity="6"/> - <fsummary>Initialize a session for compression</fsummary> + <name name="crc32" arity="2"/> + <fsummary>Calculate CRC.</fsummary> <desc> - <p>Initiates a zlib stream for compression.</p> - <p>The <c><anno>Level</anno></c> parameter decides the compression level to be - used, 0 (<c>none</c>), gives no compression at all, 1 - (<c>best_speed</c>) gives best speed and 9 - (<c>best_compression</c>) gives best compression.</p> - <p>The <c><anno>Method</anno></c> parameter decides which compression method to use, - currently the only supported method is <c>deflated</c>.</p> - <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm - of the window size (the size of the history buffer). It - should be in the range 8 through 15. Larger values - of this parameter result in better compression at the - expense of memory usage. The default value is 15 if - <c>deflateInit/2</c>. A negative <c><anno>WindowBits</anno></c> - value suppresses the zlib header (and checksum) from the - stream. Note that the zlib source mentions this only as a - undocumented feature.</p> - <p>The <c><anno>MemLevel</anno></c> parameter specifies how much memory - should be allocated for the internal compression - state. <c><anno>MemLevel</anno></c>=1 uses minimum memory but is slow and - reduces compression ratio; <c><anno>MemLevel</anno></c>=9 uses maximum - memory for optimal speed. The default value is 8.</p> - <p>The <c><anno>Strategy</anno></c> parameter is used to tune - the compression algorithm. Use the value <c>default</c> for - normal data, <c>filtered</c> for data produced by a filter (or - predictor), <c>huffman_only</c> to force Huffman encoding - only (no string match), or <c>rle</c> to limit match - distances to one (run-length encoding). Filtered data - consists mostly of small values with a somewhat random - distribution. In this case, the compression algorithm is tuned - to compress them better. The effect of <c>filtered</c>is to - force more Huffman coding and less string matching; it is - somewhat intermediate between <c>default</c> and - <c>huffman_only</c>. <c>rle</c> is designed to be almost as - fast as <c>huffman_only</c>, but give better compression for PNG - image data. The <c><anno>Strategy</anno></c> parameter only - affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately.</p> + <p>Calculates the CRC checksum for <c><anno>Data</anno></c>.</p> + </desc> + </func> + + <func> + <name name="crc32" arity="3"/> + <fsummary>Calculate CRC.</fsummary> + <desc> + <p>Updates a running CRC checksum for <c><anno>Data</anno></c>. + If <c><anno>Data</anno></c> is the empty binary or the empty iolist, + this function returns the required initial value for the CRC.</p> + <p>Example:</p> + <pre> +Crc = lists:foldl(fun(Data,Crc0) -> + zlib:crc32(Z, Crc0, Data), + end, zlib:crc32(Z,<< >>), Datas)</pre> + </desc> + </func> + + <func> + <name name="crc32_combine" arity="4"/> + <fsummary>Combine two CRCs.</fsummary> + <desc> + <p>Combines two CRC checksums into one. For two binaries or iolists, + <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and + <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c> + and <c><anno>CRC2</anno></c>.</p> + <p>This function returns the <c><anno>CRC</anno></c> checksum of + <c>[Data1,Data2]</c>, requiring only <c><anno>CRC1</anno></c>, + <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>.</p> </desc> </func> + <func> <name name="deflate" arity="2"/> - <fsummary>Compress data</fsummary> + <fsummary>Compress data.</fsummary> <desc> <p>Same as <c>deflate(<anno>Z</anno>, <anno>Data</anno>, none)</c>.</p> </desc> </func> + <func> <name name="deflate" arity="3"/> - <fsummary>Compress data</fsummary> + <fsummary>Compress data.</fsummary> <desc> - <p><c>deflate/3</c> compresses as much data as possible, and - stops when the input buffer becomes empty. It may introduce + <p>Compresses as much data as possible, and + stops when the input buffer becomes empty. It can introduce some output latency (reading input without producing any output) except when forced to flush.</p> - <p>If the parameter <c><anno>Flush</anno></c> is set to <c>sync</c>, all + <p>If <c><anno>Flush</anno></c> is set to <c>sync</c>, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. - Flushing may degrade compression for some compression algorithms and so - it should be used only when necessary.</p> - <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is flushed as with - <c>sync</c>, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using <c>full</c> too often can seriously degrade - the compression.</p> - <p>If the parameter <c><anno>Flush</anno></c> is set to <c>finish</c>, - pending input is processed, pending output is flushed and - <c>deflate/3</c> returns. Afterwards the only possible - operations on the stream are <c>deflateReset/1</c> or <c>deflateEnd/1</c>.</p> - <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately after - <c>deflateInit</c> if all compression is to be done in one step.</p> + Flushing can degrade compression for some compression algorithms; + thus, use it only when necessary.</p> + <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is + flushed as with <c>sync</c>, and the compression state is reset so + that decompression can restart from this point if previous compressed + data has been damaged or if random access is desired. Using + <c>full</c> too often can seriously degrade the compression.</p> + <p>If <c><anno>Flush</anno></c> is set to <c>finish</c>, + pending input is processed, pending output is flushed, and + <c>deflate/3</c> returns. Afterwards the only possible operations + on the stream are + <seealso marker="#deflateReset/1"><c>deflateReset/1</c></seealso> or + <seealso marker="#deflateEnd/1"><c>deflateEnd/1</c></seealso>.</p> + <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately + after <seealso marker="#deflateInit/1"><c>deflateInit</c></seealso> + if all compression is to be done in one step.</p> + <p>Example:</p> <pre> - zlib:deflateInit(Z), B1 = zlib:deflate(Z,Data), B2 = zlib:deflate(Z,<< >>,finish), @@ -221,116 +250,234 @@ zlib:deflateEnd(Z), list_to_binary([B1,B2])</pre> </desc> </func> + <func> - <name name="deflateSetDictionary" arity="2"/> - <fsummary>Initialize the compression dictionary</fsummary> + <name name="deflateEnd" arity="1"/> + <fsummary>End deflate session.</fsummary> <desc> - <p>Initializes the compression dictionary from the given byte - sequence without producing any compressed output. This - function must be called immediately after - <c>deflateInit/[1|2|6]</c> or <c>deflateReset/1</c>, before - any call of <c>deflate/3</c>. The compressor and - decompressor must use exactly the same dictionary (see - <c>inflateSetDictionary/2</c>). The adler checksum of the - dictionary is returned.</p> + <p>Ends the deflate session and cleans all data used. Notice that this + function throws a <c>data_error</c> exception if the last call to + <seealso marker="#deflate/3"><c>deflate/3</c></seealso> + was not called with <c>Flush</c> set to <c>finish</c>.</p> </desc> </func> + <func> - <name name="deflateReset" arity="1"/> - <fsummary>Reset the deflate session</fsummary> + <name name="deflateInit" arity="1"/> + <fsummary>Initialize a session for compression.</fsummary> <desc> - <p>This function is equivalent to <c>deflateEnd/1</c> - followed by <c>deflateInit/[1|2|6]</c>, but does not free - and reallocate all the internal compression state. The - stream will keep the same compression level and any other - attributes.</p> + <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p> + </desc> + </func> + + <func> + <name name="deflateInit" arity="2"/> + <fsummary>Initialize a session for compression.</fsummary> + <desc> + <p>Initializes a zlib stream for compression.</p> + <p><c><anno>Level</anno></c> decides the compression level to be + used:</p> + <list type="bulleted"> + <item>0 (<c>none</c>), gives no compression</item> + <item>1 (<c>best_speed</c>) gives best speed</item> + <item>9 (<c>best_compression</c>) gives best compression</item> + </list> + </desc> + </func> + + <func> + <name name="deflateInit" arity="6"/> + <fsummary>Initialize a session for compression.</fsummary> + <desc> + <p>Initiates a zlib stream for compression.</p> + <taglist> + <tag><c><anno>Level</anno></c></tag> + <item> + <p>Compression level to use:</p> + <list type="bulleted"> + <item>0 (<c>none</c>), gives no compression</item> + <item>1 (<c>best_speed</c>) gives best speed</item> + <item>9 (<c>best_compression</c>) gives best compression</item> + </list> + </item> + <tag><c><anno>Method</anno></c></tag> + <item> + <p>Compression method to use, currently the only supported method + is <c>deflated</c>.</p> + </item> + <tag><c><anno>WindowBits</anno></c></tag> + <item> + <p>The base two logarithm of the window size (the size of the + history buffer). It is to be in the range 8 through 15. Larger + values result in better compression at the expense of memory + usage. Defaults to 15 if <seealso marker="#deflateInit/2"> + <c>deflateInit/2</c></seealso> is used. A negative + <c><anno>WindowBits</anno></c> value suppresses the zlib header + (and checksum) from the stream. Notice that the zlib source + mentions this only as a undocumented feature.</p> + </item> + <tag><c><anno>MemLevel</anno></c></tag> + <item> + <p>Specifies how much memory is to be allocated for the internal + compression state: <c><anno>MemLevel</anno></c>=1 uses minimum + memory but is slow and reduces compression ratio; + <c><anno>MemLevel</anno></c>=9 uses maximum memory for optimal + speed. Defaults to 8.</p> + </item> + <tag><c><anno>Strategy</anno></c></tag> + <item> + <p>Tunes the compression algorithm. Use the following values:</p> + <list type="bulleted"> + <item><c>default</c> for normal data</item> + <item><c>filtered</c> for data produced by a filter (or + predictor)</item> + <item><c>huffman_only</c> to force Huffman encoding only + (no string match)</item> + <item><c>rle</c> to limit match distances to one (run-length + encoding)</item> + </list> + <p>Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of <c>filtered</c> is to + force more Huffman coding and less string matching; it is somewhat + intermediate between <c>default</c> and <c>huffman_only</c>. + <c>rle</c> is designed to be almost as fast as + <c>huffman_only</c>, but gives better compression for PNG image + data.</p> + <p><c><anno>Strategy</anno></c> affects only the compression ratio, + but not the correctness of the compressed output even if it is not + set appropriately.</p> + </item> + </taglist> </desc> </func> + <func> <name name="deflateParams" arity="3"/> - <fsummary>Dynamicly update deflate parameters</fsummary> + <fsummary>Dynamicly update deflate parameters.</fsummary> <desc> - <p>Dynamically update the compression level and compression - strategy. The interpretation of <c><anno>Level</anno></c> and - <c><anno>Strategy</anno></c> is as in <c>deflateInit/6</c>. This can be + <p>Dynamically updates the compression level and compression + strategy. The interpretation of <c><anno>Level</anno></c> and + <c><anno>Strategy</anno></c> is as in + <seealso marker="#deflateInit/6"><c>deflateInit/6</c></seealso>. + This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression level is changed, the input available so far is compressed with the - old level (and may be flushed); the new level will take - effect only at the next call of <c>deflate/3</c>.</p> - <p>Before the call of <c>deflateParams</c>, the stream state must be set as for - a call of <c>deflate/3</c>, since the currently available input may have to - be compressed and flushed.</p> + old level (and can be flushed); the new level takes + effect only at the next call of + <seealso marker="#deflate/3"><c>deflate/3</c></seealso>.</p> + <p>Before the call of <c>deflateParams</c>, the stream state must be + set as for a call of <c>deflate/3</c>, as the currently available + input may have to be compressed and flushed.</p> </desc> </func> + <func> - <name name="deflateEnd" arity="1"/> - <fsummary>End deflate session</fsummary> + <name name="deflateReset" arity="1"/> + <fsummary>Reset the deflate session.</fsummary> + <desc> + <p>Equivalent to + <seealso marker="#deflateEnd/1"><c>deflateEnd/1</c></seealso> + followed by + <seealso marker="#deflateInit/1"><c>deflateInit/1,2,6</c></seealso>, + but does not free and reallocate all the internal compression state. + The stream keeps the same compression level and any other + attributes.</p> + </desc> + </func> + + <func> + <name name="deflateSetDictionary" arity="2"/> + <fsummary>Initialize the compression dictionary.</fsummary> <desc> - <p>End the deflate session and cleans all data used. - Note that this function will throw an <c>data_error</c> - exception if the last call to - <c>deflate/3</c> was not called with <c>Flush</c> set to - <c>finish</c>.</p> + <p>Initializes the compression dictionary from the specified byte + sequence without producing any compressed output.</p> + <p>This function must be called immediately after + <seealso marker="#deflateInit/1"><c>deflateInit/1,2,6</c></seealso> or + <seealso marker="#deflateReset/1"><c>deflateReset/1</c></seealso>, + before any call of + <seealso marker="#deflate/3"><c>deflate/3</c></seealso>. + The compressor and decompressor must use the same dictionary (see + <seealso marker="#inflateSetDictionary/2"> + <c>inflateSetDictionary/2</c></seealso>).</p> + <p>The Adler checksum of the dictionary is returned.</p> </desc> </func> + <func> - <name name="inflateInit" arity="1"/> - <fsummary>Initialize a session for decompression</fsummary> + <name name="getBufSize" arity="1"/> + <fsummary>Get buffer size.</fsummary> <desc> - <p>Initialize a zlib stream for decompression.</p> + <p>Gets the size of the intermediate buffer.</p> </desc> </func> + <func> - <name name="inflateInit" arity="2"/> - <fsummary>Initialize a session for decompression</fsummary> + <name name="gunzip" arity="1"/> + <fsummary>Uncompress data with gz header.</fsummary> <desc> - <p>Initialize decompression session on zlib stream.</p> - <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm - of the maximum window size (the size of the history buffer). - It should be in the range 8 through 15. - The default value is 15 if <c>inflateInit/1</c> is used. - If a compressed stream with a larger window size is - given as input, inflate() will throw the <c>data_error</c> - exception. A negative <c><anno>WindowBits</anno></c> value makes zlib ignore the - zlib header (and checksum) from the stream. Note that the zlib - source mentions this only as a undocumented feature.</p> + <p>Uncompresses data with gz headers and checksum.</p> </desc> </func> + + <func> + <name name="gzip" arity="1"/> + <fsummary>Compress data with gz header.</fsummary> + <desc> + <p>Compresses data with gz headers and checksum.</p> + </desc> + </func> + <func> <name name="inflate" arity="2"/> - <fsummary>Decompress data</fsummary> + <fsummary>Decompress data.</fsummary> <desc> - <p><c>inflate/2</c> decompresses as much data as possible. - It may introduce some output latency (reading + <p>Decompresses as much data as possible. + It can introduce some output latency (reading input without producing any output).</p> <p>If a preset dictionary is needed at this point (see - <c>inflateSetDictionary</c> below), <c>inflate/2</c> throws a - <c>{need_dictionary,Adler}</c> exception where <c>Adler</c> is - the adler32 checksum of the dictionary chosen by the - compressor.</p> + <seealso marker="#inflateSetDictionary/2"> + <c>inflateSetDictionary/2</c></seealso>), <c>inflate/2</c> throws a + <c>{need_dictionary,Adler}</c> exception, where <c>Adler</c> is + the Adler-32 checksum of the dictionary chosen by the compressor.</p> + </desc> + </func> + + <func> + <name name="inflateChunk" arity="1"/> + <fsummary>Read next uncompressed chunk.</fsummary> + <desc> + <p>Reads the next chunk of uncompressed data, initialized by + <seealso marker="#inflateChunk/2"><c>inflateChunk/2</c></seealso>.</p> + <p>This function is to be repeatedly called, while it returns + <c>{more, Decompressed}</c>.</p> </desc> </func> + <func> <name name="inflateChunk" arity="2"/> - <fsummary>Decompress data with limited output size</fsummary> + <fsummary>Decompress data with limited output size.</fsummary> <desc> - <p>Like <c>inflate/2</c>, but decompress no more data than - will fit in the buffer configured via <c>setBufSize/2</c>. + <p>Like <seealso marker="#inflate/2"><c>inflate/2</c></seealso>, + but decompresses no more data than will fit in the buffer configured + through <seealso marker="#setBufSize/2"><c>setBufSize/2</c></seealso>. Is is useful when decompressing a stream with a high compression - ratio such that a small amount of compressed input may expand up to - 1000 times. - It returns <c>{more, Decompressed}</c>, when there is more output - available, and <c>inflateChunk/1</c> should be used to read it. - It may introduce some output latency (reading + ratio, such that a small amount of compressed input can expand up to + 1000 times.</p> + <p>This function returns <c>{more, Decompressed}</c>, when there is + more output available, and + <seealso marker="#inflateChunk/1"><c>inflateChunk/1</c></seealso> + is to be used to read it.</p> + <p>This function can introduce some output latency (reading input without producing any output).</p> <p>If a preset dictionary is needed at this point (see - <c>inflateSetDictionary</c> below), <c>inflateChunk/2</c> throws a - <c>{need_dictionary,Adler}</c> exception where <c>Adler</c> is - the adler32 checksum of the dictionary chosen by the - compressor.</p> - + <seealso marker="#inflateSetDictionary/2"> + <c>inflateSetDictionary/2</c></seealso>), this function throws a + <c>{need_dictionary,Adler}</c> exception, where <c>Adler</c> is + the Adler-32 checksum of the dictionary chosen by the compressor.</p> + <p>Example:</p> <pre> walk(Compressed, Handler) -> Z = zlib:open(), @@ -345,191 +492,126 @@ loop(Z, Handler, {more, Uncompressed}) -> Handler(Uncompressed), loop(Z, Handler, zlib:inflateChunk(Z)); loop(Z, Handler, Uncompressed) -> - Handler(Uncompressed). - </pre> - </desc> - </func> - <func> - <name name="inflateChunk" arity="1"/> - <fsummary>Read next uncompressed chunk</fsummary> - <desc> - <p>Read next chunk of uncompressed data, initialized by - <c>inflateChunk/2</c>.</p> - <p>This function should be repeatedly called, while it returns - <c>{more, Decompressed}</c>.</p> - </desc> - </func> - <func> - <name name="inflateSetDictionary" arity="2"/> - <fsummary>Initialize the decompression dictionary</fsummary> - <desc> - <p>Initializes the decompression dictionary from the given - uncompressed byte sequence. This function must be called - immediately after a call of <c>inflate/2</c> if this call - threw a <c>{need_dictionary,Adler}</c> exception. - The dictionary chosen by the - compressor can be determined from the Adler value thrown - by the call to <c>inflate/2</c>. The compressor and decompressor - must use exactly the same dictionary (see <c>deflateSetDictionary/2</c>).</p> - <p>Example:</p> - <pre> -unpack(Z, Compressed, Dict) -> - case catch zlib:inflate(Z, Compressed) of - {'EXIT',{{need_dictionary,DictID},_}} -> - zlib:inflateSetDictionary(Z, Dict), - Uncompressed = zlib:inflate(Z, []); - Uncompressed -> - Uncompressed - end.</pre> - </desc> - </func> - <func> - <name name="inflateReset" arity="1"/> - <fsummary>>Reset the inflate session</fsummary> - <desc> - <p>This function is equivalent to <c>inflateEnd/1</c> followed - by <c>inflateInit/1</c>, but does not free and reallocate all - the internal decompression state. The stream will keep - attributes that may have been set by <c>inflateInit/[1|2]</c>.</p> + Handler(Uncompressed).</pre> </desc> </func> + <func> <name name="inflateEnd" arity="1"/> - <fsummary>End inflate session</fsummary> + <fsummary>End inflate session.</fsummary> <desc> - <p>End the inflate session and cleans all data used. Note - that this function will throw a <c>data_error</c> exception + <p>Ends the inflate session and cleans all data used. Notice + that this function throws a <c>data_error</c> exception if no end of stream was found (meaning that not all data has been uncompressed).</p> </desc> </func> + <func> - <name name="setBufSize" arity="2"/> - <fsummary>Set buffer size</fsummary> - <desc> - <p>Sets the intermediate buffer size.</p> - </desc> - </func> - <func> - <name name="getBufSize" arity="1"/> - <fsummary>Get buffer size</fsummary> - <desc> - <p>Get the size of intermediate buffer.</p> - </desc> - </func> - <func> - <name name="crc32" arity="1"/> - <fsummary>Get current CRC</fsummary> - <desc> - <p>Get the current calculated CRC checksum.</p> - </desc> - </func> - <func> - <name name="crc32" arity="2"/> - <fsummary>Calculate CRC</fsummary> - <desc> - <p>Calculate the CRC checksum for <c><anno>Data</anno></c>.</p> - </desc> - </func> - <func> - <name name="crc32" arity="3"/> - <fsummary>Calculate CRC</fsummary> + <name name="inflateInit" arity="1"/> + <fsummary>Initialize a session for decompression.</fsummary> <desc> - <p>Update a running CRC checksum for <c><anno>Data</anno></c>. - If <c><anno>Data</anno></c> is the empty binary or the empty iolist, this function returns - the required initial value for the crc.</p> - <pre> -Crc = lists:foldl(fun(Data,Crc0) -> - zlib:crc32(Z, Crc0, Data), - end, zlib:crc32(Z,<< >>), Datas)</pre> + <p>Initializes a zlib stream for decompression.</p> </desc> </func> + <func> - <name name="crc32_combine" arity="4"/> - <fsummary>Combine two CRC's</fsummary> + <name name="inflateInit" arity="2"/> + <fsummary>Initialize a session for decompression.</fsummary> <desc> - <p>Combine two CRC checksums into one. For two binaries or iolists, - <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and - <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c> and - <c><anno>CRC2</anno></c>. <c>crc32_combine/4</c> returns the <c><anno>CRC</anno></c> - checksum of <c>[Data1,Data2]</c>, requiring - only <c><anno>CRC1</anno></c>, <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>. - </p> + <p>Initializes a decompression session on zlib stream.</p> + <p><c><anno>WindowBits</anno></c> is the base two logarithm + of the maximum window size (the size of the history buffer). + It is to be in the range 8 through 15. Default to 15 if + <seealso marker="#inflateInit/1"><c>inflateInit/1</c></seealso> + is used.</p> + <p>If a compressed stream with a larger window size is specified as + input, <seealso marker="#inflate/2"><c>inflate/2</c></seealso> + throws the <c>data_error</c> exception.</p> + <p>A negative <c><anno>WindowBits</anno></c> value makes zlib + ignore the zlib header (and checksum) from the stream. Notice that + the zlib source mentions this only as a undocumented feature.</p> </desc> </func> + <func> - <name name="adler32" arity="2"/> - <fsummary>Calculate the adler checksum</fsummary> + <name name="inflateReset" arity="1"/> + <fsummary>>Reset the inflate session.</fsummary> <desc> - <p>Calculate the Adler-32 checksum for <c><anno>Data</anno></c>.</p> + <p>Equivalent to + <seealso marker="#inflateEnd/1"><c>inflateEnd/1</c></seealso> + followed by + <seealso marker="#inflateInit/1"><c>inflateInit/1</c></seealso>, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that could have been set by + <c>inflateInit/1,2</c>.</p> </desc> </func> + <func> - <name name="adler32" arity="3"/> - <fsummary>Calculate the adler checksum</fsummary> + <name name="inflateSetDictionary" arity="2"/> + <fsummary>Initialize the decompression dictionary.</fsummary> <desc> - <p>Update a running Adler-32 checksum for <c><anno>Data</anno></c>. - If <c><anno>Data</anno></c> is the empty binary or the empty iolist, this function returns - the required initial value for the checksum.</p> + <p>Initializes the decompression dictionary from the specified + uncompressed byte sequence. This function must be called + immediately after a call of + <seealso marker="#inflate/2"><c>inflate/2</c></seealso> + if this call threw a <c>{need_dictionary,Adler}</c> exception. + The dictionary chosen by the compressor can be determined from the + Adler value thrown by the call to <c>inflate/2</c>. + The compressor and decompressor must use the same dictionary (see + <seealso marker="#deflateSetDictionary/2"> + <c>deflateSetDictionary/2</c></seealso>).</p> + <p>Example:</p> <pre> -Crc = lists:foldl(fun(Data,Crc0) -> - zlib:adler32(Z, Crc0, Data), - end, zlib:adler32(Z,<< >>), Datas)</pre> +unpack(Z, Compressed, Dict) -> + case catch zlib:inflate(Z, Compressed) of + {'EXIT',{{need_dictionary,DictID},_}} -> + zlib:inflateSetDictionary(Z, Dict), + Uncompressed = zlib:inflate(Z, []); + Uncompressed -> + Uncompressed + end.</pre> </desc> </func> + <func> - <name name="adler32_combine" arity="4"/> - <fsummary>Combine two Adler-32 checksums</fsummary> + <name name="open" arity="0"/> + <fsummary>Open a stream and return a stream reference.</fsummary> <desc> - <p>Combine two Adler-32 checksums into one. For two binaries or iolists, - <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and - <c><anno>Size2</anno></c>, with Adler-32 checksums <c><anno>Adler1</anno></c> and - <c><anno>Adler2</anno></c>. <c>adler32_combine/4</c> returns the <c><anno>Adler</anno></c> - checksum of <c>[Data1,Data2]</c>, requiring - only <c><anno>Adler1</anno></c>, <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>. - </p> + <p>Opens a zlib stream.</p> </desc> </func> + <func> - <name name="compress" arity="1"/> - <fsummary>Compress data with standard zlib functionality</fsummary> + <name name="setBufSize" arity="2"/> + <fsummary>Set buffer size.</fsummary> <desc> - <p>Compress data (with zlib headers and checksum).</p> + <p>Sets the intermediate buffer size.</p> </desc> </func> + <func> <name name="uncompress" arity="1"/> - <fsummary>Uncompress data with standard zlib functionality</fsummary> + <fsummary>Uncompress data with standard zlib functionality.</fsummary> <desc> - <p>Uncompress data (with zlib headers and checksum).</p> - </desc> - </func> - <func> - <name name="zip" arity="1"/> - <fsummary>Compress data without the zlib headers</fsummary> - <desc> - <p>Compress data (without zlib headers and checksum).</p> + <p>Uncompresses data with zlib headers and checksum.</p> </desc> </func> + <func> <name name="unzip" arity="1"/> - <fsummary>Uncompress data without the zlib headers</fsummary> + <fsummary>Uncompress data without the zlib headers.</fsummary> <desc> - <p>Uncompress data (without zlib headers and checksum).</p> - </desc> - </func> - <func> - <name name="gzip" arity="1"/> - <fsummary>Compress data with gz header</fsummary> - <desc> - <p>Compress data (with gz headers and checksum).</p> + <p>Uncompresses data without zlib headers and checksum.</p> </desc> </func> + <func> - <name name="gunzip" arity="1"/> - <fsummary>Uncompress data with gz header</fsummary> + <name name="zip" arity="1"/> + <fsummary>Compress data without the zlib headers.</fsummary> <desc> - <p>Uncompress data (with gz headers and checksum).</p> + <p>Compresses data without zlib headers and checksum.</p> </desc> </func> </funcs> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 2212aed5e0..e0260205e3 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -51,6 +51,7 @@ ARFLAGS=rc OMIT_OMIT_FP=no DIRTY_SCHEDULER_SUPPORT=@DIRTY_SCHEDULER_SUPPORT@ +NEW_PURGE_STRATEGY=@NEW_PURGE_STRATEGY@ ifeq ($(TYPE),debug) PURIFY = @@ -591,11 +592,8 @@ GENERATE += $(TTF_DIR)/driver_tab.c # # This list must be consistent with PRE_LOADED_MODULES in # erts/preloaded/src/Makefile. -ifeq ($(TARGET),win32) -# On windows the preloaded objects are in a resource object. -PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT) -PRELOAD_SRC = $(TARGET)/beams.rc -$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ + +PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/init.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \ @@ -606,23 +604,20 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam + $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam + +ifeq ($(TARGET),win32) +# On windows the preloaded objects are in a resource object. +PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT) +PRELOAD_SRC = $(TARGET)/beams.rc +$(PRELOAD_SRC): $(PRELOAD_BEAM) $(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@ else PRELOAD_OBJ = $(OBJDIR)/preload.o PRELOAD_SRC = $(TARGET)/preload.c -$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ - $(ERL_TOP)/erts/preloaded/ebin/init.beam \ - $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \ - $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \ - $(ERL_TOP)/erts/preloaded/ebin/prim_file.beam \ - $(ERL_TOP)/erts/preloaded/ebin/zlib.beam \ - $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam +$(PRELOAD_SRC): $(PRELOAD_BEAM) $(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@ endif @@ -744,7 +739,7 @@ EMU_OBJS = \ $(OBJDIR)/beam_ranges.o RUN_OBJS = \ - $(OBJDIR)/erl_pbifs.o $(OBJDIR)/benchmark.o \ + $(OBJDIR)/erl_pbifs.o \ $(OBJDIR)/erl_alloc.o $(OBJDIR)/erl_mtrace.o \ $(OBJDIR)/erl_alloc_util.o $(OBJDIR)/erl_goodfit_alloc.o \ $(OBJDIR)/erl_bestfit_alloc.o $(OBJDIR)/erl_afit_alloc.o \ diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h index fbd0528009..ae60904785 100644 --- a/erts/emulator/beam/atom.h +++ b/erts/emulator/beam/atom.h @@ -21,10 +21,7 @@ #ifndef __ATOM_H__ #define __ATOM_H__ -#ifndef __INDEX_H__ #include "index.h" -#endif - #include "erl_atom_table.h" #define MAX_ATOM_CHARACTERS 255 diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index badd69856e..9dae67cb2d 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -65,6 +65,7 @@ atom undefined_lambda atom DOWN='DOWN' atom UP='UP' atom EXIT='EXIT' +atom abort atom aborted atom abs_path atom absoluteURI @@ -165,6 +166,7 @@ atom commandv atom compact atom compat_rel atom compile +atom complete atom compressed atom config_h atom convert_time_unit @@ -176,6 +178,7 @@ atom const atom context_switches atom control atom copy +atom copy_literals atom counters atom cpu atom cpu_timestamp @@ -195,6 +198,7 @@ atom dgroup_leader atom dictionary atom dirty_cpu atom dirty_cpu_schedulers_online +atom dirty_execution atom dirty_io atom disable_trace atom disabled @@ -235,6 +239,7 @@ atom erlang atom ERROR='ERROR' atom error_handler atom error_logger +atom erts_code_purger atom erts_internal atom ets atom ETS_TRANSFER='ETS-TRANSFER' @@ -384,8 +389,10 @@ atom merge_trap atom meta atom meta_match_spec atom micro_seconds +atom microsecond atom microstate_accounting atom milli_seconds +atom millisecond atom min_heap_size atom min_bin_vheap_size atom minor_version @@ -402,11 +409,13 @@ atom more atom multi_scheduling atom multiline atom nano_seconds +atom nanosecond atom name atom named_table atom namelist atom native atom native_addresses +atom need_gc atom Neq='=/=' atom Neqeq='/=' atom net_kernel @@ -485,6 +494,7 @@ atom pause atom pending atom pending_driver atom pending_process +atom pending_purge_lambda atom pending_reload atom permanent atom pid @@ -494,6 +504,7 @@ atom port_count atom port_limit atom port_op atom positive +atom prepare atom print atom priority atom private @@ -554,6 +565,7 @@ atom schedulers_online atom scheme atom scientific atom scope +atom second atom seconds atom send_to_non_existing_process atom sensitive diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 15e878ba65..dddcfbb77d 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -37,13 +37,90 @@ #include "erl_bits.h" #include "erl_thr_progress.h" +#ifdef HIPE +# include "hipe_stack.h" +#endif + +static struct { + Eterm module; + erts_smp_mtx_t mtx; + Export *pending_purge_lambda; + Eterm *sprocs; + Eterm def_sprocs[10]; + Uint sp_size; + Uint sp_ix; + ErlFunEntry **funs; + ErlFunEntry *def_funs[10]; + Uint fe_size; + Uint fe_ix; +} purge_state; + +Process *erts_code_purger = NULL; + +#ifdef ERTS_DIRTY_SCHEDULERS +Process *erts_dirty_process_code_checker; +#endif +erts_smp_atomic_t erts_copy_literal_area__; +#define ERTS_SET_COPY_LITERAL_AREA(LA) \ + erts_smp_atomic_set_nob(&erts_copy_literal_area__, \ + (erts_aint_t) (LA)) +#ifdef ERTS_NEW_PURGE_STRATEGY +Process *erts_literal_area_collector = NULL; + +typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef; +struct ErtsLiteralAreaRef_ { + ErtsLiteralAreaRef *next; + ErtsLiteralArea *literal_area; +}; + +struct { + erts_smp_mtx_t mtx; + ErtsLiteralAreaRef *first; + ErtsLiteralAreaRef *last; +} release_literal_areas; +#endif + static void set_default_trace_pattern(Eterm module); static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls); static void delete_code(Module* modp); -static void decrement_refc(BeamCodeHeader*); static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); +static void +init_purge_state(void) +{ + purge_state.module = THE_NON_VALUE; + + erts_smp_mtx_init(&purge_state.mtx, "purge_state"); + + purge_state.pending_purge_lambda = + erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3); + + purge_state.sprocs = &purge_state.def_sprocs[0]; + purge_state.sp_size = sizeof(purge_state.def_sprocs); + purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]); + purge_state.sp_ix = 0; + + purge_state.funs = &purge_state.def_funs[0]; + purge_state.fe_size = sizeof(purge_state.def_funs); + purge_state.fe_size /= sizeof(purge_state.def_funs[0]); + purge_state.fe_ix = 0; +} + +void +erts_beam_bif_load_init(void) +{ +#ifdef ERTS_NEW_PURGE_STRATEGY + erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas"); + release_literal_areas.first = NULL; + release_literal_areas.last = NULL; +#endif + erts_smp_atomic_init_nob(&erts_copy_literal_area__, + (erts_aint_t) NULL); + + init_purge_state(); +} + BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) { Module* modp; @@ -516,6 +593,43 @@ badarg: BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2) +{ +#if !defined(ERTS_DIRTY_SCHEDULERS) + BIF_ERROR(BIF_P, EXC_NOTSUP); +#else + Process *rp; + int reds = 0; + Eterm res; + + if (BIF_P != erts_dirty_process_code_checker) + BIF_ERROR(BIF_P, EXC_NOTSUP); + + if (is_not_internal_pid(BIF_ARG_1)) + BIF_ERROR(BIF_P, BADARG); + + if (is_not_atom(BIF_ARG_2)) + BIF_ERROR(BIF_P, BADARG); + + rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN, + BIF_ARG_1, ERTS_PROC_LOCK_MAIN); + if (rp == ERTS_PROC_LOCK_BUSY) + ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_check_dirty_process_code_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + if (!rp) + BIF_RET(am_false); + + res = erts_check_process_code(rp, BIF_ARG_2, 0, &reds, BIF_P->fcalls); + + if (BIF_P != rp) + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + + ASSERT(is_value(res)); + + BIF_RET2(res, reds); +#endif +} + BIF_RETTYPE delete_module_1(BIF_ALIST_1) { ErtsCodeIndex code_ix; @@ -748,6 +862,8 @@ set_default_trace_pattern(Eterm module) } } +#ifndef ERTS_NEW_PURGE_STRATEGY + static ERTS_INLINE int check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) { @@ -762,12 +878,249 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) return 0; } +#endif + static Uint hfrag_literal_size(Eterm* start, Eterm* end, char* lit_start, Uint lit_size); static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, Eterm *start, Eterm *end, char *lit_start, Uint lit_size); +#ifdef ERTS_NEW_PURGE_STRATEGY + +Eterm +erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed) +{ + ErtsLiteralArea *la; + ErtsMessage *msgp; + struct erl_off_heap_header* oh; + char *literals; + Uint lit_bsize; + ErlHeapFragment *hfrag; + + la = ERTS_COPY_LITERAL_AREA(); + if (!la) + return am_ok; + + oh = la->off_heap; + literals = (char *) &la->start[0]; + lit_bsize = (char *) la->end - literals; + + /* + * If a literal is in the message queue we make an explicit copy of + * it and attach it to the heap fragment. Each message needs to be + * self contained, we cannot save the literal in the old_heap or + * any other heap than the message it self. + */ + + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + + for (msgp = c_p->msg.first; msgp; msgp = msgp->next) { + ErlHeapFragment *hf; + Uint lit_sz = 0; + + *redsp += 1; + + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) + hfrag = &msgp->hfrag; + else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag) + hfrag = msgp->data.heap_frag; + else + continue; /* Content on heap or in external term format... */ + + for (hf = hfrag; hf; hf = hf->next) { + lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size], + literals, lit_bsize); + *redsp += 1; + } + + *redsp += lit_sz / 16; /* Better value needed... */ + if (lit_sz > 0) { + ErlHeapFragment *bp = new_message_buffer(lit_sz); + Eterm *hp = bp->mem; + + for (hf = hfrag; hf; hf = hf->next) { + hfrag_literal_copy(&hp, &bp->off_heap, + &hf->mem[0], &hf->mem[hf->used_size], + literals, lit_bsize); + hfrag = hf; + } + + /* link new hfrag last */ + ASSERT(hfrag->next == NULL); + hfrag->next = bp; + bp->next = NULL; + } + } + + if (gc_allowed) { + /* + * Current implementation first tests without + * allowing GC, and then restarts the operation + * allowing GC if it is needed. It is therfore + * very likely that we will need the GC (although + * this is not completely certain). We go for + * the GC directly instead of scanning everything + * one more time... + */ + goto literal_gc; + } + + *redsp += 2; + if (any_heap_ref_ptrs(&c_p->fvalue, &c_p->fvalue+1, literals, lit_bsize)) { + c_p->freason = EXC_NULL; + c_p->fvalue = NIL; + c_p->ftrace = NIL; + } + + if (any_heap_ref_ptrs(c_p->stop, c_p->hend, literals, lit_bsize)) + goto literal_gc; + *redsp += 1; +#ifdef HIPE + if (nstack_any_heap_ref_ptrs(c_p, literals, lit_bsize)) + goto literal_gc; + *redsp += 1; +#endif + if (any_heap_refs(c_p->heap, c_p->htop, literals, lit_bsize)) + goto literal_gc; + *redsp += 1; + if (any_heap_refs(c_p->old_heap, c_p->old_htop, literals, lit_bsize)) + goto literal_gc; + + /* Check dictionary */ + *redsp += 1; + if (c_p->dictionary) { + Eterm* start = ERTS_PD_START(c_p->dictionary); + Eterm* end = start + ERTS_PD_SIZE(c_p->dictionary); + + if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) + goto literal_gc; + } + + /* Check heap fragments */ + for (hfrag = c_p->mbuf; hfrag; hfrag = hfrag->next) { + Eterm *hp, *hp_end; + + *redsp += 1; + + hp = &hfrag->mem[0]; + hp_end = &hfrag->mem[hfrag->used_size]; + if (any_heap_refs(hp, hp_end, literals, lit_bsize)) + goto literal_gc; + } + + /* + * Message buffer fragments (matched messages) + * - off heap lists should already have been moved into + * process off heap structure. + * - Check for literals + */ + for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) { + hfrag = erts_message_to_heap_frag(msgp); + for (; hfrag; hfrag = hfrag->next) { + Eterm *hp, *hp_end; + + *redsp += 1; + + hp = &hfrag->mem[0]; + hp_end = &hfrag->mem[hfrag->used_size]; + + if (any_heap_refs(hp, hp_end, literals, lit_bsize)) + goto literal_gc; + } + } + + return am_ok; + +literal_gc: + + if (!gc_allowed) + return am_need_gc; + + if (c_p->flags & F_DISABLE_GC) + return THE_NON_VALUE; + + FLAGS(c_p) |= F_NEED_FULLSWEEP; + + *redsp += erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls); + + erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize, oh); + + *redsp += lit_bsize / 64; /* Need, better value... */ + + return am_ok; +} + +static Eterm +check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) +{ + BeamInstr* start; + char* mod_start; + Uint mod_size; + Eterm* sp; + + *redsp += 1; + + /* + * Pick up limits for the module. + */ + start = (BeamInstr*) modp->old.code_hdr; + mod_start = (char *) start; + mod_size = modp->old.code_length; + + /* + * Check if current instruction or continuation pointer points into module. + */ + if (ErtsInArea(rp->i, mod_start, mod_size) + || ErtsInArea(rp->cp, mod_start, mod_size)) { + return am_true; + } + + *redsp += (STACK_START(rp) - rp->stop) / 32; + + /* + * Check all continuation pointers stored on the stack. + */ + for (sp = rp->stop; sp < STACK_START(rp); sp++) { + if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) { + return am_true; + } + } + + /* + * Check all continuation pointers stored in stackdump + * and clear exception stackdump if there is a pointer + * to the module. + */ + if (rp->ftrace != NIL) { + struct StackTrace *s; + ASSERT(is_list(rp->ftrace)); + s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace))); + if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) || + (s->current && ErtsInArea(s->current, mod_start, mod_size))) { + rp->freason = EXC_NULL; + rp->fvalue = NIL; + rp->ftrace = NIL; + } else { + int i; + for (i = 0; i < s->depth; i++) { + if (ErtsInArea(s->trace[i], mod_start, mod_size)) { + rp->freason = EXC_NULL; + rp->fvalue = NIL; + rp->ftrace = NIL; + break; + } + } + } + } + + return am_false; +} + +#else /* !ERTS_NEW_PURGE_STRATEGY, i.e, old style purge... */ + static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) { @@ -861,8 +1214,14 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); - literals = (char*) modp->old.code_hdr->literals_start; - lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; + if (modp->old.code_hdr->literal_area) { + literals = (char*) modp->old.code_hdr->literal_area->start; + lit_bsize = (char*) modp->old.code_hdr->literal_area->end - literals; + } + else { + literals = NULL; + lit_bsize = 0; + } for (msgp = rp->msg.first; msgp; msgp = msgp->next) { if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) @@ -903,12 +1262,6 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls /* Check heap, stack etc... */ if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) goto try_gc; - if (!(flags & ERTS_CPC_COPY_LITERALS)) { - /* Process ok. May contain old literals but we will be called - * again before module is purged. - */ - return am_false; - } if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) { rp->freason = EXC_NULL; rp->fvalue = NIL; @@ -916,6 +1269,10 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls } if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize)) goto try_literal_gc; +#ifdef HIPE + if (nstack_any_heap_ref_ptrs(rp, literals, lit_bsize)) + goto try_literal_gc; +#endif if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize)) goto try_literal_gc; if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize)) @@ -994,7 +1351,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls } if (need_gc & ERTS_LITERAL_GC__) { struct erl_off_heap_header* oh; - oh = modp->old.code_hdr->literals_off_heap; + oh = modp->old.code_hdr->literal_area->off_heap; *redsp += lit_bsize / 64; /* Need, better value... */ erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh); done_gc |= ERTS_LITERAL_GC__; @@ -1007,6 +1364,8 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls } +#endif /* !ERTS_NEW_PURGE_STRATEGY */ + static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) { @@ -1135,200 +1494,475 @@ hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, } } -#undef in_area +#ifdef ERTS_NEW_PURGE_STRATEGY #ifdef ERTS_SMP -static void copy_literals_commit(void*); -#endif -copy_literals_t erts_clrange = {NULL, 0, THE_NON_VALUE}; +ErtsThrPrgrLaterOp later_literal_area_switch; -/* copy literals - * - * copy_literals.ptr = LitPtr - * copy_literals.sz = LitSz - * ------ THR PROG COMMIT ----- - * - * - check process code - * - check process code - * ... - * copy_literals.ptr = NULL - * copy_literals.sz = 0 - * ------ THR PROG COMMIT ----- - * ... - */ +typedef struct { + ErtsThrPrgrLaterOp lop; + ErtsLiteralArea *la; +} ErtsLaterReleasLiteralArea; +static void +later_release_literal_area(void *vlrlap) +{ + ErtsLaterReleasLiteralArea *lrlap; + lrlap = (ErtsLaterReleasLiteralArea *) vlrlap; + erts_release_literal_area(lrlap->la); + erts_free(ERTS_ALC_T_RELEASE_LAREA, vlrlap); +} -BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2) +static void +complete_literal_area_switch(void *literal_area) { - ErtsCodeIndex code_ix; - Eterm res = am_true; + Process *p = erts_literal_area_collector; + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + erts_resume(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + if (literal_area) + erts_release_literal_area((ErtsLiteralArea *) literal_area); +} +#endif - if (is_not_atom(BIF_ARG_1) || (am_true != BIF_ARG_2 && am_false != BIF_ARG_2)) { - BIF_ERROR(BIF_P, BADARG); - } +#endif /* ERTS_NEW_PURGE_STRATEGY */ - if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_copy_literals_2], - BIF_P, BIF_ARG_1, BIF_ARG_2); +BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0) +{ +#ifndef ERTS_NEW_PURGE_STRATEGY + BIF_ERROR(BIF_P, EXC_NOTSUP); +#else + ErtsLiteralArea *unused_la; + ErtsLiteralAreaRef *la_ref; + + if (BIF_P != erts_literal_area_collector) + BIF_ERROR(BIF_P, EXC_NOTSUP); + + erts_smp_mtx_lock(&release_literal_areas.mtx); + + la_ref = release_literal_areas.first; + if (la_ref) { + release_literal_areas.first = la_ref->next; + if (!release_literal_areas.first) + release_literal_areas.last = NULL; } - code_ix = erts_active_code_ix(); + erts_smp_mtx_unlock(&release_literal_areas.mtx); - if (BIF_ARG_2 == am_true) { - Module* modp = erts_get_module(BIF_ARG_1, code_ix); - if (!modp || !modp->old.code_hdr) { - res = am_false; - goto done; - } - if (erts_clrange.ptr != NULL - && !(BIF_P->static_flags & ERTS_STC_FLG_SYSTEM_PROC)) { - res = am_aborted; - goto done; - } - erts_clrange.ptr = modp->old.code_hdr->literals_start; - erts_clrange.sz = modp->old.code_hdr->literals_end - erts_clrange.ptr; - erts_clrange.pid = BIF_P->common.id; - } else if (BIF_ARG_2 == am_false) { - if (erts_clrange.pid != BIF_P->common.id) { - res = am_false; - goto done; - } - erts_clrange.ptr = NULL; - erts_clrange.sz = 0; - erts_clrange.pid = THE_NON_VALUE; + unused_la = ERTS_COPY_LITERAL_AREA(); + + if (!la_ref) { + ERTS_SET_COPY_LITERAL_AREA(NULL); + if (unused_la) { +#ifdef ERTS_SMP + ErtsLaterReleasLiteralArea *lrlap; + lrlap = erts_alloc(ERTS_ALC_T_RELEASE_LAREA, + sizeof(ErtsLaterReleasLiteralArea)); + lrlap->la = unused_la; + erts_schedule_thr_prgr_later_cleanup_op( + later_release_literal_area, + (void *) lrlap, + &lrlap->lop, + (sizeof(ErtsLaterReleasLiteralArea) + + sizeof(ErtsLiteralArea) + + ((unused_la->end + - &unused_la->start[0]) + - 1)*(sizeof(Eterm)))); +#else + erts_release_literal_area(unused_la); +#endif + } + BIF_RET(am_false); } + ERTS_SET_COPY_LITERAL_AREA(la_ref->literal_area); + + erts_free(ERTS_ALC_T_LITERAL_REF, la_ref); + #ifdef ERTS_SMP - ASSERT(committer_state.stager == NULL); - committer_state.stager = BIF_P; - erts_schedule_thr_prgr_later_op(copy_literals_commit, NULL, &committer_state.lop); - erts_proc_inc_refc(BIF_P); + erts_schedule_thr_prgr_later_op(complete_literal_area_switch, + unused_la, + &later_literal_area_switch); erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); ERTS_BIF_YIELD_RETURN(BIF_P, am_true); +#else + erts_release_literal_area(unused_la); + BIF_RET(am_true); #endif -done: - erts_release_code_write_permission(); - BIF_RET(res); + +#endif /* ERTS_NEW_PURGE_STRATEGY */ +} + +void +erts_purge_state_add_fun(ErlFunEntry *fe) +{ + ASSERT(is_value(purge_state.module)); + if (purge_state.fe_ix >= purge_state.fe_size) { + ErlFunEntry **funs; + purge_state.fe_size += 100; + funs = erts_alloc(ERTS_ALC_T_PURGE_DATA, + sizeof(ErlFunEntry *)*purge_state.fe_size); + sys_memcpy((void *) funs, + (void *) purge_state.funs, + purge_state.fe_ix*sizeof(ErlFunEntry *)); + if (purge_state.funs != &purge_state.def_funs[0]) + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs); + purge_state.funs = funs; + } + purge_state.funs[purge_state.fe_ix++] = fe; +} + +Export * +erts_suspend_process_on_pending_purge_lambda(Process *c_p) +{ + erts_smp_mtx_lock(&purge_state.mtx); + if (is_value(purge_state.module)) { + /* + * The process c_p is about to call a fun in the code + * that we are trying to purge. Suspend it and call + * erts_code_purger:pending_purge_lambda/3. The process + * will be resumed when the purge completes or aborts, + * and will then try to do the call again. + */ + if (purge_state.sp_ix >= purge_state.sp_size) { + Eterm *sprocs; + purge_state.sp_size += 100; + sprocs = erts_alloc(ERTS_ALC_T_PURGE_DATA, + (sizeof(ErlFunEntry *) + * purge_state.sp_size)); + sys_memcpy((void *) sprocs, + (void *) purge_state.sprocs, + purge_state.sp_ix*sizeof(ErlFunEntry *)); + if (purge_state.sprocs != &purge_state.def_sprocs[0]) + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs); + purge_state.sprocs = sprocs; + } + purge_state.sprocs[purge_state.sp_ix++] = c_p->common.id; + erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_VBUMP_ALL_REDS(c_p); + } + erts_smp_mtx_unlock(&purge_state.mtx); + return purge_state.pending_purge_lambda; +} + +static void +finalize_purge_operation(Process *c_p, int succeded) +{ + Uint ix; + + if (c_p) + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); + + erts_smp_mtx_lock(&purge_state.mtx); + + ASSERT(purge_state.module != THE_NON_VALUE); + + purge_state.module = THE_NON_VALUE; + + /* + * Resume all processes that have tried to call + * funs in this code. + */ + for (ix = 0; ix < purge_state.sp_ix; ix++) { + Process *rp = erts_pid2proc(NULL, 0, + purge_state.sprocs[ix], + ERTS_PROC_LOCK_STATUS); + if (rp) { + erts_resume(rp, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + } + } + + erts_smp_mtx_unlock(&purge_state.mtx); + + if (c_p) + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + + if (purge_state.sprocs != &purge_state.def_sprocs[0]) { + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs); + purge_state.sprocs = &purge_state.def_sprocs[0]; + purge_state.sp_size = sizeof(purge_state.def_sprocs); + purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]); + } + purge_state.sp_ix = 0; + + if (purge_state.funs != &purge_state.def_funs[0]) { + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs); + purge_state.funs = &purge_state.def_funs[0]; + purge_state.fe_size = sizeof(purge_state.def_funs); + purge_state.fe_size /= sizeof(purge_state.def_funs[0]); + } + purge_state.fe_ix = 0; } #ifdef ERTS_SMP -static void copy_literals_commit(void* null) { - Process* p = committer_state.stager; -#ifdef DEBUG - committer_state.stager = NULL; -#endif - erts_release_code_write_permission(); + +static ErtsThrPrgrLaterOp purger_lop_data; + +static void +resume_purger(void *unused) +{ + Process *p = erts_code_purger; erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); - if (!ERTS_PROC_IS_EXITING(p)) { - erts_resume(p, ERTS_PROC_LOCK_STATUS); - } + erts_resume(p, ERTS_PROC_LOCK_STATUS); erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - erts_proc_dec_refc(p); } -#endif /* ERTS_SMP */ +static void +finalize_purge_abort(void *unused) +{ + erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix); -/* Do the actualy module purging and return: - * true for success - * false if no such old module - * BADARG if not an atom - */ -BIF_RETTYPE erts_internal_purge_module_1(BIF_ALIST_1) + finalize_purge_operation(NULL, 0); + + resume_purger(NULL); +} + +#endif /* ERTS_SMP */ + +BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2) { - ErtsCodeIndex code_ix; - BeamInstr* code; - BeamInstr* end; - Module* modp; - int is_blocking = 0; - Eterm ret; + if (BIF_P != erts_code_purger) + BIF_ERROR(BIF_P, EXC_NOTSUP); - if (is_not_atom(BIF_ARG_1)) { + if (is_not_atom(BIF_ARG_1)) BIF_ERROR(BIF_P, BADARG); - } - if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD1(bif_export[BIF_erts_internal_purge_module_1], - BIF_P, BIF_ARG_1); - } + switch (BIF_ARG_2) { - code_ix = erts_active_code_ix(); + case am_prepare: { + /* + * Prepare for purge by marking all fun + * entries referring to the code to purge + * with "pending purge" markers. + */ + ErtsCodeIndex code_ix; + Module* modp; + Eterm res; - /* - * Correct module? - */ + if (is_value(purge_state.module)) + BIF_ERROR(BIF_P, BADARG); - if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { - ERTS_BIF_PREP_RET(ret, am_false); - } - else { - erts_rwlock_old_code(code_ix); + code_ix = erts_active_code_ix(); /* - * Any code to purge? + * Correct module? */ - if (!modp->old.code_hdr) { - ERTS_BIF_PREP_RET(ret, am_false); - } + modp = erts_get_module(BIF_ARG_1, code_ix); + if (!modp) + res = am_false; else { /* - * Unload any NIF library + * Any code to purge? */ - if (modp->old.nif != NULL) { - /* ToDo: Do unload nif without blocking */ - erts_rwunlock_old_code(code_ix); - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - is_blocking = 1; - erts_rwlock_old_code(code_ix); - erts_unload_nif(modp->old.nif); - modp->old.nif = NULL; + erts_rlock_old_code(code_ix); + if (!modp->old.code_hdr) + res = am_false; + else { + BeamInstr* code; + BeamInstr* end; + erts_smp_mtx_lock(&purge_state.mtx); + purge_state.module = BIF_ARG_1; + erts_smp_mtx_unlock(&purge_state.mtx); + res = am_true; + code = (BeamInstr*) modp->old.code_hdr; + end = (BeamInstr *)((char *)code + modp->old.code_length); + erts_fun_purge_prepare(code, end); +#if !defined(ERTS_NEW_PURGE_STRATEGY) + ASSERT(!ERTS_COPY_LITERAL_AREA()); + ERTS_SET_COPY_LITERAL_AREA(modp->old.code_hdr->literal_area); +#endif } - + erts_runlock_old_code(code_ix); + } + +#ifndef ERTS_SMP + BIF_RET(res); +#else + if (res != am_true) + BIF_RET(res); + else { /* - * Remove the old code. + * We'll be resumed when all schedulers are guaranteed + * to see the "pending purge" markers that we've made on + * all fun entries of the code that we are about to purge. + * Processes trying to call these funs will be suspended + * before calling the funs. That is we are guaranteed not + * to get any more direct references into the code while + * checking for such references... */ - ASSERT(erts_total_code_size >= modp->old.code_length); - erts_total_code_size -= modp->old.code_length; - code = (BeamInstr*) modp->old.code_hdr; - end = (BeamInstr *)((char *)code + modp->old.code_length); - erts_cleanup_funs_on_purge(code, end); - beam_catches_delmod(modp->old.catches, code, modp->old.code_length, - code_ix); - decrement_refc(modp->old.code_hdr); - if (modp->old.code_hdr->literals_start) { - erts_free(ERTS_ALC_T_LITERAL, modp->old.code_hdr->literals_start); - } - erts_free(ERTS_ALC_T_CODE, (void *) code); - modp->old.code_hdr = NULL; - modp->old.code_length = 0; - modp->old.catches = BEAM_CATCHES_NIL; - erts_remove_from_ranges(code); - ERTS_BIF_PREP_RET(ret, am_true); + erts_schedule_thr_prgr_later_op(resume_purger, + NULL, + &purger_lop_data); + erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_BIF_YIELD_RETURN(BIF_P, am_true); } - erts_rwunlock_old_code(code_ix); +#endif } - if (is_blocking) { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + + case am_abort: { + /* + * Soft purge that detected direct references into the code + * we set out to purge. Abort the purge. + */ + + if (purge_state.module != BIF_ARG_1) + BIF_ERROR(BIF_P, BADARG); + + erts_fun_purge_abort_prepare(purge_state.funs, purge_state.fe_ix); + +#if !defined(ERTS_NEW_PURGE_STRATEGY) + ASSERT(ERTS_COPY_LITERAL_AREA()); + ERTS_SET_COPY_LITERAL_AREA(NULL); +#endif +#ifndef ERTS_SMP + erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix); + finalize_purge_operation(BIF_P, 0); + BIF_RET(am_false); +#else + /* + * We need to restore the code addresses of the funs in + * two stages in order to ensure that we do not get any + * stale suspended processes due to the purge abort. + * Restore address pointer (erts_fun_purge_abort_prepare); + * wait for thread progress; clear pending purge address + * pointer (erts_fun_purge_abort_finalize), and then + * resume processes that got suspended + * (finalize_purge_operation). + */ + erts_schedule_thr_prgr_later_op(finalize_purge_abort, + NULL, + &purger_lop_data); + erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_BIF_YIELD_RETURN(BIF_P, am_false); +#endif } - erts_release_code_write_permission(); - return ret; -} -static void -decrement_refc(BeamCodeHeader* code_hdr) -{ - struct erl_off_heap_header* oh = code_hdr->literals_off_heap; - - while (oh) { - Binary* bptr; - ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG); - bptr = ((ProcBin*)oh)->val; - if (erts_refc_dectest(&bptr->refc, 0) == 0) { - erts_bin_free(bptr); + case am_complete: { + ErtsCodeIndex code_ix; + BeamInstr* code; + Module* modp; + int is_blocking = 0; + Eterm ret; + ErtsLiteralArea *literals = NULL; + + + /* + * We have no direct references into the code. + * Complete to purge. + */ + + if (purge_state.module != BIF_ARG_1) + BIF_ERROR(BIF_P, BADARG); + + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + } + + code_ix = erts_active_code_ix(); + + /* + * Correct module? + */ + + if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { + ERTS_BIF_PREP_RET(ret, am_false); + } + else { + + erts_rwlock_old_code(code_ix); + + /* + * Any code to purge? + */ + if (!modp->old.code_hdr) { + ERTS_BIF_PREP_RET(ret, am_false); + } + else { + /* + * Unload any NIF library + */ + if (modp->old.nif != NULL) { + /* ToDo: Do unload nif without blocking */ + erts_rwunlock_old_code(code_ix); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + is_blocking = 1; + erts_rwlock_old_code(code_ix); + erts_unload_nif(modp->old.nif); + modp->old.nif = NULL; + } + + /* + * Remove the old code. + */ + ASSERT(erts_total_code_size >= modp->old.code_length); + erts_total_code_size -= modp->old.code_length; + code = (BeamInstr*) modp->old.code_hdr; + erts_fun_purge_complete(purge_state.funs, purge_state.fe_ix); + beam_catches_delmod(modp->old.catches, code, modp->old.code_length, + code_ix); + literals = modp->old.code_hdr->literal_area; + modp->old.code_hdr->literal_area = NULL; + erts_free(ERTS_ALC_T_CODE, (void *) code); + modp->old.code_hdr = NULL; + modp->old.code_length = 0; + modp->old.catches = BEAM_CATCHES_NIL; + erts_remove_from_ranges(code); + ERTS_BIF_PREP_RET(ret, am_true); + } + erts_rwunlock_old_code(code_ix); + } + if (is_blocking) { + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + } + + erts_release_code_write_permission(); + + finalize_purge_operation(BIF_P, ret == am_true); + +#if !defined(ERTS_NEW_PURGE_STRATEGY) + + ASSERT(ERTS_COPY_LITERAL_AREA() == literals); + ERTS_SET_COPY_LITERAL_AREA(NULL); + erts_release_literal_area(literals); + +#else /* ERTS_NEW_PURGE_STRATEGY */ + + if (literals) { + ErtsLiteralAreaRef *ref; + ref = erts_alloc(ERTS_ALC_T_LITERAL_REF, + sizeof(ErtsLiteralAreaRef)); + ref->literal_area = literals; + ref->next = NULL; + erts_smp_mtx_lock(&release_literal_areas.mtx); + if (release_literal_areas.last) { + release_literal_areas.last->next = ref; + release_literal_areas.last = ref; + } + else { + release_literal_areas.first = ref; + release_literal_areas.last = ref; + } + erts_smp_mtx_unlock(&release_literal_areas.mtx); + erts_queue_message(erts_literal_area_collector, + 0, + erts_alloc_message(0, NULL), + am_copy_literals, + BIF_P->common.id); } - oh = oh->next; + +#endif /* ERTS_NEW_PURGE_STRATEGY */ + + return ret; + } + + default: + BIF_ERROR(BIF_P, BADARG); + } } diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 8489897d3a..920c8b1ed0 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -858,7 +858,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) if (flags & MATCH_SET_RX_TRACE) { erts_trace_return(p, ep->code, result, &ERTS_TRACER(p)); } - if (flags & MATCH_SET_RETURN_TO_TRACE) { + if (flags & MATCH_SET_RETURN_TO_TRACE && + IS_TRACED_FL(p, F_TRACE_RETURN_TO)) { /* can only happen if(local)*/ if (applying) { /* Apply of BIF, cp is in calling function */ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4716460a6b..ef4cdf9d5a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1960,6 +1960,8 @@ void process_main(void) ErtsMessage* msgp; PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_CHK_MBUF_SZ(c_p); + PreFetch(0, next); msgp = PEEK_MESSAGE(c_p); @@ -2047,6 +2049,7 @@ void process_main(void) } ERTS_DBG_CHK_REDS(c_p, FCALLS); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2161,7 +2164,10 @@ void process_main(void) c_p->i = (BeamInstr *) Arg(0); /* L1 */ SWAPOUT; c_p->arity = 0; - erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE); + + if (!ERTS_PTMR_IS_TIMED_OUT(c_p)) + erts_smp_atomic32_read_band_relb(&c_p->state, + ~ERTS_PSFLG_ACTIVE); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); c_p->current = NULL; @@ -2580,7 +2586,9 @@ do { \ c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2611,7 +2619,9 @@ do { \ c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2641,7 +2651,9 @@ do { \ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, live); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2682,7 +2694,9 @@ do { \ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, live); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2725,7 +2739,9 @@ do { \ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, live); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2763,7 +2779,9 @@ do { \ c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2790,7 +2808,9 @@ do { \ bf = (BifFunction) Arg(0); PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2826,13 +2846,7 @@ do { \ goto context_switch3; } - if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { - if (GET_BIF_MODULE(Arg(0)) == am_ets) { - ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); - } else { - ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF); - } - } + ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(Arg(0)), GET_BIF_ADDRESS(Arg(0))); bf = GET_BIF_ADDRESS(Arg(0)); @@ -2846,7 +2860,9 @@ do { \ ASSERT(!ERTS_PROC_IS_EXITING(c_p)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); live_hf_end = c_p->mbuf; + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, I); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_HOLE_CHECK(c_p); @@ -3032,6 +3048,7 @@ do { \ if (i == 0) { StoreBifResult(4, Op1); } + ires = big_size(Op1); goto big_shift; } } else if (is_big(Op2)) { @@ -3047,7 +3064,6 @@ do { \ OpCase(i_bsl_jIssd): GetArg2(2, Op1, Op2); - do_bsl: if (is_small(Op2)) { i = signed_val(Op2); @@ -3073,16 +3089,12 @@ do { \ StoreBifResult(4, Op1); } } - Op1 = small_to_big(ires, tmp_big); -#ifdef TAG_LITERAL_PTR - Op1 |= TAG_LITERAL_PTR; -#endif + ires = 1; /* big_size(small_to_big(Op1)) */ big_shift: if (i > 0) { /* Left shift. */ - ires = big_size(Op1) + (i / D_EXP); + ires += (i / D_EXP); } else { /* Right shift. */ - ires = big_size(Op1); if (ires <= (-i / D_EXP)) ires = 3; /* ??? */ else @@ -3101,6 +3113,9 @@ do { \ goto lb_Cl_error; } TestHeapPreserve(ires+1, Arg(1), Op1); + if (is_small(Op1)) { + Op1 = small_to_big(signed_val(Op1), tmp_big); + } bigp = HTOP; Op1 = big_lshift(Op1, i, bigp); if (is_big(Op1)) { @@ -3123,6 +3138,7 @@ do { \ if (i == 0) { StoreBifResult(4, Op1); } + ires = big_size(Op1); goto big_shift; } } else if (is_big(Op2)) { @@ -3559,11 +3575,13 @@ do { \ ASSERT(c_p->scheduler_data); #endif live_hf_end = c_p->mbuf; + ERTS_CHK_MBUF_SZ(c_p); erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) nif_bif_result = THE_NON_VALUE; erts_post_nif(&env); + ERTS_CHK_MBUF_SZ(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -3593,13 +3611,7 @@ do { \ goto context_switch; } - if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { - if ((Eterm)I[-3] == am_ets) { - ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); - } else { - ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF); - } - } + ERTS_MSACC_SET_BIF_STATE_CACHED_X((Eterm)I[-3], (BifFunction)Arg(0)); c_p->current = I-3; /* In case we apply process_info/1,2 or load_nif/1 */ c_p->i = I; /* In case we apply check_process_code/2. */ @@ -3621,7 +3633,9 @@ do { \ Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf; ASSERT(!ERTS_PROC_IS_EXITING(c_p)); live_hf_end = c_p->mbuf; + ERTS_CHK_MBUF_SZ(c_p); nif_bif_result = (*bf)(c_p, reg, I); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -5261,19 +5275,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) else { /* * Dirty CPU scheduler: - * Currently two reductions consumed per - * micro second spent in the dirty NIF. + * Reductions based on time consumed by + * the dirty NIF. */ - ErtsMonotonicTime time; - time = erts_get_monotonic_time(esdp); - time -= start_time; - time = ERTS_MONOTONIC_TO_USEC(time); - time *= (CONTEXT_REDS-1)/1000 + 1; - ASSERT(time >= 0); - if (time == 0) - time = 1; /* At least one reduction */ - time += esdp->virtual_reds; - reds_used = time > INT_MAX ? INT_MAX : (int) time; + Sint64 treds; + treds = erts_time2reds(start_time, + erts_get_monotonic_time(esdp)); + treds += esdp->virtual_reds; + reds_used = treds > INT_MAX ? INT_MAX : (int) treds; } PROCESS_MAIN_CHK_LOCKS(c_p); @@ -5391,7 +5400,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) ASSERT(!c_p->scheduler_data); erts_pre_dirty_nif(esdp, &env, c_p, - (struct erl_module_nif*)I[2], NULL); + (struct erl_module_nif*)I[2]); #ifdef DEBUG result = @@ -5400,7 +5409,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) #endif (*fp)(&env, arity, reg); - erts_post_nif(&env); + erts_post_dirty_nif(&env); ASSERT(!is_value(result)); ASSERT(c_p->freason == TRAP); @@ -6535,34 +6544,49 @@ call_fun(Process* p, /* Current process. */ * representation (the module has never been loaded), * or the module defining the fun has been unloaded. */ + module = fe->module; - if ((modp = erts_get_module(module, code_ix)) != NULL - && modp->curr.code_hdr != NULL) { + + ERTS_SMP_READ_MEMORY_BARRIER; + if (fe->pend_purge_address) { /* - * There is a module loaded, but obviously the fun is not - * defined in it. We must not call the error_handler - * (or we will get into an infinite loop). + * The system is currently trying to purge the + * module containing this fun. Suspend the process + * and let it try again when the purge operation is + * done (may succeed or not). */ - goto badfun; + ep = erts_suspend_process_on_pending_purge_lambda(p); + ASSERT(ep); } + else { + if ((modp = erts_get_module(module, code_ix)) != NULL + && modp->curr.code_hdr != NULL) { + /* + * There is a module loaded, but obviously the fun is not + * defined in it. We must not call the error_handler + * (or we will get into an infinite loop). + */ + goto badfun; + } - /* - * No current code for this module. Call the error_handler module - * to attempt loading the module. - */ + /* + * No current code for this module. Call the error_handler module + * to attempt loading the module. + */ - ep = erts_find_function(erts_proc_get_error_handler(p), - am_undefined_lambda, 3, code_ix); - if (ep == NULL) { /* No error handler */ - p->current = NULL; - p->freason = EXC_UNDEF; - return NULL; + ep = erts_find_function(erts_proc_get_error_handler(p), + am_undefined_lambda, 3, code_ix); + if (ep == NULL) { /* No error handler */ + p->current = NULL; + p->freason = EXC_UNDEF; + return NULL; + } } reg[0] = module; reg[1] = fun; reg[2] = args; reg[3] = NIL; - return ep->addressv[erts_active_code_ix()]; + return ep->addressv[code_ix]; } } } else if (is_export_header(hdr)) { @@ -7017,7 +7041,11 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) /* The expensive case, need to build a hashmap */ if (n > MAP_SMALL_MAP_LIMIT) { - res = erts_hashmap_from_ks_and_vs(p,flatmap_get_keys(mp),flatmap_get_values(mp),n); + ErtsHeapFactory factory; + erts_factory_proc_init(&factory, p); + res = erts_hashmap_from_ks_and_vs(&factory,flatmap_get_keys(mp), + flatmap_get_values(mp),n); + erts_factory_close(&factory); } return res; } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 0c2743beb2..f63addb309 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -105,7 +105,7 @@ typedef struct { */ typedef struct genop { - int op; /* Opcode. */ + unsigned int op; /* Opcode. */ int arity; /* Number of arguments. */ GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */ GenOpArg* a; /* The arguments. */ @@ -283,8 +283,8 @@ typedef struct LoaderState { byte* code_start; /* Start of code file. */ unsigned code_size; /* Size of code file. */ int specific_op; /* Specific opcode (-1 if not found). */ - int num_functions; /* Number of functions in module. */ - int num_labels; /* Number of labels. */ + unsigned int num_functions; /* Number of functions in module. */ + unsigned int num_labels; /* Number of labels. */ BeamCodeHeader* hdr; /* Loaded code header */ BeamInstr* codev; /* Loaded code buffer */ int codev_size; /* Size of code buffer in words. */ @@ -303,13 +303,13 @@ typedef struct LoaderState { * Atom table. */ - int num_atoms; /* Number of atoms in atom table. */ + unsigned int num_atoms; /* Number of atoms in atom table. */ Eterm* atom; /* Atom table. */ - int num_exps; /* Number of exports. */ + unsigned int num_exps; /* Number of exports. */ ExportEntry* export; /* Pointer to export table. */ - int num_imports; /* Number of imports. */ + unsigned int num_imports; /* Number of imports. */ ImportEntry* import; /* Import entry (translated information). */ /* @@ -323,8 +323,8 @@ typedef struct LoaderState { * Lambda table. */ - int num_lambdas; /* Number of lambdas in table. */ - int lambdas_allocated; /* Size of allocated lambda table. */ + unsigned int num_lambdas; /* Number of lambdas in table. */ + unsigned int lambdas_allocated; /* Size of allocated lambda table. */ Lambda* lambdas; /* Pointer to lambdas. */ Lambda def_lambdas[16]; /* Default storage for lambda table. */ char* lambda_error; /* Delayed missing 'FunT' error. */ @@ -333,8 +333,8 @@ typedef struct LoaderState { * Literals (constant pool). */ - int num_literals; /* Number of literals in table. */ - int allocated_literals; /* Number of literal entries allocated. */ + unsigned int num_literals; /* Number of literals in table. */ + unsigned int allocated_literals; /* Number of literal entries allocated. */ Literal* literals; /* Array of literals. */ LiteralPatch* literal_patches; /* Operands that need to be patched. */ Uint total_literal_size; /* Total heap size for all literals. */ @@ -343,13 +343,13 @@ typedef struct LoaderState { * Line table. */ BeamInstr* line_item; /* Line items from the BEAM file. */ - int num_line_items; /* Number of line items. */ + unsigned int num_line_items;/* Number of line items. */ LineInstr* line_instr; /* Line instructions */ - int num_line_instrs; /* Maximum number of line instructions */ - int current_li; /* Current line instruction */ - int* func_line; /* Mapping from function to first line instr */ + unsigned int num_line_instrs; /* Maximum number of line instructions */ + unsigned int current_li; /* Current line instruction */ + unsigned int* func_line; /* Mapping from function to first line instr */ Eterm* fname; /* List of file names */ - int num_fnames; /* Number of filenames in fname table */ + unsigned int num_fnames; /* Number of filenames in fname table */ int loc_size; /* Size of location info in bytes (2/4) */ } LoaderState; @@ -663,7 +663,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader, stp->hdr->compile_ptr = NULL; stp->hdr->compile_size = 0; stp->hdr->compile_size_on_heap = 0; - stp->hdr->literals_start = NULL; + stp->hdr->literal_area = NULL; stp->hdr->md5_ptr = NULL; /* @@ -1005,8 +1005,9 @@ loader_state_dtor(Binary* magic) stp->bin = 0; } if (stp->hdr != 0) { - if (stp->hdr->literals_start) { - erts_free(ERTS_ALC_T_LITERAL, stp->hdr->literals_start); + if (stp->hdr->literal_area) { + erts_release_literal_area(stp->hdr->literal_area); + stp->hdr->literal_area = NULL; } erts_free(ERTS_ALC_T_CODE, stp->hdr); stp->hdr = 0; @@ -1330,7 +1331,7 @@ verify_chunks(LoaderState* stp) static int load_atom_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_atoms); stp->num_atoms++; @@ -1375,13 +1376,13 @@ load_atom_table(LoaderState* stp) static int load_import_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_imports); stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_imports * sizeof(ImportEntry)); for (i = 0; i < stp->num_imports; i++) { - int n; + unsigned int n; Eterm mod; Eterm func; Uint arity; @@ -1389,17 +1390,17 @@ load_import_table(LoaderState* stp) GetInt(stp, 4, n); if (n >= stp->num_atoms) { - LoadError2(stp, "import entry %d: invalid atom number %d", i, n); + LoadError2(stp, "import entry %u: invalid atom number %u", i, n); } mod = stp->import[i].module = stp->atom[n]; GetInt(stp, 4, n); if (n >= stp->num_atoms) { - LoadError2(stp, "import entry %d: invalid atom number %d", i, n); + LoadError2(stp, "import entry %u: invalid atom number %u", i, n); } func = stp->import[i].function = stp->atom[n]; GetInt(stp, 4, arity); if (arity > MAX_REG) { - LoadError2(stp, "import entry %d: invalid arity %d", i, arity); + LoadError2(stp, "import entry %u: invalid arity %d", i, arity); } stp->import[i].arity = arity; stp->import[i].patches = 0; @@ -1427,12 +1428,12 @@ load_import_table(LoaderState* stp) static int read_export_table(LoaderState* stp) { - int i; + unsigned int i; BeamInstr* address; GetInt(stp, 4, stp->num_exps); if (stp->num_exps > stp->num_functions) { - LoadError2(stp, "%d functions exported; only %d functions defined", + LoadError2(stp, "%u functions exported; only %u functions defined", stp->num_exps, stp->num_functions); } stp->export @@ -1450,16 +1451,16 @@ read_export_table(LoaderState* stp) stp->export[i].function = func; GetInt(stp, 4, arity); if (arity > MAX_REG) { - LoadError2(stp, "export table entry %d: absurdly high arity %d", i, arity); + LoadError2(stp, "export table entry %u: absurdly high arity %u", i, arity); } stp->export[i].arity = arity; GetInt(stp, 4, n); if (n >= stp->num_labels) { - LoadError3(stp, "export table entry %d: invalid label %d (highest defined label is %d)", i, n, stp->num_labels); + LoadError3(stp, "export table entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels); } value = stp->labels[n].value; if (value == 0) { - LoadError2(stp, "export table entry %d: label %d not resolved", i, n); + LoadError2(stp, "export table entry %u: label %u not resolved", i, n); } stp->export[i].address = address = stp->codev + value; @@ -1520,7 +1521,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity) static int read_lambda_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_lambdas); if (stp->num_lambdas > stp->lambdas_allocated) { @@ -1540,12 +1541,12 @@ read_lambda_table(LoaderState* stp) GetAtom(stp, n, stp->lambdas[i].function); GetInt(stp, 4, arity); if (arity > MAX_REG) { - LoadError2(stp, "lambda entry %d: absurdly high arity %d", i, arity); + LoadError2(stp, "lambda entry %u: absurdly high arity %u", i, arity); } stp->lambdas[i].arity = arity; GetInt(stp, 4, n); if (n >= stp->num_labels) { - LoadError3(stp, "lambda entry %d: invalid label %d (highest defined label is %d)", + LoadError3(stp, "lambda entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels); } stp->lambdas[i].label = n; @@ -1566,7 +1567,7 @@ read_lambda_table(LoaderState* stp) static int read_literal_table(LoaderState* stp) { - int i; + unsigned int i; uLongf uncompressed_sz; byte* uncompressed = 0; @@ -1588,7 +1589,7 @@ read_literal_table(LoaderState* stp) } for (i = 0; i < stp->num_literals; i++) { - int sz; + Uint sz; Sint heap_size; byte* p; Eterm val; @@ -1597,7 +1598,7 @@ read_literal_table(LoaderState* stp) GetInt(stp, 4, sz); /* Size of external term format. */ GetString(stp, p, sz); if ((heap_size = erts_decode_ext_size(p, sz)) < 0) { - LoadError1(stp, "literal %d: bad external format", i); + LoadError1(stp, "literal %u: bad external format", i); } if (heap_size > 0) { @@ -1607,7 +1608,7 @@ read_literal_table(LoaderState* stp) val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { - LoadError1(stp, "literal %d: bad external format", i); + LoadError1(stp, "literal %u: bad external format", i); } erts_factory_close(&factory); stp->literals[i].heap_frags = factory.heap_frags; @@ -1617,7 +1618,7 @@ read_literal_table(LoaderState* stp) erts_factory_dummy_init(&factory); val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { - LoadError1(stp, "literal %d: bad external format", i); + LoadError1(stp, "literal %u: bad external format", i); } ASSERT(is_immed(val)); stp->literals[i].heap_frags = NULL; @@ -1640,9 +1641,9 @@ read_line_table(LoaderState* stp) { unsigned version; ERTS_DECLARE_DUMMY(unsigned flags); - int num_line_items; + unsigned int num_line_items; BeamInstr* lp; - int i; + unsigned int i; BeamInstr fname_index; BeamInstr tag; @@ -1721,7 +1722,7 @@ read_line_table(LoaderState* stp) } } else if (tag == TAG_a) { if (val > stp->num_fnames) { - LoadError2(stp, "file index overflow (%d/%d)", + LoadError2(stp, "file index overflow (%u/%u)", val, stp->num_fnames); } fname_index = val; @@ -1757,9 +1758,9 @@ read_line_table(LoaderState* stp) stp->num_line_instrs * sizeof(LineInstr)); stp->current_li = 0; - stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, - stp->num_functions * - sizeof(int)); + stp->func_line = (unsigned int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, + stp->num_functions * + sizeof(int)); return 1; @@ -1783,6 +1784,10 @@ read_code_header(LoaderState* stp) */ GetInt(stp, 4, head_size); + if (head_size > stp->file_left) { + LoadError2(stp, "invalid code header size %u; bytes left %u", + head_size, stp->file_left); + } stp->code_start = stp->file_p + head_size; stp->code_size = stp->file_left - head_size; stp->file_left = head_size; @@ -1887,7 +1892,7 @@ load_code(LoaderState* stp) ci = stp->ci; for (;;) { - int new_op; + unsigned int new_op; GenOp* tmp_op; ASSERT(ci <= codev_size); @@ -1895,10 +1900,10 @@ load_code(LoaderState* stp) get_next_instr: GetByte(stp, new_op); if (new_op >= NUM_GENERIC_OPS) { - LoadError1(stp, "invalid opcode %d", new_op); + LoadError1(stp, "invalid opcode %u", new_op); } if (gen_opc[new_op].name[0] == '\0') { - LoadError1(stp, "invalid opcode %d", new_op); + LoadError1(stp, "invalid opcode %u", new_op); } @@ -2368,7 +2373,7 @@ load_code(LoaderState* stp) VerifyTag(stp, tag, TAG_u); last_label = tmp_op->a[arg].val; if (!(0 < last_label && last_label < stp->num_labels)) { - LoadError2(stp, "invalid label num %d (0 < label < %d)", + LoadError2(stp, "invalid label num %u (0 < label < %u)", tmp_op->a[arg].val, stp->num_labels); } if (stp->labels[last_label].value != 0) { @@ -2512,7 +2517,7 @@ load_code(LoaderState* stp) { Sint offset; if (function_number >= stp->num_functions) { - LoadError1(stp, "too many functions in module (header said %d)", + LoadError1(stp, "too many functions in module (header said %u)", stp->num_functions); } @@ -2591,14 +2596,14 @@ load_code(LoaderState* stp) if (stp->line_item) { BeamInstr item = code[ci-1]; BeamInstr loc; - int li; + unsigned int li; if (item >= stp->num_line_items) { - LoadError2(stp, "line instruction index overflow (%d/%d)", + LoadError2(stp, "line instruction index overflow (%u/%u)", item, stp->num_line_items); } li = stp->current_li; if (li >= stp->num_line_instrs) { - LoadError2(stp, "line instruction table overflow (%d/%d)", + LoadError2(stp, "line instruction table overflow (%u/%u)", li, stp->num_line_instrs); } loc = stp->line_item[item]; @@ -2630,6 +2635,10 @@ load_code(LoaderState* stp) * End of code found. */ case op_int_code_end: + if (function_number != stp->num_functions) { + LoadError2(stp, "too few functions (%u) in module (header said %u)", + function_number, stp->num_functions); + } stp->codev_size = codev_size; stp->ci = ci; stp->function = THE_NON_VALUE; @@ -4560,13 +4569,16 @@ freeze_code(LoaderState* stp) Eterm* ptr; LiteralPatch* lp; ErlOffHeap code_off_heap; + ErtsLiteralArea *literal_area; + Uint lit_asize; ERTS_INIT_OFF_HEAP(&code_off_heap); - ptr = (Eterm*)erts_alloc(ERTS_ALC_T_LITERAL, - stp->total_literal_size*sizeof(Eterm)); - code_hdr->literals_start = ptr; - code_hdr->literals_end = ptr + stp->total_literal_size; + lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size); + literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize); + ptr = &literal_area->start[0]; + literal_area->end = ptr + stp->total_literal_size; + for (i = 0; i < stp->num_literals; i++) { if (is_not_immed(stp->literals[i].term)) { erts_move_multi_frags(&ptr, &code_off_heap, @@ -4576,7 +4588,7 @@ freeze_code(LoaderState* stp) ptr_val(stp->literals[i].term))); } } - code_hdr->literals_off_heap = code_off_heap.first; + literal_area->off_heap = code_off_heap.first; lp = stp->literal_patches; while (lp != 0) { BeamInstr* op_ptr; @@ -4587,6 +4599,7 @@ freeze_code(LoaderState* stp) op_ptr[0] = lit->term; lp = lp->next; } + code_hdr->literal_area = literal_area; } CHKBLK(ERTS_ALC_T_CODE,code); @@ -4598,8 +4611,8 @@ freeze_code(LoaderState* stp) str_table = (byte *) (codev + stp->ci); } else { BeamCodeLineTab* const line_tab = (BeamCodeLineTab *) (codev+stp->ci); - const int ftab_size = stp->num_functions; - const int num_instrs = stp->current_li; + const unsigned int ftab_size = stp->num_functions; + const unsigned int num_instrs = stp->current_li; const BeamInstr** const line_items = (const BeamInstr**) &line_tab->func_tab[ftab_size + 1]; @@ -4759,7 +4772,7 @@ freeze_code(LoaderState* stp) static void final_touch(LoaderState* stp, struct erl_module_instance* inst_p) { - int i; + unsigned int i; int on_load = stp->on_load; unsigned catches; Uint index; @@ -5431,7 +5444,7 @@ new_genop(LoaderState* stp) static int new_label(LoaderState* stp) { - int num = stp->num_labels; + unsigned int num = stp->num_labels; stp->num_labels++; stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE, @@ -5642,6 +5655,28 @@ has_native(BeamCodeHeader *code_hdr) return result; } +void +erts_release_literal_area(ErtsLiteralArea* literal_area) +{ + struct erl_off_heap_header* oh; + + if (!literal_area) + return; + + oh = literal_area->off_heap; + + while (oh) { + Binary* bptr; + ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG); + bptr = ((ProcBin*)oh)->val; + if (erts_refc_dectest(&bptr->refc, 0) == 0) { + erts_bin_free(bptr); + } + oh = oh->next; + } + erts_free(ERTS_ALC_T_LITERAL, literal_area); +} + int erts_is_module_native(BeamCodeHeader* code_hdr) { @@ -6029,11 +6064,11 @@ stub_copy_info(LoaderState* stp, static int stub_read_export_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_exps); if (stp->num_exps > stp->num_functions) { - LoadError2(stp, "%d functions exported; only %d functions defined", + LoadError2(stp, "%u functions exported; only %u functions defined", stp->num_exps, stp->num_functions); } stp->export @@ -6047,7 +6082,7 @@ stub_read_export_table(LoaderState* stp) GetAtom(stp, n, stp->export[i].function); GetInt(stp, 4, n); if (n > MAX_REG) { - LoadError2(stp, "export table entry %d: absurdly high arity %d", i, n); + LoadError2(stp, "export table entry %u: absurdly high arity %u", i, n); } stp->export[i].arity = n; GetInt(stp, 4, n); /* Ignore label */ @@ -6061,8 +6096,8 @@ stub_read_export_table(LoaderState* stp) static void stub_final_touch(LoaderState* stp, BeamInstr* fp) { - int i; - int n = stp->num_exps; + unsigned int i; + unsigned int n = stp->num_exps; Eterm mod = fp[2]; Eterm function = fp[3]; int arity = fp[4]; @@ -6373,9 +6408,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) code_hdr->compile_ptr = NULL; code_hdr->compile_size = 0; code_hdr->compile_size_on_heap = 0; - code_hdr->literals_start = NULL; - code_hdr->literals_end = NULL; - code_hdr->literals_off_heap = 0; + code_hdr->literal_area = NULL; code_hdr->on_load_function_ptr = NULL; code_hdr->line_table = NULL; code_hdr->md5_ptr = NULL; diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index fd2dd97fee..1200bb9c6f 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -50,6 +50,7 @@ extern BeamInstr* em_call_error_handler; extern BeamInstr* em_apply_bif; extern BeamInstr* em_call_nif; +struct ErtsLiteralArea_; /* * The following variables keep a sorted list of address ranges for @@ -89,9 +90,7 @@ typedef struct beam_code_header { /* * Literal area (constant pool). */ - Eterm* literals_start; - Eterm* literals_end; - struct erl_off_heap_header* literals_off_heap; + struct ErtsLiteralArea_ *literal_area; /* * Pointer to the on_load function (or NULL if none). @@ -120,7 +119,12 @@ typedef struct beam_code_header { }BeamCodeHeader; +void erts_release_literal_area(struct ErtsLiteralArea_* literal_area); int erts_is_module_native(BeamCodeHeader* code); +void erts_beam_bif_load_init(void); +struct erl_fun_entry; +void erts_purge_state_add_fun(struct erl_fun_entry *fe); +Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p); /* * Layout of the line table. diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c deleted file mode 100644 index c8409784ef..0000000000 --- a/erts/emulator/beam/benchmark.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2002-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "sys.h" -#include "global.h" -#include "benchmark.h" - -#ifdef BM_COUNTERS -unsigned long long processes_busy; -unsigned long long processes_spawned; -unsigned long long messages_sent; -unsigned long long messages_copied; -unsigned long long messages_ego; -unsigned long long minor_gc; -unsigned long long major_gc; -#endif /* BM_COUNTERS */ - -#ifdef BM_TIMERS - -/* assuming Solaris */ -#include <time.h> -BM_TIMER_T system_clock; - -unsigned long local_pause_times[MAX_PAUSE_TIME]; -unsigned long pause_times[MAX_PAUSE_TIME]; -unsigned long pause_times_old[MAX_PAUSE_TIME]; - -BM_TIMER_T mmu; -BM_TIMER_T mmu_counter; - -BM_NEW_TIMER(timer); -BM_NEW_TIMER(system); -BM_NEW_TIMER(gc); -BM_NEW_TIMER(minor_gc); -BM_NEW_TIMER(major_gc); -BM_NEW_TIMER(minor_global_gc); -BM_NEW_TIMER(major_global_gc); -BM_NEW_TIMER(send); -BM_NEW_TIMER(copy); -BM_NEW_TIMER(size); -BM_NEW_TIMER(max_minor); -BM_NEW_TIMER(max_major); -BM_NEW_TIMER(max_global_minor); -BM_NEW_TIMER(max_global_major); -BM_NEW_TIMER(misc0); -BM_NEW_TIMER(misc1); -BM_NEW_TIMER(misc2); -#endif /* BM_TIMERS */ - -#ifdef BM_HEAP_SIZES -unsigned long long max_used_heap; -unsigned long long max_allocated_heap; -unsigned long long max_used_global_heap; -unsigned long long max_allocated_global_heap; -#endif /* BM_HEAP_SIZES */ - -#ifdef BM_MESSAGE_SIZES -unsigned long long words_sent; -unsigned long long words_copied; -unsigned long long words_prealloc; -unsigned long long message_sizes[1000]; -#endif /* BM_MESSAGE_SIZES */ - -/***** - * The following functions have to be defined, but they only have contents - * if certain keywords are defined. - */ - -void init_benchmarking() -{ -#ifdef BM_TIMERS - int i; - for (i = 0; i < 1000; i++) - { - BM_START_TIMER(system); - BM_STOP_TIMER(system); - } - timer_time = system_time / 1000; - - for (i = 0; i < MAX_PAUSE_TIME; i++) { - local_pause_times[i] = 0; - pause_times[i] = 0; - pause_times_old[i] = 0; - } - - mmu = 0; - mmu_counter = 0; - - BM_MMU_INIT(); -#endif /* BM_TIMERS */ - -#ifdef BM_COUNTERS - processes_busy = 0; - processes_spawned = 0; - messages_sent = 0; - messages_copied = 0; - messages_ego = 0; - minor_gc = 0; - major_gc = 0; -#endif /* BM_COUNTERS */ - -#ifdef BM_HEAP_SIZES - max_used_heap = 0; - max_allocated_heap = 0; - max_used_global_heap = 0; - max_allocated_global_heap = 0; -#endif /* BM_HEAP_SIZES */ - -#ifdef BM_MESSAGE_SIZES - words_sent = 0; - words_copied = 0; - words_prealloc = 0; - { - int i; - for (i = 0; i < 1000; i++) - message_sizes[i] = 0; - } -#endif /* BM_MESSAGE_SIZES */ -} - -void save_statistics() -{ -#ifdef BM_STATISTICS - FILE *file = fopen(BM_STATISTICS_FILE,"a"); - long i = 0; - - if (file) - { - erts_fprintf(file,"-------------------------------------------------------------------------\n"); - erts_fprintf(file,"The counters are reset at system start and are sums over the entire node.\n"); - erts_fprintf(file,"You may reset them manually using the BIFs in the module hipe_bifs.\n"); - erts_fprintf(file,"All times are given in milliseconds.\n"); - erts_fprintf(file,"-------------------------------------------------------------------------\n"); - - erts_fprintf(file,"Node: %T\n",erts_this_node->sysname); - -#ifdef BM_COUNTERS - erts_fprintf(file,"Number of processes spawned: %lld\n",processes_spawned); - erts_fprintf(file,"Number of local minor GCs: %lld\n",minor_gc); - erts_fprintf(file,"Number of local major GCs: %lld\n",major_gc); - erts_fprintf(file,"Number of messages sent: %lld\n",messages_sent); - erts_fprintf(file,"Number of messages copied: %lld\n",messages_copied); - erts_fprintf(file,"Number of messages sent to self: %lld\n",messages_ego); -#endif /* BM_COUNTERS */ - -#ifdef BM_MESSAGE_SIZES - erts_fprintf(file,"Number of words sent: %lld\n",words_sent); - erts_fprintf(file,"Number of words copied: %lld\n",words_copied); - erts_fprintf(file,"Number of words preallocated: %lld\n",words_prealloc); -#endif /* BM_MESSAGE_SIZES */ - -#ifdef BM_HEAP_SIZES - erts_fprintf(file,"Biggest local heap used (in words): %lld\n",max_used_heap); - erts_fprintf(file,"Biggest local heap allocated (in words): %lld\n",max_allocated_heap); - erts_fprintf(file,"Biggest global heap used (in words): %lld\n",max_used_global_heap); - erts_fprintf(file,"Biggest global heap allocated (in words): %lld\n",max_allocated_global_heap); -#endif /* BM_HEAP_SIZES */ - -#ifdef BM_TIMERS - erts_fprintf(file,"--- The total active system time is the sum of all times below ---\n"); - BM_TIME_PRINTER("Mutator time",system_time); - BM_TIME_PRINTER("Time spent in send (excluding size & copy)",send_time); - BM_TIME_PRINTER("Time spent in size",size_time); - BM_TIME_PRINTER("Time spent in copy",copy_time); - BM_TIME_PRINTER("Time spent in local minor GC",minor_gc_time); - BM_TIME_PRINTER("Time spent in local major GC",major_gc_time); - BM_TIME_PRINTER("Time spent in global minor GC",minor_global_gc_time); - BM_TIME_PRINTER("Time spent in global major GC",major_global_gc_time); - erts_fprintf(file,"---\n"); - BM_TIME_PRINTER("Maximum time spent in one separate local minor GC",max_minor_time); - BM_TIME_PRINTER("Maximum time spent in one separate local major GC",max_major_time); - BM_TIME_PRINTER("Maximum time spent in one separate global minor GC",max_global_minor_time); - BM_TIME_PRINTER("Maximum time spent in one separate global major GC",max_global_major_time); -#endif /* BM_TIMERS */ - -#if 0 - /* Save a log file for import into excel */ - - long long total_time, n; - long left, right, mid; - -#ifdef BM_COUNTERS - erts_fprintf(file,"Spawns\tLocalGC\tMAGC\tMessages\tMutator_t\tLocalGC_t\tMAGC_t\tLocMaxP\tLocMeanP\tLocGeoMP\tMAMaxP\tMAMeanP\tMAGeoMP\t\tCMAGC\tCMAGC_t\n"); - erts_fprintf(file,"%lld\t%lld\t%lld\t%lld\t", - processes_spawned, - minor_garbage_cols + major_garbage_cols, - minor_global_garbage_cols + major_global_garbage_cols, - messages_sent); -#endif /* BM_COUNTERS */ - -#ifdef BM_TIMERS - erts_fprintf(file,"%lld\t%lld\t%lld\t", - (long long)(system_time + send_time + size_time + copy_time), - (long long)(minor_gc_time + major_gc_time), - (long long)(minor_global_gc_time + major_global_gc_time)); - - total_time = 0; n = 0; - left = 0; right = 0; mid = 0; - for (i = 0; i < MAX_PAUSE_TIME; i++) { - total_time += local_pause_times[i] * i; - n += local_pause_times[i]; - if (i > mid) - right += local_pause_times[i]; - while(right > left) { - left += local_pause_times[mid++]; - right -= local_pause_times[mid]; - } - } - erts_fprintf(file,"%lld\t%lld\t%ld\t", - (long long)((max_minor_time > max_major_time ? - max_minor_time : - max_major_time)*1000), - total_time / n, - mid); - - total_time = 0; n = 0; - left = 0; right = 0; mid = 0; - for (i = 0; i < MAX_PAUSE_TIME; i++) { - if (pause_times[i] > 0) { - total_time += pause_times[i] * i; - n += pause_times[i]; - if (i > mid) - right += pause_times[i]; - while(right > left) { - left += pause_times[mid++]; - right -= pause_times[mid]; - } - } - } - erts_fprintf(file,"%lld\t%lld\t%ld\t", - (long long)((max_global_minor_time > max_global_major_time ? - max_global_minor_time : - max_global_major_time)*1000), - (n > 0 ? total_time / n : 0), - mid); - - erts_fprintf(file,"\t%lld\t%lld\n",n,total_time); - - erts_fprintf(file,"\nMinor:\n"); - for (i = 0; i < MAX_PAUSE_TIME; i++) { - if (i < 1000 || pause_times[i] > 0) { - erts_fprintf(file,"%d\t%ld\n",i,pause_times[i]); - } - } - - fprintf(file,"Major:\n"); - for (i = 0; i < MAX_PAUSE_TIME; i++) { - if (pause_times_old[i] > 0) { - fprintf(file,"%d\t%ld\n",i,pause_times_old[i]); - } - } -#endif /* BM_TIMERS */ - -#ifdef BM_TIMERS - total_time = 0; n = 0; - left = 0; right = 0; mid = 0; - fprintf(file,"\nLocal:\n"); - for (i = 0; i < MAX_PAUSE_TIME; i++) { - if (local_pause_times[i] > 0) { - erts_fprintf(file,"%d\t%ld\n",i,local_pause_times[i]); - total_time += local_pause_times[i] * i; - n += local_pause_times[i]; - if (i > mid) - right += local_pause_times[i]; - while(right > left) { - left += local_pause_times[mid++]; - right -= local_pause_times[mid]; - } - } - } - erts_fprintf(file,"Mid: %ld Mean: %ld\n",(long)mid, - (long)(n > 0 ? total_time / n : 0)); -#endif -#endif /* 0 */ - fclose(file); - } - else - fprintf(stderr,"Sorry... Can not write to %s!\n\r",BM_STATISTICS_FILE); -#endif /* BM_STATISTICS */ -} diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h deleted file mode 100644 index 0272896f4f..0000000000 --- a/erts/emulator/beam/benchmark.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2002-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifndef __BENCHMARK_H__ -#define __BENCHMARK_H__ - -/* The define __BENCHMARK__ is the master switch to turn on and off - * benchmarking. This will enable the benchmark-BIFs in hipe_bif1.c. - * Documentation for the BIFs is in hipe_bif1.c, and that is where you - * will find the information about how to accually get some data out - * from these timers and counters. - */ -/* #define __BENCHMARK__ */ - -#ifdef __BENCHMARK__ -/* - * The defines below enables different parts of the benchmaring. - * Counters and timers that are disabled, always report zero in - * the BIFs. - */ - -/* BM_TIMERS keeps track of the time spent in diferent parts of the - * system. It only measures accual active time, not time spent in idle - * mode. Currently, the Solaris hrtime_t will be used. - * To add new timers look below. - */ -#define BM_TIMERS - -/* BM_COUNTERS count all kinds of events that occurs in the system. - * Among other things it counts the number of messages, then number of - * garbage collections, the number of processes spawned etc. - * To add new counters look below. - */ -#define BM_COUNTERS - -/* BM_MESSAGE_SIZES keeps a log of the size of all messages sent in - * the system. This introduce an overhead in time for the shared heap - * system since all message sizes have to be calculated at send. - */ -/* #define BM_MESSAGE_SIZES */ - -/* BM_HEAP_SIZES goes through all processes at garbage collection time - * to sum their allocated and used heap sizes. In anything else than a - * shared heap system, this will cost. - */ -/* #define BM_HEAP_SIZES */ - -/* BM_STATISTICS saves an entry in the file BM_STATISTICS_FILE. This - * is done for each erlang node at exit time. - */ -/* #define BM_STATISTICS */ - -#endif /* __BENCHMARK__ */ - - -#ifdef BM_STATISTICS -# define BM_STATISTICS_FILE "/tmp/erlang_statistics.joppe.log" -#endif /* BM_STATISTICS */ - - -/************ There are no more settings below this line *************/ - -/* - * Maintenance and how to add new stuff is documented by the code - * below ;-) - */ - -#ifdef BM_COUNTERS -/********************************************************************* - * To add new counters: - * - * Add the variable here AND in benchmark.c. Use the macro - * BM_COUNT(var) in the code where you want to increase it. - * - */ -extern unsigned long long processes_busy; -extern unsigned long long processes_spawned; -extern unsigned long long messages_sent; -extern unsigned long long messages_copied; -extern unsigned long long messages_ego; -extern unsigned long long minor_gc; -extern unsigned long long major_gc; - -#define BM_COUNT(var) (var)++; - -#define BM_EGO_COUNT(send,rec) { \ - if ((send) == (rec)) \ - BM_COUNT(messages_ego); } - -#define BM_LAZY_COPY_START long long gcs = minor_global_gc + major_global_gc; -#define BM_LAZY_COPY_STOP { gcs = (minor_global_gc + major_global_gc) - gcs; \ - if (gcs > gc_in_copy) gc_in_copy = gcs; } - -#else /* !BM_COUNTERS */ -# define BM_COUNT(var) -# define BM_EGO_COUNT(send,rec) -# define BM_LAZY_COPY_START -# define BM_LAZY_COPY_STOP -#endif /* BM_COUNTERS */ - - -#ifdef BM_TIMERS -/********************************************************************* - * To add new timers: - * - * Add the variable below using the form extern BM_TIMER_T blah_time. - * Also add them in benchmark.c using the macro NEW_TIMER(blah). Use - * the macro BM_SWAP_TIMER(from,blah) ... BM_SWAP_TIMER(blah,to) to - * start and stop the new timer. Note, that you have to know what - * timer is running at the place where you want to insert your new - * timer to be able to stop and start (from,to) it. - * - * You can use the macros BM_STOP_TIMER(blah) and BM_START_TIMER(blah) - * around code that should not be timed at all. As above, you have to - * know what timer to start and stop. The system timer is running at - * most places in the emulator. Only the garbage collector and the - * message sending has its own timers at the moment. - * - * The timer_time used when stopping timers is the time it takes to - * start and stop the timers, calculated in init_benchmarking(). If it - * is not there, the time it takes to do this will accually be - * substantial compared to some small times in the system we want to - * meassure (send time in shared heap for instance). - */ - -/* (Assuming Solaris) */ - -#define BM_TIMER_T ErtsMonotonicTime -#define BM_START_TIMER(t) system_clock = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()) -#define BM_STOP_TIMER(t) do { \ - BM_TIMER_T tmp = (ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()) - system_clock) - timer_time; \ - t##_time += (tmp > 0 ? tmp : 0); \ -} while(0) - -#define BM_TIME_PRINTER(str,time) do { \ - int min,sec,milli,micro; \ - BM_TIMER_T tmp; \ - tmp = (time) / 1000; \ - micro = tmp % 1000; \ - tmp /= 1000; \ - milli = tmp % 1000; \ - tmp /= 1000; \ - sec = tmp % 60; \ - min = tmp / 60; \ - erts_fprintf(file,str": %d:%02d.%03d %03d\n",min,sec,milli,micro); \ -} while(0) - -extern BM_TIMER_T system_clock; - -extern BM_TIMER_T timer_time; -extern BM_TIMER_T system_time; -extern BM_TIMER_T gc_time; -extern BM_TIMER_T minor_gc_time; -extern BM_TIMER_T major_gc_time; -extern BM_TIMER_T minor_global_gc_time; -extern BM_TIMER_T major_global_gc_time; -extern BM_TIMER_T send_time; -extern BM_TIMER_T copy_time; -extern BM_TIMER_T size_time; -extern BM_TIMER_T max_minor_time; -extern BM_TIMER_T max_major_time; -extern BM_TIMER_T max_global_minor_time; -extern BM_TIMER_T max_global_major_time; -extern BM_TIMER_T misc0_time; -extern BM_TIMER_T misc1_time; -extern BM_TIMER_T misc2_time; - -#define MAX_PAUSE_TIME 500000 -extern unsigned long local_pause_times[MAX_PAUSE_TIME]; -extern unsigned long pause_times[MAX_PAUSE_TIME]; -extern unsigned long pause_times_old[MAX_PAUSE_TIME]; - -#define MMU_INTERVAL 5 /* milli seconds */ -extern BM_TIMER_T mmu_counter; -extern BM_TIMER_T mmu; - -#define BM_NEW_TIMER(t) BM_TIMER_T t##_time = 0; -#define BM_RESET_TIMER(t) t##_time = 0; -#define BM_SWAP_TIMER(t1,t2) do { BM_STOP_TIMER(t1); BM_START_TIMER(t2); } while(0) -#define BM_MMU_INIT() do { \ - BM_TIMER_T gc = gc_time; \ - while (gc > 0) { \ - if (gc > MMU_INTERVAL) { \ - gc -= MMU_INTERVAL - mmu_counter; \ - erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ - mmu_counter = 0; mmu = 0; \ - } else { \ - mmu_counter += gc; \ - if (mmu_counter >= MMU_INTERVAL) { \ - mmu_counter -= MMU_INTERVAL; \ - erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ - mmu = 0; \ - } \ - gc = 0; \ - } \ - } \ - BM_RESET_TIMER(system); \ - BM_RESET_TIMER(send); \ - BM_RESET_TIMER(copy); \ - BM_RESET_TIMER(size); \ -} while(0) - -#define BM_MMU_READ() do { \ - BM_TIMER_T mut = system_time + send_time + copy_time + size_time; \ - while (mut > 0) { \ - if (mut > MMU_INTERVAL) { \ - BM_TIMER_T tmp = MMU_INTERVAL - mmu_counter; \ - mmu += tmp; mut -= tmp; \ - erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ - mmu_counter = 0; mmu = 0; \ - } else { \ - mmu_counter += mut; mmu += mut; \ - if (mmu_counter >= MMU_INTERVAL) { \ - mmu_counter -= MMU_INTERVAL; \ - mmu -= mmu_counter; \ - erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ - mmu = mmu_counter; \ - } \ - mut = 0; \ - } \ - } \ -} while(0) - -#else /* !BM_TIMERS */ -# define BM_NEW_TIMER(t) -# define BM_START_TIMER(t) -# define BM_STOP_TIMER(t) -# define BM_RESET_TIMER(t) -# define BM_SWAP_TIMER(t1,t2) -# define BM_TIME_PRINTER(str,time) -# define BM_MMU_INIT() -# define BM_MMU_READ() -#endif /* BM_TIMERS */ - -#ifdef BM_HEAP_SIZES -extern unsigned long long max_used_heap; -extern unsigned long long max_allocated_heap; -extern unsigned long long max_used_global_heap; -extern unsigned long long max_allocated_global_heap; -#endif /* BM_HEAP_SIZES */ - -#ifdef BM_MESSAGE_SIZES -extern unsigned long long words_sent; -extern unsigned long long words_copied; -extern unsigned long long words_prealloc; -extern unsigned long long message_sizes[1000]; - -#define BM_MESSAGE_COPIED(size) { \ - words_copied += size; \ - BM_COUNT(messages_copied); } - -#define BM_PREALLOC_DATA(size) { \ - words_prealloc += size; } - -#define BM_MESSAGE(mess,send,rec) { \ - Uint msize = size_object(mess); \ - words_sent += msize; \ - if (msize < 1000) \ - message_sizes[msize]++; \ - else \ - message_sizes[999]++; \ - BM_EGO_COUNT(send,rec); \ - BM_COUNT(messages_sent); } - -#else /* !BM_MESSAGE_SIZES */ - -#define BM_MESSAGE_COPIED(size) BM_COUNT(messages_copied); -#define BM_PREALLOC_DATA(size) -#define BM_MESSAGE(mess,send,rec) { \ - BM_EGO_COUNT(send,rec); \ - BM_COUNT(messages_sent); } - -#endif /* BM_MESSAGE_SIZES */ - -void init_benchmarking(void); -void save_statistics(void); - -#endif /* _BENCHMARK_H_ */ diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index fc14061a44..d9048065c8 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4305,8 +4305,9 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2) else { locks &= ~ERTS_PROC_LOCK_STATUS; erts_smp_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS); - if (erts_smp_atomic32_read_nob(&new_member->state) - & !(ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + if (new_member == BIF_P + || !(erts_smp_atomic32_read_nob(&new_member->state) + & (ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS))) { new_member->group_leader = STORE_NC_IN_PROC(new_member, BIF_ARG_1); } @@ -4326,6 +4327,7 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2) BIF_ARG_1); bp->next = new_member->mbuf; new_member->mbuf = bp; + new_member->mbuf_sz += bp->used_size; } } } diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 065018514a..80db4eb6ff 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -161,6 +161,7 @@ bif erts_internal:port_close/1 bif erts_internal:port_connect/2 bif erts_internal:request_system_task/3 +bif erts_internal:request_system_task/4 bif erts_internal:check_process_code/2 bif erts_internal:map_to_tuple_keys/1 @@ -174,6 +175,8 @@ bif erts_internal:is_system_process/1 bif erts_internal:system_check/1 +bif erts_internal:release_literal_area_switch/0 + # inet_db support bif erlang:port_set_data/2 bif erlang:port_get_data/1 @@ -642,8 +645,9 @@ bif erts_debug:map_info/1 # New in 19.0 # -bif erts_internal:copy_literals/2 -bif erts_internal:purge_module/1 +bif erts_internal:is_process_executing_dirty/1 +bif erts_internal:check_dirty_process_code/2 +bif erts_internal:purge_module/2 bif binary:split/2 bif binary:split/3 bif erts_debug:size_shared/1 diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 464acd67f6..4a96d971c3 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -21,17 +21,8 @@ #ifndef __BIG_H__ #define __BIG_H__ -#ifndef __SYS_H__ #include "sys.h" -#endif - -#ifndef __CONFIG_H__ -#include "erl_vm.h" -#endif - -#ifndef __GLOBAL_H__ #include "global.h" -#endif typedef Uint ErtsDigit; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 227fedfb69..6e8710eb8a 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -279,6 +279,7 @@ type TRACER_NIF LONG_LIVED SYSTEM tracer_nif type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls type DIRTY_START STANDARD PROCESSES dirty_start +type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; @@ -313,6 +314,7 @@ type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data +type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area +endif # @@ -367,6 +369,8 @@ type MONITOR_LH STANDARD PROCESSES monitor_lh type NLINK_LH STANDARD PROCESSES nlink_lh type CODE LONG_LIVED CODE code type LITERAL LITERAL CODE literal +type LITERAL_REF SHORT_LIVED CODE literal_area_ref +type PURGE_DATA SHORT_LIVED CODE purge_data type DB_HEIR_DATA STANDARD ETS db_heir_data type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 3fb866733c..29ba12dfdb 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2284,9 +2284,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (ERTS_IS_ATOM_STR("dist_ctrl", BIF_ARG_1)) { DistEntry *dep; i = 0; - /* Need to be the only thread running... */ - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); + erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); for (dep = erts_visible_dist_entries; dep; dep = dep->next) ++i; for (dep = erts_hidden_dist_entries; dep; dep = dep->next) @@ -2309,8 +2307,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) res = CONS(hp, tpl, res); hp += 2; } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx); BIF_RET(res); } else if (BIF_ARG_1 == am_system_version) { erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0); @@ -2886,6 +2883,27 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(AM_tag); #endif } + else if (ERTS_IS_ATOM_STR("check_process_code",BIF_ARG_1)) { + Eterm terms[3]; + Sint length = 1; + Uint sz = 0; + Eterm *hp, res; + DECL_AM(direct_references); + + terms[0] = AM_direct_references; +#if !defined(ERTS_NEW_PURGE_STRATEGY) + { + DECL_AM(indirect_references); + terms[1] = AM_indirect_references; + terms[2] = am_copy_literals; + length = 3; + } +#endif + erts_bld_list(NULL, &sz, length, terms); + hp = HAlloc(BIF_P, sz); + res = erts_bld_list(&hp, NULL, length, terms); + BIF_RET(res); + } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index bad34211a5..128a7b3865 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1094,7 +1094,7 @@ BIF_RETTYPE ets_insert_2(BIF_ALIST_2) CHECK_TABLES(); - /* Write lock table if more than one object to keep atomicy */ + /* Write lock table if more than one object to keep atomicity */ kind = ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL) ? LCK_WRITE : LCK_WRITE_REC); @@ -1164,7 +1164,7 @@ BIF_RETTYPE ets_insert_new_2(BIF_ALIST_2) Eterm lookup_ret; DbTableMethod* meth; - /* More than one object, use LCK_WRITE to keep atomicy */ + /* More than one object, use LCK_WRITE to keep atomicity */ kind = LCK_WRITE; tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, kind); if (tb == NULL) { diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 74979f984a..5e6fe4f460 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -40,7 +40,7 @@ ** DB_FINE_LOCKED set. The table variable is_thread_safe will then indicate ** if operations need to obtain fine grained locks or not. Some operations ** will for example always use exclusive table lock to guarantee -** a higher level of atomicy. +** a higher level of atomicity. */ /* FIXATION: @@ -2866,15 +2866,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, q->hvalue = hval; q->next = NULL; *bp = b = q; - - { - int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems); - int nactive = NACTIVE(tb); - - if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) { - grow(tb, nactive); - } - } + flags |= DB_INC_TRY_GROW; } else { HashDbTerm *q, *next = b->next; @@ -2910,6 +2902,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) HashDbTerm **bp = (HashDbTerm **) handle->bp; HashDbTerm *b = *bp; erts_smp_rwmtx_t* lck = (erts_smp_rwmtx_t*) handle->lck; + HashDbTerm* free_me = NULL; ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */ @@ -2921,21 +2914,34 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) b->hvalue = INVALID_HASH; } else { *bp = b->next; - free_term(tb, b); + free_me = b; } WUNLOCK_HASH(lck); erts_smp_atomic_dec_nob(&tb->common.nitems); try_shrink(tb); - } else if (handle->flags & DB_MUST_RESIZE) { - db_finalize_resize(handle, offsetof(HashDbTerm,dbterm)); - WUNLOCK_HASH(lck); - - free_term(tb, b); - } - else { - WUNLOCK_HASH(lck); + } else { + if (handle->flags & DB_MUST_RESIZE) { + db_finalize_resize(handle, offsetof(HashDbTerm,dbterm)); + free_me = b; + } + if (handle->flags & DB_INC_TRY_GROW) { + int nactive; + int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems); + WUNLOCK_HASH(lck); + nactive = NACTIVE(tb); + + if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) { + grow(tb, nactive); + } + } else { + WUNLOCK_HASH(lck); + } } + + if (free_me) + free_term(tb, free_me); + #ifdef DEBUG handle->dbterm = 0; #endif diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 60f7067d70..4acedbfed0 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -79,6 +79,7 @@ typedef union db_table DbTable; #define DB_MUST_RESIZE 1 #define DB_NEW_OBJECT 2 +#define DB_INC_TRY_GROW 4 /* Info about a database entry while it's being updated * (by update_counter or update_element) diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 6ce1376c81..c639ba623f 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -199,14 +199,13 @@ erts_erase_fun_entry(ErlFunEntry* fe) } void -erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end) +erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end) { int limit; HashBucket** bucket; - ErlFunEntry* to_delete = NULL; int i; - erts_fun_write_lock(); + erts_fun_read_lock(); limit = erts_fun_table.size; bucket = erts_fun_table.bucket; for (i = 0; i < limit; i++) { @@ -217,22 +216,51 @@ erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end) BeamInstr* addr = fe->address; if (start <= addr && addr < end) { + fe->pend_purge_address = addr; + ERTS_SMP_WRITE_MEMORY_BARRIER; fe->address = unloaded_fun; - if (erts_refc_dectest(&fe->refc, 0) == 0) { - fe->address = (void *) to_delete; - to_delete = fe; - } + erts_purge_state_add_fun(fe); } b = b->next; } } + erts_fun_read_unlock(); +} + +void +erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no) +{ + Uint ix; - while (to_delete != NULL) { - ErlFunEntry* next = (ErlFunEntry *) to_delete->address; - erts_erase_fun_entry_unlocked(to_delete); - to_delete = next; + for (ix = 0; ix < no; ix++) { + ErlFunEntry *fe = funs[ix]; + if (fe->address == unloaded_fun) + fe->address = fe->pend_purge_address; + fe->pend_purge_address = NULL; } - erts_fun_write_unlock(); +} + +void +erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no) +{ + Uint ix; + + for (ix = 0; ix < no; ix++) + funs[ix]->pend_purge_address = NULL; +} + +void +erts_fun_purge_complete(ErlFunEntry **funs, Uint no) +{ + Uint ix; + + for (ix = 0; ix < no; ix++) { + ErlFunEntry *fe = funs[ix]; + fe->pend_purge_address = NULL; + if (erts_refc_dectest(&fe->refc, 0) == 0) + erts_erase_fun_entry(fe); + } + ERTS_SMP_WRITE_MEMORY_BARRIER; } void @@ -294,6 +322,7 @@ fun_alloc(ErlFunEntry* template) obj->module = template->module; erts_refc_init(&obj->refc, -1); obj->address = unloaded_fun; + obj->pend_purge_address = NULL; #ifdef HIPE obj->native_address = NULL; #endif diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index 8c4deea7a0..73c3e19c1c 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -44,6 +44,7 @@ typedef struct erl_fun_entry { Eterm module; /* Tagged atom for module. */ erts_refc_t refc; /* Reference count: One for code + one for each fun object in each process. */ + BeamInstr *pend_purge_address; /* address stored during a pending purge */ } ErlFunEntry; /* @@ -81,7 +82,10 @@ ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index, void erts_erase_fun_entry(ErlFunEntry* fe); void erts_cleanup_funs(ErlFunThing* funp); -void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end); +void erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end); +void erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no); +void erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no); +void erts_fun_purge_complete(ErlFunEntry **funs, Uint no); void erts_dump_fun_entries(int, void *); #endif diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index d0d74bbf44..75f8ebefbd 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -517,6 +517,8 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int erts_proc_sched_data((p))->virtual_reds += vreds; } + ERTS_CHK_MBUF_SZ(p); + ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds); return reds_left; } @@ -527,6 +529,8 @@ young_gen_usage(Process *p) Uint hsz; Eterm *aheap; + ERTS_CHK_MBUF_SZ(p); + hsz = p->mbuf_sz; if (p->flags & F_ON_HEAP_MSGQ) { @@ -590,6 +594,8 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif + ERTS_CHK_MBUF_SZ(p); + ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls) >= erts_proc_sched_data(p)->virtual_reds); @@ -672,6 +678,7 @@ do_major_collection: killed before a GC could be done. */ if (reds == -2) { ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL; + int res; erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); erts_send_exit_signal(p, p->common.id, p, &locks, @@ -683,7 +690,9 @@ do_major_collection: erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); /* We have to make sure that we have space for need on the heap */ - return delay_garbage_collection(p, live_hf_end, need, fcalls); + res = delay_garbage_collection(p, live_hf_end, need, fcalls); + ERTS_MSACC_POP_STATE_M(); + return res; } erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); @@ -882,6 +891,58 @@ erts_garbage_collect_hibernate(Process* p) } +/* + * HiPE native code stack scanning procedures: + * - fullsweep_nstack() + * - gensweep_nstack() + * - offset_nstack() + * - sweep_literals_nstack() + */ +#if defined(HIPE) + +#define GENSWEEP_NSTACK(p,old_htop,n_htop) \ + do { \ + Eterm *tmp_old_htop = old_htop; \ + Eterm *tmp_n_htop = n_htop; \ + gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \ + old_htop = tmp_old_htop; \ + n_htop = tmp_n_htop; \ + } while(0) + +/* + * offset_nstack() can ignore the descriptor-based traversal the other + * nstack procedures use and simply call offset_heap_ptr() instead. + * This relies on two facts: + * 1. The only live non-Erlang terms on an nstack are return addresses, + * and they will be skipped thanks to the low/high range check. + * 2. Dead values, even if mistaken for pointers into the low/high area, + * can be offset safely since they won't be dereferenced. + * + * XXX: WARNING: If HiPE starts storing other non-Erlang values on the + * nstack, such as floats, then this will have to be changed. + */ +static ERTS_INLINE void offset_nstack(Process* p, Sint offs, + char* area, Uint area_size) +{ + if (p->hipe.nstack) { + ASSERT(p->hipe.nsp && p->hipe.nstend); + offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p), + offs, area, area_size); + } + else { + ASSERT(!p->hipe.nsp && !p->hipe.nstend); + } +} + +#else /* !HIPE */ + +#define fullsweep_nstack(p,n_htop) (n_htop) +#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0) +#define offset_nstack(p,offs,area,area_size) do{}while(0) +#define sweep_literals_nstack(p,old_htop,area,area_size) (old_htop) + +#endif /* HIPE */ + void erts_garbage_collect_literals(Process* p, Eterm* literals, Uint byte_lit_size, @@ -944,7 +1005,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, area_size = byte_lit_size; n = setup_rootset(p, p->arg_reg, p->arity, &rootset); roots = rootset.roots; - old_htop = p->old_htop; + old_htop = sweep_literals_nstack(p, p->old_htop, area, area_size); while (n--) { Eterm* g_ptr = roots->v; Uint g_sz = roots->sz; @@ -1211,56 +1272,6 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, return -1; } -/* - * HiPE native code stack scanning procedures: - * - fullsweep_nstack() - * - gensweep_nstack() - * - offset_nstack() - */ -#if defined(HIPE) - -#define GENSWEEP_NSTACK(p,old_htop,n_htop) \ - do { \ - Eterm *tmp_old_htop = old_htop; \ - Eterm *tmp_n_htop = n_htop; \ - gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \ - old_htop = tmp_old_htop; \ - n_htop = tmp_n_htop; \ - } while(0) - -/* - * offset_nstack() can ignore the descriptor-based traversal the other - * nstack procedures use and simply call offset_heap_ptr() instead. - * This relies on two facts: - * 1. The only live non-Erlang terms on an nstack are return addresses, - * and they will be skipped thanks to the low/high range check. - * 2. Dead values, even if mistaken for pointers into the low/high area, - * can be offset safely since they won't be dereferenced. - * - * XXX: WARNING: If HiPE starts storing other non-Erlang values on the - * nstack, such as floats, then this will have to be changed. - */ -static ERTS_INLINE void offset_nstack(Process* p, Sint offs, - char* area, Uint area_size) -{ - if (p->hipe.nstack) { - ASSERT(p->hipe.nsp && p->hipe.nstend); - offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p), - offs, area, area_size); - } - else { - ASSERT(!p->hipe.nsp && !p->hipe.nstend); - } -} - -#else /* !HIPE */ - -#define fullsweep_nstack(p,n_htop) (n_htop) -#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0) -#define offset_nstack(p,offs,area,area_size) do{}while(0) - -#endif /* HIPE */ - static void do_minor(Process *p, ErlHeapFragment *live_hf_end, char *mature, Uint mature_size, diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index ebeff51aac..f1bef28186 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -735,7 +735,10 @@ proc_timeout_common(Process *proc, void *tmr) if (tmr == (void *) erts_smp_atomic_cmpxchg_mb(&proc->common.timer, ERTS_PTMR_TIMEDOUT, (erts_aint_t) tmr)) { - erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state); + erts_aint32_t state; + erts_smp_proc_lock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE); + state = erts_smp_atomic32_read_acqb(&proc->state); + erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE); if (!(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_EXITING))) erts_schedule_process(proc, state, 0); return 1; diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index fbdafec4ef..781bf024dd 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -332,8 +332,6 @@ erl_init(int ncpu, int node_tab_delete_delay, ErtsDbSpinCount db_spin_count) { - init_benchmarking(); - erts_bif_unique_init(); erts_init_monitors(); erts_init_time(time_correction, time_warp_mode); @@ -384,6 +382,7 @@ erl_init(int ncpu, erts_init_unicode(); /* after RE to get access to PCRE unicode */ erts_init_external(); erts_init_map(); + erts_beam_bif_load_init(); erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2); erts_late_init_process(); #if HAVE_ERTS_MSEG @@ -2250,7 +2249,42 @@ erl_start(int argc, char **argv) otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0, boot_argc, boot_argv); - (void) erl_system_process_otp(otp_ring0_pid, "erts_code_purger"); + { + /* + * The erts_code_purger and the erts_literal_area_collector + * system processes are *always* alive. If they terminate + * they bring the whole VM down. + */ + Eterm pid; + + pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger"); + erts_code_purger + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_code_purger && erts_code_purger->common.id == pid); + erts_proc_inc_refc(erts_code_purger); + +#ifdef ERTS_NEW_PURGE_STRATEGY + pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector"); + erts_literal_area_collector + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_literal_area_collector + && erts_literal_area_collector->common.id == pid); + erts_proc_inc_refc(erts_literal_area_collector); +#endif + +#ifdef ERTS_DIRTY_SCHEDULERS + pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker"); + erts_dirty_process_code_checker + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_dirty_process_code_checker + && erts_dirty_process_code_checker->common.id == pid); + erts_proc_inc_refc(erts_dirty_process_code_checker); +#endif + + } #ifdef ERTS_SMP erts_start_schedulers(); @@ -2345,19 +2379,18 @@ erts_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) { system_cleanup(flush_async); - save_statistics(); - if (erts_mtrace_enabled) erts_mtrace_exit((Uint32) n); + if (fmt != NULL && *fmt != '\0') + erl_error(fmt, args2); /* Print error message. */ + /* Produce an Erlang core dump if error */ if (((n == ERTS_ERROR_EXIT && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT) && erts_initialized) { erl_crash_dump_v((char*) NULL, 0, fmt, args1); } - if (fmt != NULL && *fmt != '\0') - erl_error(fmt, args2); /* Print error message. */ sys_tty_reset(n); if (n == ERTS_INTR_EXIT) diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 39c0617143..06266363b5 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -96,6 +96,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "dist_entry", "address" }, { "dist_entry_links", "address" }, { "code_write_permission", NULL }, + { "purge_state", NULL }, { "proc_status", "pid" }, { "proc_trace", "pid" }, { "ports_snapshot", NULL }, @@ -112,6 +113,9 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "export_tab", NULL }, { "fun_tab", NULL }, { "environ", NULL }, +#ifdef ERTS_NEW_PURGE_STRATEGY + { "release_literal_areas", NULL }, +#endif #endif { "efile_drv", "address" }, { "drv_ev_state_grow", NULL, }, diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 8efc983f04..979a0040b0 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -497,18 +497,50 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n, return res; } +Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n) +{ + if (n < MAP_SMALL_MAP_LIMIT) { + Eterm *ks, *vs, *hp; + flatmap_t *mp; + Eterm keys; -Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n, + hp = erts_produce_heap(factory, 3 + 1 + (2 * n), 0); + keys = make_tuple(hp); + *hp++ = make_arityval(n); + ks = hp; + hp += n; + mp = (flatmap_t*)hp; + hp += MAP_HEADER_FLATMAP_SZ; + vs = hp; + + mp->thing_word = MAP_HEADER_FLATMAP; + mp->size = n; + mp->keys = keys; + + sys_memcpy(ks, ks0, n * sizeof(Eterm)); + sys_memcpy(vs, vs0, n * sizeof(Eterm)); + + erts_validate_and_sort_flatmap(mp); + + return make_flatmap(mp); + } else { + return erts_hashmap_from_ks_and_vs(factory, ks0, vs0, n); + } + return THE_NON_VALUE; +} + + +Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory, + Eterm *ks, Eterm *vs, Uint n, Eterm key, Eterm value) { Uint32 sw, hx; Uint i,sz; hxnode_t *hxns; - ErtsHeapFactory factory; Eterm *hp, res; sz = (key == THE_NON_VALUE) ? n : (n + 1); ASSERT(sz > MAP_SMALL_MAP_LIMIT); - hp = HAlloc(p, 2 * sz); + hp = erts_produce_heap(factory, 2 * sz, 0); /* create tmp hx values and leaf ptrs */ hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, sz * sizeof(hxnode_t)); @@ -531,12 +563,9 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n hxns[i].i = i; } - erts_factory_proc_init(&factory, p); - res = hashmap_from_unsorted_array(&factory, hxns, sz, 0); - erts_factory_close(&factory); + res = hashmap_from_unsorted_array(factory, hxns, sz, 0); erts_free(ERTS_ALC_T_TMP, (void *) hxns); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); return res; } @@ -1780,11 +1809,14 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { /* the map will grow */ if (n >= MAP_SMALL_MAP_LIMIT) { + ErtsHeapFactory factory; HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp); ks = flatmap_get_keys(mp); vs = flatmap_get_values(mp); - res = erts_hashmap_from_ks_and_vs_extra(p,ks,vs,n,key,value); + erts_factory_proc_init(&factory, p); + res = erts_hashmap_from_ks_and_vs_extra(&factory,ks,vs,n,key,value); + erts_factory_close(&factory); return res; } diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 8b5c9582ba..61a841f7f0 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -98,10 +98,12 @@ Eterm* hashmap_iterator_prev(struct ErtsWStack_* s); int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp); Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int reject_dupkeys); -#define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \ - erts_hashmap_from_ks_and_vs_extra((P), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE); +#define erts_hashmap_from_ks_and_vs(F, KS, VS, N) \ + erts_hashmap_from_ks_and_vs_extra((F), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE); -Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n, +Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n); +Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory, + Eterm *ks, Eterm *vs, Uint n, Eterm k, Eterm v); const Eterm *erts_maps_get(Eterm key, Eterm map); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 71ab92937d..e4c696ae3b 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -697,9 +697,6 @@ erts_send_message(Process* sender, #ifdef SHCOPY_SEND erts_shcopy_t info; #endif - BM_STOP_TIMER(system); - BM_MESSAGE(message,sender,receiver); - BM_START_TIMER(send); #ifdef USE_VM_PROBES *sender_name = *receiver_name = '\0'; @@ -720,7 +717,6 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES Uint dt_utag_size = 0; #endif - BM_SWAP_TIMER(send,size); /* SHCOPY corrupts the heap between * copy_shared_calculate, and @@ -747,8 +743,6 @@ erts_send_message(Process* sender, #else msize = size_object(message); #endif - BM_SWAP_TIMER(size,send); - mp = erts_alloc_message_heap_state(receiver, &receiver_state, receiver_locks, @@ -760,8 +754,6 @@ erts_send_message(Process* sender, &hp, &ohp); - BM_SWAP_TIMER(send,copy); - #ifdef SHCOPY_SEND if (is_not_immed(message)) message = copy_shared_perform(message, msize, &info, &hp, ohp); @@ -792,9 +784,6 @@ erts_send_message(Process* sender, msize, tok_label, tok_lastcnt, tok_serial); } #endif - BM_MESSAGE_COPIED(msize); - BM_SWAP_TIMER(copy,send); - } else { Eterm *hp; @@ -803,22 +792,18 @@ erts_send_message(Process* sender, msize = 0; } else { - BM_SWAP_TIMER(send,size); #ifdef SHCOPY_SEND INITIALIZE_SHCOPY(info); msize = copy_shared_calculate(message, &info); #else msize = size_object(message); #endif - BM_SWAP_TIMER(size,send); - mp = erts_alloc_message_heap_state(receiver, &receiver_state, receiver_locks, msize, &hp, &ohp); - BM_SWAP_TIMER(send,copy); #ifdef SHCOPY_SEND if (is_not_immed(message)) message = copy_shared_perform(message, msize, &info, &hp, ohp); @@ -827,8 +812,6 @@ erts_send_message(Process* sender, if (is_not_immed(message)) message = copy_struct(message, msize, &hp, ohp); #endif - BM_MESSAGE_COPIED(msz); - BM_SWAP_TIMER(copy,send); } #ifdef USE_VM_PROBES DTRACE6(message_send, sender_name, receiver_name, @@ -846,8 +829,6 @@ erts_send_message(Process* sender, mp, message, sender->common.id); - BM_SWAP_TIMER(send,system); - return res; } @@ -1861,3 +1842,20 @@ void erts_factory_undo(ErtsHeapFactory* factory) factory->heap_frags = NULL; #endif } + +Uint +erts_mbuf_size(Process *p) +{ + Uint sz = 0; + ErlHeapFragment* bp; + ErtsMessage* mp; + + for (bp = p->mbuf; bp; bp = bp->next) + sz += bp->used_size; + + for (mp = p->msg_frag; mp; mp = mp->next) + for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next) + sz += bp->used_size; + + return sz; +} diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 6df969367b..42ed14e69c 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -499,4 +499,15 @@ erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage #endif +Uint erts_mbuf_size(Process *p); +#if defined(DEBUG) || 0 +# define ERTS_CHK_MBUF_SZ(P) \ + do { \ + Uint actual_mbuf_sz__ = erts_mbuf_size((P)); \ + ERTS_ASSERT((P)->mbuf_sz >= actual_mbuf_sz__); \ + } while (0) +#else +# define ERTS_CHK_MBUF_SZ(P) ((void) 1) +#endif + #endif diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c index 544bc8b983..421445fbad 100644 --- a/erts/emulator/beam/erl_msacc.c +++ b/erts/emulator/beam/erl_msacc.c @@ -40,10 +40,11 @@ #include "erl_bif_unique.h" #include "erl_map.h" #include "erl_msacc.h" +#include "erl_bif_table.h" #if ERTS_ENABLE_MSACC -static Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, Eterm **hpp, Uint *szp); +static Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory); static void erts_msacc_reset(ErtsMsAcc *msacc); static ErtsMsAcc* get_msacc(void); @@ -52,7 +53,9 @@ erts_tsd_key_t ERTS_WRITE_UNLIKELY(erts_msacc_key); #else ErtsMsAcc *ERTS_WRITE_UNLIKELY(erts_msacc) = NULL; #endif +#ifndef ERTS_MSACC_ALWAYS_ON int ERTS_WRITE_UNLIKELY(erts_msacc_enabled); +#endif static Eterm *erts_msacc_state_atoms = NULL; static erts_rwmtx_t msacc_mutex; @@ -62,6 +65,12 @@ static ErtsMsAcc *msacc_unmanaged = NULL; static Uint msacc_unmanaged_count = 0; #endif +#if ERTS_MSACC_STATE_COUNT < MAP_SMALL_MAP_LIMIT +#define DEFAULT_MSACC_MSG_SIZE (3 + 1 + ERTS_MSACC_STATE_COUNT * 2 + 3 + REF_THING_SIZE) +#else +#define DEFAULT_MSACC_MSG_SIZE (3 + ERTS_MSACC_STATE_COUNT * 3 + 3 + REF_THING_SIZE) +#endif + /* we have to split initiation as atoms are not inited in early init */ void erts_msacc_early_init(void) { #ifndef ERTS_MSACC_ALWAYS_ON @@ -88,7 +97,8 @@ void erts_msacc_init(void) { void erts_msacc_init_thread(char *type, int id, int managed) { ErtsMsAcc *msacc; - msacc = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMsAcc)); + msacc = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMsAcc) + + sizeof(ErtsMsAccPerfCntr) * ERTS_MSACC_STATE_COUNT); msacc->type = strdup(type); msacc->id = make_small(id); @@ -122,79 +132,80 @@ void erts_msacc_init_thread(char *type, int id, int managed) { #endif } +#ifdef ERTS_MSACC_EXTENDED_STATES + +void erts_msacc_set_bif_state(ErtsMsAcc *__erts_msacc_cache, Eterm mod, void *fn) { + +#ifdef ERTS_MSACC_EXTENDED_BIFS +#define BIF_LIST(Mod,Func,Arity,FuncAddr,Num) \ + if (fn == &FuncAddr) { \ + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATIC_STATE_COUNT + Num); \ + } else +#include "erl_bif_list.h" +#undef BIF_LIST + { /* The last else in the macro expansion, + this happens for internal bifs, i.e. traps etc */ + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF); + } +#else + if (mod == am_ets) { + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); + } else { + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF); + } +#endif +} + +#endif + /* * Creates a structure looking like this * #{ type => scheduler, id => 1, counters => #{ State1 => Counter1 ... StateN => CounterN}} */ static -Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, Eterm **hpp, Uint *szp) { +Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory) { + Uint sz = 0; + Eterm *hp, cvs[ERTS_MSACC_STATE_COUNT]; + Eterm key, state_map; int i; - Eterm *hp; - Eterm key, state_key, state_map; - Eterm res = THE_NON_VALUE; - flatmap_t *map; - - if (szp) { - *szp += MAP_HEADER_FLATMAP_SZ + 1 + 2*(3); - *szp += MAP_HEADER_FLATMAP_SZ + 1 + 2*(ERTS_MSACC_STATE_COUNT); - for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { - (void)erts_bld_sint64(NULL,szp,(Sint64)msacc->perf_counters[i]); + flatmap_t *map; + + hp = erts_produce_heap(factory, 4, 0); + key = TUPLE3(hp,am_counters,am_id,am_type); + + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { + cvs[i] = erts_bld_sint64(NULL, &sz,(Sint64)msacc->counters[i].pc); #ifdef ERTS_MSACC_STATE_COUNTERS - (void)erts_bld_uint64(NULL,szp,msacc->state_counters[i]); - *szp += 3; /* tuple to put state+perf counter in */ + erts_bld_uint64(NULL,&sz,msacc->counters[i].sc); + sz += 3; #endif - } } - if (hpp) { - Eterm counters[ERTS_MSACC_STATE_COUNT]; - hp = *hpp; - for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { - Eterm counter = erts_bld_sint64(&hp,NULL,(Sint64)msacc->perf_counters[i]); + hp = erts_produce_heap(factory, sz, 0); + + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { + cvs[i] = erts_bld_sint64(&hp,NULL,(Sint64)msacc->counters[i].pc); #ifdef ERTS_MSACC_STATE_COUNTERS - Eterm counter__ = erts_bld_uint64(&hp,NULL,msacc->state_counters[i]); - counters[i] = TUPLE2(hp,counter,counter__); - hp += 3; -#else - counters[i] = counter; + Eterm counter__ = erts_bld_uint64(&hp,NULL,msacc->counters[i].sc); + cvs[i] = TUPLE2(hp,cvs[i],counter__); + hp += 3; #endif - } - - key = TUPLE3(hp,am_counters,am_id,am_type); - hp += 4; - - state_key = make_tuple(hp); - hp[0] = make_arityval(ERTS_MSACC_STATE_COUNT); - - for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) - hp[1+i] = erts_msacc_state_atoms[i]; - hp += 1 + ERTS_MSACC_STATE_COUNT; - - map = (flatmap_t*)hp; - hp += MAP_HEADER_FLATMAP_SZ; - map->thing_word = MAP_HEADER_FLATMAP; - map->size = ERTS_MSACC_STATE_COUNT; - map->keys = state_key; - for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) - hp[i] = counters[i]; - hp += ERTS_MSACC_STATE_COUNT; - state_map = make_flatmap(map); - - map = (flatmap_t*)hp; - hp += MAP_HEADER_FLATMAP_SZ; - map->thing_word = MAP_HEADER_FLATMAP; - map->size = 3; - map->keys = key; - hp[0] = state_map; - hp[1] = msacc->id; - hp[2] = am_atom_put(msacc->type,strlen(msacc->type)); - hp += 3; - - *hpp = hp; - res = make_flatmap(map); } - return res; + state_map = erts_map_from_ks_and_vs(factory, erts_msacc_state_atoms, cvs, + ERTS_MSACC_STATE_COUNT); + + hp = erts_produce_heap(factory, MAP_HEADER_FLATMAP_SZ + 3, 0); + map = (flatmap_t*)hp; + hp += MAP_HEADER_FLATMAP_SZ; + map->thing_word = MAP_HEADER_FLATMAP; + map->size = 3; + map->keys = key; + hp[0] = state_map; + hp[1] = msacc->id; + hp[2] = am_atom_put(msacc->type,strlen(msacc->type)); + + return make_flatmap(map); } typedef struct { @@ -222,40 +233,31 @@ static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) { ErtsSchedulerData *esdp = erts_get_scheduler_data(); Process *rp = msaccrp->proc; ErtsMessage *msgp = NULL; - Eterm **hpp, *hp; + Eterm *hp; Eterm ref_copy = NIL, msg; - Uint sz, *szp; - ErlOffHeap *ohp = NULL; ErtsProcLocks rp_locks = (esdp && msaccrp->req_sched == esdp->no ? ERTS_PROC_LOCK_MAIN : 0); + ErtsHeapFactory factory; - sz = 0; - hpp = NULL; - szp = &sz; - - if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx); - - while (1) { - if (hpp) - ref_copy = STORE_NC(hpp, ohp, msaccrp->ref); - else - *szp += REF_THING_SIZE; - - if (msaccrp->action != ERTS_MSACC_GATHER) - msg = ref_copy; - else { - msg = erts_msacc_gather_stats(msacc, hpp, szp); - msg = erts_bld_tuple(hpp, szp, 2, ref_copy, msg); - } - if (hpp) - break; - - msgp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); - hpp = &hp; - szp = NULL; - } + if (msaccrp->action == ERTS_MSACC_GATHER) { + + msgp = erts_factory_message_create(&factory, rp, &rp_locks, DEFAULT_MSACC_MSG_SIZE); + + if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx); - if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx); + hp = erts_produce_heap(&factory, REF_THING_SIZE + 3 /* tuple */, 0); + ref_copy = STORE_NC(&hp, &msgp->hfrag.off_heap, msaccrp->ref); + msg = erts_msacc_gather_stats(msacc, &factory); + msg = TUPLE2(hp, ref_copy, msg); + + if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx); + + erts_factory_close(&factory); + } else { + ErlOffHeap *ohp = NULL; + msgp = erts_alloc_message_heap(rp, &rp_locks, REF_THING_SIZE, &hp, &ohp); + msg = STORE_NC(&hp, &msgp->hfrag.off_heap, msaccrp->ref); + } erts_queue_message(rp, rp_locks, msgp, msg, am_system); @@ -308,9 +310,9 @@ static void erts_msacc_reset(ErtsMsAcc *msacc) { if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx); for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { - msacc->perf_counters[i] = 0; + msacc->counters[i].pc = 0; #ifdef ERTS_MSACC_STATE_COUNTERS - msacc->state_counters[i] = 0; + msacc->counters[i].sc = 0; #endif } @@ -415,7 +417,7 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads) ErtsSysPerfCounter perf_counter; /* if enabled update stats */ perf_counter = erts_sys_perf_counter(); - unmanaged[i]->perf_counters[unmanaged[i]->state] += + unmanaged[i]->counters[unmanaged[i]->state].pc += perf_counter - unmanaged[i]->perf_counter; unmanaged[i]->perf_counter = perf_counter; } @@ -454,7 +456,7 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads) for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) { erts_mtx_lock(&msacc->mtx); perf_counter = erts_sys_perf_counter(); - msacc->perf_counters[msacc->state] += perf_counter - msacc->perf_counter; + msacc->counters[msacc->state].pc += perf_counter - msacc->perf_counter; msacc->perf_counter = 0; erts_mtx_unlock(&msacc->mtx); } diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h index 284388f7aa..ad7c8c5eee 100644 --- a/erts/emulator/beam/erl_msacc.h +++ b/erts/emulator/beam/erl_msacc.h @@ -35,6 +35,10 @@ this reduces overhead a little bit when profiling */ /* #define ERTS_MSACC_ALWAYS_ON 1 */ +/* Uncomment this to keep individual stats for all + of the bifs when extended states is enabled */ +/* #define ERTS_MSACC_EXTENDED_BIFS 1 */ + #define ERTS_MSACC_DISABLE 0 #define ERTS_MSACC_ENABLE 1 #define ERTS_MSACC_RESET 2 @@ -92,7 +96,13 @@ static char *erts_msacc_states[] = { #define ERTS_MSACC_STATE_SLEEP 13 #define ERTS_MSACC_STATE_TIMERS 14 -#define ERTS_MSACC_STATE_COUNT 15 +#define ERTS_MSACC_STATIC_STATE_COUNT 15 + +#ifdef ERTS_MSACC_EXTENDED_BIFS +#define ERTS_MSACC_STATE_COUNT (ERTS_MSACC_STATIC_STATE_COUNT + BIF_SIZE) +#else +#define ERTS_MSACC_STATE_COUNT ERTS_MSACC_STATIC_STATE_COUNT +#endif #if ERTS_MSACC_STATE_STRINGS static char *erts_msacc_states[] = { @@ -111,22 +121,26 @@ static char *erts_msacc_states[] = { "send", "sleep", "timers" +#ifdef ERTS_MSACC_EXTENDED_BIFS +#define BIF_LIST(Mod,Func,Arity,FuncAddr,Num) \ + ,"bif_" #Mod "_" #Func "_" #Arity +#include "erl_bif_list.h" +#undef BIF_LIST +#endif }; #endif #endif typedef struct erl_msacc_t_ ErtsMsAcc; - -struct erl_msacc_t_ { - - /* the the values below are protected by mtx iff unmanaged = 1 */ - ErtsSysPerfCounter perf_counter; - ErtsSysPerfCounter perf_counters[ERTS_MSACC_STATE_COUNT]; +typedef struct erl_msacc_p_cnt_t_ { + ErtsSysPerfCounter pc; #ifdef ERTS_MSACC_STATE_COUNTERS - Uint64 state_counters[ERTS_MSACC_STATE_COUNT]; + Uint64 sc; #endif - Uint state; +} ErtsMsAccPerfCntr; + +struct erl_msacc_t_ { /* protected by msacc_mutex in erl_msacc.c, and should be constant */ int unmanaged; @@ -135,12 +149,16 @@ struct erl_msacc_t_ { erts_tid_t tid; Eterm id; char *type; + + /* the the values below are protected by mtx iff unmanaged = 1 */ + ErtsSysPerfCounter perf_counter; + Uint state; + ErtsMsAccPerfCntr counters[]; + }; #if ERTS_ENABLE_MSACC -#define ERTS_MSACC_INLINE ERTS_GLB_INLINE - #ifdef USE_THREADS extern erts_tsd_key_t erts_msacc_key; #else @@ -276,20 +294,20 @@ void erts_msacc_init_thread(char *type, int id, int liberty); #define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) \ ERTS_MSACC_PUSH_STATE_M(); ERTS_MSACC_SET_STATE_CACHED_M(state) -ERTS_MSACC_INLINE +ERTS_GLB_INLINE void erts_msacc_set_state_um__(ErtsMsAcc *msacc,Uint state,int increment); -ERTS_MSACC_INLINE +ERTS_GLB_INLINE void erts_msacc_set_state_m__(ErtsMsAcc *msacc,Uint state,int increment); -ERTS_MSACC_INLINE +ERTS_GLB_INLINE Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc); -ERTS_MSACC_INLINE +ERTS_GLB_INLINE Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_MSACC_INLINE +ERTS_GLB_INLINE Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc) { Uint state; if (msacc->unmanaged) @@ -300,12 +318,12 @@ Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc) { return state; } -ERTS_MSACC_INLINE +ERTS_GLB_INLINE Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc) { return msacc->state; } -ERTS_MSACC_INLINE +ERTS_GLB_INLINE void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment) { if (ERTS_UNLIKELY(msacc->unmanaged)) { erts_mtx_lock(&msacc->mtx); @@ -322,7 +340,7 @@ void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment) erts_mtx_unlock(&msacc->mtx); } -ERTS_MSACC_INLINE +ERTS_GLB_INLINE void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { ErtsSysPerfCounter prev_perf_counter; Sint64 diff; @@ -334,9 +352,9 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { msacc->perf_counter = erts_sys_perf_counter(); diff = msacc->perf_counter - prev_perf_counter; ASSERT(diff >= 0); - msacc->perf_counters[msacc->state] += diff; + msacc->counters[msacc->state].pc += diff; #ifdef ERTS_MSACC_STATE_COUNTERS - msacc->state_counters[new_state] += increment; + msacc->counters[new_state].sc += increment; #endif msacc->state = new_state; } @@ -364,7 +382,7 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { #define ERTS_MSACC_SET_STATE_CACHED_M(state) #define ERTS_MSACC_POP_STATE_M() #define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) - +#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr) #endif /* ERTS_ENABLE_MSACC */ @@ -385,9 +403,13 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { #define ERTS_MSACC_SET_STATE_CACHED_M_X(state) #define ERTS_MSACC_POP_STATE_M_X() #define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_M_X(state) +#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr) #else +void erts_msacc_set_bif_state(ErtsMsAcc *msacc, Eterm mod, void *addr); + #define ERTS_MSACC_PUSH_STATE_X() ERTS_MSACC_PUSH_STATE() #define ERTS_MSACC_POP_STATE_X() ERTS_MSACC_POP_STATE() #define ERTS_MSACC_SET_STATE_X(state) ERTS_MSACC_SET_STATE(state) @@ -403,6 +425,9 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { #define ERTS_MSACC_SET_STATE_CACHED_M_X(state) ERTS_MSACC_SET_STATE_CACHED_M(state) #define ERTS_MSACC_POP_STATE_M_X() ERTS_MSACC_POP_STATE_M() #define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state) ERTS_MSACC_PUSH_AND_SET_STATE_M(state) +#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr) \ + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) \ + erts_msacc_set_bif_state(__erts_msacc_cache, Mod, Addr) #endif /* !ERTS_MSACC_EXTENDED_STATES */ diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 23931f0e54..559b4017e7 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -120,8 +120,10 @@ execution_state(ErlNifEnv *env, Process **c_pp, int *schedp) else { Process *c_p = env->proc; - if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) - ASSERT(is_scheduler() > 0); + if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) { + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) + & ERTS_PROC_LOCK_MAIN); + } else { c_p = env->proc->next; ASSERT(is_scheduler() < 0); @@ -154,7 +156,9 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp) HEAP_TOP(env->proc) = env->hp; } else { - env->heap_frag->used_size = hp - env->heap_frag->mem; + Uint usz = env->hp - env->heap_frag->mem; + env->proc->mbuf_sz += usz - env->heap_frag->used_size; + env->heap_frag->used_size = usz; ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } hp = erts_heap_alloc(env->proc, need, MIN_HEAP_FRAG_SZ); @@ -208,11 +212,16 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, #endif } +static void full_cache_env(ErlNifEnv *env); +static void cache_env(ErlNifEnv* env); +static void full_flush_env(ErlNifEnv *env); +static void flush_env(ErlNifEnv* env); + +#ifdef ERTS_DIRTY_SCHEDULERS void erts_pre_dirty_nif(ErtsSchedulerData *esdp, - ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, - Process* tracee) + ErlNifEnv* env, Process* p, + struct erl_module_nif* mod_nif) { -#ifdef ERTS_DIRTY_SCHEDULERS Process *sproc; #ifdef DEBUG erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); @@ -223,7 +232,7 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp, ASSERT(esdp); #endif - erts_pre_nif(env, p, mod_nif, tracee); + erts_pre_nif(env, p, mod_nif, NULL); sproc = esdp->dirty_shadow_process; ASSERT(sproc); @@ -235,22 +244,10 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp, sproc->next = p; sproc->common.id = p->common.id; - sproc->htop = p->htop; - sproc->stop = p->stop; - sproc->hend = p->hend; - sproc->heap = p->heap; - sproc->abandoned_heap = p->abandoned_heap; - sproc->heap_sz = p->heap_sz; - sproc->high_water = p->high_water; - sproc->old_hend = p->old_hend; - sproc->old_htop = p->old_htop; - sproc->old_heap = p->old_heap; - sproc->mbuf = NULL; - sproc->mbuf_sz = 0; - ERTS_INIT_OFF_HEAP(&sproc->off_heap); env->proc = sproc; -#endif + full_cache_env(env); } +#endif /* Temporary object header, auto-deallocated when NIF returns * or when independent environment is cleared. @@ -274,32 +271,37 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env) void erts_post_nif(ErlNifEnv* env) { erts_unblock_fpe(env->fpe_was_unmasked); + full_flush_env(env); + free_tmp_objs(env); + env->exiting = ERTS_PROC_IS_EXITING(env->proc); +} #ifdef ERTS_DIRTY_SCHEDULERS - if (!(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) +void erts_post_dirty_nif(ErlNifEnv* env) +{ + Process *c_p; + ASSERT(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC); + ASSERT(env->proc->next); + erts_unblock_fpe(env->fpe_was_unmasked); + full_flush_env(env); + free_tmp_objs(env); + c_p = env->proc->next; + env->exiting = ERTS_PROC_IS_EXITING(c_p); + ERTS_VBUMP_ALL_REDS(c_p); +} #endif - { - ASSERT(is_scheduler() > 0); - if (env->heap_frag == NULL) { - ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); - ASSERT(env->hp >= HEAP_TOP(env->proc)); - ASSERT(env->hp <= HEAP_LIMIT(env->proc)); - HEAP_TOP(env->proc) = env->hp; - } - else { - ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; - ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); - } - env->exiting = ERTS_PROC_IS_EXITING(env->proc); - } + +static void full_flush_env(ErlNifEnv* env) +{ #ifdef ERTS_DIRTY_SCHEDULERS - else { /* Dirty nif call using shadow process struct */ + if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) { + /* Dirty nif call using shadow process struct */ Process *c_p = env->proc->next; ASSERT(is_scheduler() < 0); ASSERT(env->proc->common.id == c_p->common.id); + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) + & ERTS_PROC_LOCK_MAIN); if (!env->heap_frag) { ASSERT(env->hp_end == HEAP_LIMIT(c_p)); @@ -308,11 +310,14 @@ void erts_post_nif(ErlNifEnv* env) HEAP_TOP(c_p) = env->hp; } else { + Uint usz; ASSERT(env->hp_end != HEAP_LIMIT(c_p)); ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); HEAP_TOP(c_p) = HEAP_TOP(env->proc); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; + usz = env->hp - env->heap_frag->mem; + env->proc->mbuf_sz += usz - env->heap_frag->used_size; + env->heap_frag->used_size = usz; ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); @@ -339,11 +344,48 @@ void erts_post_nif(ErlNifEnv* env) } c_p->off_heap.overhead += env->proc->off_heap.overhead; - env->exiting = ERTS_PROC_IS_EXITING(c_p); - BUMP_ALL_REDS(c_p); + return; } #endif - free_tmp_objs(env); + + flush_env(env); +} + +static void full_cache_env(ErlNifEnv* env) +{ +#ifdef ERTS_DIRTY_SCHEDULERS + if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) { + /* Dirty nif call using shadow process struct */ + Process *sproc = env->proc; + Process *c_p = sproc->next; + ASSERT(c_p); + ASSERT(is_scheduler() < 0); + ASSERT(env->proc->common.id == c_p->common.id); + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) + & ERTS_PROC_LOCK_MAIN); + + sproc->htop = c_p->htop; + sproc->stop = c_p->stop; + sproc->hend = c_p->hend; + sproc->heap = c_p->heap; + sproc->abandoned_heap = c_p->abandoned_heap; + sproc->heap_sz = c_p->heap_sz; + sproc->high_water = c_p->high_water; + sproc->old_hend = c_p->old_hend; + sproc->old_htop = c_p->old_htop; + sproc->old_heap = c_p->old_heap; + sproc->mbuf = NULL; + sproc->mbuf_sz = 0; + ERTS_INIT_OFF_HEAP(&sproc->off_heap); + + env->hp_end = HEAP_LIMIT(c_p); + env->hp = HEAP_TOP(c_p); + env->heap_frag = NULL; + return; + } +#endif + + cache_env(env); } /* Flush out our cached heap pointers to allow an ordinary HAlloc @@ -357,9 +399,12 @@ static void flush_env(ErlNifEnv* env) HEAP_TOP(env->proc) = env->hp; } else { + Uint usz; ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; + usz = env->hp - env->heap_frag->mem; + env->proc->mbuf_sz += usz - env->heap_frag->used_size; + env->heap_frag->used_size = usz; ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } } @@ -600,17 +645,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (scheduler > 0) { /* Normal scheduler */ rp = erts_proc_lookup(receiver); - if (c_p == rp) - rp_locks = ERTS_PROC_LOCK_MAIN; + if (!rp) + return 0; } else { - if (c_p && ERTS_PROC_IS_EXITING(c_p)) - return 0; - rp = erts_pid2proc_opt(c_p, 0, receiver, rp_locks, + if (c_p) { + ASSERT(scheduler < 0); /* Dirty scheduler */ + if (ERTS_PROC_IS_EXITING(c_p)) + return 0; + + if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) { + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + } + } + + rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, + receiver, rp_locks, ERTS_P2P_FLG_INC_REFC); + if (!rp) { + if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); + return 0; + } } - if (rp == NULL) - return 0; + + if (c_p == rp) + rp_locks = ERTS_PROC_LOCK_MAIN; if (menv) { flush_env(msg_env); @@ -631,9 +691,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErlOffHeap *ohp; Eterm *hp; if (env && !env->tracee) { - flush_env(env); + full_flush_env(env); mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); - cache_env(env); + full_cache_env(env); } else { erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state); @@ -656,8 +716,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (!env || !env->tracee) { - if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) + if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) { + full_flush_env(env); trace_send(c_p, receiver, msg); + full_cache_env(env); + } } #ifdef ERTS_SMP else { @@ -690,10 +753,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) { if (!msgq) { -#ifdef ERTS_SMP - ErtsThrPrgrDelayHandle dhndl; -#endif - msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE, sizeof(ErlTraceMessageQueue)); msgq->receiver = receiver; @@ -707,15 +766,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); -#ifdef ERTS_SMP - if (!scheduler) - dhndl = erts_thr_progress_unmanaged_delay(); -#endif - erts_schedule_flush_trace_messages(t_p->common.id); -#ifdef ERTS_SMP - if (!scheduler) - erts_thr_progress_unmanaged_continue(dhndl); -#endif + erts_schedule_flush_trace_messages(t_p, 0); } else { msgq->len++; *msgq->last = mp; @@ -740,6 +791,8 @@ done: rp_locks &= ~ERTS_PROC_LOCK_MAIN; if (rp_locks & ~lc_locks) erts_smp_proc_unlock(rp, rp_locks & ~lc_locks); + if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); #endif if (scheduler <= 0) erts_proc_dec_refc(rp); @@ -2215,7 +2268,7 @@ 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, exception_thrown, saved_argc, rootset_extra, and + * struct. The saved_current, 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 @@ -2229,7 +2282,7 @@ typedef struct { Export exp; struct erl_module_nif* m; NativeFunPtr fp; - Eterm saved_mfa[3]; + BeamInstr *saved_current; int exception_thrown; int saved_argc; int rootset_extra; @@ -2340,9 +2393,8 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec reg[i] = (Eterm) argv[i]; } if (need_save) { - ep->saved_mfa[0] = proc->current[0]; - ep->saved_mfa[1] = proc->current[1]; - ep->saved_mfa[2] = proc->current[2]; + ASSERT(proc->current); + ep->saved_current = proc->current; ep->saved_argc = argc; } proc->i = (BeamInstr*) ep->exp.addressv[0]; @@ -2365,21 +2417,21 @@ static void restore_nif_mfa(Process* proc, NifExport* ep, int exception) { int i; - Eterm* reg = erts_proc_sched_data(proc)->x_reg_array; ERTS_SMP_LC_ASSERT(!(proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)); ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc) & ERTS_PROC_LOCK_MAIN); - proc->current[0] = ep->saved_mfa[0]; - proc->current[1] = ep->saved_mfa[1]; - proc->current[2] = ep->saved_mfa[2]; - if (exception) + ASSERT(ep->saved_current != &ep->exp.code[0]); + proc->current = ep->saved_current; + ep->saved_current = NULL; + if (exception) { + Eterm* reg = erts_proc_sched_data(proc)->x_reg_array; for (i = 0; i < ep->saved_argc; i++) reg[i] = ep->rootset[i+1]; + } ep->saved_argc = 0; - ep->saved_mfa[0] = THE_NON_VALUE; } #ifdef ERTS_DIRTY_SCHEDULERS @@ -2454,6 +2506,7 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) */ ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep && fp); + ep->fp = NULL; erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC | ERTS_PSFLG_DIRTY_IO_PROC)); @@ -2538,7 +2591,7 @@ schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[ } ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); - need_save = (ep == NULL || is_non_value(ep->saved_mfa[0])); + need_save = (ep == NULL || !ep->saved_current); result = init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv); if (scheduler <= 0) erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); @@ -2616,7 +2669,7 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, } ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); - need_save = (ep == NULL || is_non_value(ep->saved_mfa[0])); + need_save = (ep == NULL || !ep->saved_current); if (flags) { #ifdef ERTS_DIRTY_SCHEDULERS @@ -3502,6 +3555,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee, struct enif_environment_t env; ErlHeapFragment *orig_hf = MBUF(p); ErlOffHeap orig_oh = MSO(p); + Eterm *orig_htop = HEAP_TOP(p); ASSERT(is_internal_pid(p->common.id)); MBUF(p) = NULL; clear_offheap(&MSO(p)); @@ -3523,6 +3577,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee, /* restore original heap fragment list */ MBUF(p) = orig_hf; MSO(p) = orig_oh; + HEAP_TOP(p) = orig_htop; } else { /* Nif call was done without a process context, so we create a phony one. */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 66f22979ad..bc59147c6c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -448,6 +448,9 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP; typedef enum { ERTS_PSTT_GC, /* Garbage Collect */ ERTS_PSTT_CPC, /* Check Process Code */ +#ifdef ERTS_NEW_PURGE_STRATEGY + ERTS_PSTT_CLA, /* Copy Literal Area */ +#endif ERTS_PSTT_COHMQ, /* Change off heap message queue */ ERTS_PSTT_FTMQ /* Flush trace msg queue */ } ErtsProcSysTaskType; @@ -6671,14 +6674,24 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) } static int -schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) +schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, + erts_aint32_t *fail_state_p) { int res; int locked; ErtsProcSysTaskQs *stqs, *free_stqs; - erts_aint32_t state, a, n, enq_prio; + erts_aint32_t fail_state, state, a, n, enq_prio; int enqueue; /* < 0 -> use proxy */ unsigned int prof_runnable_procs; + int strict_fail_state; + + fail_state = *fail_state_p; + /* + * If fail state something other than just exiting process, + * ensure that the task wont be scheduled when the + * receiver is in the failure state. + */ + strict_fail_state = fail_state != ERTS_PSFLG_EXITING; res = 1; /* prepare for success */ st->next = st->prev = st; /* Prep for empty prio queue */ @@ -6705,7 +6718,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); state = erts_smp_atomic32_read_nob(&p->state); - if (state & ERTS_PSFLG_EXITING) { + if (state & fail_state) { + *fail_state_p = (state & fail_state); free_stqs = stqs; res = 0; goto cleanup; @@ -6759,7 +6773,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) /* Status lock prevents out of order "runnable proc" trace msgs */ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - if (!prof_runnable_procs) { + if (!prof_runnable_procs && !strict_fail_state) { erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); locked = 0; } @@ -6770,6 +6784,11 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) erts_aint32_t e; n = e = a; + if (strict_fail_state && (a & fail_state)) { + *fail_state_p = (a & fail_state); + goto cleanup; + } + if (a & ERTS_PSFLG_FREE) goto cleanup; /* We don't want to schedule free processes... */ @@ -8177,6 +8196,8 @@ sched_dirty_cpu_thread_func(void *vesdp) esdp->thr_id += erts_no_schedulers; + erts_msacc_init_thread("dirty_cpu_scheduler", no, 0); + erts_thr_progress_register_unmanaged_thread(&callbacks); #ifdef ERTS_ENABLE_LOCK_CHECK { @@ -8222,6 +8243,8 @@ sched_dirty_io_thread_func(void *vesdp) esdp->thr_id += erts_no_schedulers + erts_no_dirty_cpu_schedulers; + erts_msacc_init_thread("dirty_io_scheduler", no, 0); + erts_thr_progress_register_unmanaged_thread(&callbacks); #ifdef ERTS_ENABLE_LOCK_CHECK { @@ -9088,6 +9111,27 @@ resume_process_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE +erts_internal_is_process_executing_dirty_1(BIF_ALIST_1) +{ + if (is_not_internal_pid(BIF_ARG_1)) + BIF_ERROR(BIF_P, BADARG); +#ifdef ERTS_DIRTY_SCHEDULERS + else { + Process *rp = erts_proc_lookup(BIF_ARG_1); + if (rp) { + erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state); + if (state & (ERTS_PSFLG_DIRTY_RUNNING + |ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + BIF_RET(am_true); + } + } + } +#endif + BIF_RET(am_false); +} + + Uint erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched) { @@ -9364,6 +9408,14 @@ erts_set_process_priority(Process *p, Eterm value) } } +#ifdef __WIN32__ +Sint64 +erts_time2reds(ErtsMonotonicTime start, ErtsMonotonicTime end) +{ + return ERTS_TIME2REDS_IMPL__(start, end); +} +#endif + static int scheduler_gc_proc(Process *c_p, int reds_left) { @@ -9494,15 +9546,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) p->reds += actual_reds; -#ifdef ERTS_SMP - erts_smp_proc_lock(p, ERTS_PROC_LOCK_TRACE); - if (p->trace_msg_q) { - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE); - erts_schedule_flush_trace_messages(p->common.id); - } else - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE); -#endif - state = erts_smp_atomic32_read_nob(&p->state); if (IS_TRACED(p)) { @@ -9522,7 +9565,15 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } - erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + +#ifdef ERTS_SMP + if (p->trace_msg_q) { + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + erts_schedule_flush_trace_messages(p, 1); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + } +#endif /* have to re-read state after taking lock */ state = erts_smp_atomic32_read_nob(&p->state); @@ -9530,9 +9581,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) #ifdef ERTS_SMP if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT)) erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_TRACE | ERTS_PROC_LOCK_STATUS)); if (p->pending_suspenders) handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_TRACE | ERTS_PROC_LOCK_STATUS)); #endif @@ -9552,7 +9605,9 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) p->scheduler_data = NULL; #endif - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(p, (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_STATUS + | ERTS_PROC_LOCK_TRACE)); ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER); @@ -9588,8 +9643,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) erts_smp_runq_lock(rq); } } - BM_STOP_TIMER(system); - } ERTS_SMP_LC_ASSERT(!is_normal_sched || !erts_thr_progress_is_blocking()); @@ -9825,10 +9878,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) goto check_activities_to_run; } - ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR); - - BM_START_TIMER(system); - /* * Take the chosen process out of the queue. */ @@ -9934,6 +9983,8 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR); + #ifdef ERTS_SMP if (flags & ERTS_RUNQ_FLG_PROTECTED) @@ -9984,6 +10035,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) if (state & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_PENDING_EXIT | ERTS_PSFLG_EXITING)) { + /* + * IMPORTANT! We need to take care of + * scheduled check-process-code requests + * before continuing with dirty execution! + */ /* Migrate to normal scheduler... */ goto sunlock_sched_out_proc; } @@ -10393,6 +10449,27 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) } break; } +#ifdef ERTS_NEW_PURGE_STRATEGY + case ERTS_PSTT_CLA: { + int fcalls; + int cla_reds = 0; + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + fcalls = reds; + else + fcalls = reds - CONTEXT_REDS; + st_res = erts_proc_copy_literal_area(c_p, + &cla_reds, + fcalls, + st->arg[0] == am_true); + reds -= cla_reds; + if (is_non_value(st_res)) { + /* Needed gc, but gc was disabled */ + save_gc_task(c_p, st, st_prio); + st = NULL; + } + break; + } +#endif case ERTS_PSTT_COHMQ: reds -= erts_complete_off_heap_message_queue_change(c_p); st_res = am_true; @@ -10443,14 +10520,15 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) switch (st->type) { case ERTS_PSTT_GC: - st_res = am_false; - break; case ERTS_PSTT_CPC: - st_res = am_false; - break; case ERTS_PSTT_COHMQ: st_res = am_false; break; +#ifdef ERTS_NEW_PURGE_STRATEGY + case ERTS_PSTT_CLA: + st_res = am_ok; + break; +#endif #ifdef ERTS_SMP case ERTS_PSTT_FTMQ: reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN); @@ -10471,22 +10549,83 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) return reds; } -BIF_RETTYPE -erts_internal_request_system_task_3(BIF_ALIST_3) +#ifdef ERTS_DIRTY_SCHEDULERS + +static BIF_RETTYPE +dispatch_system_task(Process *c_p, erts_aint_t fail_state, + ErtsProcSysTask *st, Eterm target, + Eterm prio, Eterm operation) { - Process *rp = erts_proc_lookup(BIF_ARG_1); + Process *rp; + ErtsProcLocks rp_locks = 0; + ErlOffHeap *ohp; + ErtsMessage *mp; + Eterm *hp, msg; + Uint hsz, osz; + BIF_RETTYPE ret; + + switch (st->type) { + case ERTS_PSTT_CPC: + rp = erts_dirty_process_code_checker; + ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + if (c_p == rp) { + ERTS_BIF_PREP_RET(ret, am_dirty_execution); + return ret; + } + break; + default: + rp = NULL; + ERTS_INTERNAL_ERROR("Non-dispatchable system task"); + break; + } + + ERTS_BIF_PREP_RET(ret, am_ok); + + /* + * Send message on the form: {Requester, Target, Operation} + */ + + ASSERT(is_immed(st->requester)); + ASSERT(is_immed(target)); + ASSERT(is_immed(prio)); + + osz = size_object(operation); + hsz = 5 /* 4-tuple */ + osz; + + mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp); + + msg = copy_struct(operation, osz, &hp, ohp); + msg = TUPLE4(hp, st->requester, target, prio, msg); + + erts_queue_message(rp, rp_locks, mp, msg, st->requester); + + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + + return ret; +} + +#endif + +static BIF_RETTYPE +request_system_task(Process *c_p, Eterm requester, Eterm target, + Eterm priority, Eterm operation) +{ + BIF_RETTYPE ret; + Process *rp = erts_proc_lookup(target); ErtsProcSysTask *st = NULL; - erts_aint32_t prio; + erts_aint32_t prio, fail_state = ERTS_PSFLG_EXITING; Eterm noproc_res, req_type; - if (!rp && !is_internal_pid(BIF_ARG_1)) { - if (!is_external_pid(BIF_ARG_1)) + if (!rp && !is_internal_pid(target)) { + if (!is_external_pid(target)) goto badarg; - if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry) + if (external_pid_dist_entry(target) != erts_this_dist_entry) goto badarg; } - switch (BIF_ARG_2) { + switch (priority) { case am_max: prio = PRIORITY_MAX; break; case am_high: prio = PRIORITY_HIGH; break; case am_normal: prio = PRIORITY_NORMAL; break; @@ -10494,11 +10633,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3) default: goto badarg; } - if (is_not_tuple(BIF_ARG_3)) + if (is_not_tuple(operation)) goto badarg; else { int i; - Eterm *tp = tuple_val(BIF_ARG_3); + Eterm *tp = tuple_val(operation); Uint arity = arityval(*tp); Eterm req_id; Uint req_id_sz; @@ -10536,7 +10675,7 @@ erts_internal_request_system_task_3(BIF_ALIST_3) ERTS_INIT_OFF_HEAP(&st->off_heap); hp = &st->heap[0]; - st->requester = BIF_P->common.id; + st->requester = requester; st->reply_tag = req_type; st->req_id_sz = req_id_sz; st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id, @@ -10570,26 +10709,85 @@ erts_internal_request_system_task_3(BIF_ALIST_3) st->type = ERTS_PSTT_CPC; if (!rp) goto noproc; +#ifdef ERTS_DIRTY_SCHEDULERS + /* + * If the process should start executing dirty + * code it is important that this task is + * aborted. Therefore this strict fail state... + */ + fail_state |= (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS); +#endif + break; + +#ifdef ERTS_NEW_PURGE_STRATEGY + case am_copy_literals: + if (st->arg[0] != am_true && st->arg[0] != am_false) + goto badarg; + st->type = ERTS_PSTT_CLA; + noproc_res = am_ok; + if (!rp) + goto noproc; break; +#endif default: goto badarg; } - if (!schedule_process_sys_task(rp, prio, st)) { - noproc: - notify_sys_task_executed(BIF_P, st, noproc_res); + if (!schedule_process_sys_task(rp, prio, st, &fail_state)) { + Eterm failure; + if (fail_state & ERTS_PSFLG_EXITING) { + noproc: + failure = noproc_res; + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + ret = dispatch_system_task(c_p, fail_state, st, + target, priority, operation); + goto cleanup_return; + } +#endif + else { + ERTS_INTERNAL_ERROR("Unknown failure schedule_process_sys_task()"); + failure = am_internal_error; + } + notify_sys_task_executed(c_p, st, failure); } - BIF_RET(am_ok); + ERTS_BIF_PREP_RET(ret, am_ok); + + return ret; badarg: + ERTS_BIF_PREP_ERROR(ret, c_p, BADARG); + +#ifdef ERTS_DIRTY_SCHEDULERS +cleanup_return: +#endif + if (st) { erts_cleanup_offheap(&st->off_heap); erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); } - BIF_ERROR(BIF_P, BADARG); + + return ret; +} + +BIF_RETTYPE +erts_internal_request_system_task_3(BIF_ALIST_3) +{ + return request_system_task(BIF_P, BIF_P->common.id, + BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); +} + +BIF_RETTYPE +erts_internal_request_system_task_4(BIF_ALIST_4) +{ + return request_system_task(BIF_P, BIF_ARG_1, + BIF_ARG_2, BIF_ARG_3, BIF_ARG_4); } static void @@ -10598,7 +10796,7 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type) Process *rp = erts_proc_lookup(pid); if (rp) { ErtsProcSysTask *st; - erts_aint32_t state; + erts_aint32_t state, fail_state; int i; st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, @@ -10613,21 +10811,103 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type) ERTS_INIT_OFF_HEAP(&st->off_heap); state = erts_smp_atomic32_read_nob(&rp->state); - if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), st)) + fail_state = ERTS_PSFLG_EXITING; + + if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), + st, &fail_state)) erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); } } + void erts_schedule_complete_off_heap_message_queue_change(Eterm pid) { erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ); } +#ifdef ERTS_DIRTY_SCHEDULERS + +static void +flush_dirty_trace_messages(void *vpid) +{ + Process *proc; + Eterm pid; +#ifdef ARCH_64 + pid = (Eterm) vpid; +#else + pid = *((Eterm *) vpid); + erts_free(ERTS_ALC_T_DIRTY_SL, vpid); +#endif + + proc = erts_proc_lookup(pid); + if (proc) + (void) erts_flush_trace_messages(proc, 0); +} + +#endif /* ERTS_DIRTY_SCHEDULERS */ + void -erts_schedule_flush_trace_messages(Eterm pid) +erts_schedule_flush_trace_messages(Process *proc, int force_on_proc) { +#ifdef ERTS_SMP + ErtsThrPrgrDelayHandle dhndl; +#endif + Eterm pid = proc->common.id; + +#ifdef ERTS_DIRTY_SCHEDULERS + erts_aint32_t state; + + if (!force_on_proc) { + state = erts_smp_atomic32_read_nob(&proc->state); + if (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + goto sched_flush_dirty; + } + } +#endif + +#ifdef ERTS_SMP + dhndl = erts_thr_progress_unmanaged_delay(); +#endif + erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ); + +#ifdef ERTS_SMP + erts_thr_progress_unmanaged_continue(dhndl); +#endif + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!force_on_proc) { + state = erts_smp_atomic32_read_mb(&proc->state); + if (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + void *vargp; + + sched_flush_dirty: + /* + * We traced 'proc' from another thread than + * it is executing on, and it is executing + * on a dirty scheduler. It might take a + * significant amount of time before it is + * scheduled out (where it gets opportunity + * to flush messages). We therefore schedule + * the flush on the first ordinary scheduler. + */ + +#ifdef ARCH_64 + vargp = (void *) pid; +#else + { + Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm)); + *argp = pid; + vargp = (void *) argp; + } +#endif + erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp); + } + } +#endif } static void @@ -11154,18 +11434,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). || (erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_OFF_HEAP_MSGQ)); -#ifdef BM_COUNTERS - processes_busy++; -#endif - BM_COUNT(processes_spawned); - - BM_SWAP_TIMER(system,size); #ifdef SHCOPY_SPAWN arg_size = copy_shared_calculate(args, &info); #else arg_size = size_object(args); #endif - BM_SWAP_TIMER(size,system); heap_need = arg_size; p->flags = flags; @@ -11242,18 +11515,12 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->max_arg_reg = sizeof(p->def_arg_reg)/sizeof(p->def_arg_reg[0]); p->arg_reg[0] = mod; p->arg_reg[1] = func; - BM_STOP_TIMER(system); - BM_MESSAGE(args,p,parent); - BM_START_TIMER(system); - BM_SWAP_TIMER(system,copy); #ifdef SHCOPY_SPAWN p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap); DESTROY_SHCOPY(info); #else p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap); #endif - BM_MESSAGE_COPIED(arg_size); - BM_SWAP_TIMER(copy,system); p->arity = 3; p->fvalue = NIL; @@ -12289,7 +12556,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_port_demonitor(pcontext->p, ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED, prt, mon->ref, NULL); - return; /* let erts_port_demonitor do the deletion */ } else { /* remote by pid */ ASSERT(is_external_pid(mon->pid)); dep = external_pid_dist_entry(mon->pid); @@ -12826,9 +13092,6 @@ erts_continue_exit_process(Process *p) dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL; erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); -#ifdef BM_COUNTERS - processes_busy--; -#endif if (dep) { erts_do_net_exits(dep, reason); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 7c98b60647..3347a7a60e 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -989,8 +989,7 @@ struct process { Uint32 rcount; /* suspend count */ int schedule_count; /* Times left to reschedule a low prio process */ Uint reds; /* No of reductions for this process */ - Eterm group_leader; /* Pid in charge - (can be boxed) */ + Eterm group_leader; /* Pid in charge (can be boxed) */ Uint flags; /* Trap exit, etc (no trace flags anymore) */ Eterm fvalue; /* Exit & Throw value (failure reason) */ Uint freason; /* Reason for detected failure */ @@ -1329,10 +1328,13 @@ ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp); ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp) { ErlHeapFragment* hf = MBUF(p); + Uint sz; ASSERT(hf!=NULL && (hp - hf->mem < hf->alloc_size)); - hf->used_size = hp - hf->mem; + sz = hp - hf->mem; + p->mbuf_sz -= hf->used_size - sz; + hf->used_size = sz; } #endif /* inline */ @@ -1550,9 +1552,9 @@ extern int erts_system_profile_ts_type; #define ERTS_SCHEDULER_IS_DIRTY(ESDP) \ ((ESDP)->dirty_no.s.num != 0) #define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \ - ((ESDP)->dirty_no.s.type == 0) + (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 0)) #define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \ - ((ESDP)->dirty_no.s.type == 1) + (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 1)) #else #define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 #define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0 @@ -1768,7 +1770,7 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *), ErtsThrPrgrLaterOp *, UWord); void erts_schedule_complete_off_heap_message_queue_change(Eterm pid); -void erts_schedule_flush_trace_messages(Eterm pid); +void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc); int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) @@ -2474,6 +2476,35 @@ erts_get_atom_cache_map(Process *c_p) } #endif +#ifdef __WIN32__ +/* + * Don't want erts_time2reds() inlined in beam_emu.c on windows since + * it is compiled with gcc which fails on it. Implementation is in + * erl_process.c on windows. + */ +# define ERTS_TIME2REDS_IMPL__ erts_time2reds__ +#else +# define ERTS_TIME2REDS_IMPL__ erts_time2reds +#endif + +ERTS_GLB_INLINE Sint64 ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, + ErtsMonotonicTime end); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF +ERTS_GLB_INLINE Sint64 +ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end) +{ + ErtsMonotonicTime time = end - start; + ASSERT(time >= 0); + time = ERTS_MONOTONIC_TO_USEC(time); + if (time == 0) + return (Sint64) 1; /* At least one reduction */ + /* Currently two reductions per micro second */ + time *= (CONTEXT_REDS-1)/1000 + 1; + return (Sint64) time; +} +#endif + Process *erts_pid2proc_suspend(Process *, ErtsProcLocks, Eterm, diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 21938e7684..700ed90def 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -700,6 +700,7 @@ leader_update(ErtsThrPrgrData *tpd) tpd->leader_state.chk_next_ix = no_managed; erts_atomic32_set_nob(&intrnl->misc.data.umrefc_ix.current, (erts_aint32_t) new_umrefc_ix); + tpd->leader_state.umrefc_ix.current = new_umrefc_ix; ETHR_MEMBAR(ETHR_StoreLoad); refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc); ASSERT(refc >= 0); @@ -969,8 +970,10 @@ erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle) #ifdef ERTS_ENABLE_LOCK_CHECK ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL); ERTS_LC_ASSERT(tpd && tpd->is_delaying); - tpd->is_delaying = 0; - return_tmp_thr_prgr_data(tpd); + tpd->is_delaying--; + ASSERT(tpd->is_delaying >= 0); + if (!tpd->is_delaying) + return_tmp_thr_prgr_data(tpd); #endif ASSERT(!erts_thr_progress_is_managed_thread()); @@ -995,7 +998,7 @@ erts_thr_progress_unmanaged_delay__(void) #ifdef ERTS_ENABLE_LOCK_CHECK { ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL); - tpd->is_delaying = 1; + tpd->is_delaying++; } #endif return (ErtsThrPrgrDelayHandle) umrefc_ix; diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 9e37106b88..6aa2a7500f 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -2133,22 +2133,26 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto /* Convert to common user specified time units */ switch (term) { + case am_second: case am_seconds: case make_small(1): result = ERTS_MONOTONIC_TO_SEC(val) + muloff*ERTS_MONOTONIC_OFFSET_SEC; ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); break; + case am_millisecond: case am_milli_seconds: case make_small(1000): result = ERTS_MONOTONIC_TO_MSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_MSEC; ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); break; + case am_microsecond: case am_micro_seconds: case make_small(1000*1000): result = ERTS_MONOTONIC_TO_USEC(val) + muloff*ERTS_MONOTONIC_OFFSET_USEC; ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); break; #ifdef ARCH_64 + case am_nanosecond: case am_nano_seconds: case make_small(1000*1000*1000): result = ERTS_MONOTONIC_TO_NSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_NSEC; @@ -2159,7 +2163,7 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto Eterm value, native_res; #ifndef ARCH_64 Sint user_res; - if (term == am_nano_seconds) + if (term == am_nanosecond || term == am_nano_seconds) goto to_nano_seconds; if (term_to_Sint(term, &user_res)) { if (user_res == 1000*1000*1000) { diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 4cf38bf894..8c84303997 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -493,8 +493,8 @@ erts_get_system_seq_tracer(void) if (st != erts_tracer_nil && call_enabled_tracer(st, NULL, TRACE_FUN_ENABLED, am_trace_status, am_undefined) == am_remove) { - erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil); - st = erts_tracer_nil; + st = erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil); + ERTS_TRACER_CLEAR(&st); } return st; @@ -813,6 +813,9 @@ trace_send(Process *p, Eterm to, Eterm msg) ErtsTracerNif *tnif = NULL; ErtsTracingEvent* te; Eterm pam_result; +#ifdef ERTS_SMP + ErtsThrPrgrDelayHandle dhndl; +#endif ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)); @@ -837,6 +840,10 @@ trace_send(Process *p, Eterm to, Eterm msg) } else pam_result = am_true; +#ifdef ERTS_SMP + dhndl = erts_thr_progress_unmanaged_delay(); +#endif + if (is_internal_pid(to)) { if (!erts_proc_lookup(to)) goto send_to_non_existing_process; @@ -852,6 +859,11 @@ trace_send(Process *p, Eterm to, Eterm msg) send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND, operation, msg, to, pam_result); } + +#ifdef ERTS_SMP + erts_thr_progress_unmanaged_continue(dhndl); +#endif + erts_match_set_release_result_trace(p, pam_result); } @@ -1167,6 +1179,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, Eterm transformed_args[MAX_ARG]; ErtsTracer pre_ms_tracer = erts_tracer_nil; + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN); + ASSERT(tracer); if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) { /* Breakpoint trace enabled without specifying tracer => @@ -3108,6 +3122,7 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer) if (is_not_nil(*tracer)) { Uint offs = 2; UWord size = 2 * sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp); + ErtsThrPrgrLaterOp *lop; ASSERT(is_list(*tracer)); if (is_not_immed(ERTS_TRACER_STATE(*tracer))) { hf = (void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))); @@ -3115,6 +3130,16 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer) size = hf->alloc_size * sizeof(Eterm) + sizeof(ErlHeapFragment); ASSERT(offs == size_object(*tracer)); } + + /* sparc assumes that all structs are double word aligned, so we + have to align the ErtsThrPrgrLaterOp struct otherwise it may + segfault.*/ + if ((UWord)(ptr_val(*tracer) + offs) % (sizeof(UWord)*2) == sizeof(UWord)) + offs += 1; + + lop = (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs); + ASSERT((UWord)lop % (sizeof(UWord)*2) == 0); + /* We schedule the free:ing of the tracer until after a thread progress has been made so that we know that no schedulers have any references to it. Because we do this, it is possible to release all locks of a @@ -3122,9 +3147,7 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer) without having to worry if it is free'd. */ erts_schedule_thr_prgr_later_cleanup_op( - free_tracer, (void*)(*tracer), - (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs), - size); + free_tracer, (void*)(*tracer), lop, size); } if (is_nil(new_tracer)) { @@ -3134,16 +3157,17 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer) Not sure if it is worth it, we save 4 words (sizeof(ErlHeapFragment)) per tracer. */ Eterm *hp = erts_alloc(ERTS_ALC_T_HEAP_FRAG, - 2*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp)); + 3*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp)); *tracer = CONS(hp, ERTS_TRACER_MODULE(new_tracer), ERTS_TRACER_STATE(new_tracer)); } else { Eterm *hp, tracer_state = ERTS_TRACER_STATE(new_tracer), tracer_module = ERTS_TRACER_MODULE(new_tracer); Uint sz = size_object(tracer_state); - hf = new_message_buffer(sz + 2 /* cons cell */ + (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm)); + hf = new_message_buffer(sz + 2 /* cons cell */ + + (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1); hp = hf->mem + 2; - hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm); + hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1; *tracer = copy_struct(tracer_state, sz, &hp, &hf->off_heap); *tracer = CONS(hf->mem, tracer_module, *tracer); ASSERT((void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))) == hf); diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h index 8c81cbd410..1e7bb8514b 100644 --- a/erts/emulator/beam/export.h +++ b/erts/emulator/beam/export.h @@ -21,14 +21,8 @@ #ifndef __EXPORT_H__ #define __EXPORT_H__ -#ifndef __SYS_H__ #include "sys.h" -#endif - -#ifndef __INDEX_H__ #include "index.h" -#endif - #include "code_ix.h" /* diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index f3d4ac56cd..b2c76aa605 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -35,7 +35,6 @@ #include "register.h" #include "erl_fun.h" #include "erl_node_tables.h" -#include "benchmark.h" #include "erl_process.h" #include "erl_sys_driver.h" #include "erl_debug.h" @@ -62,9 +61,12 @@ struct enif_environment_t /* ErlNifEnv */ extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); +#ifdef ERTS_DIRTY_SCHEDULERS extern void erts_pre_dirty_nif(ErtsSchedulerData *, struct enif_environment_t*, Process*, - struct erl_module_nif*, Process* tracee); + struct erl_module_nif*); +extern void erts_post_dirty_nif(struct enif_environment_t* env); +#endif extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(int to, void* to_arg); void erts_unload_nif(struct erl_module_nif* nif); @@ -999,17 +1001,33 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); /* beam_bif_load.c */ #define ERTS_CPC_ALLOW_GC (1 << 0) -#define ERTS_CPC_COPY_LITERALS (1 << 1) -#define ERTS_CPC_ALL (ERTS_CPC_ALLOW_GC | ERTS_CPC_COPY_LITERALS) +#define ERTS_CPC_ALL ERTS_CPC_ALLOW_GC Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls); +#ifdef ERTS_NEW_PURGE_STRATEGY +Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed); +#endif -typedef struct { - Eterm *ptr; - Uint sz; - Eterm pid; -} copy_literals_t; +typedef struct ErtsLiteralArea_ { + struct erl_off_heap_header *off_heap; + Eterm *end; + Eterm start[1]; /* beginning of area */ +} ErtsLiteralArea; + +#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \ + (sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1)) + +extern erts_smp_atomic_t erts_copy_literal_area__; +#define ERTS_COPY_LITERAL_AREA() \ + ((ErtsLiteralArea *) erts_smp_atomic_read_nob(&erts_copy_literal_area__)) -extern copy_literals_t erts_clrange; +#ifdef ERTS_NEW_PURGE_STRATEGY +extern Process *erts_literal_area_collector; +#endif +#ifdef ERTS_DIRTY_SCHEDULERS +extern Process *erts_dirty_process_code_checker; +#endif + +extern Process *erts_code_purger; /* beam_load.c */ typedef struct { @@ -1092,12 +1110,19 @@ typedef struct { #define INITIALIZE_SHCOPY(info) \ do { \ + ErtsLiteralArea *larea__ = ERTS_COPY_LITERAL_AREA();\ info.queue_start = info.queue_default; \ info.bitstore_start = info.bitstore_default; \ info.shtable_start = info.shtable_default; \ info.literal_size = 0; \ - info.range_ptr = erts_clrange.ptr; \ - info.range_sz = erts_clrange.sz; \ + if (larea__) { \ + info.range_ptr = &larea__->start[0]; \ + info.range_sz = larea__->end - info.range_ptr; \ + } \ + else { \ + info.range_ptr = NULL; \ + info.range_sz = 0; \ + } \ } while(0) #define DESTROY_SHCOPY(info) \ diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index e255b961f1..cd038d100b 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -35,9 +35,9 @@ static const int h_size_table[] = { 2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */ 1201, 1597, - 2411, 3203, + 2411, 3203, 4813, 6421, - 9643, 12853, + 9643, 12853, 19289, 25717, 51437, 102877, @@ -49,8 +49,8 @@ static const int h_size_table[] = { 6584983, 13169977, 26339969, - 52679969, - -1 + 52679969, + -1 }; /* @@ -69,7 +69,7 @@ void hash_get_info(HashInfo *hi, Hash *h) for (i = 0; i < size; i++) { int depth = 0; HashBucket* b = h->bucket[i]; - + while (b != (HashBucket*) 0) { objects++; depth++; @@ -112,7 +112,7 @@ void hash_info(int to, void *arg, Hash* h) /* * Returns size of table in bytes. Stored objects not included. */ -int +int hash_table_sz(Hash *h) { int i; @@ -190,7 +190,7 @@ void hash_delete(Hash* h) HashBucket* b = h->bucket[i]; while (b != (HashBucket*) 0) { HashBucket* b_next = b->next; - + h->fun.free((void*) b); b = b_next; } @@ -250,7 +250,7 @@ void* hash_get(Hash* h, void* tmpl) HashValue hval = h->fun.hash(tmpl); int ix = hval % h->size; HashBucket* b = h->bucket[ix]; - + while(b != (HashBucket*) 0) { if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) return (void*) b; @@ -294,7 +294,7 @@ void* hash_erase(Hash* h, void* tmpl) int ix = hval % h->size; HashBucket* b = h->bucket[ix]; HashBucket* prev = 0; - + while(b != 0) { if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) { if (prev != 0) @@ -326,7 +326,7 @@ hash_remove(Hash *h, void *tmpl) int ix = hval % h->size; HashBucket *b = h->bucket[ix]; HashBucket *prev = NULL; - + while (b) { if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) { if (prev) @@ -355,4 +355,3 @@ void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2) } } } - diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h index 9f773d8faa..4e769c0119 100644 --- a/erts/emulator/beam/hash.h +++ b/erts/emulator/beam/hash.h @@ -25,9 +25,7 @@ #ifndef __HASH_H__ #define __HASH_H__ -#ifndef __SYS_H__ #include "sys.h" -#endif typedef unsigned long HashValue; typedef struct hash Hash; diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h index 218779c33b..0a109d8699 100644 --- a/erts/emulator/beam/index.h +++ b/erts/emulator/beam/index.h @@ -26,13 +26,8 @@ #ifndef __INDEX_H__ #define __INDEX_H__ -#ifndef __HASH_H__ #include "hash.h" -#endif - -#ifndef ERL_ALLOC_H__ #include "erl_alloc.h" -#endif typedef struct index_slot { diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index cb8792dffa..77dbe92241 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -6591,16 +6591,20 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p, ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay(); #endif erts_aint32_t state; + int res = 1; Port *prt = erts_port_lookup_raw((Eterm) port_id); - if (!prt) - return -1; + if (!prt) { + res = -1; + goto done; + } state = erts_atomic32_read_nob(&prt->state); if (state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP | ERTS_PORT_SFLG_CLOSING)) { if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) - return -1; + res = -1; else - return 0; + res = 0; + goto done; } if (connected_p) { #ifdef ERTS_SMP @@ -6609,25 +6613,27 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p, #endif *connected_p = ERTS_PORT_GET_CONNECTED(prt); } + +done: + #ifdef ERTS_SMP if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) { + ERTS_SMP_LC_ASSERT(!prt || !erts_lc_is_port_locked(prt)); erts_thr_progress_unmanaged_continue(dhndl); ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); } else #endif - { + if (res == 1) { + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); *trace_prt = prt; } - ERTS_SMP_LC_ASSERT(dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED - ? erts_lc_is_port_locked(prt) - : !erts_lc_is_port_locked(prt)); - return 1; + return res; } int erl_drv_output_term(ErlDrvTermData port_id, ErlDrvTermData* data, int len) { /* May be called from arbitrary thread */ - Eterm connected; + Eterm connected = NIL; /* Shut up faulty warning... */ Port *prt = NULL; int res = deliver_term_check_port(port_id, &connected, &prt); if (res <= 0) diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h index 4e12731d85..5a60bc90d9 100644 --- a/erts/emulator/beam/module.h +++ b/erts/emulator/beam/module.h @@ -21,9 +21,7 @@ #ifndef __MODULE_H__ #define __MODULE_H__ -#ifndef __INDEX_H__ #include "index.h" -#endif struct erl_module_instance { BeamCodeHeader* code_hdr; diff --git a/erts/emulator/beam/safe_hash.h b/erts/emulator/beam/safe_hash.h index 6910b33004..285103cb17 100644 --- a/erts/emulator/beam/safe_hash.h +++ b/erts/emulator/beam/safe_hash.h @@ -26,14 +26,9 @@ #ifndef __SAFE_HASH_H__ #define __SAFE_HASH_H__ - -#ifndef __SYS_H__ #include "sys.h" -#endif - #include "erl_alloc.h" - typedef unsigned long SafeHashValue; typedef int (*SHCMP_FUN)(void*, void*); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 675fafa726..6786657faf 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -85,7 +85,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra) && HEAP_TOP(p) >= p->space_verified_from && HEAP_TOP(p) + need <= p->space_verified_from + p->space_verified && HEAP_LIMIT(p) - HEAP_TOP(p) >= need) { - + Uint consumed = need + (HEAP_TOP(p) - p->space_verified_from); ASSERT(consumed <= p->space_verified); p->space_verified -= consumed; @@ -102,6 +102,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra) if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) { Eterm* ret = bp->mem + bp->used_size; bp->used_size += need; + p->mbuf_sz += need; return ret; } #ifdef DEBUG @@ -124,7 +125,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra) MBUF(p) = bp; bp->alloc_size = n; bp->used_size = need; - MBUF_SIZE(p) += n; + MBUF_SIZE(p) += need; bp->off_heap.first = NULL; bp->off_heap.overhead = 0; return bp->mem; @@ -638,7 +639,7 @@ erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp, ui = uint_to_big(uints[i], *hpp); *hpp += BIG_UINT_HEAP_SIZE; } - + res = CONS(*hpp+3, TUPLE2(*hpp, atoms[i], ui), res); *hpp += 5; } @@ -676,14 +677,14 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, ui1 = uint_to_big(uints1[i], *hpp); *hpp += BIG_UINT_HEAP_SIZE; } - + if (IS_USMALL(0, uints2[i])) ui2 = make_small(uints2[i]); else { ui2 = uint_to_big(uints2[i], *hpp); *hpp += BIG_UINT_HEAP_SIZE; } - + res = CONS(*hpp+4, TUPLE3(*hpp, atoms[i], ui1, ui2), res); *hpp += 6; } @@ -794,7 +795,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash) Uint b; Uint lshift = bitoffs; Uint rshift = 8 - lshift; - + while (sz--) { b = (previous << lshift) & 0xFF; previous = *ptr++; @@ -805,7 +806,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash) b = (previous << lshift) & 0xFF; previous = *ptr++; b |= previous >> rshift; - + b >>= 8 - bitsize; hash = (hash*FUNNY_NUMBER1 + b) * FUNNY_NUMBER12 + bitsize; } @@ -835,21 +836,21 @@ Uint32 make_hash(Eterm term_arg) do { \ Uint32 x = (Uint32) (Expr); \ hash = \ - (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \ - ((x >> 8) & 0xFF)) * (Prime1) + \ - ((x >> 16) & 0xFF)) * (Prime1) + \ + (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \ + ((x >> 8) & 0xFF)) * (Prime1) + \ + ((x >> 16) & 0xFF)) * (Prime1) + \ (x >> 24)); \ } while(0) -#define UINT32_HASH_RET(Expr, Prime1, Prime2) \ +#define UINT32_HASH_RET(Expr, Prime1, Prime2) \ UINT32_HASH_STEP(Expr, Prime1); \ hash = hash * (Prime2); \ - break - - + break + + /* * Significant additions needed for real 64 bit port with larger fixnums. - */ + */ /* * Note, for the simple 64bit port, not utilizing the @@ -864,7 +865,7 @@ tail_recur: hash = hash*FUNNY_NUMBER3 + 1; break; case ATOM_DEF: - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(term))->slot.bucket.hvalue); break; case SMALL_DEF: @@ -893,9 +894,9 @@ tail_recur: Export* ep = *((Export **) (export_val(term) + 1)); hash = hash * FUNNY_NUMBER11 + ep->code[2]; - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue); - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue); break; } @@ -906,7 +907,7 @@ tail_recur: Uint num_free = funp->num_free; hash = hash * FUNNY_NUMBER10 + num_free; - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue); hash = hash*FUNNY_NUMBER2 + funp->fe->old_index; hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; @@ -931,7 +932,7 @@ tail_recur: UINT32_HASH_RET(internal_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10); case EXTERNAL_REF_DEF: UINT32_HASH_RET(external_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10); - case FLOAT_DEF: + case FLOAT_DEF: { FloatDef ff; GET_DOUBLE(term, ff); @@ -958,12 +959,12 @@ tail_recur: ** as multiplications on a Sparc is so slow. */ hash = hash*FUNNY_NUMBER2 + unsigned_val(*list); - + if (is_not_list(CDR(list))) { WSTACK_PUSH(stack, MAKE_HASH_CDR_POST_OP); term = CDR(list); goto tail_recur; - } + } list = list_val(CDR(list)); } WSTACK_PUSH2(stack, CDR(list), MAKE_HASH_CDR_PRE_OP); @@ -1004,17 +1005,17 @@ tail_recur: } hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3; break; - } + } case MAP_DEF: hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term); break; - case TUPLE_DEF: + case TUPLE_DEF: { Eterm* ptr = tuple_val(term); Uint arity = arityval(*ptr); WSTACK_PUSH3(stack, (UWord) arity, (UWord)(ptr+1), (UWord) arity); - op = MAKE_HASH_TUPLE_OP; + op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: case MAKE_HASH_TERM_ARRAY_OP: @@ -1031,8 +1032,8 @@ tail_recur: hash = hash*FUNNY_NUMBER9 + arity; } break; - } - + } + default: erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op); return 0; @@ -1159,8 +1160,8 @@ make_hash2(Eterm term) if (y < 0) { \ UINT32_HASH(-y, AConst); \ /* Negative numbers are unnecessarily mixed twice. */ \ - } \ - UINT32_HASH(y, AConst); \ + } \ + UINT32_HASH(y, AConst); \ } while(0) #define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2) @@ -1242,7 +1243,7 @@ make_hash2(Eterm term) int arity = header_arity(hdr); Eterm* elem = tuple_val(term); UINT32_HASH(arity, HCONST_9); - if (arity == 0) /* Empty tuple */ + if (arity == 0) /* Empty tuple */ goto hash2_common; for (i = arity; ; i--) { term = elem[i]; @@ -1329,7 +1330,7 @@ make_hash2(Eterm term) { Export* ep = *((Export **) (export_val(term) + 1)); UINT32_HASH_2 - (ep->code[2], + (ep->code[2], atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue, HCONST); UINT32_HASH @@ -1343,7 +1344,7 @@ make_hash2(Eterm term) ErlFunThing* funp = (ErlFunThing *) fun_val(term); Uint num_free = funp->num_free; UINT32_HASH_2 - (num_free, + (num_free, atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue, HCONST); UINT32_HASH_2 @@ -1468,7 +1469,7 @@ make_hash2(Eterm term) goto hash2_common; } break; - + default: erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term); } @@ -1541,7 +1542,7 @@ make_hash2(Eterm term) } case HASH_MAP_PAIR: hash_xor_pairs ^= hash; - hash = 0; + hash = 0; goto hash2_common; default: break; @@ -1678,17 +1679,22 @@ make_internal_hash(Eterm term) * the order in which keys and values are encountered. * We therefore calculate context independent hashes for all . * key-value pairs and then xor them together. + * + * We *do* need to use an initial seed for each pair, i.e. the + * hash value, so the hash value is reset for each pair with 'hash'. + * If we don't, no additional entropy is given to the system and the + * hash collision resolution in maps:from_list/1 would fail. */ ESTACK_PUSH(s, hash_xor_pairs); ESTACK_PUSH(s, hash); ESTACK_PUSH(s, HASH_MAP_TAIL); - hash = 0; - hash_xor_pairs = 0; for (i = size - 1; i >= 0; i--) { + ESTACK_PUSH(s, hash); /* initial seed for all pairs */ ESTACK_PUSH(s, HASH_MAP_PAIR); ESTACK_PUSH(s, vs[i]); ESTACK_PUSH(s, ks[i]); } + hash_xor_pairs = 0; goto pop_next; } case HAMT_SUBTAG_HEAD_ARRAY: @@ -1700,7 +1706,6 @@ make_internal_hash(Eterm term) ESTACK_PUSH(s, hash_xor_pairs); ESTACK_PUSH(s, hash); ESTACK_PUSH(s, HASH_MAP_TAIL); - hash = 0; hash_xor_pairs = 0; } switch (hdr & _HEADER_MAP_SUBTAG_MASK) { @@ -1717,6 +1722,7 @@ make_internal_hash(Eterm term) while (i) { if (is_list(*ptr)) { Eterm* cons = list_val(*ptr); + ESTACK_PUSH(s, hash); /* initial seed for all pairs */ ESTACK_PUSH(s, HASH_MAP_PAIR); ESTACK_PUSH(s, CDR(cons)); ESTACK_PUSH(s, CAR(cons)); @@ -1906,6 +1912,7 @@ make_internal_hash(Eterm term) pop_next: if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); + return hash; } @@ -1920,7 +1927,7 @@ make_internal_hash(Eterm term) } case HASH_MAP_PAIR: hash_xor_pairs ^= hash; - hash = 0; + hash = (Uint32) ESTACK_POP(s); /* initial seed for all pairs */ goto pop_next; case HASH_CDR: @@ -1953,8 +1960,8 @@ Uint32 make_broken_hash(Eterm term) DECLARE_WSTACK(stack); unsigned op; tail_recur: - op = tag_val_def(term); - for (;;) { + op = tag_val_def(term); + for (;;) { switch (op) { case NIL_DEF: hash = hash*FUNNY_NUMBER3 + 1; @@ -1976,8 +1983,7 @@ tail_recur: { /* like a bignum */ Uint32 y4 = (Uint32) y2; hash = hash*FUNNY_NUMBER2 + ((y4 << 16) | (y4 >> 16)); - if (y3) - { + if (y3) { hash = hash*FUNNY_NUMBER2 + ((y3 << 16) | (y3 >> 16)); arity++; } @@ -2020,9 +2026,9 @@ tail_recur: Export* ep = *((Export **) (export_val(term) + 1)); hash = hash * FUNNY_NUMBER11 + ep->code[2]; - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue); - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue); break; } @@ -2033,7 +2039,7 @@ tail_recur: Uint num_free = funp->num_free; hash = hash * FUNNY_NUMBER10 + num_free; - hash = hash*FUNNY_NUMBER1 + + hash = hash*FUNNY_NUMBER1 + (atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue); hash = hash*FUNNY_NUMBER2 + funp->fe->old_index; hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; @@ -2065,7 +2071,7 @@ tail_recur: case EXTERNAL_REF_DEF: hash = hash*FUNNY_NUMBER9 + external_ref_numbers(term)[0]; break; - case FLOAT_DEF: + case FLOAT_DEF: { FloatDef ff; GET_DOUBLE(term, ff); @@ -2149,7 +2155,7 @@ tail_recur: } #else -#error "unsupported D_EXP size" +#error "unsupported D_EXP size" #endif hash = hash * (is_neg ? FUNNY_NUMBER3 : FUNNY_NUMBER2) + arity; } @@ -2158,14 +2164,14 @@ tail_recur: case MAP_DEF: hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term); break; - case TUPLE_DEF: + case TUPLE_DEF: { Eterm* ptr = tuple_val(term); Uint arity = arityval(*ptr); WSTACK_PUSH3(stack, (UWord) arity, (UWord) (ptr+1), (UWord) arity); op = MAKE_HASH_TUPLE_OP; - }/*fall through*/ + }/*fall through*/ case MAKE_HASH_TUPLE_OP: case MAKE_HASH_TERM_ARRAY_OP: { @@ -2193,7 +2199,7 @@ tail_recur: DESTROY_WSTACK(stack); return hash; - + #undef MAKE_HASH_TUPLE_OP #undef MAKE_HASH_TERM_ARRAY_OP #undef MAKE_HASH_CDR_PRE_OP @@ -2353,13 +2359,13 @@ static int do_send_term_to_logger(Eterm tag, Eterm gleader, } static ERTS_INLINE int -send_info_to_logger(Eterm gleader, char *buf, int len) +send_info_to_logger(Eterm gleader, char *buf, int len) { return do_send_to_logger(am_info_msg, gleader, buf, len); } static ERTS_INLINE int -send_warning_to_logger(Eterm gleader, char *buf, int len) +send_warning_to_logger(Eterm gleader, char *buf, int len) { Eterm tag; switch (erts_error_logger_warnings) { @@ -2371,7 +2377,7 @@ send_warning_to_logger(Eterm gleader, char *buf, int len) } static ERTS_INLINE int -send_error_to_logger(Eterm gleader, char *buf, int len) +send_error_to_logger(Eterm gleader, char *buf, int len) { return do_send_to_logger(am_error, gleader, buf, len); } @@ -2613,7 +2619,7 @@ tailrecur_ne: break; /* not equal */ case TAG_PRIMARY_BOXED: - { + { Eterm hdr = *boxed_val(a); switch (hdr & _TAG_HEADER_MASK) { case ARITYVAL_SUBTAG: @@ -2639,7 +2645,7 @@ tailrecur_ne: Uint b_bitsize; Uint a_bitoffs; Uint b_bitoffs; - + if (!is_binary(b)) { goto not_equal; } @@ -2671,7 +2677,7 @@ tailrecur_ne: { ErlFunThing* f1; ErlFunThing* f2; - + if (!is_fun(b)) goto not_equal; f1 = (ErlFunThing *) fun_val(a); @@ -2702,7 +2708,7 @@ tailrecur_ne: if(ap->header == bp->header && ap->node == bp->node) { ASSERT(1 == external_data_words(a)); ASSERT(1 == external_data_words(b)); - + if (ap->data.ui[0] == bp->data.ui[0]) goto pop_next; } break; /* not equal */ @@ -2759,7 +2765,7 @@ tailrecur_ne: if (alen == 3 && blen == 3) { /* Most refs are of length 3 */ if (anum[1] == bnum[1] && anum[2] == bnum[2]) { - goto pop_next; + goto pop_next; } else { goto not_equal; } @@ -2784,7 +2790,7 @@ tailrecur_ne: for (i = common_len; i < blen; i++) if (bnum[i] != 0) goto not_equal; - } + } } goto pop_next; } @@ -2792,7 +2798,7 @@ tailrecur_ne: case NEG_BIG_SUBTAG: { int i; - + if (!is_big(b)) goto not_equal; aa = big_val(a); @@ -2810,7 +2816,7 @@ tailrecur_ne: { FloatDef af; FloatDef bf; - + if (is_float(b)) { GET_DOUBLE(a, af); GET_DOUBLE(b, bf); @@ -2889,7 +2895,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'sz' */ } goto tailrecur_ne; } - + pop_next: if (!WSTACK_ISEMPTY(stack)) { UWord something = WSTACK_POP(stack); @@ -3093,7 +3099,7 @@ tailrecur_ne: } anode = erts_this_node; adata = internal_port_data(a); - + port_common: CMP_NODES(anode, bnode); ON_CMP_GOTO((Sint)(adata - bdata)); @@ -3111,7 +3117,7 @@ tailrecur_ne: } anode = erts_this_node; adata = internal_pid_data(a); - + pid_common: if (adata != bdata) { RETURN_NEQ(adata < bdata ? -1 : 1); @@ -3336,7 +3342,7 @@ tailrecur_ne: diff = f1->num_free - f2->num_free; if (diff != 0) { RETURN_NEQ(diff); - } + } i = f1->num_free; if (i == 0) goto pop_next; aa = f1->env; @@ -3397,10 +3403,10 @@ tailrecur_ne: anum = internal_thing_ref_numbers(athing); alen = internal_thing_ref_no_of_numbers(athing); } - + ref_common: CMP_NODES(anode, bnode); - + ASSERT(alen > 0 && blen > 0); if (alen != blen) { if (alen > blen) { @@ -3418,7 +3424,7 @@ tailrecur_ne: } while (alen < blen); } } - + ASSERT(alen == blen); for (i = (Sint) alen - 1; i >= 0; i--) if (anum[i] != bnum[i]) @@ -3633,8 +3639,8 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */ } a = *aa; b = *bb; - goto tailrecur; - + goto tailrecur; + pop_next: if (!WSTACK_ISEMPTY(stack)) { UWord something = WSTACK_POP(stack); @@ -3897,19 +3903,19 @@ intlist_to_buf(Eterm list, char *buf, Sint len) Eterm* listptr; Sint sz = 0; - if (is_nil(list)) + if (is_nil(list)) return 0; if (is_not_list(list)) return -1; listptr = list_val(list); while (sz < len) { - if (!is_byte(*listptr)) + if (!is_byte(*listptr)) return -1; buf[sz++] = unsigned_val(*listptr); if (is_nil(*(listptr + 1))) return(sz); - if (is_not_list(*(listptr + 1))) + if (is_not_list(*(listptr + 1))) return -1; listptr = list_val(*(listptr + 1)); } @@ -4157,10 +4163,10 @@ do { \ } else if (yield_support && --yield_count <= 0) goto L_yield; } - + res = len; - L_return: + L_return: DESTROY_ESTACK(s); @@ -5053,7 +5059,7 @@ Process *p; if(p) print_process_info(ERTS_PRINT_STDERR, NULL, p); } - + void ppi(Eterm pid) { pp(erts_proc_lookup(pid)); @@ -5079,5 +5085,3 @@ ps(Process* p, Eterm* stop) } } #endif - - diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 5ce0e1de9e..254d3baeb1 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -11407,6 +11407,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) VALGRIND_MAKE_MEM_DEFINED(mhdr.msg_control, mhdr.msg_controllen); /*suppress "uninitialised bytes"*/ mhdr.msg_flags = 0; /* Not used with "sendmsg" */ + inet_output_count(desc, data_len); /* Now do the actual sending. NB: "flags" in "sendmsg" itself are NOT used: */ code = sock_sendmsg(desc->s, &mhdr, 0); diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index 2c0fbbee2d..409fd0ef89 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -121,6 +121,22 @@ define(NSP,%rsp)dnl /* + * Debugging macros + * + * Keeps track of whether context has been saved in the debug build, allowing us + * to detect when the garbage collector is called when it shouldn't. + */ +`#ifdef DEBUG +# define SET_GC_UNSAFE \ + movq $1, P_GCUNSAFE(P) +# define SET_GC_SAFE \ + movq $0, P_GCUNSAFE(P) +#else +# define SET_GC_UNSAFE +# define SET_GC_SAFE +#endif' + +/* * Context switching macros. */ `#define SWITCH_C_TO_ERLANG_QUICK \ @@ -133,12 +149,14 @@ define(NSP,%rsp)dnl `#define SAVE_CACHED_STATE \ SAVE_HP; \ - SAVE_FCALLS' + SAVE_FCALLS; \ + SET_GC_SAFE' `#define RESTORE_CACHED_STATE \ RESTORE_HP; \ RESTORE_HEAP_LIMIT; \ - RESTORE_FCALLS' + RESTORE_FCALLS; \ + SET_GC_UNSAFE' `#define SWITCH_C_TO_ERLANG \ RESTORE_CACHED_STATE; \ diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 9cf3bf74fd..21739726bb 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -600,10 +600,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu) define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)') /* - * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2). + * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3). */ define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)') define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)') +define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)') /* * Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1. diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index b37ed3c68a..f3404888d5 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -94,6 +94,7 @@ ASYM(nbif_return): .nosave_exit: /* switch to C stack */ SWITCH_ERLANG_TO_C_QUICK + SET_GC_SAFE /* restore C callee-save registers, drop frame, return */ movq (%rsp), %rbp # kills P movq 8(%rsp), %rbx @@ -398,6 +399,7 @@ nbif_4_simple_exception: movl %eax, P_NARITY(P) # Note: narity is a 32-bit field /* find and prepare to invoke the handler */ SWITCH_ERLANG_TO_C_QUICK # The cached state is clean and need not be saved. + SET_GC_SAFE movq P, %rdi call CSYM(hipe_handle_exception) # Note: hipe_handle_exception() conses SWITCH_C_TO_ERLANG # %rsp updated by hipe_find_handler() diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4 index ae9ec752bb..68a6faa70b 100644 --- a/erts/emulator/hipe/hipe_arm_asm.m4 +++ b/erts/emulator/hipe/hipe_arm_asm.m4 @@ -48,6 +48,24 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive `#define TEMP_LR r8' /* + * Debugging macros + * + * Keeps track of whether context has been saved in the debug build, allowing us + * to detect when the garbage collector is called when it shouldn't. + */ +`#ifdef DEBUG +# define SET_GC_UNSAFE(SCRATCH) \ + mov SCRATCH, #1; \ + str SCRATCH, [P, #P_GCUNSAFE] +# define SET_GC_SAFE(SCRATCH) \ + mov SCRATCH, #0; \ + str SCRATCH, [P, #P_GCUNSAFE] +#else +# define SET_GC_UNSAFE(SCRATCH) +# define SET_GC_SAFE(SCRATCH) +#endif' + +/* * Context switching macros. * * RESTORE_CONTEXT and RESTORE_CONTEXT_QUICK do not affect @@ -59,12 +77,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive `#define RESTORE_CONTEXT_QUICK \ mov lr, TEMP_LR' -`#define SAVE_CACHED_STATE \ - str HP, [P, #P_HP]; \ - str NSP, [P, #P_NSP]' +`#define SAVE_CACHED_STATE \ + str HP, [P, #P_HP]; \ + str NSP, [P, #P_NSP]; \ + SET_GC_SAFE(HP)' -`#define RESTORE_CACHED_STATE \ - ldr HP, [P, #P_HP]; \ +`#define RESTORE_CACHED_STATE \ + SET_GC_UNSAFE(HP); \ + ldr HP, [P, #P_HP]; \ ldr NSP, [P, #P_NSP]' `#define SAVE_CONTEXT_BIF \ @@ -75,12 +95,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive ldr HP, [P, #P_HP]' `#define SAVE_CONTEXT_GC \ + SET_GC_SAFE(TEMP_LR); \ mov TEMP_LR, lr; \ str lr, [P, #P_NRA]; \ str NSP, [P, #P_NSP]; \ str HP, [P, #P_HP]' `#define RESTORE_CONTEXT_GC \ + SET_GC_UNSAFE(HP); \ ldr HP, [P, #P_HP]' /* diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index d9c9952dbf..d7a2fec04a 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -198,8 +198,9 @@ $1: * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-2 parameters and + * Generate native interface for a BIF with 0-3 parameters and * standard failure mode. * The BIF may do a GC. */ @@ -279,6 +280,36 @@ $1: .type $1, %function #endif') +define(gc_bif_interface_3, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov r0, P + NBIF_ARG(r1,3,0) + NBIF_ARG(r2,3,1) + NBIF_ARG(r3,3,2) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_GC + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + str r2, [r0, #P_ARG1] + str r3, [r0, #P_ARG2] + add r1, r0, #P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF(3) + + /* Restore registers. Check for exception. */ + cmp r0, #THE_NON_VALUE + RESTORE_CONTEXT_GC + beq nbif_3_simple_exception + NBIF_RET(3) + .size $1, .-$1 + .type $1, %function +#endif') + /* * gc_nofail_primop_interface_1(nbif_name, cbif_name) * diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index 49ffa8b1d8..5b7f8ad52d 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -342,6 +342,7 @@ nbif_4_gc_after_bif: str r1, [P, #P_NARITY] str TEMP_LR, [P, #P_NRA] str NSP, [P, #P_NSP] + SET_GC_SAFE(TEMP_LR) mov TEMP_LR, lr mov r3, #0 /* Pass 0 in arity */ mov r2, #0 /* Pass NULL in regs */ @@ -349,6 +350,7 @@ nbif_4_gc_after_bif: mov r0, P bl erts_gc_after_bif_call mov lr, TEMP_LR + SET_GC_UNSAFE(TEMP_LR) ldr TEMP_LR, [P, #P_NRA] mov r1, #0 str r1, [P, #P_NARITY] @@ -404,6 +406,7 @@ nbif_4_simple_exception: str NSP, [P, #P_NSP] str TEMP_LR, [P, #P_NRA] str r1, [P, #P_NARITY] + SET_GC_SAFE(r0) /* find and prepare to invoke the handler */ mov r0, P bl hipe_handle_exception /* Note: hipe_handle_exception() conses */ @@ -423,6 +426,7 @@ nbif_4_simple_exception: str NSP, [P, #P_NSP] str r1, [P, #P_NARITY] str TEMP_LR, [P, #P_NRA] + SET_GC_SAFE(NSP) b .nosave_exit /* diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c index 08adbd474e..5e127755c6 100644 --- a/erts/emulator/hipe/hipe_bif1.c +++ b/erts/emulator/hipe/hipe_bif1.c @@ -124,757 +124,3 @@ BIF_RETTYPE hipe_bifs_trap_count_clear_0(BIF_ALIST_0) hipe_trap_count = 0; BIF_RET(make_small(count)); } - -/***************************************************************************** - * BIFs for benchmarking. These only do useful things if - * __BENCHMARK__ is defined in beam/benchmark.h. For documentation - * about how to add new counters or maintain the existing counters, - * see benchmark.h. - * - * If benchmarking is not enabled all BIFs will return false. If the - * required benchmark feature is not enabled, the counter will remain - * zero. - * - * process_info/0 -> { Number of live processes, - * Processes spawned in total } - * - * Live processes are increased when a new process is created, and - * decreased when a process dies. Processes spawned is increased - * when a process is created. - * - * - * process_info_clear/0 -> true - * - * Will reset the processes spawned-counters to zero. If this is - * done at some improper time, live processes may become a negative - * value. This is not a problem in itself, just as long as you know - * about it. - * - * - * message_info/0 -> { Messages sent, - * Messages copied, - * Ego messages (sender = receiver), - * Words sent, - * Words copied, - * Words preallocated } - * - * Counting the words sent in a shared heap system will affect - * runtime performance since it means that we have to calculate the - * size of the mesage. With private heaps, this is done anyway and - * will not affect performance. - * - * - * message_info_clear/0 -> true - * - * Reset the message counters to zero. - * - * - * message_sizes/0 -> true - * - * Displays a text-mode bar diagram with message sizes. There are no - * guaranties that this is printed in a way the Erlang system is - * supposed to print things. - * - * - * gc_info/0 -> { Minor collections, - * Major collections, - * Used heap, - * Allocated heap, - * Max used heap, - * Max allocated heap } - * - * Information about private heap garbage collections. Number of - * minor and major collections, how much heap is used and allocated - * and how much heap has been in use and allocated at most since the - * counters were reset. - * - * - * shared_gc_info/0 -> { Minor collections of the shared heap, - * Major collections of the shared heap, - * Used shared heap, - * Allocated shared heap, - * Max used shared heap, - * Max allocated shared heap } - * - * The same as above, but for the shared heap / message area. Note, - * that in a shared heap system the max used heap and max allocated - * heap are mostly the same, since the heap allways is filled before - * a garbage collection, and most garbage collections do not enlarge - * the heap. The private heap numbers are much more interesting. - * - * - * incremental_gc_info/0 -> { Complete minor GC cycles, - * Complete major GC cycles, - * Minor GC stages, - * Major GC stages } - * - * - * gc_info_clear/0 -> true - * - * Reset counters for both private and shared garbage collection. - * - * - * BM Timers - * --------- - * - * All timers returns tuples of the kind: { Minutes, Seconds, Milliseconds } - * except for the max times in garbage collection where times are normally - * small. The tuple is therefor: { Seconds, Milliseconds, Microseconds } - * - * system_timer/0 -> Mutator time - * - * This timer is not a real-time clock, it only runs when a process - * is scheduled to run. You can not find out the accual time a - * program has taken to run using this timer. - * - * - * system_timer_clear/0 -> true - * - * Reset system timer to zero. - * - * - * send_timer/0 -> { Send time, - * Copy time, - * Size time } - * - * Time spent in sending messages. The copy time and size time are - * only active if the copying is needed in send. Copying of data - * into ETS-tables etc is not timed with this timer. - * - * - * send_timer_clear/0 -> true - * - * Reset send timers to zero. - * - * - * gc_timer/0 -> { Time in minor collection, - * Time in major collection, - * Max time in minor collection (�s), - * Max time in major collection (�s) } - * - * Total time spent in garbage collection of the private heaps. The - * max times are for one separate collection. - * - * - * shared_gc_timer/0 -> { Time in minor collection, - * Time in major collection, - * Max time in minor collection (�s), - * Max time in major collection (�s) } - * - * Total time spent in garbage collection of the shared heap / - * message area. The max times are for one separate collection. - * - * - * gc_timer_clear/0 -> true - * - * Reset private and shared garbage collection timers to zero. Note, - * that the max-times are also reset. - * - * - * misc_timer/0 -> { Misc 0, Misc 1, Misc 2 } - * - * Timers for debug purposes. In a normal system, these timers are - * never used. Add these timers at places where you want to time - * something not covered here. Use BM_SWAP_TIMER(from,to) to start - * one of the misc timers. - * - * ... code timed by the system timer ... - * BM_SWAP_TIMER(system,misc1); - * ... code we want to time ... - * BM_SWAP_TIMER(misc1,system); - * ... back on system time ... - * - * - * misc_timer_clear/0 -> true - * - * Reset misc timers to zero. - */ - -BIF_RETTYPE hipe_bifs_process_info_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ -#ifndef BM_COUNTERS - Uint processes_busy = 0; - Uint processes_spawned = 0; -#endif - Eterm *hp; - - hp = HAlloc(BIF_P, 3); - BIF_RET(TUPLE2(hp, - make_small(processes_busy), - make_small(processes_spawned))); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_process_info_clear_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ -#ifdef BM_COUNTERS - processes_spawned = 0; -#endif - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_message_info_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - Eterm *hp; -#ifndef BM_COUNTERS - unsigned long messages_sent = 0; - unsigned long messages_copied = 0; - unsigned long messages_ego = 0; -#endif -#ifndef BM_MESSAGE_SIZES - unsigned long words_sent = 0; - unsigned long words_copied = 0; - unsigned long words_prealloc = 0; -#endif - - hp = HAlloc(BIF_P, 7); - BIF_RET(TUPLE6(hp, - make_small(messages_sent), - make_small(messages_copied), - make_small(messages_ego), - make_small(words_sent), - make_small(words_copied), - make_small(words_prealloc))); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_message_info_clear_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ -#ifdef BM_COUNTERS - messages_sent = 0; - messages_copied = 0; - messages_ego = 0; -#endif -#ifdef BM_MESSAGE_SIZES - words_sent = 0; - words_copied = 0; - words_prealloc = 0; - { - int i; - for (i = 0; i < 1000; i++) - message_sizes[i] = 0; - } -#endif - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_message_sizes_0(BIF_ALIST_0) -{ -#ifdef BM_MESSAGE_SIZES - int i, j, max = 0; - int tmp[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; - - for (i = 0; i < 65; i++) { - tmp[0] += message_sizes[i]; - if (tmp[0] > max) - max = tmp[0]; - } - for (i = 65; i < 999; i++) { - tmp[i / 100 + 1] += message_sizes[i]; - if (tmp[i / 100 + 1] > max) - max = tmp[i / 100 + 1]; - } - tmp[11] = message_sizes[999]; - if (tmp[11] > max) - max = tmp[11]; - for (i = -1; i < 11; i++) { - int num = (tmp[i + 1] * 50) / max; - if (i == -1) - printf("\n\r 0 - 64: (%6d) |", tmp[0]); - else if (i == 0) - printf("\n\r 65 - 99: (%6d) |", tmp[1]); - else if (i == 10) - printf("\n\r >= 1000: (%6d) |", tmp[11]); - else - printf("\n\r%3d - %3d: (%6d) |", i * 100, i * 100 + 99, - tmp[i + 1]); - - for (j = 0; j < num; j++) - printf("."); - } - printf("\n\r"); - - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ -#ifndef BM_COUNTERS - Uint minor_gc = 0; - Uint major_gc = 0; -#endif -#ifndef BM_HEAP_SIZES - Uint max_used_heap = 0; - Uint max_allocated_heap = 0; -#endif - Eterm *hp; - Uint used_heap = (BIF_P->htop - BIF_P->heap) + - (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) + - MBUF_SIZE(BIF_P); - - Uint alloc_heap = (BIF_P->hend - BIF_P->heap) + - (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) + - MBUF_SIZE(BIF_P); - - hp = HAlloc(BIF_P, 7); - BIF_RET(TUPLE6(hp, - make_small((Uint)minor_gc), - make_small((Uint)major_gc), - make_small((Uint)used_heap), - make_small((Uint)alloc_heap), - make_small(max_used_heap), - make_small(max_allocated_heap))); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ -#if !(defined(BM_COUNTERS)) - Uint minor_global_gc = 0; - Uint major_global_gc = 0; -#endif -#ifndef BM_HEAP_SIZES - Uint max_used_global_heap = 0; - Uint max_allocated_global_heap = 0; -#endif - Eterm *hp; - - Uint tmp_used_heap = 0; - Uint tmp_allocated_heap = 0; - - hp = HAlloc(BIF_P, 7); - BIF_RET(TUPLE6(hp, - make_small((uint)minor_global_gc), - make_small((uint)major_global_gc), - make_small(tmp_used_heap), - make_small(tmp_allocated_heap), - make_small(max_used_global_heap), - make_small(max_allocated_global_heap))); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ -#if !defined(BM_COUNTERS) - Uint minor_gc_cycles = 0; - Uint major_gc_cycles = 0; - Uint minor_gc_stages = 0; - Uint major_gc_stages = 0; -#endif - Eterm *hp; - - hp = HAlloc(BIF_P, 5); - BIF_RET(TUPLE4(hp, - make_small(minor_gc_cycles), - make_small(major_gc_cycles), - make_small(minor_gc_stages), - make_small(major_gc_stages))); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - -#ifdef BM_COUNTERS - minor_gc = 0; - major_gc = 0; -#endif - -#ifdef BM_HEAP_SIZES - max_used_heap = 0; - max_allocated_heap = 0; - max_used_global_heap = 0; - max_allocated_global_heap = 0; -#endif - - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0) -{ -#ifdef BM_TIMERS - int i; - int total_time = 0, n = 0; - int left = 0, right = 0, mid = 0; - - printf("Pause times in minor collection:\r\n"); - for (i = 0; i < MAX_PAUSE_TIME; i++) { - if (pause_times[i] > 0) { - printf("%d: %ld\r\n", i, pause_times[i]); - total_time += pause_times[i] * i; - n += pause_times[i]; - - if (i > mid) - right += pause_times[i]; - - while (right > left) { - left += pause_times[mid++]; - right -= pause_times[mid]; - } - } - } - - printf("Number of collections: %d\r\n", n); - printf("Total collection time: %d\r\n", total_time); - if (n > 0) - printf("Mean pause time: %d\r\n", total_time / n); - - printf("Geometrical mean: %d\r\n", mid); - - total_time = 0; n = 0; - left = 0; right = 0; mid = 0; - printf("Pause times in major collection:\r\n"); - for (i = 0; i < MAX_PAUSE_TIME; i++) { - if (pause_times_old[i] > 0) { - printf("%d: %ld\r\n", i, pause_times_old[i]); - total_time += pause_times_old[i] * i; - n += pause_times_old[i]; - } - } - - printf("Number of collections: %d\r\n", n); - printf("Total collection time: %d\r\n", total_time); - if (n > 0) - printf("Mean pause time: %d\r\n", total_time / n); - - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -/* XXX: these macros have free variables */ -#ifdef BM_TIMERS -#define MAKE_TIME(_timer_) { \ - BM_TIMER_T tmp = _timer_##_time / 1000000; \ - milli = tmp % 1000; \ - tmp /= 1000; \ - sec = tmp % 60; \ - min = tmp / 60; } - -#define MAKE_MICRO_TIME(_timer_) { \ - BM_TIMER_T tmp = _timer_##_time / 1000; \ - micro = tmp % 1000; \ - tmp /= 1000; \ - milli = tmp % 1000; \ - sec = tmp / 1000; } - -#else -#define MAKE_TIME(_timer_) -#define MAKE_MICRO_TIME(_timer_) -#endif - -BIF_RETTYPE hipe_bifs_system_timer_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - uint min = 0; - uint sec = 0; - uint milli = 0; - Eterm *hp; - - hp = HAlloc(BIF_P, 4); - MAKE_TIME(system); - BIF_RET(TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli))); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_system_timer_clear_0(BIF_ALIST_0) -{ -#ifdef BM_TIMERS - system_time = 0; - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_send_timer_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - uint min = 0; - uint sec = 0; - uint milli = 0; - Eterm *hp; - Eterm sendtime, copytime, sizetime; - - hp = HAlloc(BIF_P, 4 * 4); - - MAKE_TIME(send); - sendtime = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_TIME(copy); - copytime = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_TIME(size); - sizetime = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - BIF_RET(TUPLE3(hp, sendtime, copytime, sizetime)); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_send_timer_clear_0(BIF_ALIST_0) -{ -#ifdef BM_TIMERS - send_time = 0; - copy_time = 0; - size_time = 0; - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_gc_timer_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - Eterm *hp; - uint min = 0; - uint sec = 0; - uint milli = 0; - uint micro = 0; - Eterm minor, major, max_min, max_maj; - - hp = HAlloc(BIF_P, 4 * 4 + 5); - - MAKE_TIME(minor_gc); - minor = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_TIME(major_gc); - major = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_MICRO_TIME(max_minor); - max_min = TUPLE3(hp, - make_small(sec), - make_small(milli), - make_small(micro)); - hp += 4; - - MAKE_MICRO_TIME(max_major); - max_maj = TUPLE3(hp, - make_small(sec), - make_small(milli), - make_small(micro)); - hp += 4; - - BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj)); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_shared_gc_timer_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - Eterm *hp; - uint min = 0; - uint sec = 0; - uint milli = 0; - uint micro = 0; - Eterm minor, major, max_min, max_maj; - - hp = HAlloc(BIF_P, 4 * 4 + 5); - - MAKE_TIME(minor_global_gc); - minor = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_TIME(major_global_gc); - major = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_MICRO_TIME(max_global_minor); - max_min = TUPLE3(hp, - make_small(sec), - make_small(milli), - make_small(micro)); - hp += 4; - - MAKE_MICRO_TIME(max_global_major); - max_maj = TUPLE3(hp, - make_small(sec), - make_small(milli), - make_small(micro)); - hp += 4; - - BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj)); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_gc_timer_clear_0(BIF_ALIST_0) -{ -#ifdef BM_TIMERS - minor_gc_time = 0; - major_gc_time = 0; - max_minor_time = 0; - max_major_time = 0; - minor_global_gc_time = 0; - major_global_gc_time = 0; - max_global_minor_time = 0; - max_global_major_time = 0; - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_misc_timer_0(BIF_ALIST_0) -{ -#ifdef __BENCHMARK__ - uint min = 0; - uint sec = 0; - uint milli = 0; - Eterm *hp; - Eterm misctime1, misctime2, misctime3; - - hp = HAlloc(BIF_P, 4 * 4); - - MAKE_TIME(misc0); - misctime1 = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_TIME(misc1); - misctime2 = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - - MAKE_TIME(misc2); - misctime3 = TUPLE3(hp, - make_small(min), - make_small(sec), - make_small(milli)); - hp += 4; - BIF_RET(TUPLE3(hp, misctime1, misctime2, misctime3)); -#else - BIF_RET(am_false); -#endif -} - -BIF_RETTYPE hipe_bifs_misc_timer_clear_0(BIF_ALIST_0) -{ -#ifdef BM_TIMERS - misc0_time = 0; - misc1_time = 0; - misc2_time = 0; - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - -#undef MAKE_TIME -#undef MAKE_MICRO_TIME - -/* - * HiPE hrvtime(). - * These implementations are currently available: - * + The fallback, which is the same as {X,_} = runtime(statistics). - */ - -static double fallback_get_hrvtime(void) -{ - unsigned long ms_user; - - elapsed_time_both(&ms_user, NULL, NULL, NULL); - return (double)ms_user; -} - -/* - * Fallback, if nothing better exists. - * This is the same as {X,_} = statistics(runtime), which uses - * times(2) on Unix systems. - */ - -#define hrvtime_is_started() 1 -#define start_hrvtime() do{}while(0) -#define stop_hrvtime() do{}while(0) -#define get_hrvtime() fallback_get_hrvtime() - -BIF_RETTYPE hipe_bifs_get_hrvtime_0(BIF_ALIST_0) -{ - Eterm *hp; - Eterm res; - FloatDef f; - - if (!hrvtime_is_started()) - start_hrvtime(); - f.fd = get_hrvtime(); - hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT); - res = make_float(hp); - PUT_DOUBLE(f, hp); - BIF_RET(res); -} - -BIF_RETTYPE hipe_bifs_stop_hrvtime_0(BIF_ALIST_0) -{ - stop_hrvtime(); - BIF_RET(am_true); -} diff --git a/erts/emulator/hipe/hipe_bif1.tab b/erts/emulator/hipe/hipe_bif1.tab index c5b452f199..4be0ad0e9c 100644 --- a/erts/emulator/hipe/hipe_bif1.tab +++ b/erts/emulator/hipe/hipe_bif1.tab @@ -27,24 +27,3 @@ bif hipe_bifs:call_count_get/1 bif hipe_bifs:call_count_clear/1 bif hipe_bifs:trap_count_get/0 bif hipe_bifs:trap_count_clear/0 -bif hipe_bifs:process_info/0 -bif hipe_bifs:process_info_clear/0 -bif hipe_bifs:message_info/0 -bif hipe_bifs:message_info_clear/0 -bif hipe_bifs:message_sizes/0 -bif hipe_bifs:gc_info/0 -bif hipe_bifs:shared_gc_info/0 -bif hipe_bifs:incremental_gc_info/0 -bif hipe_bifs:gc_info_clear/0 -bif hipe_bifs:pause_times/0 -bif hipe_bifs:system_timer/0 -bif hipe_bifs:system_timer_clear/0 -bif hipe_bifs:send_timer/0 -bif hipe_bifs:send_timer_clear/0 -bif hipe_bifs:gc_timer/0 -bif hipe_bifs:shared_gc_timer/0 -bif hipe_bifs:gc_timer_clear/0 -bif hipe_bifs:misc_timer/0 -bif hipe_bifs:misc_timer_clear/0 -bif hipe_bifs:get_hrvtime/0 -bif hipe_bifs:stop_hrvtime/0 diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 29095a5389..dcf3447af9 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -96,6 +96,7 @@ * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * * A BIF which may do a GC or walk the native stack. * May read NSP, NSP_LIMIT, NRA, HP, HP_LIMIT, and FCALLS. @@ -263,32 +264,34 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc) ',)dnl /* - * Standard BIFs. - * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index) + * BIFs that disable GC while trapping are called via a wrapper + * to reserve stack space for the "trap frame". + * They occasionally need to call the garbage collector in order to make room + * for the trap frame on the BEAM stack. */ +gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1) +gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2) +gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1) +gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2) +gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1) +gc_bif_interface_3(nbif_binary_to_list_3, hipe_wrapper_binary_to_list_3) +gc_bif_interface_1(nbif_bitstring_to_list_1, hipe_wrapper_bitstring_to_list_1) +gc_bif_interface_1(nbif_list_to_binary_1, hipe_wrapper_list_to_binary_1) +gc_bif_interface_1(nbif_iolist_to_binary_1, hipe_wrapper_iolist_to_binary_1) +gc_bif_interface_1(nbif_binary_list_to_bin_1, hipe_wrapper_binary_list_to_bin_1) +gc_bif_interface_1(nbif_list_to_bitstring_1, hipe_wrapper_list_to_bitstring_1) +gc_bif_interface_2(nbif_send_2, hipe_wrapper_send_2) +gc_bif_interface_3(nbif_send_3, hipe_wrapper_send_3) +gc_bif_interface_2(nbif_ebif_bang_2, hipe_wrapper_ebif_bang_2) +gc_bif_interface_2(nbif_maps_merge_2, hipe_wrapper_maps_merge_2) -/* BIFs that disable GC while trapping are called via a wrapper - * to reserve stack space for the "trap frame". + +/* + * Standard BIFs. + * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index) */ -define(CFUN,`ifelse( -$1, term_to_binary_1, hipe_wrapper_$1, -$1, term_to_binary_2, hipe_wrapper_$1, -$1, binary_to_term_1, hipe_wrapper_$1, -$1, binary_to_term_2, hipe_wrapper_$1, -$1, binary_to_list_1, hipe_wrapper_$1, -$1, binary_to_list_3, hipe_wrapper_$1, -$1, bitstring_to_list_1, hipe_wrapper_$1, -$1, list_to_binary_1, hipe_wrapper_$1, -$1, iolist_to_binary_1, hipe_wrapper_$1, -$1, binary_list_to_bin_1, hipe_wrapper_$1, -$1, list_to_bitstring_1, hipe_wrapper_$1, -$1, send_2, hipe_wrapper_$1, -$1, send_3, hipe_wrapper_$1, -$1, ebif_bang_2, hipe_wrapper_$1, -$1, maps_merge_2, hipe_wrapper_$1, -$1)') -define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') +define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, $4)') include(TARGET/`erl_bif_list.h') /* diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index d0619a0609..68c65dea27 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -46,6 +46,8 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop) /* arch-specific nstack walk state */ struct nstack_walk_state walk_state; + ASSERT(!p->hipe.gc_is_unsafe); + if (!p->hipe.nstack) { ASSERT(!p->hipe.nsp && !p->hipe.nstend); return n_htop; @@ -136,6 +138,8 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) char *mature; Uint mature_size; + ASSERT(!p->hipe.gc_is_unsafe); + if (!p->hipe.nstack) { ASSERT(!p->hipe.nsp && !p->hipe.nstend); return; @@ -233,3 +237,122 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) } abort(); } + +Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area, + Uint area_size) +{ + /* known nstack walk state */ + Eterm *nsp; + Eterm *nsp_end; + const struct sdesc *sdesc; + /* arch-specific nstack walk state */ + struct nstack_walk_state walk_state; + + ASSERT(!p->hipe.gc_is_unsafe); + + if (!p->hipe.nstack) { + ASSERT(!p->hipe.nsp && !p->hipe.nstend); + return old_htop; + } + if (!nstack_walk_init_check(p)) + return old_htop; + + ASSERT(p->hipe.nsp && p->hipe.nstend); + nsp = nstack_walk_nsp_begin(p); + nsp_end = nstack_walk_nsp_end(p); + sdesc = nstack_walk_init_sdesc_ignore_trap(p, &walk_state); + + while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) { + unsigned long ra; + unsigned sdesc_size = nstack_walk_frame_size(sdesc); + unsigned i = 0; + unsigned mask = sdesc->livebits[0]; + for (;;) { + if (mask & 1) { + Eterm *nsp_i = nstack_walk_frame_index(nsp, i); + Eterm gval = *nsp_i; + if (is_boxed(gval)) { + Eterm *ptr = boxed_val(gval); + Eterm val = *ptr; + if (IS_MOVED_BOXED(val)) { + ASSERT(is_boxed(val)); + *nsp_i = val; + } else if (ErtsInArea(ptr, area, area_size)) { + MOVE_BOXED(ptr, val, old_htop, nsp_i); + } + } else if (is_list(gval)) { + Eterm *ptr = list_val(gval); + Eterm val = *ptr; + if (IS_MOVED_CONS(val)) { + *nsp_i = ptr[1]; + } else if (ErtsInArea(ptr, area, area_size)) { + MOVE_CONS(ptr, val, old_htop, nsp_i); + } + } + } + if (++i >= sdesc_size) + break; + if (i & 31) + mask >>= 1; + else + mask = sdesc->livebits[i >> 5]; + } + ra = nstack_walk_frame_ra(nsp, sdesc); + if (ra == (unsigned long)nbif_stack_trap_ra) + ra = (unsigned long)p->hipe.ngra; + sdesc = hipe_find_sdesc(ra); + nsp = nstack_walk_next_frame(nsp, sdesc_size); + } + return old_htop; +} + +int +nstack_any_heap_ref_ptrs(Process *rp, char* mod_start, Uint mod_size) +{ + Eterm *nsp; + Eterm *nsp_end; + const struct sdesc *sdesc; + /* arch-specific nstack walk state */ + struct nstack_walk_state walk_state; + + ASSERT(!rp->hipe.gc_is_unsafe); + + if (!rp->hipe.nstack || !nstack_walk_init_check(rp)) return 0; + ASSERT(rp->hipe.nsp && rp->hipe.nstend); + nsp = nstack_walk_nsp_begin(rp); + nsp_end = nstack_walk_nsp_end(rp); + sdesc = nstack_walk_init_sdesc_ignore_trap(rp, &walk_state); + + while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) { + unsigned long ra; + unsigned sdesc_size = nstack_walk_frame_size(sdesc); + unsigned i = 0; + unsigned mask = sdesc->livebits[0]; + for (;;) { + if (mask & 1) { + Eterm *nsp_i = nstack_walk_frame_index(nsp, i); + Eterm val = *nsp_i; + switch (primary_tag(val)) { + case TAG_PRIMARY_BOXED: + case TAG_PRIMARY_LIST: + if (ErtsInArea(val, mod_start, mod_size)) { + return 1; + } + break; + } + } + if (++i >= sdesc_size) + break; + if (i & 31) + mask >>= 1; + else + mask = sdesc->livebits[i >> 5]; + } + ra = nstack_walk_frame_ra(nsp, sdesc); + if (ra == (unsigned long)nbif_stack_trap_ra) + ra = (unsigned long)rp->hipe.ngra; + sdesc = hipe_find_sdesc(ra); + nsp = nstack_walk_next_frame(nsp, sdesc_size); + } + return 0; +} diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 0d3493ec6c..b9d4226705 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -525,6 +525,12 @@ static const struct rts_param rts_params[] = { { 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) }, { 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE }, + + { 53, "P_GCUNSAFE", +#ifdef DEBUG + 1, offsetof(struct process, hipe.gc_is_unsafe) +#endif + }, }; #define NR_PARAMS ARRAY_SIZE(rts_params) diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index 57b4208bee..b540562185 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -212,8 +212,9 @@ ASYM($1): * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-2 parameters and + * Generate native interface for a BIF with 0-3 parameters and * standard failure mode. * The BIF may do a GC. */ @@ -300,6 +301,39 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(gc_bif_interface_3, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + GLOBAL(ASYM($1)) +ASYM($1): + /* Set up C argument registers. */ + mr r3, P + NBIF_ARG(r4,3,0) + NBIF_ARG(r5,3,1) + NBIF_ARG(r6,3,2) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_GC + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ + STORE r5, P_ARG1(r3) + STORE r6, P_ARG2(r3) + addi r4, r3, P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + CMPI r3, THE_NON_VALUE + RESTORE_CONTEXT_GC + beq- 1f + NBIF_RET(3) +1: /* workaround for bc:s small offset operand */ + b CSYM(nbif_3_simple_exception) + HANDLE_GOT_MBUF(3) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + /* * gc_nofail_primop_interface_1(nbif_name, cbif_name) * diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index 21c4239753..a8d5972280 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -52,6 +52,9 @@ struct hipe_process_state { #if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) void (*bif_callee)(void); /* When calling BIF's via debug wrapper */ #endif +#ifdef DEBUG + UWord gc_is_unsafe; /* Nonzero when GC-required state is on stack */ +#endif }; extern void hipe_arch_print_pcb(struct hipe_process_state *p); @@ -68,6 +71,9 @@ static __inline__ void hipe_init_process(struct hipe_process_state *p) p->nra = NULL; #endif p->narity = 0; +#ifdef DEBUG + p->gc_is_unsafe = 0; +#endif } static __inline__ void hipe_delete_process(struct hipe_process_state *p) diff --git a/erts/emulator/hipe/hipe_risc_gc.h b/erts/emulator/hipe/hipe_risc_gc.h index 315f8e7f9f..09568c140e 100644 --- a/erts/emulator/hipe/hipe_risc_gc.h +++ b/erts/emulator/hipe/hipe_risc_gc.h @@ -51,6 +51,19 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state) return sdesc; } +static inline const struct sdesc* +nstack_walk_init_sdesc_ignore_trap(const Process *p, + struct nstack_walk_state *state) +{ + unsigned long ra = (unsigned long)p->hipe.nra; + const struct sdesc *sdesc; + if (ra == (unsigned long)&nbif_stack_trap_ra) + ra = (unsigned long)p->hipe.ngra; + sdesc = hipe_find_sdesc(ra); + state->sdesc0 = sdesc; + return sdesc; +} + static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0) { Eterm *nsp = p->hipe.nsp; diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index 2e886ec1d1..1389beaa61 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -210,8 +210,9 @@ $1: * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-2 parameters and + * Generate native interface for a BIF with 0-3 parameters and * standard failure mode. * The BIF may do a GC. */ @@ -295,6 +296,37 @@ $1: .type $1, #function #endif') +define(gc_bif_interface_3, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov P, %o0 + NBIF_ARG(%o1,3,0) + NBIF_ARG(%o2,3,1) + NBIF_ARG(%o3,3,2) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_GC + st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg + st %o2, [%o0+P_ARG1] + st %o3, [%o0+P_ARG2] + add %o0, P_ARG0, %o1 + CALL_BIF($2) + nop + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + TEST_GOT_EXN(3) + RESTORE_CONTEXT_GC + NBIF_RET(3) + HANDLE_GOT_MBUF(3) + .size $1, .-$1 + .type $1, #function +#endif') + /* * gc_nofail_primop_interface_1(nbif_name, cbif_name) * diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h index 4ea7d5c031..afa0ed4256 100644 --- a/erts/emulator/hipe/hipe_stack.h +++ b/erts/emulator/hipe/hipe_stack.h @@ -131,5 +131,8 @@ static __inline__ void hipe_check_nstack(Process *p, unsigned nwords) */ extern Eterm *fullsweep_nstack(Process *p, Eterm *n_htop); extern void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop); +extern Eterm *sweep_literals_nstack(Process *p, Eterm *n_htop, char *area, + Uint area_size); +extern int nstack_any_heap_ref_ptrs(Process *, char* mod_start, Uint mod_size); #endif /* HIPE_STACK_H */ diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index b8ac5046d5..c0c149733c 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -671,10 +671,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu) define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)') /* - * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2). + * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3). */ define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)') define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)') +define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)') /* * Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1. diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h index c22b28c2d5..00fe03d8f9 100644 --- a/erts/emulator/hipe/hipe_x86_gc.h +++ b/erts/emulator/hipe/hipe_x86_gc.h @@ -81,6 +81,23 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state) #endif } +static inline const struct sdesc* +nstack_walk_init_sdesc_ignore_trap(const Process *p, + struct nstack_walk_state *state) +{ +#ifdef SKIP_YOUNGEST_FRAME + unsigned long ra = p->hipe.nsp[0]; + const struct sdesc *sdesc; + if (ra == (unsigned long)nbif_stack_trap_ra) + ra = (unsigned long)p->hipe.ngra; + sdesc = hipe_find_sdesc(ra); + state->sdesc0 = sdesc; + return sdesc; +#else + return nstack_walk_init_sdesc(p, state); +#endif +} + static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0) { #ifdef SKIP_YOUNGEST_FRAME diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md index 30bc5327a7..728f315263 100644 --- a/erts/emulator/internal_doc/Tracing.md +++ b/erts/emulator/internal_doc/Tracing.md @@ -57,7 +57,7 @@ generations of breakpoints are kept and indentified by index of 0 and 1. The global atomic variables `erts_active_bp_index` will determine which generation of breakpoints running code will use. -### Atomicy Without Atomic Operations +### Atomicity Without Atomic Operations Not using the code loading generations (or any other code duplication) means that `trace_pattern` must at some point write to the active beam diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index e394d84f73..b8a28bcc18 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -75,6 +75,7 @@ #include "erl_driver.h" #include "erl_alloc.h" #include "erl_msacc.h" +#include "erl_misc_utils.h" #if !defined(ERTS_POLL_USE_EPOLL) \ && !defined(ERTS_POLL_USE_DEVPOLL) \ @@ -2132,16 +2133,19 @@ get_timeout(ErtsPollSet ps, if (timeout > (ErtsMonotonicTime) INT_MAX) timeout = (ErtsMonotonicTime) INT_MAX; save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000); break; case 1000000: /* Round up to nearest even micro second */ timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1; save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000); break; case 1000000000: /* Round up to nearest even nano second */ timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1; save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000); break; default: ERTS_INTERNAL_ERROR("Invalid resolution"); @@ -2452,7 +2456,15 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, } #endif - res = check_fd_events(ps, to, no_fds); + while (1) { + res = check_fd_events(ps, to, no_fds); + if (res != 0) + break; + if (to == ERTS_POLL_NO_TIMEOUT) + break; + if (erts_get_monotonic_time(NULL) >= timeout_time) + break; + } woke_up(ps); diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 6beb316350..69fc6c2879 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -54,6 +54,7 @@ #include <stdlib.h> #include <stdio.h> +#include <stdarg.h> #include <sys/wait.h> #define WANT_NONBLOCKING @@ -74,15 +75,22 @@ //#define HARD_DEBUG #ifdef HARD_DEBUG -#define DEBUG_PRINT(fmt, ...) fprintf(stderr, fmt "\r\n", ##__VA_ARGS__) +#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%d:" fmt "\r\n", getpid(), ##__VA_ARGS__) #else #define DEBUG_PRINT(fmt, ...) #endif -#define ABORT(fmt, ...) do { \ - fprintf(stderr, "erl_child_setup: " fmt "\r\n", ##__VA_ARGS__); \ - abort(); \ - } while(0) +static char abort_reason[200]; /* for core dump inspection */ + +static void ABORT(const char* fmt, ...) +{ + va_list arglist; + va_start(arglist, fmt); + vsprintf(abort_reason, fmt, arglist); + fprintf(stderr, "erl_child_setup: %s\r\n", abort_reason); + va_end(arglist); + abort(); +} #ifdef DEBUG void @@ -123,12 +131,13 @@ static int sigchld_pipe[2]; static int start_new_child(int pipes[]) { + int errln = -1; int size, res, i, pos = 0; char *buff, *o_buff; - char *cmd, *wd, **new_environ, **args = NULL; + char *cmd, *cwd, *wd, **new_environ, **args = NULL; - Sint cnt, flags; + Sint32 cnt, flags; /* only child executes here */ @@ -137,6 +146,7 @@ start_new_child(int pipes[]) } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); if (res <= 0) { + errln = __LINE__; goto child_error; } @@ -148,10 +158,12 @@ start_new_child(int pipes[]) if ((res = read(pipes[0], buff + pos, size - pos)) < 0) { if (errno == ERRNO_BLOCK || errno == EINTR) continue; + errln = __LINE__; goto child_error; } if (res == 0) { errno = EPIPE; + errln = __LINE__; goto child_error; } pos += res; @@ -160,12 +172,16 @@ start_new_child(int pipes[]) o_buff = buff; flags = get_int32(buff); - buff += sizeof(Sint32); + buff += sizeof(flags); DEBUG_PRINT("flags = %d", flags); cmd = buff; buff += strlen(buff) + 1; + + cwd = buff; + buff += strlen(buff) + 1; + if (*buff == '\0') { wd = NULL; } else { @@ -177,10 +193,10 @@ start_new_child(int pipes[]) DEBUG_PRINT("wd = %s", wd); cnt = get_int32(buff); - buff += sizeof(Sint32); + buff += sizeof(cnt); new_environ = malloc(sizeof(char*)*(cnt + 1)); - DEBUG_PRINT("env_len = %ld", cnt); + DEBUG_PRINT("env_len = %d", cnt); for (i = 0; i < cnt; i++, buff++) { new_environ[i] = buff; while(*buff != '\0') buff++; @@ -190,7 +206,7 @@ start_new_child(int pipes[]) if (o_buff + size != buff) { /* This is a spawn executable call */ cnt = get_int32(buff); - buff += sizeof(Sint32); + buff += sizeof(cnt); args = malloc(sizeof(char*)*(cnt + 1)); for (i = 0; i < cnt; i++, buff++) { args[i] = buff; @@ -201,7 +217,12 @@ start_new_child(int pipes[]) if (o_buff + size != buff) { errno = EINVAL; - goto child_error; + errln = __LINE__; + fprintf(stderr,"erl_child_setup: failed with protocol " + "error %d on line %d", errno, errln); + /* we abort here as it is most likely a symptom of an + emulator/erl_child_setup bug */ + abort(); } DEBUG_PRINT("read ack"); @@ -213,12 +234,32 @@ start_new_child(int pipes[]) ASSERT(res == sizeof(proto)); } } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); + if (res < 1) { errno = EPIPE; + errln = __LINE__; goto child_error; } - DEBUG_PRINT("Do that forking business: '%s'\n",cmd); + DEBUG_PRINT("Set cwd to: '%s'",cwd); + + if (chdir(cwd) < 0) { + /* This is not good, it probably means that the cwd of + beam is invalid. We ignore it and try anyways as + the child might now need a cwd or the chdir below + could take us to a valid directory. + */ + } + + DEBUG_PRINT("Set wd to: '%s'",wd); + + if (wd && chdir(wd) < 0) { + int err = errno; + fprintf(stderr,"spawn: Could not cd to %s\r\n", wd); + _exit(err); + } + + DEBUG_PRINT("Do that forking business: '%s'",cmd); /* When the dup2'ing below is done, only fd's 0, 1, 2 and maybe 3, 4 should survive the @@ -228,25 +269,34 @@ start_new_child(int pipes[]) if (flags & FORKER_FLAG_USE_STDIO) { /* stdin for process */ if (flags & FORKER_FLAG_DO_WRITE && - dup2(pipes[0], 0) < 0) + dup2(pipes[0], 0) < 0) { + errln = __LINE__; goto child_error; + } /* stdout for process */ if (flags & FORKER_FLAG_DO_READ && - dup2(pipes[1], 1) < 0) + dup2(pipes[1], 1) < 0) { + errln = __LINE__; goto child_error; + } } else { /* XXX will fail if pipes[0] == 4 (unlikely..) */ - if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) + if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) { + errln = __LINE__; goto child_error; - if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) + } + if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) { + errln = __LINE__; goto child_error; + } } - if (dup2(pipes[2], 2) < 0) - goto child_error; - - if (wd && chdir(wd) < 0) + /* we do the dup2 of stderr last so that errors + in child_error will be printed to stderr */ + if (dup2(pipes[2], 2) < 0) { + errln = __LINE__; goto child_error; + } #if defined(USE_SETPGRP_NOARGS) /* SysV */ (void) setpgrp(); @@ -268,9 +318,14 @@ start_new_child(int pipes[]) } else { execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ); } + + DEBUG_PRINT("exec error: %d",errno); + _exit(errno); + child_error: - DEBUG_PRINT("exec error: %d\r\n",errno); - _exit(128 + errno); + fprintf(stderr,"erl_child_setup: failed with error %d on line %d\r\n", + errno, errln); + _exit(errno); } @@ -293,7 +348,7 @@ child_error: * for posterity. */ static void handle_sigchld(int sig) { - int buff[2], res; + int buff[2], res, __preverrno = errno; sys_sigblock(SIGCHLD); @@ -307,6 +362,16 @@ static void handle_sigchld(int sig) { } sys_sigrelease(SIGCHLD); + + /* We save and restore the original errno as otherwise + the thread we are running in may end up with an + unexpected errno. An example of when this happened + was when the select in main had gotten an EINTR but + before the errno was checked the signal handler + was called and set errno to ECHILD from waitpid + which caused erl_child_setup to abort as it does + not expect ECHILD to be set after select */ + errno = __preverrno; } #if defined(__ANDROID__) @@ -368,7 +433,7 @@ main(int argc, char *argv[]) sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, 0) == -1) { - perror(0); + perror(NULL); exit(1); } @@ -461,7 +526,7 @@ main(int argc, char *argv[]) proto.action = ErtsSysForkerProtoAction_SigChld; proto.u.sigchld.error_number = ibuff[1]; - DEBUG_PRINT("send %s to %d", buff, uds_fd); + DEBUG_PRINT("send sigchld to %d (errno = %d)", uds_fd, ibuff[1]); if (write(uds_fd, &proto, sizeof(proto)) < 0) { if (errno == EINTR) continue; diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 3a0d23cd36..b64b0d87f6 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -282,7 +282,7 @@ ERTS_GLB_INLINE ErtsSysPerfCounter erts_sys_perf_counter(void); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE ErtsSysPerfCounter +ERTS_GLB_FORCE_INLINE ErtsSysPerfCounter erts_sys_perf_counter() { return (*erts_sys_time_data__.r.o.perf_counter)(); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 6fb86f6dda..089efec3e8 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -715,11 +715,13 @@ static RETSIGTYPE suspend_signal(void) static RETSIGTYPE suspend_signal(int signum) #endif { - int res; - int buf[1]; - do { - res = read(sig_suspend_fds[0], buf, sizeof(int)); - } while (res < 0 && errno == EINTR); + int res, buf[1], __errno = errno; + do { + res = read(sig_suspend_fds[0], buf, sizeof(int)); + } while (res < 0 && errno == EINTR); + + /* restore previous errno in case read changed it */ + errno = __errno; } #endif /* #ifdef ERTS_SYS_SUSPEND_SIGNAL */ diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 812112fb91..400f163652 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -554,7 +554,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, ErtsSysDriverData *dd; char *cmd_line; char wd_buff[MAXPATHLEN+1]; - char *wd; + char *wd, *cwd; int ifd[2], ofd[2], stderrfd; if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO; @@ -631,24 +631,22 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, return ERL_DRV_ERROR_ERRNO; } - if (opts->wd == NULL) { - if ((wd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) { - /* on some OSs this call opens a fd in the - background which means that this can - return EMFILE */ - int err = errno; - close_pipes(ifd, ofd); - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - if (new_environ != environ) - erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); - erts_smp_rwmtx_runlock(&environ_rwmtx); - errno = err; - return ERL_DRV_ERROR_ERRNO; - } - } else { - wd = opts->wd; + if ((cwd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) { + /* on some OSs this call opens a fd in the + background which means that this can + return EMFILE */ + int err = errno; + close_pipes(ifd, ofd); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + erts_smp_rwmtx_runlock(&environ_rwmtx); + errno = err; + return ERL_DRV_ERROR_ERRNO; } + wd = opts->wd; + { struct iovec *io_vector; int iov_len = 5; @@ -660,6 +658,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, | (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0) | (opts->read_write & DO_WRITE ? FORKER_FLAG_DO_WRITE : 0); + if (wd) iov_len++; + /* count number of elements in environment */ while(new_environ[env_len] != NULL) env_len++; @@ -688,6 +688,10 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, return ERL_DRV_ERROR_ERRNO; } + /* + * Whitebox test port_SUITE:pipe_limit_env + * assumes this command payload format. + */ io_vector[i].iov_base = (void*)&buffsz; io_vector[i++].iov_len = sizeof(buffsz); @@ -700,10 +704,16 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, io_vector[i++].iov_len = len; buffsz += len; - io_vector[i].iov_base = wd; + io_vector[i].iov_base = cwd; io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; buffsz += io_vector[i++].iov_len; + if (wd) { + io_vector[i].iov_base = wd; + io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; + buffsz += io_vector[i++].iov_len; + } + io_vector[i].iov_base = nullbuff; io_vector[i++].iov_len = 1; buffsz += io_vector[i-1].iov_len; @@ -765,9 +775,14 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, if (res < (buffsz + sizeof(buffsz))) { /* we only wrote part of the command payload. Enqueue the rest. */ for (i = 0; i < iov_len; i++) { - driver_enq(port_num, io_vector[i].iov_base, io_vector[i].iov_len); + if (res >= io_vector[i].iov_len) + res -= io_vector[i].iov_len; + else { + driver_enq(port_num, io_vector[i].iov_base + res, + io_vector[i].iov_len - res); + res = 0; + } } - driver_deq(port_num, res); driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1); } diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index b580211eff..2e48c475d5 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -70,6 +70,7 @@ MODULES= \ guard_SUITE \ hash_SUITE \ hibernate_SUITE \ + hipe_SUITE \ list_bif_SUITE \ lttng_SUITE \ map_SUITE \ diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 4f20ad3656..b1f7e06bf5 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -223,7 +223,7 @@ recv_after_32bit(_, _) -> blaster() -> receive {go, TimeoutTime} -> - Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds), + Tmo = TimeoutTime - erlang:monotonic_time(millisecond), receive after Tmo -> ok end end. @@ -234,7 +234,7 @@ spawn_blasters(N) -> receive_after_blast(Config) when is_list(Config) -> PMs = spawn_blasters(10000), - TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000, + TimeoutTime = erlang:monotonic_time(millisecond) + 5000, lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs), lists:foreach(fun ({P, M}) -> receive diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 84cf4921d3..3a721095e2 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -342,7 +342,7 @@ start_node_1(Config, Opts) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index bb0632ae08..7094cee992 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -469,12 +469,12 @@ hs_busy_pcmd(Prt, Opts, StartFun, EndFun) -> P = spawn_link(fun () -> erlang:yield(), Tester ! {self(), doing_port_command}, - Start = erlang:monotonic_time(micro_seconds), + Start = erlang:monotonic_time(microsecond), Res = try {return, port_command(Prt, [], Opts)} catch Exception:Error -> {Exception, Error} end, - End = erlang:monotonic_time(micro_seconds), + End = erlang:monotonic_time(microsecond), Time = round((End - Start)/1000), Tester ! {self(), port_command_result, Res, Time} end), @@ -717,7 +717,7 @@ run_command(_M,spawn,{Args,Opts}) -> run_command(M,spawn,Args) -> run_command(M,spawn,{Args,[]}); run_command(Mod,Func,Args) -> - erlang:display({{Mod,Func,Args}, erlang:system_time(micro_seconds)}), + erlang:display({{Mod,Func,Args}, erlang:system_time(microsecond)}), apply(Mod,Func,Args). validate_scenario(Data,[{print,Var}|T]) -> diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 2347a3d4ef..34515efa3d 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -20,7 +20,8 @@ -module(code_SUITE). -export([all/0, suite/0, init_per_suite/1, end_per_suite/1, - versions/1,new_binary_types/1, + versions/1,new_binary_types/1, call_purged_fun_code_gone/1, + call_purged_fun_code_reload/1, call_purged_fun_code_there/1, t_check_process_code/1,t_check_old_code/1, t_check_process_code_ets/1, external_fun/1,get_chunk/1,module_md5/1,make_stub/1, @@ -34,7 +35,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [versions, new_binary_types, t_check_process_code, + [versions, new_binary_types, call_purged_fun_code_gone, + call_purged_fun_code_reload, call_purged_fun_code_there, t_check_process_code, t_check_process_code_ets, t_check_old_code, external_fun, get_chunk, module_md5, make_stub, make_stub_many_funs, constant_pools, constant_refc_binaries, false_dependency, @@ -127,12 +129,169 @@ new_binary_types(Config) when is_list(Config) -> bit_sized_binary(Bin))), ok. +call_purged_fun_code_gone(Config) when is_list(Config) -> + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + call_purged_fun_test(Priv, Data, code_gone), + ok. + +call_purged_fun_code_reload(Config) when is_list(Config) -> + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + Path = code:get_path(), + true = code:add_path(Priv), + try + call_purged_fun_test(Priv, Data, code_reload) + after + code:set_path(Path) + end, + ok. + +call_purged_fun_code_there(Config) when is_list(Config) -> + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + call_purged_fun_test(Priv, Data, code_there), + ok. + +call_purged_fun_test(Priv, Data, Type) -> + File = filename:join(Data, "my_code_test2"), + Code = filename:join(Priv, "my_code_test2"), + + catch erlang:purge_module(my_code_test2), + catch erlang:delete_module(my_code_test2), + catch erlang:purge_module(my_code_test2), + + {ok,my_code_test2} = c:c(File, [{outdir,Priv}]), + + T = ets:new(my_code_test2_fun_table, []), + ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}), + ets:insert(T, {my_fun2,my_code_test2:make_fun2()}), + + spawn(fun () -> + [{my_fun2,F2}] = ets:lookup(T, my_fun2), + F2(fun () -> + receive after infinity -> ok end + end, + fun () -> ok end), + exit(completed) + end), + + PurgeType = case Type of + code_gone -> + ok = file:delete(Code++".beam"), + true; + code_reload -> + true; + code_there -> + false + end, + + true = erlang:delete_module(my_code_test2), + + Purge = start_purge(my_code_test2, PurgeType), + + {P0, M0} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4712 = F(1), + exit(completed) + end), + + wait_until(fun () -> + {status, suspended} + == process_info(P0, status) + end), + + ok = continue_purge(Purge), + + {P1, M1} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4713 = F(2), + exit(completed) + end), + {P2, M2} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4714 = F(3), + exit(completed) + end), + + wait_until(fun () -> + {status, suspended} + == process_info(P1, status) + end), + wait_until(fun () -> + {status, suspended} + == process_info(P2, status) + end), + + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P0, current_function), + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P1, current_function), + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P2, current_function), + + case Type of + code_there -> + false = complete_purge(Purge); + _ -> + {true, true} = complete_purge(Purge) + end, + + case Type of + code_gone -> + receive + {'DOWN', M0, process, P0, Reason0} -> + {undef, _} = Reason0 + end, + receive + {'DOWN', M1, process, P1, Reason1} -> + {undef, _} = Reason1 + end, + receive + {'DOWN', M2, process, P2, Reason2} -> + {undef, _} = Reason2 + end; + _ -> + receive + {'DOWN', M0, process, P0, Reason0} -> + completed = Reason0 + end, + receive + {'DOWN', M1, process, P1, Reason1} -> + completed = Reason1 + end, + receive + {'DOWN', M2, process, P2, Reason2} -> + completed = Reason2 + end, + catch erlang:purge_module(my_code_test2), + catch erlang:delete_module(my_code_test2), + catch erlang:purge_module(my_code_test2) + end, + ok. + t_check_process_code(Config) when is_list(Config) -> + case check_process_code_handle(indirect_references) of + false -> {skipped, "check_process_code() ignores funs"}; + true -> t_check_process_code_test(Config) + end. + +t_check_process_code_test(Config) -> Priv = proplists:get_value(priv_dir, Config), Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "my_code_test"), Code = filename:join(Priv, "my_code_test"), + catch erlang:purge_module(my_code_test), + catch erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + {ok,my_code_test} = c:c(File, [{outdir,Priv}]), MyFun = fun(X, Y) -> X + Y end, %Confuse things. @@ -231,11 +390,16 @@ gc1() -> ok. %% Test check_process_code/2 in combination with a fun obtained from an ets table. t_check_process_code_ets(Config) when is_list(Config) -> - case test_server:is_native(?MODULE) of - true -> - {skip,"Native code"}; - false -> - do_check_process_code_ets(Config) + case check_process_code_handle(indirect_references) of + false -> + {skipped, "check_process_code() ignores funs"}; + true -> + case test_server:is_native(?MODULE) of + true -> + {skip,"Native code"}; + false -> + do_check_process_code_ets(Config) + end end. do_check_process_code_ets(Config) -> @@ -243,8 +407,9 @@ do_check_process_code_ets(Config) -> Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "my_code_test"), - erlang:purge_module(my_code_test), - erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + catch erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), {ok,my_code_test} = c:c(File, [{outdir,Priv}]), T = ets:new(my_code_test, []), @@ -295,8 +460,8 @@ t_check_old_code(Config) when is_list(Config) -> Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "my_code_test"), - erlang:purge_module(my_code_test), - erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + catch erlang:delete_module(my_code_test), catch erlang:purge_module(my_code_test), false = erlang:check_old_code(my_code_test), @@ -971,3 +1136,39 @@ flush() -> receive _ -> flush() after 0 -> ok end. id(I) -> I. + +check_process_code_handle(What) -> + lists:member(What, erlang:system_info(check_process_code)). + +wait_until(Fun) -> + case Fun() of + true -> + ok; + false -> + receive after 100 -> ok end, + wait_until(Fun) + end. + +start_purge(Mod, Type) when is_atom(Mod) + andalso ((Type == true) + orelse (Type == false)) -> + Ref = make_ref(), + erts_code_purger ! {test_purge, Mod, self(), Type, Ref}, + receive + {started, Ref} -> + Ref + end. + +continue_purge(Ref) when is_reference(Ref) -> + erts_code_purger ! {continue, Ref}, + receive + {continued, Ref} -> + ok + end. + +complete_purge(Ref) when is_reference(Ref) -> + erts_code_purger ! {complete, Ref}, + receive + {test_purge, Res, Ref} -> + Res + end. diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl index d2386157d6..9d12aa9897 100644 --- a/erts/emulator/test/code_SUITE_data/my_code_test.erl +++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl @@ -24,5 +24,3 @@ make_fun(A) -> fun(X) -> A + X end. - - diff --git a/erts/emulator/test/code_SUITE_data/my_code_test2.erl b/erts/emulator/test/code_SUITE_data/my_code_test2.erl new file mode 100644 index 0000000000..57973535d4 --- /dev/null +++ b/erts/emulator/test/code_SUITE_data/my_code_test2.erl @@ -0,0 +1,32 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(my_code_test2). + +-export([make_fun/1, make_fun2/0]). + +make_fun(A) -> + fun(X) -> A + X end. + +make_fun2() -> + fun (F1,F2) -> + F1(), + F2() + end. diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index 83b098a704..658bdc41b6 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -33,7 +33,8 @@ dirty_nif_exception/1, call_dirty_nif_exception/1, dirty_scheduler_exit/1, dirty_call_while_terminated/1, dirty_heap_access/1, dirty_process_info/1, - dirty_process_register/1, dirty_process_trace/1]). + dirty_process_register/1, dirty_process_trace/1, + code_purge/1, dirty_nif_send_traced/1]). -define(nif_stub,nif_stub_error(?LINE)). @@ -48,7 +49,9 @@ all() -> dirty_heap_access, dirty_process_info, dirty_process_register, - dirty_process_trace]. + dirty_process_trace, + code_purge, + dirty_nif_send_traced]. init_per_suite(Config) -> try erlang:system_info(dirty_cpu_schedulers) of @@ -145,9 +148,9 @@ dirty_scheduler_exit(Config) when is_list(Config) -> [ok] = mcall(Node, [fun() -> ok = erlang:load_nif(NifLib, []), - Start = erlang:monotonic_time(milli_seconds), + Start = erlang:monotonic_time(millisecond), ok = test_dirty_scheduler_exit(), - End = erlang:monotonic_time(milli_seconds), + End = erlang:monotonic_time(millisecond), io:format("Time=~p ms~n", [End-Start]), ok end]), @@ -230,7 +233,11 @@ dirty_call_while_terminated(Config) when is_list(Config) -> process_info(self(), binary))), process_flag(trap_exit, OT), - ok. + try + blipp:blupp(Bin) + catch + _ : _ -> ok + end. dirty_heap_access(Config) when is_list(Config) -> {ok, Node} = start_node(Config), @@ -349,6 +356,103 @@ dirty_process_trace(Config) when is_list(Config) -> ok end). +dirty_code_test_code() -> + " +-module(dirty_code_test). + +-export([func/1]). + +func(Fun) -> + Fun(), + blipp:blapp(). + +". + +code_purge(Config) when is_list(Config) -> + Path = ?config(data_dir, Config), + File = filename:join(Path, "dirty_code_test.erl"), + ok = file:write_file(File, dirty_code_test_code()), + {ok, dirty_code_test, Bin} = compile:file(File, [binary]), + {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), + Start = erlang:monotonic_time(), + {Pid1, Mon1} = spawn_monitor(fun () -> + dirty_code_test:func(fun () -> + %% Sleep for 6 seconds + %% in dirty nif... + dirty_sleeper() + end) + end), + {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), + {Pid2, Mon2} = spawn_monitor(fun () -> + dirty_code_test:func(fun () -> + %% Sleep for 6 seconds + %% in dirty nif... + dirty_sleeper() + end) + end), + receive + {'DOWN', Mon1, process, Pid1, _} -> + ct:fail(premature_death) + after 100 -> + ok + end, + true = erlang:purge_module(dirty_code_test), + receive + {'DOWN', Mon1, process, Pid1, Reason1} -> + killed = Reason1 + end, + receive + {'DOWN', Mon2, process, Pid2, _} -> + ct:fail(premature_death) + after 100 -> + ok + end, + true = erlang:delete_module(dirty_code_test), + receive + {'DOWN', Mon2, process, Pid2, _} -> + ct:fail(premature_death) + after 100 -> + ok + end, + true = erlang:purge_module(dirty_code_test), + receive + {'DOWN', Mon2, process, Pid2, Reason2} -> + killed = Reason2 + end, + End = erlang:monotonic_time(), + Time = erlang:convert_time_unit(End-Start, native, milli_seconds), + io:format("Time=~p~n", [Time]), + true = Time =< 1000, + ok. + +dirty_nif_send_traced(Config) when is_list(Config) -> + Parent = self(), + Rcvr = spawn_link(fun() -> + Self = self(), + receive {ok, Self} -> ok end, + Parent ! {Self, received} + end), + Sndr = spawn_link(fun () -> + receive {Parent, go} -> ok end, + {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr), + Parent ! {self(), sent} + end), + 1 = erlang:trace(Sndr, true, [send]), + Start = erlang:monotonic_time(), + Sndr ! {self(), go}, + receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end, + receive {Rcvr, received} -> ok end, + End1 = erlang:monotonic_time(), + Time1 = erlang:convert_time_unit(End1-Start, native, 1000), + io:format("Time1: ~p milliseconds~n", [Time1]), + true = Time1 < 500, + receive {Sndr, sent} -> ok end, + End2 = erlang:monotonic_time(), + Time2 = erlang:convert_time_unit(End2-Start, native, 1000), + io:format("Time2: ~p milliseconds~n", [Time2]), + true = Time2 >= 1900, + ok. + %% %% Internal... %% @@ -400,7 +504,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). @@ -431,6 +535,7 @@ mcall(Node, Funs) -> lib_loaded() -> false. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. +send_wait_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. dirty_call_while_terminated_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index d92933a096..a0019e5d95 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -100,6 +100,32 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_ return result; } +static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + ErlNifPid pid; + ErlNifEnv* menv; + int res; + + if (!enif_get_local_pid(env, argv[0], &pid)) + return enif_make_badarg(env); + result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); + menv = enif_alloc_env(); + res = enif_send(env, &pid, menv, result); + enif_free_env(menv); + +#ifdef __WIN32__ + Sleep(2000); +#else + sleep(2); +#endif + + if (!res) + return enif_make_badarg(env); + else + return result; +} + static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { switch (argc) { @@ -237,6 +263,7 @@ static ErlNifFunc nif_funcs[] = {"lib_loaded", 0, lib_loaded}, {"call_dirty_nif", 3, call_dirty_nif}, {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"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}, {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND}, diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index c6939a695d..6994bfef83 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -43,7 +43,7 @@ lost_exit/1, link_to_dead/1, link_to_dead_new_node/1, applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1, trap_bif_1/1, trap_bif_2/1, trap_bif_3/1, - stop_dist/1, + stop_dist/1, dist_auto_connect_never/1, dist_auto_connect_once/1, dist_parallel_send/1, atom_roundtrip/1, @@ -66,13 +66,13 @@ sendersender/4, sendersender2/4]). %% epmd_module exports --export([start_link/0, register_node/2, port_please/2]). +-export([start_link/0, register_node/2, register_node/3, port_please/2]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 4}}]. -all() -> +all() -> [ping, {group, bulk_send}, {group, local_send}, link_to_busy, exit_to_busy, lost_exit, link_to_dead, link_to_dead_new_node, applied_monitor_node, @@ -83,7 +83,7 @@ all() -> bad_dist_structure, {group, bad_dist_ext}, start_epmd_false, epmd_module]. -groups() -> +groups() -> [{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]}, {local_send, [], [local_send_small, local_send_big, local_send_legal]}, @@ -844,59 +844,50 @@ dist_auto_connect_once(Config) when is_list(Config) -> %% Result is sent here through relay node. dist_auto_connect_never(Config) when is_list(Config) -> Self = self(), - {ok, RelayNode} = - start_node(dist_auto_connect_relay), - spawn(RelayNode, + {ok, RelayNode} = start_node(dist_auto_connect_relay), + spawn(RelayNode, fun() -> register(dist_auto_connect_relay, self()), - dist_auto_connect_relay(Self) + dist_auto_connect_relay(Self) end), {ok, Handle} = dist_auto_connect_start(dist_auto_connect, never), - Result = - receive - {do_dist_auto_connect, ok} -> - ok; - {do_dist_auto_connect, Error} -> - {error, Error}; - Other -> - {error, Other} - after 32000 -> - timeout - end, + Result = receive + {do_dist_auto_connect, ok} -> + ok; + {do_dist_auto_connect, Error} -> + {error, Error}; + Other -> + {error, Other} + after 32000 -> + timeout + end, stop_node(RelayNode), - Stopped = dist_auto_connect_stop(Handle), - Junk = - receive - {do_dist_auto_connect, _} = J -> - J - after 0 -> - ok - end, + Stopped = dist_auto_connect_stop(Handle), + Junk = receive + {do_dist_auto_connect, _} = J -> J + after 0 -> ok + end, {ok, ok, ok} = {Result, Stopped, Junk}, ok. do_dist_auto_connect([never]) -> Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()), - io:format("~p:do_dist_auto_connect([false]) Node=~p~n", - [?MODULE, Node]), + io:format("~p:do_dist_auto_connect([false]) Node=~p~n", [?MODULE, Node]), Ping = net_adm:ping(Node), - io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", - [?MODULE, Ping]), + io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", [?MODULE, Ping]), Result = case Ping of pang -> ok; _ -> {error, Ping} end, - io:format("~p:do_dist_auto_connect([false]) Result=~p~n", - [?MODULE, Result]), + io:format("~p:do_dist_auto_connect([false]) Result=~p~n", [?MODULE, Result]), net_kernel:connect_node(Node), catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result}; % receive after 1000 -> ok end, % halt(); do_dist_auto_connect(Arg) -> - io:format("~p:do_dist_auto_connect(~p)~n", - [?MODULE, Arg]), + io:format("~p:do_dist_auto_connect(~p)~n", [?MODULE, Arg]), receive after 10000 -> ok end, halt(). @@ -912,11 +903,11 @@ dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) -> [%"xterm -e ", atom_to_list(lib:progname()), % " -noinput ", - " -detached ", + " -detached ", long_or_short(), " ", Name, " -setcookie ", Cookie, " -pa ", ModuleDir, - " -s ", atom_to_list(?MODULE), + " -s ", atom_to_list(?MODULE), " do_dist_auto_connect ", ValueStr, " -kernel dist_auto_connect ", ValueStr]), io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]), @@ -947,7 +938,7 @@ dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) -> end. -dist_auto_connect_relay(Parent) -> +dist_auto_connect_relay(Parent) -> receive X -> catch Parent ! X end, @@ -1321,7 +1312,7 @@ get_conflicting_unicode_atoms(CIX, N) -> start_monitor(Offender,P) -> Parent = self(), Q = spawn(Offender, - fun () -> + fun () -> Ref = erlang:monitor(process,P), Parent ! {self(),ref,Ref}, receive @@ -1458,8 +1449,8 @@ bad_dist_structure(Config) when is_list(Config) -> pong = rpc:call(Victim, net_adm, ping, [Offender]), P ! two, P ! check_msgs, - receive - {P, messages_checked} -> ok + receive + {P, messages_checked} -> ok after 5000 -> exit(victim_is_dead) end, @@ -1765,7 +1756,7 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) -> pong = net_adm:ping(Node), DPrt = dport(Node), Bad1 = case WhereToPutSelf of - 0 -> + 0 -> Bad; N when N > 0 -> setelement(N,Bad,self()) @@ -1779,8 +1770,8 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) -> port_command(DPrt, DData), Parent ! {DData,Done} end), - receive - {WhatSent,Done} -> + receive + {WhatSent,Done} -> io:format("Offender sent ~p~n",[WhatSent]), ok after 5000 -> @@ -1887,7 +1878,7 @@ dmsg_fake_hdr2() -> 1, size(A2), A2, 2, size(A3), A3]. -dmsg_ext(Term) -> +dmsg_ext(Term) -> <<131, Res/binary>> = term_to_binary(Term), Res. @@ -1934,7 +1925,9 @@ epmd_module(Config) when is_list(Config) -> start_link() -> ignore. -register_node(_Name, Port) -> +register_node(Name, Port) -> + register_node(Name, Port, inet_tcp). +register_node(_Name, Port, _Driver) -> %% Save the port number we're listening on. application:set_env(kernel, dist_listen_port, Port), Creation = rand:uniform(3), @@ -1957,7 +1950,7 @@ port_please(_Name, _Ip) -> %%% Utilities timestamp() -> - erlang:monotonic_time(milli_seconds). + erlang:monotonic_time(millisecond). start_node(X) -> start_node(X, [], []). @@ -1972,7 +1965,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) -> [] -> []; _ -> [{erl,[{release,Rel}]}] end, - test_server:start_node(Name, slave, + test_server:start_node(Name, slave, [{args, Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""} | RelArg]); @@ -1981,7 +1974,7 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])))), start_node(Name, Args, Rel). @@ -2040,17 +2033,15 @@ inet_rpc_server_loop(Sock) -> start_relay_node(Node, Args) -> Pa = filename:dirname(code:which(?MODULE)), Cookie = "NOT"++atom_to_list(erlang:get_cookie()), - {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, - {active, false}]), + {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]), {ok, Port} = inet:port(LSock), {ok, Host} = inet:gethostname(), RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++ Host ++ " " ++ integer_to_list(Port), - {ok, NN} = - test_server:start_node(Node, peer, - [{args, Args ++ - " -setcookie "++Cookie++" -pa "++Pa++" "++ - RunArg}]), + {ok, NN} = test_server:start_node(Node, peer, + [{args, Args ++ + " -setcookie "++Cookie++" -pa "++Pa++" "++ + RunArg}]), [N,H] = string:tokens(atom_to_list(NN),"@"), {ok, Sock} = gen_tcp:accept(LSock), pang = net_adm:ping(NN), @@ -2066,7 +2057,7 @@ wait_dead(N,H,0) -> wait_dead(N,H,X) -> case erl_epmd:port_please(N,H) of {port,_,_} -> - receive + receive after 1000 -> ok end, @@ -2100,7 +2091,7 @@ node_monitor(Master) -> Master ! {nodeup, node(), Node} end, Nodes0), - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Nodes0]), node_monitor_loop(Master); false -> net_kernel:monitor_nodes(false, Opts), @@ -2121,7 +2112,7 @@ node_monitor_loop(Master) -> receive {nodeup, Node, _InfoList} = Msg -> Master ! {nodeup, node(), Node}, - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]), node_monitor_loop(Master); {nodedown, Node, InfoList} = Msg -> Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of @@ -2129,7 +2120,7 @@ node_monitor_loop(Master) -> _ -> undefined end, Master ! {nodedown, node(), Node, Reason}, - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]), node_monitor_loop(Master) end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index f134a197aa..1df72193a6 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -114,7 +114,7 @@ -define(MAX_DATA_SIZE, 16384). % This is the allowed delay when testing the driver timer functionality --define(delay, 100). +-define(delay, 400). -define(heap_binary_size, 64). @@ -401,7 +401,7 @@ try_timeouts(Port, Timeout) -> true -> try_timeouts(Port, Timeout div 2) end - after Timeout + ?delay -> + after Timeout + 100*?delay -> ct:fail("driver failed to timeout") end. @@ -437,7 +437,7 @@ try_cancel(Port, Timeout) -> Timeout == 0 -> ok; true -> try_cancel(Port, Timeout div 2) end - after ?delay -> + after 100*?delay -> ct:fail("No message from driver") end end. @@ -505,7 +505,7 @@ try_change_timer(Port, Timeout) -> true -> try_timeouts(Port, Timeout div 2) end - after Timeout + ?delay -> + after Timeout + 100*?delay -> ct:fail("driver failed to timeout") end. @@ -2427,7 +2427,7 @@ erl_millisecs() -> erl_millisecs(erlang:monotonic_time()). erl_millisecs(MonotonicTime) -> - (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native). + (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native). %% Start/stop drivers. start_driver(Config, Name, Binary) -> @@ -2481,7 +2481,7 @@ start_node(Config) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 93d2065ba3..89e1aefb50 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1011,7 +1011,7 @@ get_names(N, T, Acc) -> ++ "-" ++ atom_to_list(T) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]). diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 1180a45585..3ce849b88e 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -364,7 +364,7 @@ monotonic_time() -> try erlang:monotonic_time() catch error:undef -> erlang:now() end. subtr(Before, After) when is_integer(Before), is_integer(After) -> - erlang:convert_time_unit(After-Before, native, micro_seconds); + erlang:convert_time_unit(After-Before, native, microsecond); subtr({_,_,_}=Before, {_,_,_}=After) -> timer:now_diff(After, Before). diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index e85addae3a..36b1f9179f 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -276,7 +276,7 @@ start_node(Config) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl new file mode 100644 index 0000000000..a556b4ddc0 --- /dev/null +++ b/erts/emulator/test/hipe_SUITE.erl @@ -0,0 +1,67 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(hipe_SUITE). +-export([all/0, t_copy_literals/1]). + +all() -> + case erlang:system_info(hipe_architecture) of + undefined -> {skip, "HiPE is disabled"}; + _ -> [t_copy_literals] + end. + +t_copy_literals(doc) -> + "Check that BEAM literals referenced from HiPE stack are copied by" + " check_process_code"; +t_copy_literals(Config) when is_list(Config) -> + %% Compile the the ref_cell and literals modules. + Data = proplists:get_value(data_dir, Config), + Priv = proplists:get_value(priv_dir, Config), + RefFile = filename:join(Data, "ref_cell"), + {ok,ref_cell} = c:c(RefFile, [{outdir,Priv},native]), + true = code:is_module_native(ref_cell), + LitFile = filename:join(Data, "literals"), + {ok,literals} = c:c(LitFile, [{outdir,Priv}]), + + %% store references to literals on HiPE stacks + PA = ref_cell:start_link(), + ref_cell:call(PA, {put_res_of, fun literals:a/0}), + PB = ref_cell:start_link_deep(), + ref_cell:call(PB, {put_res_of, fun literals:b/0}), + + %% purge the literals + _ = (catch erlang:purge_module(literals)), + true = erlang:delete_module(literals), + true = erlang:purge_module(literals), + + %% Give the literal collector some time to work... + receive after 2000 -> ok end, + + %% check that the ex-literals are ok + [a,b,c] = ref_cell:call(PA, get), + {a,b,c} = ref_cell:call(PB, get), + + %% cleanup + ref_cell:call(PA, done), + ref_cell:call(PB, done), + _ = (catch erlang:purge_module(ref_cell)), + true = erlang:delete_module(ref_cell), + true = erlang:purge_module(ref_cell), + ok. diff --git a/erts/emulator/test/hipe_SUITE_data/literals.erl b/erts/emulator/test/hipe_SUITE_data/literals.erl new file mode 100644 index 0000000000..31e443970f --- /dev/null +++ b/erts/emulator/test/hipe_SUITE_data/literals.erl @@ -0,0 +1,26 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(literals). + +-export([a/0, b/0]). + +a() -> [a,b,c]. +b() -> {a,b,c}. diff --git a/erts/emulator/test/hipe_SUITE_data/ref_cell.erl b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl new file mode 100644 index 0000000000..2654e4077b --- /dev/null +++ b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl @@ -0,0 +1,64 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(ref_cell). + +-export([start_link/0, start_link_deep/0, call/2]). + +-compile(native). + +-define(DEPTH, 100). +-define(ALLOCS, 500). + +start_link() -> + spawn_link(fun() -> loop(undefined) end). + +start_link_deep() -> + spawn_link(fun() -> go_deep(?DEPTH) end). + +%% Create a stack large enough to get a graylimit trap placed next time there's +%% a minor gc. +go_deep(0) -> + alloc_some(?ALLOCS), + loop(undefined), + 0; +go_deep(Depth) -> + go_deep(Depth-1)+1. + +%% Do some allocation to trigger a minor gc +alloc_some(Amount) -> + Check = (Amount * (Amount + 1)) div 2, + Check = lists:sum(lists:seq(1, Amount)). + +call(Pid, Call) -> + Pid ! {Call, self()}, + receive {Pid, Res} -> Res end. + +loop(Thing) -> + receive + {done, Pid} -> Pid ! {self(), done}; + {{put_res_of, Fun}, Pid} -> + NewThing = Fun(), + Pid ! {self(), put}, + loop(NewThing); + {get, Pid} -> + Pid ! {self(), Thing}, + loop(Thing) + end. diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl index 7c055a31f9..c9a380a229 100644 --- a/erts/emulator/test/long_timers_test.erl +++ b/erts/emulator/test/long_timers_test.erl @@ -27,11 +27,16 @@ %%% Created : 21 Aug 2006 by Rickard Green <[email protected]> %%%------------------------------------------------------------------- +-define(HIGH_CPU_INFO, "Ignored due to high CPU utilization."). +-define(MISSING_CPU_INFO, "Ignored due to missing CPU utilization information."). -define(MAX_TIMEOUT, 60). % Minutes --define(MAX_LATE_MS, 15*1000). % Milliseconds +-define(MAX_LATE_MS, 1000). % Milliseconds -define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___'). +-define(HIGH_UTIL, 96.0). +-define(UTIL_INTERVAL, 10000). + -define(DRV_NAME, timer_driver). % First byte in communication with the timer driver @@ -72,52 +77,149 @@ check_result() -> receive {'DOWN', Mon, process, _, Reason} -> {?REG_NAME, 'DOWN', Reason}; - {result, ?REG_NAME, TORs, Start, End} -> + {result, ?REG_NAME, TORs, Start, End, UtilData} -> erlang:demonitor(Mon), receive {'DOWN', Mon, _, _, _} -> ok after 0 -> ok end, stop_node(Node), - check(TORs, ms((End - Start) - max_late()), ok) + Res = check(TORs, Start, End, UtilData, ms((End - Start) - max_late()), ok), + io:format("Start = ~p~n End = ~p~n UtilData = ~p~n", [Start, End, UtilData]), + Res end. +res(New, Old) when New == failed; Old == failed -> + failed; +res(New, Old) when New == missing_cpu_info; Old == missing_cpu_info -> + missing_cpu_info; +res(New, Old) when New == high_cpu; Old == high_cpu -> + high_cpu; +res(New, _Old) -> + New. + check([#timeout_rec{timeout = Timeout, type = Type, timeout_diff = undefined} | TORs], + Start, + End, + UtilData, NeedRes, - _Ok) when Timeout < NeedRes -> - io:format("~p timeout = ~p ms failed! No timeout.~n", - [Type, Timeout]), - check(TORs, NeedRes, failed); + Ok) when Timeout < NeedRes -> + {NewOk, HCPU} = case had_high_cpu_util(Start, + Timeout, + End - Timeout*1000, + UtilData) of + yes -> {res(high_cpu, Ok), ?HIGH_CPU_INFO}; + no -> {res(failed, Ok), ""}; + missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO} + end, + io:format("~p timeout = ~p ms FAILED! No timeout. ~s~n", + [Type, Timeout, HCPU]), + check(TORs, Start, End, UtilData, NeedRes, NewOk); check([#timeout_rec{timeout_diff = undefined} | TORs], + Start, + End, + UtilData, NeedRes, Ok) -> - check(TORs, NeedRes, Ok); + check(TORs, Start, End, UtilData, NeedRes, Ok); check([#timeout_rec{timeout = Timeout, type = Type, timeout_diff = {error, Reason}} | TORs], + Start, + End, + UtilData, NeedRes, _Ok) -> - io:format("~p timeout = ~p ms failed! exit reason ~p~n", + io:format("~p timeout = ~p ms FAILED! exit reason ~p~n", [Type, Timeout, Reason]), - check(TORs, NeedRes, failed); + check(TORs, Start, End, UtilData, NeedRes, failed); check([#timeout_rec{timeout = Timeout, type = Type, timeout_diff = TimeoutDiff} | TORs], + Start, + End, + UtilData, NeedRes, Ok) -> - {NewOk, SuccessStr} = case ((0 =< TimeoutDiff) - andalso (TimeoutDiff =< max_late())) of - true -> {Ok, "succeeded"}; - false -> {failed, "FAILED"} + {NewOk, SuccessStr, HCPU} = case {(0 =< TimeoutDiff), + (TimeoutDiff =< max_late())} of + {true, true} -> + {res(ok, Ok), "succeeded", ""}; + {false, _} -> + {res(failed, Ok), "FAILED", ""}; + _ -> + case had_high_cpu_util(Start, + Timeout, + TimeoutDiff, + UtilData) of + yes -> {res(high_cpu, Ok), "FAILED", ?HIGH_CPU_INFO}; + no -> {res(failed, Ok), "FAILED", ""}; + missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO} + end end, - io:format("~s timeout = ~s ms ~s! timeout diff = ~s.~n", + io:format("~s timeout = ~s ms ~s! timeout diff = ~s. ~s~n", [type_str(Type), time_str(Timeout), SuccessStr, - time_str(TimeoutDiff, erlang:convert_time_unit(1, seconds, native))]), - check(TORs, NeedRes, NewOk); -check([], _NeedRes, Ok) -> + time_str(TimeoutDiff, 1000000), + HCPU]), + check(TORs, Start, End, UtilData, NeedRes, NewOk); +check([],_Start,_End,_UtilData,_NeedRes, Ok) -> Ok. +% TargetTimeout in ms, other in us. +had_high_cpu_util(StartTime, + TargetTimeout, + TimeoutDiff, + UtilData) -> + TargetTo = StartTime + TargetTimeout*1000, + ActTo = TargetTo + TimeoutDiff, + hcpu(ActTo, TargetTo, UtilData). + +hcpu(_ActTo, _TargetTo, [{UT, 0} | _] = UD) -> + missing; %% Util is the integer zero when not supported... +%% UT2 =:= UT1 +hcpu(ActTo, TargetTo, [{UT, _}, {UT, _} | _] = UD) -> + hcpu(ActTo, TargetTo, tl(UD)); +%% UT2 > UT1 > ActTo > TargetTo +hcpu(ActTo, TargetTo, [{_UT2, _}, {UT1, _} | _] = UD) when UT1 > ActTo -> + hcpu(ActTo, TargetTo, tl(UD)); +%% UT2 >= ActTo > TargetTo >= UT1 +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _]) when UT2 >= ActTo, + TargetTo >= UT1 -> + case U >= (((ActTo - TargetTo) / (UT2 - UT1)) + * (?HIGH_UTIL/100.0)) of + true -> yes; + false -> no + end; +%% UT2 >= ActTo >= UT1 > TargetTo +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _] = UD) when UT2 >= ActTo, + ActTo >= UT1, + UT1 > TargetTo -> + case U >= (((ActTo - UT1) / (UT2 - UT1)) + * (?HIGH_UTIL/100.0)) of + true -> hcpu(ActTo, TargetTo, tl(UD)); + false -> no + end; +%% ActTo > UT2 >= TargetTo >= UT1 +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _]) when ActTo > UT2, + TargetTo >= UT1 -> + case U >= (((UT2 - TargetTo) / (UT2 - UT1)) + * (?HIGH_UTIL/100.0)) of + true -> yes; + false -> no + end; +%% ActTo > UT2 > UT1 > TargetTo +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _] = UD) when ActTo > UT2, + UT1 > TargetTo -> + case U >= ?HIGH_UTIL of + true -> hcpu(ActTo, TargetTo, tl(UD)); + false -> no + end. + type_str(receive_after) -> "receive ... after"; type_str(bif_timer) -> "BIF timer"; type_str(driver) -> "driver". @@ -142,24 +244,24 @@ unit_str(Res) -> Res. to_diff(Timeout, Start, Stop) -> %% 'Timeout' in milli seconds - %% 'Start', 'Stop', and result in native unit - (Stop - Start) - erlang:convert_time_unit(Timeout, milli_seconds, native). + %% 'Start', 'Stop', and result in micro seconds + (Stop - Start) - Timeout*1000. ms(Time) -> - erlang:convert_time_unit(Time, native, milli_seconds). + erlang:convert_time_unit(Time, microsecond, millisecond). max_late() -> - erlang:convert_time_unit(?MAX_LATE_MS, milli_seconds, native). + erlang:convert_time_unit(?MAX_LATE_MS, millisecond, microsecond). receive_after(Timeout) -> - Start = erlang:monotonic_time(), + Start = erlang:monotonic_time(microsecond), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), type = receive_after, timeout = Timeout} after Timeout -> - Stop = erlang:monotonic_time(), + Stop = erlang:monotonic_time(microsecond), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), @@ -174,7 +276,7 @@ receive_after(Timeout) -> driver(Timeout) -> Port = open_port({spawn, ?DRV_NAME},[]), link(Port), - Start = erlang:monotonic_time(), + Start = erlang:monotonic_time(microsecond), erlang:port_command(Port, <<?START_TIMER, Timeout:32>>), receive {get_result, ?REG_NAME} -> @@ -182,7 +284,7 @@ driver(Timeout) -> type = driver, timeout = Timeout}; {Port,{data,[?TIMER]}} -> - Stop = erlang:monotonic_time(), + Stop = erlang:monotonic_time(microsecond), unlink(Port), true = erlang:port_close(Port), receive @@ -197,7 +299,7 @@ driver(Timeout) -> end. bif_timer(Timeout) -> - Start = erlang:monotonic_time(), + Start = erlang:monotonic_time(microsecond), Tmr = erlang:start_timer(Timeout, self(), ok), receive {get_result, ?REG_NAME} -> @@ -205,7 +307,7 @@ bif_timer(Timeout) -> type = bif_timer, timeout = Timeout}; {timeout, Tmr, ok} -> - Stop = erlang:monotonic_time(), + Stop = erlang:monotonic_time(microsecond), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), @@ -218,13 +320,22 @@ bif_timer(Timeout) -> end. test(Starter, DrvDir, StartDone) -> + process_flag(priority, high), erl_ddll:start(), ok = load_driver(DrvDir, ?DRV_NAME), process_flag(trap_exit, true), register(?REG_NAME, self()), {group_leader, GL} = process_info(whereis(net_kernel),group_leader), group_leader(GL, self()), - Start = erlang:monotonic_time(), + try + application:start(sasl), + application:start(os_mon) + catch + _ : _ -> + ok + end, + UtilData = new_util(), + Start = erlang:monotonic_time(microsecond), TORs = lists:map(fun (Min) -> TO = Min*60*1000, [#timeout_rec{pid = spawn_opt( @@ -252,16 +363,27 @@ test(Starter, DrvDir, StartDone) -> lists:seq(1, ?MAX_TIMEOUT)), FlatTORs = lists:flatten(TORs), Starter ! StartDone, - test_loop(FlatTORs, Start). + test_loop(FlatTORs, Start, UtilData). + +new_util() -> + new_util([]). + +new_util(UtilData) -> + Util = cpu_sup:util(), + Time = erlang:monotonic_time(microsecond), + [{Time, Util} | UtilData]. -test_loop(TORs, Start) -> +test_loop(TORs, Start, UtilData) -> receive {get_result, ?REG_NAME, Pid} -> - End = erlang:monotonic_time(), - Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End}, + End = erlang:monotonic_time(microsecond), + EndUtilData = new_util(UtilData), + Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End, EndUtilData}, erl_ddll:unload_driver(?DRV_NAME), erl_ddll:stop(), exit(bye) + after ?UTIL_INTERVAL -> + test_loop(TORs, Start, new_util(UtilData)) end. get_test_results(TORs) -> diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index b3870f0313..5af676c409 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -77,6 +77,7 @@ t_ets/1, t_dets/1, t_tracing/1, + t_hash_entropy/1, %% instruction-level tests t_has_map_fields/1, @@ -140,6 +141,7 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large, t_pdict, t_ets, t_tracing, + t_hash_entropy, %% instruction-level tests t_has_map_fields, @@ -3020,6 +3022,39 @@ do_badmap_17(Config) -> id(I) -> I. +%% OTP-13763 +t_hash_entropy(Config) when is_list(Config) -> + %% entropy bug in 18.3, 19.0 + M1 = maps:from_list([{#{"id" => I}, ok}||I <- lists:seq(1,50000)]), + + #{ #{"id" => 100} := ok, + #{"id" => 200} := ok, + #{"id" => 300} := ok, + #{"id" => 400} := ok, + #{"id" => 500} := ok, + #{"id" => 600} := ok, + #{"id" => 700} := ok, + #{"id" => 800} := ok, + #{"id" => 900} := ok, + #{"id" => 25061} := ok, + #{"id" => 39766} := ok } = M1, + + M0 = maps:from_list([{I,ok}||I <- lists:seq(1,33)]), + M2 = maps:from_list([{M0#{"id" => I}, ok}||I <- lists:seq(1,50000)]), + + ok = maps:get(M0#{"id" => 100}, M2), + ok = maps:get(M0#{"id" => 200}, M2), + ok = maps:get(M0#{"id" => 300}, M2), + ok = maps:get(M0#{"id" => 400}, M2), + ok = maps:get(M0#{"id" => 500}, M2), + ok = maps:get(M0#{"id" => 600}, M2), + ok = maps:get(M0#{"id" => 700}, M2), + ok = maps:get(M0#{"id" => 800}, M2), + ok = maps:get(M0#{"id" => 900}, M2), + ok = maps:get(M0#{"id" => 25061}, M2), + ok = maps:get(M0#{"id" => 39766}, M2), + ok. + %% OTP-13146 %% Provoke major GC with a lot of "fat" maps on external format in msg queue %% causing heap fragments to be allocated. diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 44e77dfad0..e084b9482d 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -200,7 +200,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 90d2bd8c5d..827ed817cc 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -697,7 +697,7 @@ mixer(Config) when is_list(Config) -> named_down(Config) when is_list(Config) -> Name = list_to_atom(atom_to_list(?MODULE) ++ "-named_down-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Prio = process_flag(priority,high), %% Spawn a bunch of high prio cpu bound processes to prevent diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index 1493e52655..12928ed6d8 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -443,7 +443,7 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) -> {'DOWN', M, process, P, _} -> ok end end, Ps), - Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native), + Res = (Stop-Start)/erlang:convert_time_unit(1,second,native), Caller ! {?MODULE, self(), Res} end, TP = spawn_link(T), diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a0e9f1bad6..8df2733fac 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1443,6 +1443,17 @@ otp_9828_loop(Bin, Val) -> consume_timeslice(Config) when is_list(Config) -> + case {erlang:system_info(debug_compiled), + erlang:system_info(lock_checking)} of + {false, false} -> + consume_timeslice_test(Config); + {false, true} -> + {skipped, "Lock checking enabled"}; + _ -> + {skipped, "Debug compiled"} + end. + +consume_timeslice_test(Config) when is_list(Config) -> CONTEXT_REDS = 2000, Me = self(), Go = make_ref(), @@ -1692,7 +1703,7 @@ nif_raise_exceptions(NifFunc) -> end, ok, ExcTerms). -define(ERL_NIF_TIME_ERROR, -9223372036854775808). --define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]). +-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]). nif_monotonic_time(Config) -> ?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit), @@ -1758,8 +1769,8 @@ chk_toffs([TU|TUs]) -> chk_toffs(TUs). nif_convert_time_unit(Config) -> - ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit), - ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds), + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit), + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second), ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit), lists:foreach(fun (Offset) -> lists:foreach(fun (Diff) -> @@ -1808,7 +1819,7 @@ nif_convert_time_unit(Config) -> ctu_loop(0) -> ok; ctu_loop(N) -> - chk_ctu(erlang:monotonic_time(nano_seconds)), + chk_ctu(erlang:monotonic_time(nanosecond)), ctu_loop(N-1). chk_ctu(Time) -> @@ -1823,7 +1834,7 @@ chk_ctu(Time, [FromTU|FromTUs]) -> chk_ctu(_Time, _FromTU, []) -> ok; chk_ctu(Time, FromTU, [ToTU|ToTUs]) -> - T = erlang:convert_time_unit(Time, nano_seconds, FromTU), + T = erlang:convert_time_unit(Time, nanosecond, FromTU), TE = erlang:convert_time_unit(T, FromTU, ToTU), TN = convert_time_unit(T, FromTU, ToTU), case TE =:= TN of diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 13846244d4..f2b1ef9d24 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -38,10 +38,10 @@ static ERL_NIF_TERM atom_self; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_join; static ERL_NIF_TERM atom_binary_resource_type; -static ERL_NIF_TERM atom_seconds; -static ERL_NIF_TERM atom_milli_seconds; -static ERL_NIF_TERM atom_micro_seconds; -static ERL_NIF_TERM atom_nano_seconds; +static ERL_NIF_TERM atom_second; +static ERL_NIF_TERM atom_millisecond; +static ERL_NIF_TERM atom_microsecond; +static ERL_NIF_TERM atom_nanosecond; typedef struct @@ -147,10 +147,10 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_ok = enif_make_atom(env,"ok"); atom_join = enif_make_atom(env,"join"); atom_binary_resource_type = enif_make_atom(env,"binary_resource_type"); - atom_seconds = enif_make_atom(env,"seconds"); - atom_milli_seconds = enif_make_atom(env,"milli_seconds"); - atom_micro_seconds = enif_make_atom(env,"micro_seconds"); - atom_nano_seconds = enif_make_atom(env,"nano_seconds"); + atom_second = enif_make_atom(env,"second"); + atom_millisecond = enif_make_atom(env,"millisecond"); + atom_microsecond = enif_make_atom(env,"microsecond"); + atom_nanosecond = enif_make_atom(env,"nanosecond"); *priv_data = data; return 0; @@ -1808,13 +1808,13 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM if (argc != 1) return atom_false; - if (enif_compare(argv[0], atom_seconds) == 0) + if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; - else if (enif_compare(argv[0], atom_milli_seconds) == 0) + else if (enif_compare(argv[0], atom_millisecond) == 0) time_unit = ERL_NIF_MSEC; - else if (enif_compare(argv[0], atom_micro_seconds) == 0) + else if (enif_compare(argv[0], atom_microsecond) == 0) time_unit = ERL_NIF_USEC; - else if (enif_compare(argv[0], atom_nano_seconds) == 0) + else if (enif_compare(argv[0], atom_nanosecond) == 0) time_unit = ERL_NIF_NSEC; else time_unit = 4711; /* invalid time unit */ @@ -1829,13 +1829,13 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg if (argc != 1) return atom_false; - if (enif_compare(argv[0], atom_seconds) == 0) + if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; - else if (enif_compare(argv[0], atom_milli_seconds) == 0) + else if (enif_compare(argv[0], atom_millisecond) == 0) time_unit = ERL_NIF_MSEC; - else if (enif_compare(argv[0], atom_micro_seconds) == 0) + else if (enif_compare(argv[0], atom_microsecond) == 0) time_unit = ERL_NIF_USEC; - else if (enif_compare(argv[0], atom_nano_seconds) == 0) + else if (enif_compare(argv[0], atom_nanosecond) == 0) time_unit = ERL_NIF_NSEC; else time_unit = 4711; /* invalid time unit */ @@ -1856,24 +1856,24 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE val = (ErlNifTime) i64; - if (enif_compare(argv[1], atom_seconds) == 0) + if (enif_compare(argv[1], atom_second) == 0) from = ERL_NIF_SEC; - else if (enif_compare(argv[1], atom_milli_seconds) == 0) + else if (enif_compare(argv[1], atom_millisecond) == 0) from = ERL_NIF_MSEC; - else if (enif_compare(argv[1], atom_micro_seconds) == 0) + else if (enif_compare(argv[1], atom_microsecond) == 0) from = ERL_NIF_USEC; - else if (enif_compare(argv[1], atom_nano_seconds) == 0) + else if (enif_compare(argv[1], atom_nanosecond) == 0) from = ERL_NIF_NSEC; else from = 4711; /* invalid time unit */ - if (enif_compare(argv[2], atom_seconds) == 0) + if (enif_compare(argv[2], atom_second) == 0) to = ERL_NIF_SEC; - else if (enif_compare(argv[2], atom_milli_seconds) == 0) + else if (enif_compare(argv[2], atom_millisecond) == 0) to = ERL_NIF_MSEC; - else if (enif_compare(argv[2], atom_micro_seconds) == 0) + else if (enif_compare(argv[2], atom_microsecond) == 0) to = ERL_NIF_USEC; - else if (enif_compare(argv[2], atom_nano_seconds) == 0) + else if (enif_compare(argv[2], atom_nanosecond) == 0) to = ERL_NIF_NSEC; else to = 4711; /* invalid time unit */ diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 536c91d4ae..af18545bff 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -1092,7 +1092,7 @@ wait_until(Pred) -> get_nodefirstname_string() -> atom_to_list(?MODULE) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])). diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index f91d84beea..ffe7d40139 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -309,13 +309,13 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) -> %% uncomment lines below to get life sign (debug) receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) -> - % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds), + % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond), % erlang:display({round(T/1000),P1Rs,P2Rs}), receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000); receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) -> Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0, - native, milli_seconds), % test time remaining + native, millisecond), % test time remaining Remain1 = if Remain < 0 -> 0; true -> diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index ee07699884..4323849465 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -83,6 +83,7 @@ bad_port_messages/1, basic_ping/1, cd/1, + cd_relative/1, close_deaf_port/1, count_fds/1, dying_port/1, @@ -91,6 +92,7 @@ exit_status/1, exit_status_multi_scheduling_block/1, huge_env/1, + pipe_limit_env/1, input_only/1, iter_max_ports/1, line/1, @@ -102,6 +104,7 @@ mon_port_name_demonitor/1, mon_port_named/1, mon_port_origin_dies/1, + mon_port_owner_dies/1, mon_port_pid_demonitor/1, mon_port_remote_on_remote/1, mon_port_driver_die/1, @@ -137,7 +140,7 @@ win_massive_client/1 ]). --export([do_iter_max_ports/2]). +-export([do_iter_max_ports/2, relative_cd/0]). %% Internal exports. -export([tps/3]). @@ -158,7 +161,7 @@ all() -> {group, multiple_packets}, parallell, dying_port, port_program_with_path, open_input_file_port, open_output_file_port, name1, env, huge_env, bad_env, cd, - bad_args, + cd_relative, pipe_limit_env, bad_args, exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line, stderr_to_stdout, otp_3906, otp_4389, win_massive, mix_up_ports, otp_5112, otp_5119, @@ -171,6 +174,7 @@ all() -> mon_port_remote_on_remote, mon_port_bad_remote_on_local, mon_port_origin_dies, + mon_port_owner_dies, mon_port_named, mon_port_bad_named, mon_port_pid_demonitor, @@ -1002,6 +1006,55 @@ huge_env(Config) when is_list(Config) -> ct:fail("Open port failed ~p:~p",[E,R]) end. +%% Test to spawn program with command payload buffer +%% just around pipe capacity (9f779819f6bda734c5953468f7798) +pipe_limit_env(Config) when is_list(Config) -> + Cmd = "true", + CmdSize = command_payload_size(Cmd), + Limits = [4096, 16384, 65536], % Try a couple of common pipe buffer sizes + + lists:foreach(fun(Lim) -> + lists:foreach(fun(L) -> pipe_limit_env_do(L, Cmd, CmdSize) + end, lists:seq(Lim-5, Lim+5)) + end, Limits), + ok. + +pipe_limit_env_do(Bytes, Cmd, CmdSize) -> + case env_of_bytes(Bytes-CmdSize) of + [] -> skip; + Env -> + try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of + P -> + receive + {P, {exit_status,N}} = M -> + %% Bug caused exit_status 150 (EINVAL+128) + 0 = N + end + catch E:R -> + %% Have to catch the error here, as printing the stackdump + %% in the ct log is way to heavy for some test machines. + ct:fail("Open port failed ~p:~p",[E,R]) + end + end. + +%% environ format: KEY=VALUE\0 +env_of_bytes(Bytes) when Bytes > 3 -> + Env = [{"X",lists:duplicate(Bytes-3, $x)}]; +env_of_bytes(_) -> []. + +%% White box assumption about payload written to pipe +%% for Cmd and current environment (see spawn_start in sys_driver.c) +command_payload_size(Cmd) -> + EnvSize = lists:foldl(fun(E,Acc) -> length(E) + 1 + Acc end, + 0, os:getenv()), + {ok, PWD} = file:get_cwd(), + (4 % buffsz + + 4 % flags + + 5 + length(Cmd) + 1 % "exec $Cmd" + + length(PWD) + 1 % $PWD + + 1 % nullbuff + + 4 % env_len + + EnvSize). %% Test bad 'args' options. bad_args(Config) when is_list(Config) -> @@ -1036,8 +1089,7 @@ cd(Config) when is_list(Config) -> Cmd = Program ++ " -pz " ++ DataDir ++ " -noshell -s port_test pwd -s erlang halt", _ = open_port({spawn, Cmd}, - [{cd, TestDir}, - {line, 256}]), + [{cd, TestDir}, {line, 256}]), receive {_, {data, {eol, String}}} -> case filename_equal(String, TestDir) of @@ -1063,7 +1115,74 @@ cd(Config) when is_list(Config) -> Other3 -> ct:fail({env, Other3}) end, - ok. + + InvalidDir = filename:join(DataDir, "invaliddir"), + try open_port({spawn, Cmd}, + [{cd, InvalidDir}, exit_status, {line, 256}]) of + _ -> + receive + {_, {exit_status, _}} -> + ok; + Other4 -> + ct:fail({env, Other4}) + end + catch error:eacces -> + %% This happens on Windows + ok + end, + + %% Check that there are no lingering messages + receive + Other5 -> + ct:fail({env, Other5}) + after 10 -> + ok + end. + +%% Test that an emulator that has set it's cwd to +%% something other then when it started, can use +%% relative {cd,"./"} to open port and that cd will +%% be relative the new cwd and not the original +cd_relative(Config) -> + + Program = atom_to_list(lib:progname()), + DataDir = proplists:get_value(data_dir, Config), + TestDir = filename:join(DataDir, "dir"), + + Cmd = Program ++ " -pz " ++ filename:dirname(code:where_is_file("port_SUITE.beam")) ++ + " -noshell -s port_SUITE relative_cd -s erlang halt", + + _ = open_port({spawn, Cmd}, [{line, 256}, {cd, TestDir}]), + + receive + {_, {data, {eol, String}}} -> + case filename_equal(String, TestDir) of + true -> + ok; + false -> + ct:fail({cd_relative, String}) + end; + Other -> + ct:fail(Other) + end. + +relative_cd() -> + + Program = atom_to_list(lib:progname()), + ok = file:set_cwd(".."), + {ok, Cwd} = file:get_cwd(), + + Cmd = Program ++ " -pz " ++ Cwd ++ + " -noshell -s port_test pwd -s erlang halt", + + _ = open_port({spawn, Cmd}, [{line, 256}, {cd, "./dir"}, exit_status]), + + receive + {_, {data, {eol, String}}} -> + io:format("~s~n",[String]); + Other -> + io:format("ERROR: ~p~n",[Other]) + end. filename_equal(A, B) -> case os:type() of @@ -1867,7 +1986,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> Parent = self(), io:format("SleepSecs = ~p~n", [SleepSecs]), PortProg = "sleep " ++ integer_to_list(SleepSecs), - Start = erlang:monotonic_time(micro_seconds), + Start = erlang:monotonic_time(microsecond), NoProcs = case NoSchedsOnln of NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS -> NProcs; @@ -1941,12 +2060,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> receive {P, started, SIds} -> SIds end end, Procs), - StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, + StartedTime = (erlang:monotonic_time(microsecond) - Start)/1000000, io:format("StartedTime = ~p~n", [StartedTime]), true = StartedTime < SleepSecs, erlang:system_flag(multi_scheduling, block), lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs), - DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, + DoneTime = (erlang:monotonic_time(microsecond) - Start)/1000000, io:format("DoneTime = ~p~n", [DoneTime]), true = DoneTime > SleepSecs, ok = verify_multi_scheduling_blocked(), @@ -2520,6 +2639,29 @@ mon_port_origin_dies(Config) -> Port5 ! {self(), {command, <<"1">>}}, % make port quit ok. +%% Port and Monitor owner dies before port is closed +%% This testcase checks for a regression memory leak in erts +%% when the controlling and monitoring process is the same process +%% and the process dies +mon_port_owner_dies(Config) -> + Self = self(), + Proc = spawn(fun() -> + Port = create_port(Config, ["-h1", "-q"]), + Self ! {test_started, Port}, + erlang:monitor(port, Port), + receive stop -> ok end + end), + erlang:monitor(process, Proc), % we want to sync with its death + Port = receive {test_started,P} -> P + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(Proc, Port)), + Proc ! stop, + %% receive from monitor + receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc, _}, ExitP5) + after 1000 -> ?assert(false) end, + ok. + %% Monitor a named port mon_port_named(Config) -> Name6 = test_port6, diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index dae8990f56..0f999e0efe 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -372,7 +372,7 @@ eat_high(Low) -> process_flag(priority, high), receive after 1000 -> ok end, exit(Low, {you, are, dead}), - loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)). + loop(erlang:monotonic_time() + erlang:convert_time_unit(5,second,native)). %% Busy loop for 5 seconds. @@ -2376,7 +2376,7 @@ no_priority_inversion2(Config) when is_list(Config) -> [{priority, max}, monitor, link]) end, lists:seq(1, 2*erlang:system_info(schedulers))), - receive after 500 -> ok end, + receive after 2000 -> ok end, {PL, ML} = spawn_opt(fun () -> tok_loop() end, @@ -2567,7 +2567,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index f18d79d770..3aee15a8fc 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -793,13 +793,13 @@ update_cpu_info(Config) when is_list(Config) -> io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [OldAff, OldOnline, erlang:system_info(scheduler_bindings)]), case {erlang:system_info(logical_processors_available), OldAff} of - {Avail, _} when Avail == unknown; OldAff == unknown -> + {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 -> %% Nothing much to test; just a smoke test case erlang:system_info(update_cpu_info) of unchanged -> ok; changed -> ok end; - _ -> + {Avail, _} -> try adjust_schedulers_online(), case erlang:system_info(schedulers_online) of @@ -810,7 +810,7 @@ update_cpu_info(Config) when is_list(Config) -> %% unset least significant bit Aff = (OldAff band (OldAff - 1)), set_affinity_mask(Aff), - Onln1 = Onln0 - 1, + Onln1 = Avail - 1, case adjust_schedulers_online() of {Onln0, Onln1} -> Onln1 = erlang:system_info(schedulers_online), @@ -1072,20 +1072,27 @@ scheduler_threads(Config) when is_list(Config) -> {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"), %% Configure 2x scheduler threads only {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"), - %% Test resetting the scheduler counts - ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", - {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd), - %% Test negative +S settings, but only for SMP-enabled emulators - case SmpSupport of - false -> ok; - true -> - SchedMinus1 = Sched-1, - SchedOnlnMinus1 = SchedOnln-1, - {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), - {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), - {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1") - end, - ok. + case {erlang:system_info(logical_processors), + erlang:system_info(logical_processors_available)} of + {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) -> + %% Test resetting the scheduler counts + ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", + {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd), + %% Test negative +S settings, but only for SMP-enabled emulators + case {SmpSupport, LProc > 1, LProcAvail > 1} of + {true, true, true} -> + SchedMinus1 = LProc-1, + SchedOnlnMinus1 = LProcAvail-1, + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), + {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"), + ok; + _ -> + {comment, "Skipped reduced amount of schedulers test due to too few logical processors"} + end; + _ -> %% Skipped when missing info about logical processors... + {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"} + end. dirty_scheduler_threads(Config) when is_list(Config) -> SmpSupport = erlang:system_info(smp_support), @@ -1312,11 +1319,33 @@ scheduler_suspend_test(Config, Schedulers) -> true -> ok end, - erlang:system_info(schedulers_state) + until(fun () -> + {_A, B, C} = erlang:system_info( + schedulers_state), + B == C + end, + erlang:monotonic_time() + + erlang:convert_time_unit(1, + seconds, + native)), + erlang:system_info(schedulers_state) end]), stop_node(Node), ok. - + +until(Pred, MaxTime) -> + case Pred() of + true -> + true; + false -> + case erlang:monotonic_time() > MaxTime of + true -> + false; + false -> + receive after 100 -> ok end, + until(Pred, MaxTime) + end + end. sst0_loop(0) -> ok; @@ -1978,10 +2007,10 @@ do_it(Tracer, Low, Normal, High, Max) -> do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) -> OldPrio = process_flag(priority, max), go_work(Low, Normal, High, Max), - StartWait = erlang:monotonic_time(milli_seconds), + StartWait = erlang:monotonic_time(millisecond), %% Give the emulator a chance to balance the load... wait_balance(5), - EndWait = erlang:monotonic_time(milli_seconds), + EndWait = erlang:monotonic_time(millisecond), BalanceWait = EndWait-StartWait, erlang:display({balance_wait, BalanceWait}), Timeout = (15 - 4)*60*1000 - BalanceWait, @@ -2181,7 +2210,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 0b11fa13f5..7e516176f7 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -484,7 +484,7 @@ repeat(Fun, N) when is_integer(N) -> start_node(Config) -> Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) - ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Pa = filename:dirname(code:which(?MODULE)), test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]). diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 042c7225d5..5eccdc562b 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -152,7 +152,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Opts = [{args, "-pa "++Pa++" "++Args}], diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 71ef003b25..a1f12ba93c 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -129,11 +129,15 @@ do_runtime_update(0) -> {comment,"Never close enough"}; do_runtime_update(N) -> {T1,Diff0} = statistics(runtime), - spawn_link(fun cpu_heavy/0), + {CPUHog, CPUHogMon} = spawn_opt(fun cpu_heavy/0,[link,monitor]), receive after 1000 -> ok end, {T2,Diff} = statistics(runtime), + unlink(CPUHog), + exit(CPUHog, kill), + true = is_integer(T1+T2+Diff0+Diff), io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]), + receive {'DOWN',CPUHogMon,process,CPUHog,_} -> ok end, if T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok; true -> do_runtime_update(N-1) @@ -311,8 +315,17 @@ scheduler_wall_time(Config) when is_list(Config) -> true -> exit({fullload, FullLoad}) end, - [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]], + KillHog = fun (HP) -> + HPM = erlang:monitor(process, HP), + exit(HP, kill), + receive + {'DOWN', HPM, process, HP, killed} -> + ok + end + end, + [KillHog(Pid) || Pid <- [P1|HalfHogs++LastHogs]], AfterLoad = get_load(), + io:format("AfterLoad=~p~n", [AfterLoad]), {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad}, true = erlang:system_flag(scheduler_wall_time, false) after diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index f31d474c20..a4aedb31f6 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -508,7 +508,7 @@ start_node(Config, Envs) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 87b8c62cfa..9501569814 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -295,7 +295,7 @@ timestamp(Config) when is_list(Config) -> os_system_time_offset() -> erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(), - native, micro_seconds). + native, microsecond). had_time_warp(Secs) -> had_time_warp(os_system_time_offset(), Secs). @@ -488,12 +488,12 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) -> MonotonicTimeUnit = rpc:call(Node, erlang, convert_time_unit, - [1, seconds, native]), + [1, second, native]), UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime, MonotonicTimeUnit, - milli_seconds), + millisecond), io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]), - End = erlang:monotonic_time(milli_seconds), + End = erlang:monotonic_time(millisecond), stop_node(Node), try true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100), @@ -810,10 +810,10 @@ do_check_erlang_timestamp(Done, Mon, TO) -> MaxMon = erlang:monotonic_time(), TsMin = erlang:convert_time_unit(MinMon+TO, native, - micro_seconds), + microsecond), TsMax = erlang:convert_time_unit(MaxMon+TO, native, - micro_seconds), + microsecond), TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec, case (TsMin =< TsTime) andalso (TsTime =< TsMax) of true -> diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index a5f11bd959..7cbd93a0f3 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -74,7 +74,7 @@ all() -> %% Basic start_timer/3 functionality start_timer_1(Config) when is_list(Config) -> Ref1 = erlang:start_timer(1000, self(), plopp), - ok = get(1100, {timeout, Ref1, plopp}), + ok = get(1400, {timeout, Ref1, plopp}), false = erlang:read_timer(Ref1), false = erlang:cancel_timer(Ref1), @@ -83,12 +83,12 @@ start_timer_1(Config) when is_list(Config) -> Ref2 = erlang:start_timer(1000, self(), plapp), Left2 = erlang:cancel_timer(Ref2), UpperLimit = 1000, - true = (Left2 > 900) and (Left2 =< UpperLimit), + true = (Left2 > 600) and (Left2 =< UpperLimit), empty = get_msg(), false = erlang:cancel_timer(Ref2), Ref3 = erlang:start_timer(1000, self(), plopp), - no_message = get(900, {timeout, Ref3, plopp}), + no_message = get(600, {timeout, Ref3, plopp}), ok. %% Basic send_after/3 functionality @@ -489,7 +489,7 @@ registered_process(Config) when is_list(Config) -> same_time_yielding(Config) when is_list(Config) -> Mem = mem(), SchdlrsOnln = erlang:system_info(schedulers_online), - Tmo = erlang:monotonic_time(milli_seconds) + 3000, + Tmo = erlang:monotonic_time(millisecond) + 3000, Tmrs = lists:map(fun (I) -> process_flag(scheduler, (I rem SchdlrsOnln) + 1), erlang:start_timer(Tmo, self(), hej, [{abs, true}]) @@ -497,7 +497,7 @@ same_time_yielding(Config) when is_list(Config) -> lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), true = mem_larger_than(Mem), lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs), - Done = erlang:monotonic_time(milli_seconds), + Done = erlang:monotonic_time(millisecond), true = Done >= Tmo, case erlang:system_info(build_type) of opt -> true = Done < Tmo + 200; @@ -517,10 +517,10 @@ same_time_yielding_with_cancel_other(Config) when is_list(Config) -> do_cancel_tmrs(Tmo, Tmrs, Tester) -> BeginCancel = erlang:convert_time_unit(Tmo, - milli_seconds, - micro_seconds) - 100, + millisecond, + microsecond) - 100, busy_wait_until(fun () -> - erlang:monotonic_time(micro_seconds) >= BeginCancel + erlang:monotonic_time(microsecond) >= BeginCancel end), lists:foreach(fun (Tmr) -> erlang:cancel_timer(Tmr, @@ -535,7 +535,7 @@ do_cancel_tmrs(Tmo, Tmrs, Tester) -> same_time_yielding_with_cancel_test(Other, Accessor) -> Mem = mem(), SchdlrsOnln = erlang:system_info(schedulers_online), - Tmo = erlang:monotonic_time(milli_seconds) + 3000, + Tmo = erlang:monotonic_time(millisecond) + 3000, Tester = self(), Cancelor = case Other of false -> @@ -656,7 +656,7 @@ get_msg() -> start_slave() -> Pa = filename:dirname(code:which(?MODULE)), Name = atom_to_list(?MODULE) - ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])), {ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]), Node. diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 491b37ae46..f60c777ba1 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -289,9 +289,9 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsTy make_ts(timestamp) -> erlang:now(); make_ts(monotonic_timestamp) -> - erlang:monotonic_time(nano_seconds); + erlang:monotonic_time(nanosecond); make_ts(strict_monotonic_timestamp) -> - MT = erlang:monotonic_time(nano_seconds), + MT = erlang:monotonic_time(nanosecond), UMI = erlang:unique_integer([monotonic]), {MT, UMI}. diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 6582ad134b..26f96a1766 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -294,7 +294,7 @@ combo(Config) when is_list(Config) -> T0 = erlang:monotonic_time(), with_bif(Nbc), T1 = erlang:monotonic_time(), - TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds), + TimeB = erlang:convert_time_unit(T1-T0, native, microsecond), %% List = collect(100), @@ -655,13 +655,13 @@ execute(Pids, Mfa) when is_list(Pids) -> [P ! {self(), execute, Mfa} || P <- Pids], As = [receive {P, answer, Answer} -> Answer end || P <- Pids], T1 = erlang:monotonic_time(), - {As, erlang:convert_time_unit(T1-T0, native, micro_seconds)}; + {As, erlang:convert_time_unit(T1-T0, native, microsecond)}; execute(P, Mfa) -> T0 = erlang:monotonic_time(), P ! {self(), execute, Mfa}, A = receive {P, answer, Answer} -> Answer end, T1 = erlang:monotonic_time(), - {A, erlang:convert_time_unit(T1-T0, native, micro_seconds)}. + {A, erlang:convert_time_unit(T1-T0, native, microsecond)}. diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 74c05f24e0..c297acd78b 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -439,6 +439,14 @@ return_test() -> ?RT(?MODULE,slave,2), shutdown(), ?NM, + + %% Test a regression where turning off return_to tracing + %% on yourself would cause a segfault. + Pid = setup([call,return_to]), + erlang:trace_pattern({'_','_','_'},[],[local]), + apply_slave(erlang,trace,[Pid, false, [all]]), + shutdown(), + ok. on_and_off_test() -> diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index d1085c1958..ab56018373 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -191,7 +191,13 @@ node_container_refc_check(Config) when is_list(Config) -> ok. long_timers(Config) when is_list(Config) -> - ok = long_timers_test:check_result(). + case long_timers_test:check_result() of + ok -> ok; + high_cpu -> {comment, "Ignored failures due to high CPU utilization"}; + missing_cpu_info -> {comment, "Ignored failures due to missing CPU utilization information"}; + Fail -> ct:fail(Fail) + end. + pollset_size(Config) when is_list(Config) -> Name = pollset_size_testcase_initial_state_holder, diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 15fb718c47..e2bf302cca 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3581,9 +3581,24 @@ document etp-block %--------------------------------------------------------------------------- end +define etp-smp-atomic + if (etp_smp_compiled) + set $arg1 = (($arg0).counter) + else + set $arg1 = ($arg0) + end +end + +document etp-smp-atomic +%--------------------------------------------------------------------------- +% Read an erts_smp_atomic_t value from $arg0 into $arg1 +%--------------------------------------------------------------------------- +end + define etp-carrier-blocks set $etp_crr = (Carrier_t*) $arg0 - set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7) + etp-smp-atomic $etp_crr->allctr $etp_alc + set $etp_alc = (Allctr_t*)($etp_alc & ~7) set $etp_crr_end = ((char*)$etp_crr + ($etp_crr->chdr & ~7) - (sizeof(void*) & ~8)) set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size) set $etp_prev_blk = 0 diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages index 54f2d90c28..0afff13571 100644 --- a/erts/etc/unix/format_man_pages +++ b/erts/etc/unix/format_man_pages @@ -107,7 +107,7 @@ case :"$TARGET" in if [ -f $file ]; then name=`echo $file | sed 's/\.[^.]*$//'` sec=`echo $file | sed 's/.*\.//'` - /usr/bin/groff -Tascii -mandoc $ERL_ROOT/man/man$sec/$file \ + /usr/bin/groff -Tutf8 -mandoc $ERL_ROOT/man/man$sec/$file \ > $ERL_ROOT/man/cat$sec/$file fi done diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 30210ac172..6a92e18648 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -240,6 +240,7 @@ int main(int argc, char **argv) int off_argv; int calculated_pipename = 0; int highest_pipe_num = 0; + int sleepy_child = 0; program_name = argv[0]; @@ -250,6 +251,11 @@ int main(int argc, char **argv) init_outbuf(); + if (!strcmp(argv[1],"-sleepy-child")) { /* For test purpose only */ + sleepy_child = 1; + ++i; + } + if (!strcmp(argv[1],"-daemon")) { daemon_init(); ++i; @@ -392,6 +398,9 @@ int main(int argc, char **argv) exit(1); } if (childpid == 0) { + if (sleepy_child) + sleep(1); + /* Child */ sf_close(mfd); /* disassociate from control terminal */ @@ -904,20 +913,32 @@ static int create_fifo(char *name, int perm) * Find a master device, open and return fd and slave device name. */ +#ifdef HAVE_WORKING_POSIX_OPENPT + /* + * Use openpty() on OpenBSD even if we have posix_openpt() + * as there is a race when read from master pty returns 0 + * if child has not yet opened slave pty. + * (maybe other BSD's have the same problem?) + */ +# if !(defined(__OpenBSD__) && defined(HAVE_OPENPTY)) +# define TRY_POSIX_OPENPT +# endif +#endif + static int open_pty_master(char **ptyslave, int *sfdp) { int mfd; /* Use the posix_openpt if working, as this guarantees creation of the slave device properly. */ -#if defined(HAVE_WORKING_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4)) -# ifdef HAVE_WORKING_POSIX_OPENPT - if ((mfd = posix_openpt(O_RDWR)) >= 0) { +#if defined(TRY_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4)) +# ifdef TRY_POSIX_OPENPT + mfd = posix_openpt(O_RDWR); # elif defined(__sun) && defined(__SVR4) mfd = sf_open("/dev/ptmx", O_RDWR, 0); +# endif if (mfd >= 0) { -# endif if ((*ptyslave = ptsname(mfd)) != NULL && grantpt(mfd) == 0 && unlockpt(mfd) == 0) { diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl index b87c6cc550..589781c8e8 100644 --- a/erts/example/time_compat.erl +++ b/erts/example/time_compat.erl @@ -272,6 +272,10 @@ system_flag(Flag, Value) -> %% integer_time_unit(native) -> 1000*1000; +integer_time_unit(nanosecond) -> 1000*1000*1000; +integer_time_unit(microsecond) -> 1000*1000; +integer_time_unit(millisecond) -> 1000; +integer_time_unit(second) -> 1; integer_time_unit(nano_seconds) -> 1000*1000*1000; integer_time_unit(micro_seconds) -> 1000*1000; integer_time_unit(milli_seconds) -> 1000; diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h index a4a5d1d510..55566ddf74 100644 --- a/erts/include/internal/erl_misc_utils.h +++ b/erts/include/internal/erl_misc_utils.h @@ -56,4 +56,33 @@ int erts_map_win_error_to_errno(DWORD win_error); int erts_get_last_win_errno(void); #endif +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + +/* + * ERTS_PREMATURE_TIMEOUT() expects time units + * 1000 (millisec), 1000000 (microsec), or + * 1000000000 (nanosec). Might not work properly + * otherwise. + */ +#undef ERTS_USE_PREMATURE_TIMEOUT +#undef ERTS_PREMATURE_TIMEOUT + +#if defined(__DARWIN__) +#define ERTS_USE_PREMATURE_TIMEOUT 1 +#define ERTS_PREMATURE_TIMEOUT(TMO, TU) \ + ((TMO) >= 1 * ((TU) / 1000) \ + ? ((TMO) >= 20 * ((TU) / 1000) \ + ? 15 * ((TU) / 1000) \ + : ((TMO) >= 5 * ((TU) / 1000) \ + ? 3 * ((TU) / 1000) \ + : 5 * ((TU) / 10000))) \ + : 0) + +#else +#define ERTS_USE_PREMATURE_TIMEOUT 0 +#define ERTS_PREMATURE_TIMEOUT(TMO, TU) (0) +#endif + #endif /* #ifndef ERL_MISC_UTILS_H_ */ diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index eef88d5002..464875570a 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -184,6 +184,8 @@ return_event_on: #include <errno.h> #include <string.h> +#include "erl_misc_utils.h" + #ifdef __DARWIN__ struct ethr_event_fdsets___ { @@ -346,12 +348,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) ethr_sint32_t val; int res, ulres; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */ + ethr_sint64_t time = 0; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */ -#endif -#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC - struct timespec cond_timeout; + ethr_sint64_t timeout_time = 0; /* SHUT UP annoying faulty warning... */ #endif val = ethr_atomic32_read(&e->state); @@ -365,30 +364,27 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) if (timeout == 0) return ETIMEDOUT; else { - time = timeout; - switch (e->fd[0]) { - case ETHR_EVENT_INVALID_FD__: #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); + timeout_time = ethr_get_monotonic_time(); + timeout_time += timeout; #endif + switch (e->fd[0]) { + case ETHR_EVENT_INVALID_FD__: + time = timeout; setup_nonblocking_pipe(e); break; #ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC case ETHR_EVENT_COND_TIMEDWAIT__: - time += ethr_get_monotonic_time(); - cond_timeout.tv_sec = time / (1000*1000*1000); - cond_timeout.tv_nsec = time % (1000*1000*1000); + time = -1; if (spincount == 0) goto set_event_off_waiter; break; #endif default: + time = timeout; /* Already initialized pipe... */ if (spincount == 0) goto set_select_timeout; -#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); -#endif break; } } @@ -418,6 +414,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) || e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__ #endif ) { +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + struct timespec cond_timeout; +#endif set_event_off_waiter: @@ -446,9 +445,35 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) #ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC if (timeout > 0) { + if (time != timeout_time) { + time = timeout_time; + +#if ERTS_USE_PREMATURE_TIMEOUT + { + ethr_sint64_t rtmo; + + rtmo = timeout_time - ethr_get_monotonic_time(); + if (rtmo <= 0) { + res = ETIMEDOUT; + break; + } + time = timeout_time; + time -= ERTS_PREMATURE_TIMEOUT(rtmo, 1000*1000*1000); + } +#endif + + cond_timeout.tv_sec = time / (1000*1000*1000); + cond_timeout.tv_nsec = time % (1000*1000*1000); + } res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout); - if (res == EINTR || res == ETIMEDOUT) + if (res == EINTR + || (res == ETIMEDOUT +#if ERTS_USE_PREMATURE_TIMEOUT + && time == timeout_time +#endif + )) { break; + } } else #endif @@ -477,7 +502,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) struct timeval select_timeout; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - time -= ethr_get_monotonic_time() - start; +#if ERTS_USE_PREMATURE_TIMEOUT + restart_select: +#endif + + time = timeout_time - ethr_get_monotonic_time(); if (time <= 0) return ETIMEDOUT; #endif @@ -492,6 +521,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) */ time = ((time - 1) / 1000) + 1; +#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \ + && ERTS_USE_PREMATURE_TIMEOUT + time -= ERTS_PREMATURE_TIMEOUT(time, 1000*1000); +#endif + select_timeout.tv_sec = time / (1000*1000); select_timeout.tv_usec = time % (1000*1000); @@ -559,7 +593,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) goto return_event_on; - +#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \ + && ERTS_USE_PREMATURE_TIMEOUT + if (res == ETIMEDOUT) + goto restart_select; /* Verify timeout */ +#endif } return res; diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex b62da04bfd..c68debeabc 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex a0da864824..a1eb126098 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam Binary files differnew file mode 100644 index 0000000000..a7ac116c05 --- /dev/null +++ b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex d897c8e92f..22817be8f4 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differnew file mode 100644 index 0000000000..71f3c2ec8c --- /dev/null +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex b856bff4fe..849273f746 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex fba03d52bd..ae4861c336 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 4a447d3a09..2ab9edaf5e 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -44,7 +44,9 @@ PRE_LOADED_ERL_MODULES = \ erts_code_purger \ erlang \ erts_internal \ - erl_tracer + erl_tracer \ + erts_literal_area_collector \ + erts_dirty_process_code_checker PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index edf79b8f75..652a954807 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -59,6 +59,7 @@ -export_type([timestamp/0]). -export_type([time_unit/0]). +-export_type([deprecated_time_unit/0]). -type ext_binary() :: binary(). -type timestamp() :: {MegaSecs :: non_neg_integer(), @@ -67,12 +68,20 @@ -type time_unit() :: pos_integer() - | 'seconds' + | 'second' + | 'millisecond' + | 'microsecond' + | 'nanosecond' + | 'native' + | 'perf_counter' + | deprecated_time_unit(). + +%% Deprecated symbolic units... +-type deprecated_time_unit() :: + 'seconds' | 'milli_seconds' | 'micro_seconds' - | 'nano_seconds' - | 'native' - | 'perf_counter'. + | 'nano_seconds'. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Native code BIF stubs and their types @@ -1365,19 +1374,33 @@ convert_time_unit(Time, FromUnit, ToUnit) -> FU = case FromUnit of native -> erts_internal:time_unit(); perf_counter -> erts_internal:perf_counter_unit(); + nanosecond -> 1000*1000*1000; + microsecond -> 1000*1000; + millisecond -> 1000; + second -> 1; + + %% Deprecated symbolic units... nano_seconds -> 1000*1000*1000; micro_seconds -> 1000*1000; milli_seconds -> 1000; seconds -> 1; + _ when FromUnit > 0 -> FromUnit end, TU = case ToUnit of native -> erts_internal:time_unit(); perf_counter -> erts_internal:perf_counter_unit(); + nanosecond -> 1000*1000*1000; + microsecond -> 1000*1000; + millisecond -> 1000; + second -> 1; + + %% Deprecated symbolic units... nano_seconds -> 1000*1000*1000; micro_seconds -> 1000*1000; milli_seconds -> 1000; seconds -> 1; + _ when ToUnit > 0 -> ToUnit end, case Time < 0 of diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index e18da28905..7ab06164b4 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -37,7 +37,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0"]} + {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0.1"]} ]}. %% vim: ft=erlang diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index d1e64342e0..ee4fcedd2d 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -22,7 +22,7 @@ %% Purpose : Implement system process erts_code_purger %% to handle code module purging. --export([start/0, purge/1, soft_purge/1]). +-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3]). -spec start() -> term(). start() -> @@ -40,10 +40,43 @@ loop() -> Res = do_soft_purge(Mod), From ! {reply, soft_purge, Res, Ref}; + {test_purge, Mod, From, Type, Ref} when is_atom(Mod), is_pid(From) -> + do_test_purge(Mod, From, Type, Ref); + _Other -> ignore end, loop(). +%% +%% Processes that tries to call a fun that belongs to +%% a module that currently is being purged will end +%% up here (pending_purge_lambda) in a suspended state. +%% When the purge operation completes or aborts (soft +%% purge that failed) these processes will be resumed. +%% +pending_purge_lambda(_Module, Fun, Args) -> + %% + %% When the process is resumed, the following + %% scenarios exist: + %% * The code that the fun refers to is still + %% there due to a failed soft purge. The + %% call to the fun will succeed via apply/2. + %% * The code was purged, and a current version + %% of the module is loaded which does not + %% contain this fun. The call will result + %% in an exception being raised. + %% * The code was purged, and no current + %% version of the module is loaded. An attempt + %% to load the module (via the error_handler) + %% will be made. This may or may not succeed. + %% If the module is loaded, it may or may + %% not contain the fun. The call will + %% succeed if the error_handler was able + %% to load the module and loaded module + %% contains this fun; otherwise, an exception + %% will be raised. + %% + apply(Fun, Args). %% purge(Module) %% Kill all processes running code from *old* Module, and then purge the @@ -60,16 +93,14 @@ purge(Mod) when is_atom(Mod) -> Result end. - do_purge(Mod) -> - case erts_internal:copy_literals(Mod, true) of - false -> - {false, false}; - true -> - DidKill = check_proc_code(erlang:processes(), Mod, true), - true = erts_internal:copy_literals(Mod, false), - WasPurged = erts_internal:purge_module(Mod), - {WasPurged, DidKill} + case erts_internal:purge_module(Mod, prepare) of + false -> + {false, false}; + true -> + DidKill = check_proc_code(erlang:processes(), Mod, true), + true = erts_internal:purge_module(Mod, complete), + {true, DidKill} end. %% soft_purge(Module) @@ -85,21 +116,17 @@ soft_purge(Mod) -> Result end. - do_soft_purge(Mod) -> - case erts_internal:copy_literals(Mod, true) of + case erts_internal:purge_module(Mod, prepare) of false -> true; true -> - DoPurge = check_proc_code(erlang:processes(), Mod, false), - true = erts_internal:copy_literals(Mod, false), - case DoPurge of - false -> - false; - true -> - erts_internal:purge_module(Mod), - true - end + Res = check_proc_code(erlang:processes(), Mod, false), + erts_internal:purge_module(Mod, + case Res of + false -> abort; + true -> complete + end) end. %% @@ -283,8 +310,7 @@ cpc_sched_kill(Pid, cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) -> erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, - {allow_gc, AllowGc}, - {copy_literals, true}]). + {allow_gc, AllowGc}]). cpc_request_gc(CpcS, [Pid|Pids]) -> cpc_request(CpcS, Pid, true), @@ -297,3 +323,72 @@ cpc_init(CpcS, [Pid|Pids], NoReqs) -> cpc_init(CpcS, Pids, NoReqs+1). % end of check_proc_code() implementation. + +%% +%% FOR TESTING ONLY +%% +%% do_test_purge() is for testing only. The purge is done +%% as usual, but the tester can control when to enter the +%% specific phases. +%% +do_test_purge(Mod, From, Type, Ref) when Type == true; Type == false -> + Mon = erlang:monitor(process, From), + Res = case Type of + true -> do_test_hard_purge(Mod, From, Ref, Mon); + false -> do_test_soft_purge(Mod, From, Ref, Mon) + end, + From ! {test_purge, Res, Ref}, + erlang:demonitor(Mon, [flush]), + ok; +do_test_purge(_, _, _, _) -> + ok. + +do_test_soft_purge(Mod, From, Ref, Mon) -> + PrepRes = erts_internal:purge_module(Mod, prepare), + TestRes = test_progress(started, From, Mon, Ref, ok), + case PrepRes of + false -> + _ = test_progress(continued, From, Mon, Ref, TestRes), + true; + true -> + Res = check_proc_code(erlang:processes(), Mod, false), + _ = test_progress(continued, From, Mon, Ref, TestRes), + erts_internal:purge_module(Mod, + case Res of + false -> abort; + true -> complete + end) + end. + +do_test_hard_purge(Mod, From, Ref, Mon) -> + PrepRes = erts_internal:purge_module(Mod, prepare), + TestRes = test_progress(started, From, Mon, Ref, ok), + case PrepRes of + false -> + _ = test_progress(continued, From, Mon, Ref, TestRes), + {false, false}; + true -> + DidKill = check_proc_code(erlang:processes(), Mod, true), + _ = test_progress(continued, From, Mon, Ref, TestRes), + true = erts_internal:purge_module(Mod, complete), + {true, DidKill} + end. + +test_progress(_State, _From, _Mon, _Ref, died) -> + %% Test process died; continue so we wont + %% leave the system in an inconsistent + %% state... + died; +test_progress(started, From, Mon, Ref, ok) -> + From ! {started, Ref}, + receive + {'DOWN', Mon, process, From, _} -> died; + {continue, Ref} -> ok + end; +test_progress(continued, From, Mon, Ref, ok) -> + From ! {continued, Ref}, + receive + {'DOWN', Mon, process, From, _} -> died; + {complete, Ref} -> ok + end. + diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl new file mode 100644 index 0000000000..911642082c --- /dev/null +++ b/erts/preloaded/src/erts_dirty_process_code_checker.erl @@ -0,0 +1,82 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(erts_dirty_process_code_checker). + +-export([start/0]). + +%% +%% The erts_dirty_process_code_checker is started at +%% VM boot by the VM. It is a spawned as a system +%% process, i.e, the whole VM will terminate if +%% this process terminates. +%% +start() -> + process_flag(trap_exit, true), + msg_loop(). + +msg_loop() -> + _ = receive + Request -> + handle_request(Request) + end, + msg_loop(). + +check_process(Requester, Target, ReqId, Module) -> + Result = erts_internal:check_dirty_process_code(Target, Module), + Requester ! {check_process_code, ReqId, Result}. + +handle_request({Requester, + Target, + Prio, + {check_process_code, + ReqId, + Module, + _Flags} = Op}) -> + %% + %% Target may have stopped executing dirty since the + %% initial request was made. Check its current state + %% and try to send the request if possible; otherwise, + %% check the dirty executing process and send the result... + %% + try + case erts_internal:is_process_executing_dirty(Target) of + true -> + check_process(Requester, Target, ReqId, Module); + false -> + case erts_internal:request_system_task(Requester, + Target, + Prio, + Op) of + ok -> + ok; + dirty_execution -> + check_process(Requester, Target, ReqId, Module) + end + end + catch + _ : _ -> + ok %% Ignore all failures; someone passed us garbage... + end; +handle_request(_Garbage) -> + ignore. + + + diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 2459ea2a2c..6229754c8c 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -38,11 +38,13 @@ -export([system_check/1, gather_system_check_result/1]). --export([request_system_task/3]). +-export([request_system_task/3, request_system_task/4]). -export([check_process_code/3]). --export([copy_literals/2]). --export([purge_module/1]). +-export([check_dirty_process_code/2]). +-export([is_process_executing_dirty/1]). +-export([release_literal_area_switch/0]). +-export([purge_module/2]). -export([flush_monitor_messages/3]). @@ -204,14 +206,25 @@ port_info(_Result, _Item) -> -spec request_system_task(Pid, Prio, Request) -> 'ok' when Prio :: 'max' | 'high' | 'normal' | 'low', Request :: {'garbage_collect', term()} - | {'check_process_code', term(), module(), non_neg_integer()}, + | {'check_process_code', term(), module(), non_neg_integer()} + | {'copy_literals', term(), boolean()}, Pid :: pid(). request_system_task(_Pid, _Prio, _Request) -> erlang:nif_error(undefined). +-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when + Prio :: 'max' | 'high' | 'normal' | 'low', + Request :: {'garbage_collect', term()} + | {'check_process_code', term(), module(), non_neg_integer()} + | {'copy_literals', term(), boolean()}, + RequesterPid :: pid(), + TargetPid :: pid(). + +request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) -> + erlang:nif_error(undefined). + -define(ERTS_CPC_ALLOW_GC, (1 bsl 0)). --define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)). -spec check_process_code(Module, Flags) -> boolean() when Module :: module(), @@ -223,7 +236,7 @@ check_process_code(_Module, _Flags) -> Pid :: pid(), Module :: module(), RequestId :: term(), - Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()}, + Option :: {async, RequestId} | {allow_gc, boolean()}, OptionList :: [Option], CheckResult :: boolean() | aborted. check_process_code(Pid, Module, OptionList) -> @@ -265,8 +278,6 @@ get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) -> get_cpc_opts(Options, AsyncTuple, Flags); get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) -> get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC)); -get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) -> - get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit)); get_cpc_opts([], Async, Flags) -> {Async, Flags}. @@ -275,15 +286,26 @@ cpc_flags(OldFlags, Bit, true) -> cpc_flags(OldFlags, Bit, false) -> OldFlags band (bnot Bit). --spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when - Module :: module(), - Bool :: boolean(). -copy_literals(_Mod, _Bool) -> +-spec check_dirty_process_code(Pid,Module) -> 'true' | 'false' when + Pid :: pid(), + Module :: module(). +check_dirty_process_code(_Pid,_Module) -> erlang:nif_error(undefined). --spec purge_module(Module) -> boolean() when - Module :: module(). -purge_module(_Module) -> +-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when + Pid :: pid(). +is_process_executing_dirty(_Pid) -> + erlang:nif_error(undefined). + +-spec release_literal_area_switch() -> 'true' | 'false'. + +release_literal_area_switch() -> + erlang:nif_error(undefined). + +-spec purge_module(Module, Op) -> boolean() when + Module :: module(), + Op :: 'prepare' | 'abort' | 'complete'. +purge_module(_Module, _Op) -> erlang:nif_error(undefined). -spec system_check(Type) -> 'ok' when diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl new file mode 100644 index 0000000000..3befad8dfb --- /dev/null +++ b/erts/preloaded/src/erts_literal_area_collector.erl @@ -0,0 +1,113 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(erts_literal_area_collector). + +-export([start/0]). + +%% Currently we only allow two outstanding literal +%% copying jobs that garbage collect in order to +%% copy the literals. Maybe we could allow more +%% than two outstanding processes, but for now we +%% play it safe... +-define(MAX_GC_OUTSTND, 2). + +%% +%% The erts_literal_area_collector is started at +%% VM boot by the VM. It is a spawned as a system +%% process, i.e, the whole VM will terminate if +%% this process terminates. +%% +start() -> + process_flag(trap_exit, true), + msg_loop(undefined, 0, 0, []). + +%% +%% The VM will send us a 'copy_literals' message +%% when it has a new literal area that needs to +%% be handled is added. We will also be informed +%% about more areas when we call +%% erts_internal:release_literal_area_switch(). +%% +msg_loop(Area, Outstnd, GcOutstnd, NeedGC) -> + receive + + %% A new area to handle has arrived... + copy_literals when Outstnd == 0 -> + switch_area(); + + %% Process (_Pid) has completed the request... + {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 -> + switch_area(); %% Last process completed... + {copy_literals, {Area, false, _Pid}, ok} -> + msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC); + {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] -> + msg_loop(Area, Outstnd-1, GcOutstnd-1, []); + {copy_literals, {Area, true, _Pid}, ok} -> + send_copy_req(hd(NeedGC), Area, true), + msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC)); + + %% Process (Pid) failed to complete the request + %% since it needs to garbage collect in order to + %% complete the request... + {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND -> + send_copy_req(Pid, Area, true), + msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC); + {copy_literals, {Area, false, Pid}, need_gc} -> + msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]); + + %% Not handled message regarding the area that we + %% currently are working with. Crash the VM so + %% we notice this bug... + {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) -> + exit({not_handled_message, Msg}); + + %% Unexpected garbage message. Get rid of it... + _Ignore -> + msg_loop(Area, Outstnd, GcOutstnd, NeedGC) + + end. + +switch_area() -> + Res = erts_internal:release_literal_area_switch(), + erlang:garbage_collect(), %% Almost no live data now... + case Res of + false -> + %% No more areas to handle... + msg_loop(undefined, 0, 0, []); + true -> + %% Send requests to all processes to copy + %% all live data they have referring to the + %% literal area that is to be released... + Area = make_ref(), + Outstnd = send_copy_reqs(erlang:processes(), Area, false), + msg_loop(Area, Outstnd, 0, []) + end. + +send_copy_reqs(Ps, Area, GC) -> + send_copy_reqs(Ps, Area, GC, 0). + +send_copy_reqs([], _Area, _GC, N) -> + N; +send_copy_reqs([P|Ps], Area, GC, N) -> + send_copy_req(P, Area, GC), + send_copy_reqs(Ps, Area, GC, N+1). + +send_copy_req(P, Area, GC) -> + erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}). diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 45468b3b9c..962528f7ab 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -423,9 +423,6 @@ loop(State) -> Loaded = State#state.loaded, %% boot_loop but is handled here From ! {init,Loaded}, %% anyway. loop(State); - {From, {ensure_loaded, _}} -> - From ! {init, not_allowed}, - loop(State); Msg -> loop(handle_msg(Msg,State)) end. @@ -465,6 +462,8 @@ do_handle_msg(Msg,State) -> From ! {init,ok}, {new_state,State#state{subscribed = [Pid|Subscribed]}} end; + {From, {ensure_loaded, _}} -> + From ! {init, not_allowed}; X -> case whereis(user) of undefined -> @@ -670,9 +669,9 @@ unload(_) -> do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())). do_unload([M|Mods]) -> - catch erts_internal:purge_module(M), + catch erlang:purge_module(M), catch erlang:delete_module(M), - catch erts_internal:purge_module(M), + catch erlang:purge_module(M), do_unload(Mods); do_unload([]) -> purge_all_hipe_refs(), diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 560810d222..bcf16402b0 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -2401,13 +2401,13 @@ get_addrs([F|Addrs]) -> {Addr,Rest} = get_addr(F, Addrs), [Addr|get_addrs(Rest)]. -get_addr(?INET_AF_LOCAL, [0]) -> - {{local,<<>>},[]}; get_addr(?INET_AF_LOCAL, [N|Addr]) -> {A,Rest} = lists:split(N, Addr), {{local,iolist_to_binary(A)},Rest}; +get_addr(?INET_AF_UNSPEC, Rest) -> + {{unspec,<<>>},Rest}; get_addr(?INET_AF_UNDEFINED, Rest) -> - {{undefined,0},Rest}; + {{undefined,<<>>},Rest}; get_addr(Family, [P1,P0|Addr]) -> {IP,Rest} = get_ip(Family, Addr), {{IP,?u16(P1, P0)},Rest}. diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile index dfd8153f32..dd7e2ea530 100644 --- a/erts/start_scripts/Makefile +++ b/erts/start_scripts/Makefile @@ -69,7 +69,7 @@ debug opt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC) rel: $(REL_SCRIPTS) -RELEASES.src: +RELEASES.src: $(SS_ROOT)/start_sasl.rel $(gen_verbose)$(INSTALL_DIR) $(SS_TMP) $(V_at)( cd $(SS_TMP) && \ $(ERL) -noinput +B -eval 'release_handler:create_RELEASES("%ERL_ROOT%", "$(SS_ROOT)", "$(SS_ROOT)/start_sasl.rel", []), halt()') diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index 47d38bde7c..fe1ccba1e2 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -22,6 +22,7 @@ -export([all/0, suite/0]). -export([basic/1,heavy/1,heavier/1,defunct/1]). +-export([sleepy_child/1]). -export([ping_me_back/1]). -include_lib("common_test/include/ct.hrl"). @@ -31,17 +32,17 @@ suite() -> {timetrap, {minutes, 2}}]. all() -> - [basic, heavy, heavier, defunct]. + [basic, sleepy_child, heavy, heavier, defunct]. basic(Config) when is_list(Config) -> case os:type() of - {unix,_} -> basic_1(Config); + {unix,_} -> basic_1(Config, "basic", ""); _ -> {skip,"Not Unix"} end. -basic_1(Config) -> - {Node,Pipe} = do_run_erl(Config, "basic"), +basic_1(Config, Case, Opt) -> + {Node,Pipe} = do_run_erl(Config, Case, Opt), ToErl = open_port({spawn,"to_erl "++Pipe}, []), erlang:port_command(ToErl, "halt().\r\n"), @@ -55,6 +56,12 @@ basic_1(Config) -> ok. +sleepy_child(Config) when is_list(Config) -> + case os:type() of + {unix,_} -> basic_1(Config, "sleepy_child", "-sleepy-child "); + _ -> {skip,"Not Unix"} + end. + heavy(Config) when is_list(Config) -> case os:type() of {unix,_} -> heavy_1(Config); @@ -233,12 +240,14 @@ defunct_2(Config, Perl) -> %%% Utilities. do_run_erl(Config, Case) -> + do_run_erl(Config, Case, ""). +do_run_erl(Config, Case, Opt) -> Priv = proplists:get_value(priv_dir, Config), LogDir = filename:join(Priv, Case), ok = file:make_dir(LogDir), Pipe = LogDir ++ "/", NodeName = "run_erl_node_" ++ Case, - Cmd = "run_erl "++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++ + Cmd = "run_erl "++Opt++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " -s " ++ ?MODULE_STRING ++ " ping_me_back " ++ atom_to_list(node()) ++ "\"", diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index 281a47134f..d474c71c4f 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -214,7 +214,19 @@ dump_core(#core_search_conf{ cerl = Cerl }, Core) -> format_core(Conf, {ignore, Core}) -> format_core(Conf, Core, "[ignored] "); format_core(Conf, Core) -> - format_core(Conf, Core, ""). + format_core(Conf, Core, ""), + + %% Try print (log dir) name of offending application + CoreDir = filename:dirname(Core), + lists:foreach(fun(TestDir) -> + case filelib:is_dir(filename:join(CoreDir,TestDir)) of + true -> + io:format(" from ~s~n", [TestDir]); + false -> + no + end + end, + filelib:wildcard("*.logs", CoreDir)). format_core(#core_search_conf{file = false}, Core, Ignore) -> io:format(" ~s~s " ++ time_fstr() ++ "~s~n", diff --git a/erts/vsn.mk b/erts/vsn.mk index 6ad3680213..acd4509304 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 8.0 +VSN = 8.0.3 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/asn1/doc/src/asn1_getting_started.xml b/lib/asn1/doc/src/asn1_getting_started.xml index 3c8ec24723..d40b294c39 100644 --- a/lib/asn1/doc/src/asn1_getting_started.xml +++ b/lib/asn1/doc/src/asn1_getting_started.xml @@ -678,7 +678,7 @@ ok 1081,32,1043,1085,1086,1084]</pre> <p>For details, see the <seealso marker="stdlib:unicode">unicode</seealso> - module in <c>stdlib</c>.</p> + module in STDLIB.</p> <p>In the following example, this ASN.1 specification is used:</p> <pre> diff --git a/lib/asn1/doc/src/asn1_introduction.xml b/lib/asn1/doc/src/asn1_introduction.xml index d8b81aa467..e4f406364d 100644 --- a/lib/asn1/doc/src/asn1_introduction.xml +++ b/lib/asn1/doc/src/asn1_introduction.xml @@ -30,7 +30,7 @@ <file>asn1_introduction.xml</file> </header> - <p>The <c>ASN.1</c> application provides the following:</p> + <p>The ASN.1 application provides the following:</p> <list type="bulleted"> <item>An ASN.1 compiler for Erlang, which generates encode and diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index 68d335f451..ac3d9c828e 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -329,7 +329,7 @@ </item> <item> <p> - The <c>asn1</c> application would fail to build if the + The ASN.1 application would fail to build if the <c>.erlang</c> file printed something to standard output.</p> <p> Own Id: OTP-11360</p> @@ -997,7 +997,7 @@ also been extended. </item><item> The <c>configure</c> scripts of <c>erl_interface</c> and <c>odbc</c> now search for thread libraries and thread library quirks the - same way as <c>erts</c> do. </item><item> The + same way as ERTS do. </item><item> The <c>configure</c> script of the <c>odbc</c> application now also looks for odbc libraries in <c>lib64</c> and <c>lib/64</c> directories when building on a 64-bit diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl index 557eca0ffd..869ea310aa 100644 --- a/lib/asn1/src/asn1_db.erl +++ b/lib/asn1/src/asn1_db.erl @@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table, loop(State); {save, OutFile, Mod} -> [{_,Mtab}] = ets:lookup(Table, Mod), - ok = ets:tab2file(Mtab, OutFile), + TempFile = OutFile ++ ".#temp", + ok = ets:tab2file(Mtab, TempFile), + ok = file:rename(TempFile, OutFile), loop(State); {From, {new, Mod, Erule}} -> [] = ets:lookup(Table, Mod), %Assertion. diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index dd269f095d..8783b5418d 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -221,9 +221,8 @@ check_pass(#st{code=M,file=File,includes=Includes, {error,St#st{error=Reason}} end. -save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) -> +save_pass(#st{code=M,erule=Erule}=St) -> ok = asn1ct_check:storeindb(#state{erule=Erule}, M), - asn1_db:dbsave(DbFile,M#module.name), {ok,St}. parse_listing(#st{code=Code,outfile=OutFile0}=St) -> diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index 1b4c3b3c77..dc614db4f2 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -34,12 +34,16 @@ run_dialyzer() -> compile(File, Config, Options) -> compile_all([File], Config, Options). -compile_all(Files, Config, Options) -> +compile_all(Files, Config, Options0) -> DataDir = proplists:get_value(data_dir, Config), CaseDir = proplists:get_value(case_dir, Config), - [compile_file(filename:join(DataDir, F), [{outdir, CaseDir}, - debug_info|Options]) - || F <- Files], + Options = [{outdir,CaseDir},debug_info|Options0], + + Comp = fun(F) -> + compile_file(filename:join(DataDir, F), Options) + end, + p_run(Comp, Files), + dialyze(Files, Options), ok. @@ -94,9 +98,9 @@ compile_file(File, Options) -> try ok = asn1ct:compile(File, [warnings_as_errors|Options]) catch - Class:Reason -> - ct:print("Failed to compile ~s\n", [File]), - erlang:error({compile_failed, {File, Options}, {Class, Reason}}) + _:Reason -> + ct:print("Failed to compile ~s\n~p", [File,Reason]), + error end. compile_erlang(Mod, Config, Options) -> @@ -219,3 +223,38 @@ ber_get_len(<<0:1,L:7,T/binary>>) -> ber_get_len(<<1:1,Octets:7,T0/binary>>) -> <<L:Octets/unit:8,T/binary>> = T0, {L,T}. + +%% p_run(fun(Data) -> ok|error, List) -> ok +%% Will fail the test case if there were any errors. + +p_run(Test, List) -> + S = erlang:system_info(schedulers), + N = case test_server:is_cover() of + false -> + S + 1; + true -> + %% Cover is running. Using too many processes + %% could slow us down. + min(S, 4) + end, + %%io:format("p_run: ~p parallel processes\n", [N]), + p_run_loop(Test, List, N, [], 0). + +p_run_loop(_, [], _, [], Errors) -> + case Errors of + 0 -> ok; + N -> ct:fail({N,errors}) + end; +p_run_loop(Test, [H|T], N, Refs, Errors) when length(Refs) < N -> + {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end), + p_run_loop(Test, T, N, [Ref|Refs], Errors); +p_run_loop(Test, List, N, Refs0, Errors0) -> + receive + {'DOWN',Ref,process,_,Res} -> + Errors = case Res of + ok -> Errors0; + error -> Errors0+1 + end, + Refs = Refs0 -- [Ref], + p_run_loop(Test, List, N, Refs, Errors) + end. diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index 3f83747485..48ffe653e4 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -566,7 +566,7 @@ (which also causes the test case process to terminate).</p> <p>Elements from the <c>Config</c> list can, for example, be read - with <c>proplists:get_value/2</c> in <c>STDLIB</c> + with <c>proplists:get_value/2</c> in STDLIB (or the macro <c>?config</c> defined in <c>ct.hrl</c>).</p> <p>If you decide not to run the test case after all, return diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml index 264bcff251..ffc64cba67 100644 --- a/lib/common_test/doc/src/ct.xml +++ b/lib/common_test/doc/src/ct.xml @@ -777,7 +777,7 @@ caught by any installed event manager.</p> <p>See also - <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p> + <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p> </desc> </func> @@ -1236,7 +1236,7 @@ <v>Millisecs = integer() | float()</v> </type> <desc><marker id="sleep-1"/> - <p>This function, similar to <c>timer:sleep/1</c> in <c>STDLIB</c>, + <p>This function, similar to <c>timer:sleep/1</c> in STDLIB, suspends the test case for a specified time. However, this function also multiplies <c>Time</c> with the <c>multiply_timetraps</c> value (if set) and under certain @@ -1330,7 +1330,7 @@ caught by any installed event manager.</p> <p>See also - <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>. + <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>. </p> </desc> </func> diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml index 3b1e564b66..c2cf29c530 100644 --- a/lib/common_test/doc/src/ct_hooks.xml +++ b/lib/common_test/doc/src/ct_hooks.xml @@ -94,7 +94,7 @@ <seealso marker="#Module:id-1"><c>ct_hooks:id/1</c></seealso>, or a <c>reference</c> (created using <seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso> - in <c>ERTS</c>) if + in ERTS) if <seealso marker="#Module:id-1"><c>ct_hooks:id/1</c></seealso> is not implemented.</p> diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 1998f15697..0e4c35e11f 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -374,7 +374,7 @@ <title>Example CTH</title> <p>The following CTH logs information about a test run into a format parseable by <seealso marker="kernel:file#consult-1">file:consult/1</seealso> - (in <c>Kernel</c>): + (in Kernel): </p> <code> %%% @doc Common Test Example Common Test Hook module. @@ -499,13 +499,13 @@ <tag><c>cth_log_redirect</c></tag> <item> <p>Built-in</p> - <p>Captures all <c>error_logger</c> and <c>SASL</c> logging + <p>Captures all <c>error_logger</c> and SASL logging events and prints them to the current test case log. If an event cannot be associated with a test case, it is printed in the <c>Common Test</c> framework log. This happens for test cases running in parallel and events occuring in-between test cases. You can configure the level of - <seealso marker="sasl:sasl_app"><c>SASL</c></seealso> events report - using the normal <c>SASL</c> mechanisms.</p> + <seealso marker="sasl:sasl_app">SASL</seealso> events report + using the normal SASL mechanisms.</p> </item> <tag><c>cth_surefire</c></tag> <item> diff --git a/lib/common_test/doc/src/ct_ssh.xml b/lib/common_test/doc/src/ct_ssh.xml index d00737aa5a..137e4c3f1d 100644 --- a/lib/common_test/doc/src/ct_ssh.xml +++ b/lib/common_test/doc/src/ct_ssh.xml @@ -64,7 +64,7 @@ <p><c>ConnType = ssh | sftp</c>.</p> <p>For other types, see - <seealso marker="ssh:ssh"><c>ssh:ssh(3)</c></seealso>.</p> + <seealso marker="ssh:ssh"><c>ssh(3)</c></seealso>.</p> <p>All time-out parameters in <c>ct_ssh</c> functions are values in milliseconds.</p> @@ -88,7 +88,7 @@ <tag><c>ssh_sftp_return() = term()</c></tag> <item><marker id="type-ssh_sftp_return"/> <p>Return value from an - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp</c></seealso> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp</c></seealso> function.</p></item> </taglist> </section> @@ -104,7 +104,7 @@ </type> <desc><marker id="apread-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -118,7 +118,7 @@ </type> <desc><marker id="apread-5"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -132,7 +132,7 @@ </type> <desc><marker id="apwrite-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -146,7 +146,7 @@ </type> <desc><marker id="apwrite-5"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -160,7 +160,7 @@ </type> <desc><marker id="aread-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -174,7 +174,7 @@ </type> <desc><marker id="aread-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -188,7 +188,7 @@ </type> <desc><marker id="awrite-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -202,7 +202,7 @@ </type> <desc><marker id="awrite-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -216,7 +216,7 @@ </type> <desc><marker id="close-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -230,7 +230,7 @@ </type> <desc><marker id="close-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -310,7 +310,7 @@ </type> <desc><marker id="del_dir-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -324,7 +324,7 @@ </type> <desc><marker id="del_dir-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -338,7 +338,7 @@ </type> <desc><marker id="delete-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -352,7 +352,7 @@ </type> <desc><marker id="delete-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -423,7 +423,7 @@ </type> <desc><marker id="get_file_info-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -437,7 +437,7 @@ </type> <desc><marker id="get_file_info-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -451,7 +451,7 @@ </type> <desc><marker id="list_dir-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -465,7 +465,7 @@ </type> <desc><marker id="list_dir-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -479,7 +479,7 @@ </type> <desc><marker id="make_dir-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -493,7 +493,7 @@ </type> <desc><marker id="make_dir-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -507,7 +507,7 @@ </type> <desc><marker id="make_symlink-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -521,7 +521,7 @@ </type> <desc><marker id="make_symlink-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -535,7 +535,7 @@ </type> <desc><marker id="open-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -549,7 +549,7 @@ </type> <desc><marker id="open-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -563,7 +563,7 @@ </type> <desc><marker id="opendir-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -577,7 +577,7 @@ </type> <desc><marker id="opendir-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -591,7 +591,7 @@ </type> <desc><marker id="position-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -605,7 +605,7 @@ </type> <desc><marker id="position-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -619,7 +619,7 @@ </type> <desc><marker id="pread-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -633,7 +633,7 @@ </type> <desc><marker id="pread-5"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -647,7 +647,7 @@ </type> <desc><marker id="pwrite-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -661,7 +661,7 @@ </type> <desc><marker id="pwrite-5"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -675,7 +675,7 @@ </type> <desc><marker id="read-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -689,7 +689,7 @@ </type> <desc><marker id="read-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -703,7 +703,7 @@ </type> <desc><marker id="read_file-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -717,7 +717,7 @@ </type> <desc><marker id="read_file-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -731,7 +731,7 @@ </type> <desc><marker id="read_file_info-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -745,7 +745,7 @@ </type> <desc><marker id="read_file_info-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -759,7 +759,7 @@ </type> <desc><marker id="read_link-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -773,7 +773,7 @@ </type> <desc><marker id="read_link-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -787,7 +787,7 @@ </type> <desc><marker id="read_link_info-2"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -801,7 +801,7 @@ </type> <desc><marker id="read_link_info-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -853,7 +853,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> <p>If <c>End</c> is a fun, this fun is called with one argument, the data value in a received <c>ssh_cm</c> message (see - <seealso marker="ssh:ssh_connection"><c>ssh:ssh_connection(3)</c></seealso>. + <seealso marker="ssh:ssh_connection"><c>ssh_connection(3)</c></seealso>. The fun is to return either <c>true</c> to end the receiving operation (and have the so far collected data returned) or <c>false</c> to wait for more data from the server. Even if a fun @@ -872,7 +872,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </type> <desc><marker id="rename-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -886,7 +886,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </type> <desc><marker id="rename-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -1070,7 +1070,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </type> <desc><marker id="write-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -1084,7 +1084,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </type> <desc><marker id="write-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -1098,7 +1098,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </type> <desc><marker id="write_file-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -1112,7 +1112,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </type> <desc><marker id="write_file-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -1126,7 +1126,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </type> <desc><marker id="write_file_info-3"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> @@ -1140,7 +1140,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </type> <desc><marker id="write_file_info-4"/> <p>For information and other types, see - <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p> + <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p> </desc> </func> </funcs> diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml index e2a45e894b..eba3c3030d 100644 --- a/lib/common_test/doc/src/ct_telnet.xml +++ b/lib/common_test/doc/src/ct_telnet.xml @@ -198,7 +198,7 @@ <item><marker id="type-prompt_regexp"/> <p>Regular expression matching all possible prompts for a specific target type. <c>regexp</c> must not have any groups, that is, when - matching, <c>re:run/3</c> (in <c>STDLIB</c>) must return a list with + matching, <c>re:run/3</c> (in STDLIB) must return a list with one single element.</p></item> </taglist> </section> diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml index 2978226a19..bd9ed21cb4 100644 --- a/lib/common_test/doc/src/event_handler_chapter.xml +++ b/lib/common_test/doc/src/event_handler_chapter.xml @@ -50,7 +50,7 @@ pass the information on. The event handlers are Erlang modules implemented by the <c>Common Test</c> user according to the <c>gen_event</c> behavior (for details, see module - <seealso marker="stdlib:gen_event"><c>stdlib:gen_event</c></seealso> and + <seealso marker="stdlib:gen_event"><c>gen_event</c></seealso> and section <seealso marker="doc/design_principles:events"><c>gen_event Behaviour</c></seealso> in OTP Design Principles in the System Documentation). @@ -69,8 +69,8 @@ manager, either by telling <c>Common Test</c> to install them before the test run (described later), or by adding the handlers dynamically during the test run using - <seealso marker="stdlib:gen_event#add_handler-3"><c>stdlib:gen_event:add_handler/3</c></seealso> or - <seealso marker="stdlib:gen_event#add_sup_handler-3"><c>stdlib:gen_event:add_sup_handler/3</c></seealso>. + <seealso marker="stdlib:gen_event#add_handler-3"><c>gen_event:add_handler/3</c></seealso> or + <seealso marker="stdlib:gen_event#add_sup_handler-3"><c>gen_event:add_sup_handler/3</c></seealso>. In the latter scenario, the reference of the <c>Common Test</c> event manager is required. To get it, call <seealso marker="ct#get_event_mgr_ref-0"><c>ct:get_event_mgr_ref/0</c></seealso> diff --git a/lib/common_test/doc/src/introduction.xml b/lib/common_test/doc/src/introduction.xml index 40724f24e9..df12bea6dd 100644 --- a/lib/common_test/doc/src/introduction.xml +++ b/lib/common_test/doc/src/introduction.xml @@ -45,7 +45,7 @@ </list> <p><c>Common Test</c> also integrates use of the OTP <seealso marker="tools:cover">cover</seealso> tool in application - <c>Tools</c> for code coverage analysis of Erlang/OTP programs.</p> + Tools for code coverage analysis of Erlang/OTP programs.</p> <p><c>Common Test</c> executes test suite programs automatically, without operator interaction. Test progress and results are diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 43e36adfb6..76e306c4ed 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -274,7 +274,7 @@ <note><p>Directories passed to <c>Common Test</c> can have either relative or absolute paths.</p></note> - <note><p>Any start flags to the Erlang runtime system (application <c>ERTS</c>) can also be passed as + <note><p>Any start flags to the Erlang runtime system (application ERTS) can also be passed as parameters to <c>ct_run</c>. It is, for example, useful to be able to pass directories to be added to the Erlang code server search path with flag <c>-pa</c> or <c>-pz</c>. If you have common help- or library @@ -286,7 +286,7 @@ <p>The absolute path of directory <c>chat_server/ebin</c> is here passed to the code server. This is essential because relative paths are stored by the code server as relative, and <c>Common Test</c> changes - the current working directory of <c>ERTS</c> during the test run.</p> + the current working directory of ERTS during the test run.</p> </note> <p>The <c>ct_run</c> program sets the exit status before shutting down. The following values @@ -1258,7 +1258,7 @@ <p>The minor log files contain full details of every single test case, each in a separate file. This way, it is straightforward to compare the latest results to that of previous - test runs, even if the set of test cases changes. If application <c>SASL</c> + test runs, even if the set of test cases changes. If application SASL is running, its logs are also printed to the current minor log file by the <seealso marker="common_test:ct_hooks_chapter#builtin_cths"> cth_log_redirect built-in hook</seealso>. diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 83daf771a6..7bd2ccf588 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -269,10 +269,10 @@ <p>As parameter <c>Config</c> is a list of key-value tuples, that is, a data type called a property list, it can be handled by the - <seealso marker="stdlib:proplists"><c>stdlib:proplists</c></seealso> module. + <seealso marker="stdlib:proplists"><c>proplists</c></seealso> module. A value can, for example, be searched for and returned with function <seealso marker="stdlib:proplists#get_value-2"><c>proplists:get_value/2</c></seealso>. - Also, or alternatively, the general <seealso marker="stdlib:lists"><c>stdlib:lists</c></seealso> + Also, or alternatively, the general <seealso marker="stdlib:lists"><c>lists</c></seealso> module contains useful functions. Normally, the only operations performed on <c>Config</c> is insert (adding a tuple to the head of the list) and lookup. <c>Common Test</c> provides a simple macro named <c>?config</c>, @@ -652,7 +652,7 @@ <title>Parallel Test Cases and I/O</title> <p>A parallel test case has a private I/O server as its group leader. (For a description of the group leader concept, see - <seealso marker="erts:index"><c>ERTS</c></seealso>). + <seealso marker="erts:index">ERTS</seealso>). The central I/O server process, which handles the output from regular test cases and configuration functions, does not respond to I/O messages during execution of parallel groups. This is important to understand @@ -1032,8 +1032,8 @@ 6. Categorized error, importance = 99</pre> <p>The arguments <c>Format</c> and <c>FormatArgs</c> in <c>ct:log/print/pal</c> are - always passed on to the <c>stdlib</c> function <c>io:format/3</c> (For details, - see the <seealso marker="stdlib:io"><c>stdlib:io</c></seealso> manual page).</p> + always passed on to the STDLIB function <c>io:format/3</c> (For details, + see the <seealso marker="stdlib:io"><c>io</c></seealso> manual page).</p> <p><c>ct:pal/4</c> and <c>ct:log/5</c> add headers to strings being printed to the log file. The strings are also wrapped in div tags with a CSS class diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl index 23ba1ab981..8b59d3ab23 100644 --- a/lib/common_test/src/ct_gen_conn.erl +++ b/lib/common_test/src/ct_gen_conn.erl @@ -27,7 +27,7 @@ -export([start/4, stop/1, get_conn_pid/1, check_opts/1]). -export([call/2, call/3, return/2, do_within_time/2]). --export([log/3, start_log/1, cont_log/2, end_log/0]). +-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]). %%---------------------------------------------------------------------- %% Exported types @@ -175,6 +175,14 @@ cont_log(Format,Args) -> log(cont_log,[Format,Args]). %%%----------------------------------------------------------------- +%%% @spec cont_log_no_timestamp(Format,Args) -> ok +%%% +%%% @doc Log activities on the current connection (tool-internal use only). +%%% @see ct_logs:cont_log/2 +cont_log_no_timestamp(Format,Args) -> + log(cont_log_no_timestamp,[Format,Args]). + +%%%----------------------------------------------------------------- %%% @spec end_log() -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 53245c596a..9282a9f81d 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -32,7 +32,7 @@ -export([init/2, close/2, init_tc/1, end_tc/1]). -export([register_groupleader/2, unregister_groupleader/1]). -export([get_log_dir/0, get_log_dir/1]). --export([log/3, start_log/1, cont_log/2, end_log/0]). +-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]). -export([set_stylesheet/2, clear_stylesheet/1]). -export([add_external_logs/1, add_link/3]). -export([make_last_run_index/0]). @@ -373,6 +373,20 @@ cont_log(Format,Args) -> ok. %%%----------------------------------------------------------------- +%%% @spec cont_log_no_timestamp(Format,Args) -> ok +%%% +%%% @doc Adds information about an activity (tool-internal use only). +%%% +%%% @see start_log/1 +%%% @see end_log/0 +cont_log_no_timestamp([],[]) -> + ok; +cont_log_no_timestamp(Format,Args) -> + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, + [{Format,Args}],true}), + ok. + +%%%----------------------------------------------------------------- %%% @spec end_log() -> ok %%% %%% @doc Ends the logging of an activity (tool-internal use only). @@ -595,7 +609,6 @@ div_header(Class,Printer) -> div_footer() -> "</pre></div>\n<pre>". - maybe_log_timestamp() -> {MS,S,US} = ?now, case get(log_timestamp) of diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl index e7a9cfa843..f22959d457 100644 --- a/lib/common_test/src/ct_make.erl +++ b/lib/common_test/src/ct_make.erl @@ -342,5 +342,7 @@ check_includes2(Epp, File, ObjMTime) -> epp:close(Epp), false; {error, _Error} -> + check_includes2(Epp, File, ObjMTime); + {warning, _Warning} -> check_includes2(Epp, File, ObjMTime) end. diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index 8fb411ec4f..34d27ed5f4 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -954,7 +954,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port}, true -> ok; false -> - ct_gen_conn:cont_log(String,Args) + ct_gen_conn:cont_log_no_timestamp(String,Args) end; ForcePrint == true -> @@ -965,7 +965,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port}, %% called ct_gen_conn:log(heading(Action,Name1),String,Args); false -> - ct_gen_conn:cont_log(String,Args) + ct_gen_conn:cont_log_no_timestamp(String,Args) end end end. @@ -1224,7 +1224,6 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO, EOMod = if TotalTO /= infinity -> EO#eo{total_timeout=trunc(TotalTO)}; true -> EO end, - ExpectFun = case EOMod#eo.seq of true -> fun() -> seq_expect(Name,Pid,Data,Pattern,Acc,EOMod) @@ -1247,38 +1246,34 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO, true -> IdleTO end, + {PatOrPats1,Acc1,Rest1} = case NotFinished of + {nomatch,Rest0} -> + %% one expect + {Pattern,[],Rest0}; + {continue,Pats0,Acc0,Rest0} -> + %% sequence + {Pats0,Acc0,Rest0} + end, case timer:tc(ct_gen_conn, do_within_time, [Fun,BreakAfter]) of - {_,{error,Reason}} -> + {_,{error,Reason}} -> %% A timeout will occur when the telnet connection %% is idle for EO#eo.idle_timeout milliseconds. + if Rest1 /= [] -> + log(name_or_pid(Name,Pid)," ~ts",[Rest1]); + true -> + ok + end, {error,Reason}; {_,{ok,Data1}} when TotalTO == infinity -> - case NotFinished of - {nomatch,Rest} -> - %% One expect - teln_expect1(Name,Pid,Rest++Data1, - Pattern,[],EOMod); - {continue,Patterns1,Acc1,Rest} -> - %% Sequence - teln_expect1(Name,Pid,Rest++Data1, - Patterns1,Acc1,EOMod) - end; + teln_expect1(Name,Pid,Rest1++Data1,PatOrPats1,Acc1,EOMod); {Elapsed,{ok,Data1}} -> TVal = TotalTO - (Elapsed/1000), if TVal =< 0 -> {error,timeout}; true -> EO1 = EO#eo{total_timeout = TVal}, - case NotFinished of - {nomatch,Rest} -> - %% One expect - teln_expect1(Name,Pid,Rest++Data1, - Pattern,[],EO1); - {continue,Patterns1,Acc1,Rest} -> - %% Sequence - teln_expect1(Name,Pid,Rest++Data1, - Patterns1,Acc1,EO1) - end + teln_expect1(Name,Pid,Rest1++Data1, + PatOrPats1,Acc1,EO1) end end end. @@ -1416,14 +1411,14 @@ match_lines(Name,Pid,Data,Patterns,EO) -> case one_line(Data,[]) of {noline,Rest} when FoundPrompt=/=false -> %% This is the line including the prompt - case match_line(Name,Pid,Rest,Patterns,FoundPrompt,EO) of + case match_line(Name,Pid,Rest,Patterns,FoundPrompt,false,EO) of nomatch -> {nomatch,prompt}; {Tag,Match} -> {Tag,Match,[]} end; {noline,Rest} when EO#eo.prompt_check==false -> - case match_line(Name,Pid,Rest,Patterns,false,EO) of + case match_line(Name,Pid,Rest,Patterns,false,false,EO) of nomatch -> {nomatch,Rest}; {Tag,Match} -> @@ -1432,7 +1427,7 @@ match_lines(Name,Pid,Data,Patterns,EO) -> {noline,Rest} -> {nomatch,Rest}; {Line,Rest} -> - case match_line(Name,Pid,Line,Patterns,false,EO) of + case match_line(Name,Pid,Line,Patterns,false,true,EO) of nomatch -> match_lines(Name,Pid,Rest,Patterns,EO); {Tag,Match} -> @@ -1440,45 +1435,50 @@ match_lines(Name,Pid,Data,Patterns,EO) -> end end. - %% For one line, match each pattern -match_line(Name,Pid,Line,Patterns,FoundPrompt,EO) -> - match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,match). +match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO) -> + match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO,match). -match_line(Name,Pid,Line,[prompt|Patterns],false,EO,RetTag) -> - match_line(Name,Pid,Line,Patterns,false,EO,RetTag); -match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) -> +match_line(Name,Pid,Line,[prompt|Patterns],false,Term,EO,RetTag) -> + match_line(Name,Pid,Line,Patterns,false,Term,EO,RetTag); +match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_Term,_EO,RetTag) -> log(name_or_pid(Name,Pid)," ~ts",[Line]), log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]), {RetTag,{prompt,FoundPrompt}}; -match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag) - when PromptType==FoundPrompt -> +match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_Term, + _EO,RetTag) when PromptType==FoundPrompt -> log(name_or_pid(Name,Pid)," ~ts",[Line]), log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]), {RetTag,{prompt,FoundPrompt}}; -match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag) +match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term, + EO,RetTag) when PromptType=/=FoundPrompt -> - match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag); -match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) -> + match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag); +match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) -> case re:run(Line,Pattern,[{capture,all,list}]) of nomatch -> - match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag); + match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag); {match,Match} -> log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]), {RetTag,{Tag,Match}} end; -match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) -> +match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) -> case re:run(Line,Pattern,[{capture,all,list}]) of nomatch -> - match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag); + match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag); {match,Match} -> log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]), {RetTag,Match} end; -match_line(Name,Pid,Line,[],FoundPrompt,EO,match) -> - match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,EO,halt); -match_line(Name,Pid,Line,[],_FoundPrompt,_EO,halt) -> +match_line(Name,Pid,Line,[],FoundPrompt,Term,EO,match) -> + match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,Term,EO,halt); +%% print any terminated line that can not be matched +match_line(Name,Pid,Line,[],_FoundPrompt,true,_EO,halt) -> log(name_or_pid(Name,Pid)," ~ts",[Line]), + nomatch; +%% if there's no line termination, Line is saved as Rest (above) and will +%% be printed later +match_line(_Name,_Pid,_Line,[],_FoundPrompt,false,_EO,halt) -> nomatch. one_line([$\n|Rest],Line) -> diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl index e819f345de..7f3eb699de 100644 --- a/lib/common_test/src/erl2html2.erl +++ b/lib/common_test/src/erl2html2.erl @@ -130,6 +130,8 @@ parse_preprocessed_file(Epp, File, InCorrectFile) -> throw({error,Reason,InCorrectFile}); {error,_Reason} -> parse_preprocessed_file(Epp, File, InCorrectFile); + {warning,_} -> + parse_preprocessed_file(Epp, File, InCorrectFile); {eof,_Location} -> [] end. diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl index e6939d23c0..dface99b8f 100644 --- a/lib/common_test/test/ct_auto_compile_SUITE.erl +++ b/lib/common_test/test/ct_auto_compile_SUITE.erl @@ -27,6 +27,7 @@ %%% The suites used for the test are located in the data directory. %%%------------------------------------------------------------------- -module(ct_auto_compile_SUITE). +-warning("Ignore me -- testing that the debugger can handle warnings"). -compile(export_all). diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl index 9879e0f20d..cbbfe408a8 100644 --- a/lib/common_test/test/ct_config_SUITE.erl +++ b/lib/common_test/test/ct_config_SUITE.erl @@ -113,10 +113,14 @@ userconfig_static(Config) when is_list(Config) -> ["config_static_SUITE"]). userconfig_dynamic(Config) when is_list(Config) -> - run_test(config_dynamic_SUITE, - Config, - {userconfig, {config_driver, "config_server"}}, - ["config_dynamic_SUITE"]). + case skip_dynamic() of + true -> {skip,"TimeWarpingOS"}; + false -> + run_test(config_dynamic_SUITE, + Config, + {userconfig, {config_driver, "config_server"}}, + ["config_dynamic_SUITE"]) + end. testspec_legacy(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -147,16 +151,20 @@ testspec_static(Config) when is_list(Config) -> file:delete(filename:join(ConfigDir, "spec_static.spec")). testspec_dynamic(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - ConfigDir = ?config(config_dir, Config), - make_spec(DataDir, ConfigDir, "spec_dynamic.spec", - [config_dynamic_SUITE], - [{userconfig, {config_driver, "config_server"}}]), - run_test(config_dynamic_SUITE, - Config, - {spec, filename:join(ConfigDir, "spec_dynamic.spec")}, - []), - file:delete(filename:join(ConfigDir, "spec_dynamic.spec")). + case skip_dynamic() of + true -> {skip,"TimeWarpingOS"}; + false -> + DataDir = ?config(data_dir, Config), + ConfigDir = ?config(config_dir, Config), + make_spec(DataDir, ConfigDir, "spec_dynamic.spec", + [config_dynamic_SUITE], + [{userconfig, {config_driver, "config_server"}}]), + run_test(config_dynamic_SUITE, + Config, + {spec, filename:join(ConfigDir, "spec_dynamic.spec")}, + []), + file:delete(filename:join(ConfigDir, "spec_dynamic.spec")) + end. @@ -198,6 +206,23 @@ setup_env(Test, Config, CTConfig) -> reformat_events(Events, EH) -> ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% Test related to 'localtime' will often fail if the test host is +%%% time warping, so let's just skip the 'dynamic' tests then. +skip_dynamic() -> + case os:getenv("TS_EXTRA_PLATFORM_LABEL") of + TSExtraPlatformLabel when is_list(TSExtraPlatformLabel) -> + case string:str(TSExtraPlatformLabel,"TimeWarpingOS") of + 0 -> false; + _ -> true + end; + _ -> + false + end. + + + %%%----------------------------------------------------------------- %%% TEST EVENTS %%%----------------------------------------------------------------- diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl index a65275da43..f2580ad8e9 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl @@ -83,7 +83,9 @@ end_per_suite(Config) -> %% Running the netconf server in a remote node, test that the client %% process terminates if the remote node goes down. remote_crash(Config) -> - {ok,Node} = ct_slave:start(nc_remote_crash), + {ok,Node} = ct_slave:start(nc_remote_crash,[{boot_timeout,15}, + {init_timeout,15}, + {startup_timeout,15}]), Pa = filename:dirname(code:which(?NS)), true = rpc:call(Node,code,add_patha,[Pa]), rpc:call(Node,code,load_file,[crypto]), diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl index e62bc617fa..2412ea6aba 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl @@ -302,10 +302,14 @@ table_trans(Fun,Args) -> S -> apply(Fun,Args); Pid -> + Ref = erlang:monitor(process,Pid), Pid ! {table_trans,Fun,Args,self()}, receive {table_trans_done,Result} -> - Result + erlang:demonitor(Ref,[flush]), + Result; + {'DOWN',Ref,process,Pid,Reason} -> + exit({main_ns_proc_died,Reason}) after 20000 -> exit(table_trans_timeout) end diff --git a/lib/common_test/test_server/ts_run.erl b/lib/common_test/test_server/ts_run.erl index 66db1ff9a7..82ae44ec06 100644 --- a/lib/common_test/test_server/ts_run.erl +++ b/lib/common_test/test_server/ts_run.erl @@ -258,7 +258,7 @@ make_command(Vars, Spec, State) -> run_batch(Vars, _Spec, State) -> process_flag(trap_exit, true), - Command = State#state.command ++ " -noinput -s erlang halt", + Command = State#state.command ++ " -noinput -eval \"erlang:halt(0,[{flush,false}]).\"", ts_lib:progress(Vars, 1, "Command: ~ts~n", [Command]), io:format(user, "Command: ~ts~n",[Command]), Port = open_port({spawn, Command}, [stream, in, eof, exit_status]), diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 61e214294e..3ce37b98e9 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -136,7 +136,7 @@ (see <seealso marker="erts:absform">The Abstract Format</seealso> in ERTS User's Guide) in the compiled beam module. Tools - such as <c>Debugger</c>, <c>Xref</c>, and <c>Cover</c> require + such as Debugger, Xref, and Cover require the debug information to be included.</p> <p><em>Warning</em>: Source code can be reconstructed from @@ -544,7 +544,7 @@ module.beam: module.erl \ 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 Xref tool can be used. See also <seealso marker="tools:xref#deprecated_function">xref(3)</seealso> and the function @@ -846,7 +846,7 @@ pi() -> 3.1416. <section> <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> + from the module <c>list</c> in STDLIB.</p> <p>This feature must be explicitly enabled with a compiler option or a <c>-compile()</c> attribute in the source module.</p> diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index e25cdc580c..dd6b132a92 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,23 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A literal binary matching regression was introduced in + 19.0 where a match could fail to resolve to the right + clause. This has now been fixed.</p> + <p> + Own Id: OTP-13738</p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.0</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -263,7 +280,7 @@ <item> <p> The <c>cerl</c> and <c>cerl_trees</c> modules in the - <c>compiler</c> application are now documented.</p> + Compiler application are now documented.</p> <p> Own Id: OTP-11978</p> </item> @@ -1965,7 +1982,7 @@ <c>RightExpr</c> or vice versa. The evaluation order is only important if the expressions contains and/or depends on operations with side-effects, such as message passing - or <c>ets</c> operations.</p> + or ETS operations.</p> <p> Own Id: OTP-7206</p> </item> diff --git a/lib/compiler/doc/src/ref_man.xml b/lib/compiler/doc/src/ref_man.xml index f5466553c0..c32c499008 100644 --- a/lib/compiler/doc/src/ref_man.xml +++ b/lib/compiler/doc/src/ref_man.xml @@ -30,7 +30,7 @@ <file>application.sgml</file> </header> <description> - <p>The <c>Compiler</c> application compiles Erlang + <p>The Compiler 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/beam_block.erl b/lib/compiler/src/beam_block.erl index 85d332c56e..ec41925beb 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -159,14 +159,43 @@ find_fixpoint(OptFun, Is0) -> end. %% move_allocates(Is0) -> Is -%% Move allocate instructions upwards in the instruction stream, in the -%% hope of getting more possibilities for optimizing away moves later. +%% Move allocate instructions upwards in the instruction stream +%% (within the same block), in the hope of getting more possibilities +%% for optimizing away moves later. %% -%% NOTE: Moving allocation instructions is only safe because it is done -%% immediately after code generation so that we KNOW that if {x,X} is -%% initialized, all x registers with lower numbers are also initialized. -%% That assumption may not be true after other optimizations, such as -%% the beam_utils:live_opt/1 optimization. +%% For example, we can transform the following instructions: +%% +%% get_tuple_element x(1) Element => x(2) +%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live +%% +%% to the following instructions: +%% +%% allocate_zero StackSize 2 %% x(0) and x(1) are live +%% get_tuple_element x(1) Element => x(2) +%% +%% NOTE: Since the beam_reorder pass has been run, it is no longer +%% safe to assume that if x(N) is initialized, then all lower-numbered +%% x registers are also initialized. +%% +%% For example, in general it is not safe to transform the following +%% instructions: +%% +%% get_tuple_element x(0) Element => x(1) +%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live +%% +%% to the following instructions: +%% +%% allocate_zero StackSize 3 +%% get_tuple_element x(0) Element => x(1) +%% +%% The transformation is safe if and only if x(1) has been +%% initialized previously. Unfortunately, beam_reorder may have moved +%% a get_tuple_element instruction so that x(1) is not always +%% initialized when this code is reached. To find whether or not x(1) +%% is initialized, we would need to analyze all code preceding these +%% two instructions (across branches). Since we currently don't have +%% any practical mechanism for doing that, we will have to +%% conservatively assume that the transformation is unsafe. move_allocates([{block,Bl0}|Is]) -> Bl = move_allocates_1(reverse(Bl0), []), @@ -175,27 +204,19 @@ move_allocates([I|Is]) -> [I|move_allocates(Is)]; move_allocates([]) -> []. -move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) -> - {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0), - move_allocates_1(Is, Acc); +move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) -> + case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of + {false,_} -> + move_allocates_1(Is, [I|Acc0]); + {true,not_possible} -> + move_allocates_1(Is, [I|Acc0]); + {true,Live} when is_integer(Live) -> + A = {set,[],[],{alloc,Live,Info}}, + move_allocates_1(Is, [A,I|Acc]) + end; move_allocates_1([I|Is], Acc) -> move_allocates_1(Is, [I|Acc]); -move_allocates_1([], Is) -> Is. - -move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) -> - Live = Live0, % Assertion. - Alloc = {alloc,Live,combine_alloc(Info0, Info)}, - move_allocates_2(Alloc, Is, Acc); -move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) -> - case alloc_may_pass(I) of - false -> - {Is0,[{set,[],[],Alloc0}|Acc]}; - true -> - Alloc = {alloc,alloc_live_regs(I, Live),Info}, - move_allocates_2(Alloc, Is, [I|Acc]) - end; -move_allocates_2(Alloc, [], Acc) -> - {[],[{set,[],[],Alloc}|Acc]}. +move_allocates_1([], Acc) -> Acc. alloc_may_pass({set,_,_,{alloc,_,_}}) -> false; alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false; @@ -204,9 +225,6 @@ alloc_may_pass({set,_,_,put_list}) -> false; alloc_may_pass({set,_,_,put}) -> false; alloc_may_pass({set,_,_,_}) -> true. -combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) -> - {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}. - %% opt([Instruction]) -> [Instruction] %% Optimize the instruction stream inside a basic block. @@ -393,10 +411,19 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) -> %% opt_alloc(Instructions) -> Instructions' %% Optimises all allocate instructions. +opt_alloc([{set,[],[],{alloc,Live0,Info0}}, + {set,[],[],{alloc,Live,Info}}|Is]) -> + Live = Live0, %Assertion. + Alloc = combine_alloc(Info0, Info), + I = {set,[],[],{alloc,Live,Alloc}}, + opt_alloc([I|Is]); opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) -> [{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is]; opt_alloc([I|Is]) -> [I|opt_alloc(Is)]; opt_alloc([]) -> []. + +combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) -> + {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}. %% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr] %% Generates the optimal sequence of instructions for @@ -445,13 +472,14 @@ count_ones(Bits, Acc) -> alloc_live_regs({set,Ds,Ss,_}, Regs0) -> Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)), - live_regs(Rset). + live_regs(0, Rset). -live_regs(Regs) -> - live_regs_1(0, Regs). - -live_regs_1(N, 0) -> N; -live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1). +live_regs(N, 0) -> + N; +live_regs(N, Regs) when Regs band 1 =:= 1 -> + live_regs(N+1, Regs bsr 1); +live_regs(_, _) -> + not_possible. x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N))); x_dead([_|Rs], Regs) -> x_dead(Rs, Regs); diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl index b01f58f683..6f6d742293 100644 --- a/lib/compiler/src/beam_dead.erl +++ b/lib/compiler/src/beam_dead.erl @@ -272,17 +272,14 @@ backward([{jump,{f,To}}=J|[{bif,Op,_,Ops,Reg}|Is]=Is0], D, Acc) -> catch throw:not_possible -> backward(Is0, D, [J|Acc]) end; -backward([{test,bs_start_match2,F,Live,[R,_]=Args,Ctxt}|Is], D, +backward([{test,bs_start_match2,F,_,[R,_],Ctxt}=I|Is], D, [{test,bs_match_string,F,[Ctxt,Bs]}, {test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) -> - {f,To0} = F, - To = shortcut_bs_start_match(To0, R, D), case beam_utils:is_killed(Ctxt, Acc0, D) of true -> - Eq = {test,is_eq_exact,{f,To},[R,{literal,Bs}]}, + Eq = {test,is_eq_exact,F,[R,{literal,Bs}]}, backward(Is, D, [Eq|Acc0]); false -> - I = {test,bs_start_match2,{f,To},Live,Args,Ctxt}, backward(Is, D, [I|Acc]) end; backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) -> diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 09cd3aa2d4..48b5a32814 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) -> end; share_1([{func_info,_,_,_}=I|Is], _, [], Acc) -> reverse(Is, [I|Acc]); +share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) -> + Dict = clean_non_sharable(Dict0), + share_1(Is, Dict, [I|Seq], Acc); share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) -> Dict = clean_non_sharable(Dict0), share_1(Is, Dict, [I|Seq], Acc); share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) -> Dict = clean_non_sharable(Dict0), share_1(Is, Dict, [I|Seq], Acc); +share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) -> + Dict = clean_non_sharable(Dict0), + share_1(Is, Dict, [I|Seq], Acc); share_1([I|Is], Dict, Seq, Acc) -> case is_unreachable_after(I) of false -> @@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) -> end. clean_non_sharable(Dict) -> - %% We are passing in or out of a 'try' block. Remove - %% sequences that should not shared over the boundaries - %% of a 'try' block. Since the end of the sequence must match, - %% the only possible match between a sequence outside and - %% 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. + %% We are passing in or out of a 'catch' or 'try' block. Remove + %% sequences that should not be shared over the boundaries of the + %% block. Since the end of the sequence must match, the only + %% possible match between a sequence outside and a sequence inside + %% the 'catch'/'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. maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict). sharable_with_try([{line,_}|_]) -> %% This sequence may cause an exception and may potentially - %% match a sequence on the other side of the 'try' block + %% match a sequence on the other side of the 'catch'/'try' block %% boundary. false; sharable_with_try([_|Is]) -> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 82ff8a95f3..e951a25e04 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -43,6 +43,10 @@ -type abstract_code() :: [erl_parse:abstract_form()]. +%% Internal representations used for 'from_asm' and 'from_beam' compilation can +%% also be valid, but have no relevant types defined. +-type forms() :: abstract_code() | cerl:c_module(). + -type option() :: atom() | {atom(), term()} | {'d', atom(), term()}. -type err_info() :: {erl_anno:line() | 'none', @@ -88,7 +92,7 @@ file(File, Opt) -> forms(Forms) -> forms(Forms, ?DEFAULT_OPTIONS). --spec forms(abstract_code(), [option()] | option()) -> comp_ret(). +-spec forms(forms(), [option()] | option()) -> comp_ret(). forms(Forms, Opts) when is_list(Opts) -> do_compile({forms,Forms}, [binary|Opts++env_default_opts()]); @@ -116,7 +120,7 @@ noenv_file(File, Opts) when is_list(Opts) -> noenv_file(File, Opt) -> noenv_file(File, [Opt|?DEFAULT_OPTIONS]). --spec noenv_forms(abstract_code(), [option()] | option()) -> comp_ret(). +-spec noenv_forms(forms(), [option()] | option()) -> comp_ret(). noenv_forms(Forms, Opts) when is_list(Opts) -> do_compile({forms,Forms}, [binary|Opts]); @@ -236,6 +240,8 @@ format_error({epp,E}) -> epp:format_error(E); format_error(write_error) -> "error writing file"; +format_error({write_error, Error}) -> + io_lib:format("error writing file: ~ts", [file:format_error(Error)]); format_error({rename,From,To,Error}) -> io_lib:format("failed to rename ~ts to ~ts: ~ts", [From,To,file:format_error(Error)]); @@ -1206,7 +1212,7 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) -> end, {ok,St} catch - exit:_ -> + error:_ -> %% Couldn't write to output Makefile. Err = {St#compile.ifile,[{none,?MODULE,write_error}]}, {error,St#compile{errors=St#compile.errors++[Err]}} @@ -1479,8 +1485,8 @@ save_binary_1(St) -> end, {error,St#compile{errors=St#compile.errors ++ Es}} end; - {error,_Error} -> - Es = [{Tfile,[{none,compile,write_error}]}], + {error,Error} -> + Es = [{Tfile,[{none,compile,{write_error,Error}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. @@ -1628,8 +1634,8 @@ listing(LFun, Ext, St) -> LFun(Lf, Code), ok = file:close(Lf), {ok,St}; - {error,_Error} -> - Es = [{Lfile,[{none,compile,write_error}]}], + {error,Error} -> + Es = [{Lfile,[{none,compile,{write_error,Error}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index e0de50f3ae..08b02101a6 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -468,7 +468,8 @@ bitstr(#c_bitstr{val=Val,size=Size}=BinSeg, Sub) -> %% Currently, we don't attempt to check binaries because they %% are difficult to check. -is_safe_simple(#c_var{}, _) -> true; +is_safe_simple(#c_var{}=Var, _) -> + not cerl:is_c_fname(Var); is_safe_simple(#c_cons{hd=H,tl=T}, Sub) -> is_safe_simple(H, Sub) andalso is_safe_simple(T, Sub); is_safe_simple(#c_tuple{es=Es}, Sub) -> is_safe_simple_list(Es, Sub); diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl index 4bcb252833..9fcb6e497d 100644 --- a/lib/compiler/test/beam_block_SUITE.erl +++ b/lib/compiler/test/beam_block_SUITE.erl @@ -21,7 +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, - get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1]). + get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1, + erl_202/1]). %% The only test for the following functions is that %% the code compiles and is accepted by beam_validator. @@ -37,7 +38,8 @@ groups() -> [{p,[parallel], [get_map_elements, otp_7345, - move_opt_across_gc_bif + move_opt_across_gc_bif, + erl_202 ]}]. init_per_suite(Config) -> @@ -135,6 +137,27 @@ positive(speaking) -> paris([], P) -> P + 1. + +%% See https://bugs.erlang.org/browse/ERL-202. +%% Test that move_allocates/1 in beam_block doesn't move allocate +%% when it would not be safe. + +-record(erl_202_r1, {y}). +-record(erl_202_r2, {x}). + +erl_202(_Config) -> + Ref = make_ref(), + Ref = erl_202({{1,2},Ref}, 42), + + {Ref} = erl_202({7,8}, #erl_202_r1{y=#erl_202_r2{x=Ref}}), + + ok. + +erl_202({{_, _},X}, _) -> + X; +erl_202({_, _}, #erl_202_r1{y=R2}) -> + {R2#erl_202_r2.x}. + %%% %%% The only test of the following code is that it compiles. %%% diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl index 0b13adaff2..088f63606c 100644 --- a/lib/compiler/test/beam_jump_SUITE.erl +++ b/lib/compiler/test/beam_jump_SUITE.erl @@ -21,7 +21,7 @@ -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, - undefined_label/1]). + undefined_label/1,ambiguous_catch_try_state/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -32,7 +32,8 @@ all() -> groups() -> [{p,[parallel], - [undefined_label + [undefined_label, + ambiguous_catch_try_state ]}]. init_per_suite(Config) -> @@ -57,3 +58,17 @@ flights(0, [], []) when [], 0; 0.0, [], false -> clark; flights(_, Reproduction, introduction) when false, Reproduction -> responsible. + +%% [ERL-209] beam_jump would share 'catch' blocks, causing an +%% ambiguous_catch_try_state error in beam_validator. + +ambiguous_catch_try_state(_Config) -> + {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} = + checks(42), + ok. + +river() -> song. + +checks(Wanted) -> + %% Must be one line to cause the unsafe optimization. + {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}. diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 376d2c8e9a..ced0e39d06 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -26,7 +26,7 @@ unused_multiple_values_error/1,unused_multiple_values/1, multiple_aliases/1,redundant_boolean_clauses/1, mixed_matching_clauses/1,unnecessary_building/1, - no_no_file/1]). + no_no_file/1,configuration/1]). -export([foo/0,foo/1,foo/2,foo/3]). @@ -45,7 +45,7 @@ groups() -> unused_multiple_values_error,unused_multiple_values, multiple_aliases,redundant_boolean_clauses, mixed_matching_clauses,unnecessary_building, - no_no_file]}]. + no_no_file,configuration]}]. init_per_suite(Config) -> @@ -499,4 +499,16 @@ experiment() -> end, ok. + +%% Make sure we don't try to move a fun into a guard. +configuration(_Config) -> + {'EXIT',_} = (catch configuration()), + ok. + +configuration() -> + [forgotten || Components <- enemy, is_tuple(fun art/0)]. + +art() -> + creating. + id(I) -> I. diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 31402ac717..127679ba69 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -24,7 +24,7 @@ pmatch/1,mixed/1,aliases/1,non_matching_aliases/1, match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, selectify/1,underscore/1,match_map/1,map_vars_used/1, - coverage/1,grab_bag/1]). + coverage/1,grab_bag/1,literal_binary/1]). -include_lib("common_test/include/ct.hrl"). @@ -40,7 +40,7 @@ groups() -> match_in_call,untuplify, shortcut_boolean,letify_guard,selectify, underscore,match_map,map_vars_used,coverage, - grab_bag]}]. + grab_bag,literal_binary]}]. init_per_suite(Config) -> @@ -574,6 +574,15 @@ grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) -> ok end. +%% Regression in 19.0, reported by Alexei Sholik +literal_binary(_Config) -> + 3 = literal_binary_match(bar,<<"y">>), + ok. + +literal_binary_match(bar, <<"x">>) -> 1; +literal_binary_match(_, <<"x">>) -> 2; +literal_binary_match(_, <<"y">>) -> 3; +literal_binary_match(_, _) -> fail. id(I) -> I. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 23dd4bd4b1..334784657e 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.0 +COMPILER_VSN = 7.0.1 diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 7183c395ae..eee1a88723 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -37,7 +37,9 @@ #include <openssl/opensslconf.h> #include <openssl/crypto.h> +#ifndef OPENSSL_NO_DES #include <openssl/des.h> +#endif /* #ifndef OPENSSL_NO_DES */ /* #include <openssl/idea.h> This is not supported on the openssl OTP requires */ #include <openssl/dsa.h> #include <openssl/rsa.h> @@ -458,16 +460,29 @@ struct cipher_type_t { const size_t key_len; /* != 0 to also match on key_len */ }; +#ifdef OPENSSL_NO_DES +#define COND_NO_DES_PTR(Ptr) (NULL) +#else +#define COND_NO_DES_PTR(Ptr) (Ptr) +#endif + struct cipher_type_t cipher_types[] = { {{"rc2_cbc"}, {&EVP_rc2_cbc}}, - {{"des_cbc"}, {&EVP_des_cbc}}, - {{"des_cfb"}, {&EVP_des_cfb8}}, - {{"des_ecb"}, {&EVP_des_ecb}}, - {{"des_ede3_cbc"}, {&EVP_des_ede3_cbc}}, - {{"des_ede3_cbf"}, + {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}}, + {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}}, + {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}}, + {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}}, + {{"des_ede3_cbf"}, /* Misspelled, retained */ +#ifdef HAVE_DES_ede3_cfb_encrypt + {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} +#else + {NULL} +#endif + }, + {{"des_ede3_cfb"}, #ifdef HAVE_DES_ede3_cfb_encrypt - {&EVP_des_ede3_cfb8} + {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} #else {NULL} #endif @@ -749,7 +764,7 @@ static ERL_NIF_TERM algo_hash[8]; /* increase when extending the list */ static int algo_pubkey_cnt; static ERL_NIF_TERM algo_pubkey[7]; /* increase when extending the list */ static int algo_cipher_cnt; -static ERL_NIF_TERM algo_cipher[20]; /* increase when extending the list */ +static ERL_NIF_TERM algo_cipher[23]; /* increase when extending the list */ static void init_algorithms_types(ErlNifEnv* env) { @@ -785,10 +800,13 @@ static void init_algorithms_types(ErlNifEnv* env) algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); algo_cipher_cnt = 0; +#ifndef OPENSSL_NO_DES algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3"); #ifdef HAVE_DES_ede3_cfb_encrypt algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb"); +#endif #endif algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128"); @@ -800,8 +818,11 @@ static void init_algorithms_types(ErlNifEnv* env) #ifdef HAVE_AES_IGE algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256"); #endif +#ifndef OPENSSL_NO_DES algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb"); +#endif algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64"); diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 5a5627747c..eda0f7af51 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -136,7 +136,7 @@ <code>stream_cipher() = rc4 | aes_ctr </code> <code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc | - blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | rc2_cbc </code> + blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc </code> <code>aead_cipher() = aes_gcm | chacha20_poly1305 </code> @@ -161,7 +161,7 @@ </p> <code> cipher_algorithms() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ctr | aes_gcm | aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc | des_cfb | - des3_cbc | des3_cbf | des_ede3 | rc2_cbc | rc4 </code> + des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code> <code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code> <p>Note that ec_gf2m is not strictly a public key algorithm, but a restriction on what curves are supported with ecdsa and ecdh. diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index 7d3a85326f..56e165a1d1 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -815,7 +815,7 @@ also been extended. </item><item> The <c>configure</c> scripts of <c>erl_interface</c> and <c>odbc</c> now search for thread libraries and thread library quirks the - same way as <c>erts</c> do. </item><item> The + same way as ERTS do. </item><item> The <c>configure</c> script of the <c>odbc</c> application now also looks for odbc libraries in <c>lib64</c> and <c>lib/64</c> directories when building on a 64-bit diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 025d57e9c5..da8626e38a 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -274,7 +274,7 @@ hmac_final_n(Context, HashLen) -> %% Ecrypt/decrypt %%% -spec block_encrypt(des_cbc | des_cfb | - des3_cbc | des3_cbf | des_ede3 | + des3_cbc | des3_cbf | des3_cfb | des_ede3 | blowfish_cbc | blowfish_cfb64 | blowfish_ofb64 | aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | aes_ige256 | aes_cbc | @@ -301,6 +301,9 @@ block_encrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc; block_encrypt(des3_cbf, Key0, Ivec, Data) -> Key = check_des3_key(Key0), block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, true); +block_encrypt(des3_cfb, Key0, Ivec, Data) -> + Key = check_des3_key(Key0), + block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, true); block_encrypt(aes_ige256, Key, Ivec, Data) -> aes_ige_crypt_nif(Key, Ivec, Data, true); block_encrypt(aes_gcm, Key, Ivec, {AAD, Data}) -> @@ -311,7 +314,7 @@ block_encrypt(chacha20_poly1305, Key, Ivec, {AAD, Data}) -> chacha20_poly1305_encrypt(Key, Ivec, AAD, Data). -spec block_decrypt(des_cbc | des_cfb | - des3_cbc | des3_cbf | des_ede3 | + des3_cbc | des3_cbf | des3_cfb | des_ede3 | blowfish_cbc | blowfish_cfb64 | blowfish_ofb64 | aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | aes_ige256 | aes_cbc | @@ -338,6 +341,9 @@ block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc; block_decrypt(des3_cbf, Key0, Ivec, Data) -> Key = check_des3_key(Key0), block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false); +block_decrypt(des3_cfb, Key0, Ivec, Data) -> + Key = check_des3_key(Key0), + block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false); block_decrypt(aes_ige256, Key, Ivec, Data) -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false)); block_decrypt(aes_gcm, Key, Ivec, {AAD, Data, Tag}) -> @@ -857,10 +863,10 @@ des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) -> binary(). des3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) -> - block_encrypt(des3_cbf, [Key1, Key2, Key3], IVec, Data). + block_encrypt(des3_cfb, [Key1, Key2, Key3], IVec, Data). des3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) -> - block_decrypt(des3_cbf, [Key1, Key2, Key3], IVec, Data). + block_decrypt(des3_cfb, [Key1, Key2, Key3], IVec, Data). %% %% Blowfish diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 6732f27824..7b07cef33f 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -50,6 +50,7 @@ all() -> {group, des_cfb}, {group, des3_cbc}, {group, des3_cbf}, + {group, des3_cfb}, {group, des_ede3}, {group, blowfish_cbc}, {group, blowfish_ecb}, @@ -94,6 +95,7 @@ groups() -> {des3_cbc,[], [block]}, {des_ede3,[], [block]}, {des3_cbf,[], [block]}, + {des3_cfb,[], [block]}, {rc2_cbc,[], [block]}, {aes_cbc128,[], [block]}, {aes_cfb8,[], [block]}, @@ -381,11 +383,8 @@ block_cipher({Type, Key, IV, PlainText, CipherText}) -> ct:fail({{crypto, block_decrypt, [Type, Key, IV, CipherText]}, {expected, Plain}, {got, Other1}}) end. -block_cipher_increment({Type, Key, IV, PlainTexts}) when Type == des_cbc; - Type == des3_cbc; - Type == aes_cbc; - Type == des_cbf - -> +block_cipher_increment({Type, Key, IV, PlainTexts}) + when Type == des_cbc; Type == aes_cbc; Type == des3_cbc -> block_cipher_increment(Type, Key, IV, IV, PlainTexts, iolist_to_binary(PlainTexts), []); block_cipher_increment({Type, Key, IV, PlainTexts, _CipherText}) when Type == aes_cbc -> Plain = iolist_to_binary(PlainTexts), @@ -582,6 +581,8 @@ do_block_iolistify({des3_cbc = Type, Key, IV, PlainText}) -> {Type, Key, IV, des_iolistify(PlainText)}; do_block_iolistify({des3_cbf = Type, Key, IV, PlainText}) -> {Type, Key, IV, des_iolistify(PlainText)}; +do_block_iolistify({des3_cfb = Type, Key, IV, PlainText}) -> + {Type, Key, IV, des_iolistify(PlainText)}; do_block_iolistify({des_ede3 = Type, Key, IV, PlainText}) -> {Type, Key, IV, des_iolistify(PlainText)}; do_block_iolistify({Type, Key, PlainText}) -> @@ -792,6 +793,9 @@ group_config(des3_cbc, Config) -> group_config(des3_cbf, Config) -> Block = des3_cbf(), [{block, Block} | Config]; +group_config(des3_cfb, Config) -> + Block = des3_cfb(), + [{block, Block} | Config]; group_config(des_ede3, Config) -> Block = des_ede3(), [{block, Block} | Config]; @@ -1193,7 +1197,16 @@ des_ede3() -> des3_cbf() -> [{des3_cbf, - [hexstr2bin("0123456789abcdef"), + [hexstr2bin("0123456789abcdef"), + hexstr2bin("fedcba9876543210"), + hexstr2bin("0f2d4b6987a5c3e1")], + hexstr2bin("1234567890abcdef"), + <<"Now is the time for all ">> + }]. + +des3_cfb() -> + [{des3_cfb, + [hexstr2bin("0123456789abcdef"), hexstr2bin("fedcba9876543210"), hexstr2bin("0f2d4b6987a5c3e1")], hexstr2bin("1234567890abcdef"), diff --git a/lib/crypto/test/old_crypto_SUITE.erl b/lib/crypto/test/old_crypto_SUITE.erl index 0d97290d10..4a6753b2ed 100644 --- a/lib/crypto/test/old_crypto_SUITE.erl +++ b/lib/crypto/test/old_crypto_SUITE.erl @@ -58,6 +58,7 @@ des_cfb_iter/1, des_ecb/1, des3_cbc/1, + des3_cbf/1, des3_cfb/1, rc2_cbc/1, aes_cfb/1, @@ -102,7 +103,7 @@ groups() -> hmac_rfc2202, hmac_rfc4231_sha224, hmac_rfc4231_sha256, hmac_rfc4231_sha384, hmac_rfc4231_sha512, des_cbc, aes_cfb, aes_cbc, - des_cfb, des_cfb_iter, des3_cbc, des3_cfb, rc2_cbc, + des_cfb, des_cfb_iter, des3_cbc, des3_cbf, des3_cfb, rc2_cbc, aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb, rand_uniform_test, strong_rand_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, @@ -969,6 +970,9 @@ des_cbc(doc) -> des_cbc(suite) -> []; des_cbc(Config) when is_list(Config) -> + if_supported(des_cbc, fun des_cbc_do/0). + +des_cbc_do() -> ?line Key = hexstr2bin("0123456789abcdef"), ?line IVec = hexstr2bin("1234567890abcdef"), ?line Plain = "Now is the time for all ", @@ -992,6 +996,9 @@ des_cbc_iter(doc) -> des_cbc_iter(suite) -> []; des_cbc_iter(Config) when is_list(Config) -> + if_supported(des_cbc, fun des_cbc_iter_do/0). + +des_cbc_iter_do() -> ?line Key = hexstr2bin("0123456789abcdef"), ?line IVec = hexstr2bin("1234567890abcdef"), ?line Plain1 = "Now is the time ", @@ -1011,6 +1018,9 @@ des_cfb(doc) -> des_cfb(suite) -> []; des_cfb(Config) when is_list(Config) -> + if_supported(des_cfb, fun des_cfb_do/0). + +des_cfb_do() -> ?line Key = hexstr2bin("0123456789abcdef"), ?line IVec = hexstr2bin("1234567890abcdef"), ?line Plain = "Now is the", @@ -1027,6 +1037,9 @@ des_cfb_iter(doc) -> des_cfb_iter(suite) -> []; des_cfb_iter(Config) when is_list(Config) -> + if_supported(des_cfb, fun des_cfb_iter_do/0). + +des_cfb_iter_do() -> ?line Key = hexstr2bin("0123456789abcdef"), ?line IVec = hexstr2bin("1234567890abcdef"), ?line Plain1 = "Now i", @@ -1045,6 +1058,9 @@ des_ecb(doc) -> des_ecb(suite) -> []; des_ecb(Config) when is_list(Config) -> + if_supported(des_ecb, fun des_ecb_do/0). + +des_ecb_do() -> ?line Key = hexstr2bin("0123456789abcdef"), ?line Cipher1 = crypto:des_ecb_encrypt(Key, "Now is t"), ?line m(Cipher1, hexstr2bin("3fa40e8a984d4815")), @@ -1081,6 +1097,9 @@ des3_cbc(doc) -> des3_cbc(suite) -> []; des3_cbc(Config) when is_list(Config) -> + if_supported(des3_cbc, fun des3_cbc_do/0). + +des3_cbc_do() -> ?line Key1 = hexstr2bin("0123456789abcdef"), ?line Key2 = hexstr2bin("fedcba9876543210"), ?line Key3 = hexstr2bin("0f2d4b6987a5c3e1"), @@ -1112,6 +1131,19 @@ des3_cbc(Config) when is_list(Config) -> %% %% +des3_cbf(doc) -> + "Encrypt and decrypt according to CFB 3DES, and check the result."; +des3_cbf(suite) -> + []; +des3_cbf(Config) when is_list(Config) -> + case openssl_version() of + V when V < 16#90705F -> {skipped,"OpenSSL version too old"}; + _ -> + if_supported(des3_cbf, fun des3_cfb_do/0) + end. + +%% +%% des3_cfb(doc) -> "Encrypt and decrypt according to CFB 3DES, and check the result."; des3_cfb(suite) -> @@ -1119,7 +1151,8 @@ des3_cfb(suite) -> des3_cfb(Config) when is_list(Config) -> case openssl_version() of V when V < 16#90705F -> {skipped,"OpenSSL version too old"}; - _ -> des3_cfb_do() + _ -> + if_supported(des3_cfb, fun des3_cfb_do/0) end. des3_cfb_do() -> diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile index 6c9617ca69..0f724b6f17 100644 --- a/lib/debugger/doc/src/Makefile +++ b/lib/debugger/doc/src/Makefile @@ -114,7 +114,7 @@ release_docs_spec: docs $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" - (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc") $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" diff --git a/lib/debugger/doc/src/i.xml b/lib/debugger/doc/src/i.xml index db89f23494..628b91e9e4 100644 --- a/lib/debugger/doc/src/i.xml +++ b/lib/debugger/doc/src/i.xml @@ -45,7 +45,7 @@ attached manually or automatically.</p> <p>By preference, these functions can be included in module - <seealso marker="stdlib:shell_default"><c>stdlib:shell_default</c></seealso>. + <seealso marker="stdlib:shell_default"><c>shell_default</c></seealso>. By default, they are included in that module.</p> </description> @@ -372,7 +372,7 @@ </fsummary> <desc> <p>Returns the current version number of the interpreter. - Same as the version number of the <c>Debugger</c> application.</p> + Same as the version number of the Debugger application.</p> </desc> </func> diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 3561454685..3e959e8e30 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -214,7 +214,6 @@ handle_call({new_process, Pid, Meta, Function}, _From, State) -> %% Code loading handle_call({load, Mod, Src, Bin}, _From, State) -> - %% Create an ETS table for storing information about the module Db = State#state.db, ModDb = ets:new(Mod, [ordered_set, public]), @@ -285,23 +284,28 @@ handle_call({contents, Mod, Pid}, _From, State) -> {reply, {ok, Bin}, State}; handle_call({raw_contents, Mod, Pid}, _From, State) -> Db = State#state.db, - [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}), - ModDb = if - Pid =:= any -> hd(ModDbs); - true -> - lists:foldl(fun(T, not_found) -> - [{T, Pids}] = ets:lookup(Db, T), - case lists:member(Pid, Pids) of - true -> T; - false -> not_found - end; - (_T, T) -> T - end, - not_found, - ModDbs) - end, - [{mod_raw, Bin}] = ets:lookup(ModDb, mod_raw), - {reply, {ok, Bin}, State}; + case ets:lookup(Db, {Mod, refs}) of + [{{Mod, refs}, ModDbs}] -> + ModDb = + if + Pid =:= any -> hd(ModDbs); + true -> + lists:foldl(fun(T, not_found) -> + [{T, Pids}] = ets:lookup(Db, T), + case lists:member(Pid, Pids) of + true -> T; + false -> not_found + end; + (_T, T) -> T + end, + not_found, + ModDbs) + end, + [{mod_raw, Bin}] = ets:lookup(ModDb, mod_raw), + {reply, {ok, Bin}, State}; + [] -> % code not interpreted + {reply, not_found, State} + end; handle_call({is_interpreted, Mod, Name, Arity}, _From, State) -> Db = State#state.db, Reply = case ets:lookup(Db, {Mod, refs}) of diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index 6af19af33b..29c8e8cefb 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -818,11 +818,14 @@ gui_show_module(Win, Mod, Line, _Cm, Pid, How) -> gui_load_module(Win, Mod, _Pid) -> dbg_wx_trace_win:display(Win,{text, "Loading module..."}), - %% Contents = int:contents(Mod, Pid), - {ok, Contents} = dbg_iserver:call({raw_contents, Mod, any}), - Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents), - dbg_wx_trace_win:display(Win,{text, ""}), - Win2. + case dbg_iserver:call({raw_contents, Mod, any}) of + {ok, Contents} -> + Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents), + dbg_wx_trace_win:display(Win,{text, ""}), + Win2; + not_found -> + dbg_wx_trace_win:show_no_code(Win) + end. gui_update_bindings(Win,Meta) -> Bs = int:meta(Meta, bindings, nostack), diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl index 91fc0d08cb..86d009238f 100644 --- a/lib/debugger/src/dbg_wx_view.erl +++ b/lib/debugger/src/dbg_wx_view.erl @@ -263,7 +263,11 @@ shortcut(_) -> false. gui_load_module(Win, Mod) -> dbg_wx_trace_win:display(Win,{text, "Loading module..."}), - {ok, Contents} = dbg_iserver:call({raw_contents, Mod, any}), - Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents), - dbg_wx_trace_win:display(Win,{text, ""}), - Win2. + case dbg_iserver:call({raw_contents, Mod, any}) of + {ok, Contents} -> + Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents), + dbg_wx_trace_win:display(Win,{text, ""}), + Win2; + not_found -> + dbg_wx_trace_win:show_no_code(Win) + end. diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index a5a52fee61..6400072b1f 100644 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -32,6 +32,20 @@ <p>This document describes the changes made to the Dialyzer application.</p> +<section><title>Dialyzer 3.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fix a map related bug.</p> + <p> + Own Id: OTP-13709 Aux Id: ERL-177, PR-1115 </p> + </item> + </list> + </section> + +</section> + <section><title>Dialyzer 3.0</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index bcac8afe64..7f86520c06 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -282,7 +282,7 @@ cl_check_log(none) -> cl_check_log(Output) -> io:format(" Check output file `~s' for details\n", [Output]). --spec format_warning(raw_warning()) -> string(). +-spec format_warning(raw_warning() | dial_warning()) -> string(). format_warning(W) -> format_warning(W, basename). diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 976a2b8955..a72368f9f8 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -768,19 +768,9 @@ picky_contract_check(CSig0, Sig0, MFA, WarningInfo, Contract, RecDict, Acc) -> end. extra_contract_warning(MFA, WarningInfo, Contract, CSig, Sig, RecDict) -> - %% We do not want to depend upon erl_types:t_to_string() possibly - %% hiding the contents of opaque types. - SigUnopaque = erl_types:t_unopaque(Sig), - CSigUnopaque = erl_types:t_unopaque(CSig), - SigString0 = - lists:flatten(dialyzer_utils:format_sig(SigUnopaque, RecDict)), - ContractString0 = - lists:flatten(dialyzer_utils:format_sig(CSigUnopaque, RecDict)), - %% The only difference is in record fields containing 'undefined' or not. - IsUndefRecordFieldsRelated = SigString0 =:= ContractString0, {IsRemoteTypesRelated, SubtypeRelation} = is_remote_types_related(Contract, CSig, Sig, MFA, RecDict), - case IsUndefRecordFieldsRelated orelse IsRemoteTypesRelated of + case IsRemoteTypesRelated of true -> no_warning; false -> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 9399789464..963c953447 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -978,12 +978,21 @@ handle_case(Tree, Map, State) -> false -> State1 end, Map2 = join_maps_begin(Map1), - {MapList, State3, Type} = + {MapList, State3, Type, Warns} = handle_clauses(Clauses, Arg, ArgType, ArgType, State2, - [], Map2, [], []), + [], Map2, [], [], []), + %% Non-Erlang BEAM languages, such as Elixir, expand language constructs + %% into case statements. In that case, we do not want to warn on + %% individual clauses not matching unless none of them can. + SupressForced = is_compiler_generated(cerl:get_ann(Tree)) + andalso not (t_is_none(Type)), + State4 = lists:foldl(fun({T,R,M,F}, S) -> + state__add_warning( + S,T,R,M,F andalso (not SupressForced)) + end, State3, Warns), Map3 = join_maps_end(MapList, Map2), debug_pp_map(Map3), - {State3, Map3, Type} + {State4, Map3, Type} end. %%---------------------------------------- @@ -1082,22 +1091,24 @@ handle_receive(Tree, Map, State) -> RaceListSize + 1, State); false -> State end, - {MapList, State2, ReceiveType} = + {MapList, State2, ReceiveType, Warns} = handle_clauses(Clauses, ?no_arg, t_any(), t_any(), State1, [], Map, - [], []), + [], [], []), + State3 = lists:foldl(fun({T,R,M,F}, S) -> state__add_warning(S,T,R,M,F) end, + State2, Warns), Map1 = join_maps(MapList, Map), - {State3, Map2, TimeoutType} = traverse(Timeout, Map1, State2), - Opaques = State3#state.opaques, + {State4, Map2, TimeoutType} = traverse(Timeout, Map1, State3), + Opaques = State4#state.opaques, case (t_is_atom(TimeoutType, Opaques) andalso (t_atom_vals(TimeoutType, Opaques) =:= ['infinity'])) of true -> - {State3, Map2, ReceiveType}; + {State4, Map2, ReceiveType}; false -> Action = cerl:receive_action(Tree), - {State4, Map3, ActionType} = traverse(Action, Map, State3), + {State5, Map3, ActionType} = traverse(Action, Map, State4), Map4 = join_maps([Map3, Map1], Map), Type = t_sup(ReceiveType, ActionType), - {State4, Map4, Type} + {State5, Map4, Type} end. %%---------------------------------------- @@ -1245,7 +1256,7 @@ handle_tuple(Tree, Map, State) -> %% Clauses %% handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, - Acc, ClauseAcc) -> + Acc, ClauseAcc, WarnAcc0) -> IsRaceAnalysisEnabled = is_race_analysis_enabled(State), State1 = case IsRaceAnalysisEnabled of @@ -1258,8 +1269,8 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, State); false -> State end, - {State2, ClauseMap, BodyType, NewArgType} = - do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1), + {State2, ClauseMap, BodyType, NewArgType, WarnAcc} = + do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1, WarnAcc0), {NewClauseAcc, State3} = case IsRaceAnalysisEnabled of true -> @@ -1277,9 +1288,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, false -> {[BodyType|CaseTypes], [ClauseMap|Acc]} end, handle_clauses(Left, Arg, NewArgType, OrigArgType, State3, - NewCaseTypes, MapIn, NewAcc, NewClauseAcc); + NewCaseTypes, MapIn, NewAcc, NewClauseAcc, WarnAcc); handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, - ClauseAcc) -> + ClauseAcc, WarnAcc) -> State1 = case is_race_analysis_enabled(State) of true -> @@ -1289,9 +1300,9 @@ handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, RaceListSize + 1, State); false -> State end, - {lists:reverse(Acc), State1, t_sup(CaseTypes)}. + {lists:reverse(Acc), State1, t_sup(CaseTypes), WarnAcc}. -do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> +do_clause(C, Arg, ArgType0, OrigArgType, Map, State, Warns) -> Pats = cerl:clause_pats(C), Guard = cerl:clause_guard(C), Body = cerl:clause_body(C), @@ -1323,7 +1334,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> [cerl_prettypr:format(C), format_type(ArgType0, State1)]), case state__warning_mode(State1) of false -> - {State1, Map, t_none(), ArgType0}; + {State1, Map, t_none(), ArgType0, Warns}; true -> {Msg, Force} = case t_is_none(ArgType0) of @@ -1403,8 +1414,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> {record_match, _} -> ?WARN_MATCHING; {pattern_match_cov, _} -> ?WARN_MATCHING end, - {state__add_warning(State1, WarnType, C, Msg, Force), - Map, t_none(), ArgType0} + {State1, Map, t_none(), ArgType0, [{WarnType, C, Msg, Force}|Warns]} end; {Map2, PatTypes} -> Map3 = @@ -1437,9 +1447,9 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> false -> {guard_fail_pat, [PatString, format_type(ArgType0, State1)]} end, - State2 = + Warn = case Reason of - none -> state__add_warning(State1, ?WARN_MATCHING, C, DefaultMsg); + none -> {?WARN_MATCHING, C, DefaultMsg, false}; {FailGuard, Msg} -> case is_compiler_generated(cerl:get_ann(FailGuard)) of false -> @@ -1448,15 +1458,15 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> {neg_guard_fail, _} -> ?WARN_MATCHING; {opaque_guard, _} -> ?WARN_OPAQUE end, - state__add_warning(State1, WarnType, FailGuard, Msg); + {WarnType, FailGuard, Msg, false}; true -> - state__add_warning(State1, ?WARN_MATCHING, C, Msg) + {?WARN_MATCHING, C, Msg, false} end end, - {State2, Map, t_none(), NewArgType}; + {State1, Map, t_none(), NewArgType, [Warn|Warns]}; Map4 -> {RetState, RetMap, BodyType} = traverse(Body, Map4, State1), - {RetState, RetMap, BodyType, NewArgType} + {RetState, RetMap, BodyType, NewArgType, Warns} end end. diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile index f43e04dd59..0d8fba438c 100644 --- a/lib/dialyzer/test/Makefile +++ b/lib/dialyzer/test/Makefile @@ -12,6 +12,7 @@ AUXILIARY_FILES=\ dialyzer_common.erl\ file_utils.erl\ dialyzer_SUITE.erl\ + abstract_SUITE.erl\ plt_SUITE.erl # ---------------------------------------------------- diff --git a/lib/dialyzer/test/abstract_SUITE.erl b/lib/dialyzer/test/abstract_SUITE.erl new file mode 100644 index 0000000000..269db3e836 --- /dev/null +++ b/lib/dialyzer/test/abstract_SUITE.erl @@ -0,0 +1,102 @@ +%% This suite contains cases that cannot be written +%% in Erlang itself and must be done via the abstract +%% format. + +-module(abstract_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include("dialyzer_test_constants.hrl"). + +-export([suite/0, all/0, init_per_suite/0, init_per_suite/1]). +-export([generated_case/1]). + +suite() -> + [{timetrap, {minutes, 1}}]. +all() -> + [generated_case]. + +init_per_suite() -> + [{timetrap, ?plt_timeout}]. +init_per_suite(Config) -> + OutDir = ?config(priv_dir, Config), + case dialyzer_common:check_plt(OutDir) of + fail -> {skip, "Plt creation/check failed."}; + ok -> [{dialyzer_options, []}|Config] + end. + +generated_case(Config) when is_list(Config) -> + %% Equivalent to: + %% + %% -module(foo). + %% -export(bar). + %% bar() -> + %% Arg = sample, + %% case Arg of + %% #{} -> map; + %% _ -> Arg:fn() + %% end. + %% + %% Except the case statement and its clauses are marked as autogenerated. + [] = + test([{attribute,1,module,foo}, + {attribute,2,export,[{bar,0}]}, + {function,3,bar,0, + [{clause,3,[],[], + [{match,4,{var,4,'Arg'},{atom,4,sample}}, + {'case',[{location,5},{generated,true}],{var,5,'Arg'}, + [{clause,[{location,6},{generated,true}],[{map,6,[]}],[], + [{atom,6,map}]}, + {clause,[{location,7},{generated,true}],[{var,7,'_'}],[], + [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}], + Config, [], []), + %% With the first clause not auto-generated + [{warn_matching,{_,6},_}] = + test([{attribute,1,module,foo}, + {attribute,2,export,[{bar,0}]}, + {function,3,bar,0, + [{clause,3,[],[], + [{match,4,{var,4,'Arg'},{atom,4,sample}}, + {'case',[{location,5},{generated,true}],{var,5,'Arg'}, + [{clause,6,[{map,6,[]}],[], + [{atom,6,map}]}, + {clause,[{location,7},{generated,true}],[{var,7,'_'}],[], + [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}], + Config, [], []), + %% With Arg set to [] so neither clause matches + [{warn_return_no_exit,{_,3},_}, + {warn_matching,{_,6},_}, + {warn_failing_call,{_,7},_}] = + test([{attribute,1,module,foo}, + {attribute,2,export,[{bar,0}]}, + {function,3,bar,0, + [{clause,3,[],[], + [{match,4,{var,4,'Arg'},{nil,4}}, + {'case',[{location,5},{generated,true}],{var,5,'Arg'}, + [{clause,[{location,6},{generated,true}],[{map,6,[]}],[], + [{atom,6,map}]}, + {clause,[{location,7},{generated,true}],[{var,7,'_'}],[], + [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}], + Config, [], []), + ok. + +test(Prog, Config, COpts, DOpts) -> + {ok, BeamFile} = compile(Config, Prog, COpts), + run_dialyzer(Config, succ_typings, [BeamFile], DOpts). + +compile(Config, Prog, CompileOpts) -> + OutDir = ?config(priv_dir, Config), + Opts = [{outdir, OutDir}, debug_info, return_errors | CompileOpts], + {ok, Module, Source} = compile:forms(Prog, Opts), + BeamFile = filename:join([OutDir, lists:concat([Module, ".beam"])]), + ok = file:write_file(BeamFile, Source), + {ok, BeamFile}. + +run_dialyzer(Config, Analysis, Files, Opts) -> + OutDir = ?config(priv_dir, Config), + PltFilename = dialyzer_common:plt_file(OutDir), + dialyzer:run([{analysis_type, Analysis}, + {files, Files}, + {init_plt, PltFilename}, + {check_plt, false}, + {from, byte_code} | + Opts]). diff --git a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options index 50991c9bc5..cb6a88786e 100644 --- a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options +++ b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options @@ -1 +1,2 @@ {dialyzer_options, []}. +{time_limit, 2}. diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl index d2b1026c06..48083a2731 100644 --- a/lib/dialyzer/test/dialyzer_common.erl +++ b/lib/dialyzer/test/dialyzer_common.erl @@ -7,7 +7,7 @@ -module(dialyzer_common). --export([check_plt/1, check/4, create_all_suites/0, new_tests/2]). +-export([check_plt/1, check/4, create_all_suites/0, new_tests/2, plt_file/1]). -include_lib("kernel/include/file.hrl"). @@ -39,7 +39,7 @@ check_plt(OutDir) -> io:format("Checking plt:"), - PltFilename = filename:join(OutDir, ?plt_filename), + PltFilename = plt_file(OutDir), case file:read_file_info(PltFilename) of {ok, _} -> dialyzer_check_plt(PltFilename); {error, _ } -> @@ -63,6 +63,11 @@ check_plt(OutDir) -> end end. +-spec plt_file(string()) -> string(). + +plt_file(OutDir) -> + filename:join(OutDir, ?plt_filename). + dialyzer_check_plt(PltFilename) -> try dialyzer:run([{analysis_type, plt_check}, {init_plt, PltFilename}]) of @@ -119,7 +124,7 @@ build_plt(PltFilename) -> 'same' | {differ, [term()]}. check(TestCase, Opts, Dir, OutDir) -> - PltFilename = filename:join(OutDir, ?plt_filename), + PltFilename = plt_file(OutDir), SrcDir = filename:join(Dir, ?input_files_directory), ResDir = filename:join(Dir, ?result_files_directory), Filename = filename:join(SrcDir, atom_to_list(TestCase)), diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl new file mode 100644 index 0000000000..827984b20b --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl @@ -0,0 +1,9 @@ +-module(a). +-export([to_map/1, to_map/2]). +-type t() :: #{type := b:t()}. + +-spec to_map(t()) -> map(). +to_map(Resource) -> to_map(Resource, #{}). + +-spec to_map(t(), map()) -> map(). +to_map(_, Map) when is_map(Map) -> #{}. diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl new file mode 100644 index 0000000000..31f9bb6b3e --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl @@ -0,0 +1,3 @@ +-module(b). +-export_type([t/0]). +-type t() :: binary(). diff --git a/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl b/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl new file mode 100644 index 0000000000..40214a1887 --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl @@ -0,0 +1,13 @@ +-module(opaque_bif). +-export([o1/1]). +-export_type([opaque_any_map/0]). +-opaque opaque_any_map() :: map(). + +%% ERL-249: A bug with opaque arguments to maps:merge/2 +%% Reported by Felipe Ripoll on 6/9/2016 +-spec o1(opaque_any_map()) -> opaque_any_map(). +o1(Map) -> + maps:merge(o1_c(), Map). + +-spec o1_c() -> opaque_any_map(). +o1_c() -> #{}. diff --git a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options index 3ff26b87db..ffdf8270c8 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options +++ b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options @@ -1 +1,2 @@ {dialyzer_options, [{warnings, [no_unused, no_return]}]}. +{time_limit, 2}. diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 077fe01e85..b9a28afdd9 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 3.0 +DIALYZER_VSN = 3.0.1 diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 7f61620fc1..6bf748a727 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -123,7 +123,7 @@ ERL_COMPILE_FLAGS += \ # erl/hrl from dictionary file. gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia $(dia_verbose) \ - ../bin/diameterc -o gen -i $(EBIN) $< + escript ../bin/diameterc -o gen -i $(EBIN) $< opt: $(TARGET_FILES) diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 4a005b853d..f48e4347ee 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -98,7 +98,7 @@ -record(listener, {ref :: reference(), socket :: gen_sctp:sctp_socket(), - count = 0 :: uint(), %% attached transport processes + service = false :: false | pid(), %% service process pending = {0, queue:new()}, accept :: [match()]}). %% Field pending implements two queues: the first of transport-to-be @@ -129,11 +129,14 @@ -> {ok, pid(), [inet:ip_address()]} when Ref :: diameter:transport_ref(). -start(T, #diameter_service{capabilities = Caps}, Opts) +start(T, Svc, Opts) when is_list(Opts) -> + #diameter_service{capabilities = Caps, + pid = SPid} + = Svc, diameter_sctp_sup:start(), %% start supervisors on demand Addrs = Caps#diameter_caps.host_ip_address, - s(T, Addrs, lists:map(fun ip/1, Opts)). + s(T, Addrs, SPid, lists:map(fun ip/1, Opts)). ip({ifaddr, A}) -> {ip, A}; @@ -144,18 +147,22 @@ ip(T) -> %% when there is not yet an association to assign it, or at comm_up on %% a new association in which case the call retrieves a transport from %% the pending queue. -s({accept, Ref} = A, Addrs, Opts) -> - {LPid, LAs} = listener(Ref, {Opts, Addrs}), - try gen_server:call(LPid, {A, self()}, infinity) of - {ok, TPid} -> {ok, TPid, LAs} +s({accept, Ref} = A, Addrs, SPid, Opts) -> + {ok, LPid, LAs} = listener(Ref, {Opts, Addrs}), + try gen_server:call(LPid, {A, self(), SPid}, infinity) of + {ok, TPid} -> + {ok, TPid, LAs}; + No -> + {error, No} catch - exit: Reason -> {error, Reason} + exit: Reason -> + {error, Reason} end; %% This implementation is due to there being no accept call in %% gen_sctp in order to be able to accept a new association only %% *after* an accepting transport has been spawned. -s({connect = C, Ref}, Addrs, Opts) -> +s({connect = C, Ref}, Addrs, _SPid, Opts) -> diameter_sctp_sup:start_child({C, self(), Opts, Addrs, Ref}). %% start_link/1 @@ -281,24 +288,23 @@ i({K, Ref}, #transport{mode = {accept, _}} = S) -> %% Accepting processes can be started concurrently: ensure only one %% listener is started. -listener(LRef, T) -> - diameter_sync:call({?MODULE, listener, LRef}, - {?MODULE, listener, [{LRef, T}]}, +listener(Ref, T) -> + diameter_sync:call({?MODULE, listener, Ref}, + {?MODULE, listener, [{Ref, T}]}, infinity, infinity). -listener({LRef, T}) -> - l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T). +listener({Ref, T}) -> + l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T). %% Existing listening process ... l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) -> - {LAs, _Sock} = AS, - {LPid, LAs}; + {LAs, _Sock} = AS, + {ok, LPid, LAs}; %% ... or not. -l([], LRef, T) -> - {ok, LPid, LAs} = diameter_sctp_sup:start_child({listen, LRef, T}), - {LPid, LAs}. +l([], Ref, T) -> + diameter_sctp_sup:start_child({listen, Ref, T}). %% open/3 @@ -364,11 +370,17 @@ type(T) -> %% # handle_call/3 %% --------------------------------------------------------------------------- -handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref, - count = K} - = S) -> +handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref} = S) -> {TPid, NewS} = accept(Ref, Pid, S), - {reply, {ok, TPid}, NewS#listener{count = K+1}}; + {reply, {ok, TPid}, NewS}; + +handle_call({{accept, _} = T, Pid, SPid}, From, #listener{service = P} = S) -> + handle_call({T, Pid}, From, if not is_pid(P), is_pid(SPid) -> + monitor(process, SPid), + S#listener{service = SPid}; + true -> + S + end); handle_call(_, _, State) -> {reply, nok, State}. @@ -441,6 +453,13 @@ l({sctp, Sock, _RA, _RP, Data} = T, #listener{socket = Sock, setopts(Sock), NewS; +%% Service process has died. +l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid, + socket = Sock}) -> + gen_sctp:close(Sock), + x(T); + +%% Accepting process has died. l({'DOWN', _MRef, process, TPid, _}, #listener{pending = {_,Q}} = S) -> down(queue:member(TPid, Q), TPid, S); @@ -454,20 +473,17 @@ l({transport, remove, _} = T, #listener{socket = Sock}) -> %% Accepting transport has died. %% One that's waiting for transport start in the pending queue ... -down(true, TPid, #listener{pending = {N,Q}, - count = K} - = S) -> +down(true, TPid, #listener{pending = {N,Q}} = S) -> NQ = queue:filter(fun(P) -> P /= TPid end, Q), if N < 0 -> %% awaiting an association ... - S#listener{count = K-1, - pending = {N+1, NQ}}; + S#listener{pending = {N+1, NQ}}; true -> %% ... or one has been assigned S#listener{pending = {N-1, NQ}} end; %% ... or one that's already attached. -down(false, _TPid, #listener{count = K} = S) -> - S#listener{count = K-1}. +down(false, _TPid, S) -> + S. %% t/2 %% diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index 546c2cfa5e..44abc5c3b4 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -71,11 +71,8 @@ %% a process owning the listening port. %% Listener process state. --record(listener, {socket :: inet:socket(), - count = 1 :: non_neg_integer()}). %% accepting processes -%% The count of accepting processes was previously used to terminate -%% the listening process, but diameter_reg:subscribe/2 is now used for -%% this. Leave the the count for trace purposes. +-record(listener, {socket :: inet:socket(), + service = false :: false | pid()}). %% service process %% Monitor process state. -record(monitor, @@ -138,11 +135,15 @@ | {ok, pid()} when Ref :: diameter:transport_ref(). -start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) -> +start({T, Ref}, Svc, Opts) -> + #diameter_service{capabilities = Caps, + pid = SPid} + = Svc, + diameter_tcp_sup:start(), %% start tcp supervisors on demand {Mod, Rest} = split(Opts), Addrs = Caps#diameter_caps.host_ip_address, - Arg = {T, Ref, Mod, self(), Rest, Addrs}, + Arg = {T, Ref, Mod, self(), Rest, Addrs, SPid}, diameter_tcp_sup:start_child(Arg). split([{module, M} | Opts]) -> @@ -196,7 +197,7 @@ init(T) -> %% i/1 %% A transport process. -i({T, Ref, Mod, Pid, Opts, Addrs}) +i({T, Ref, Mod, Pid, Opts, Addrs, SPid}) when T == accept; T == connect -> monitor(process, Pid), @@ -214,7 +215,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) ?DEFAULT_FRAGMENT_TIMEOUT), ?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}), Throttle = proplists:get_value(throttle_cb, OwnOpts, false), - Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs), + Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs, SPid), MPid ! {stop, self()}, %% tell the monitor to die M = if SslOpts -> ssl; true -> Mod end, putr(?REF_KEY, Ref), @@ -228,6 +229,11 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) %% Put the reference in the process dictionary since we now use it %% advertise the ssl socket after TLS upgrade. +i({T, _Ref, _Mod, _Pid, _Opts, _Addrs} = Arg) %% from old code + when T == accept; + T == connect -> + i(erlang:append_element(Arg, _SPid = false)); + %% A monitor process to kill the transport if the parent dies. i(#monitor{parent = Pid, transport = TPid} = S) -> proc_lib:init_ack({ok, self()}), @@ -240,16 +246,18 @@ i(#monitor{parent = Pid, transport = TPid} = S) -> %% death. However, a link can be unlinked and this is exactly what %% gen_tcp seems to so. Links should be left to supervisors. -i({listen, LRef, APid, {Mod, Opts, Addrs}}) -> - [_] = diameter_config:subscribe(LRef, transport), %% assert existence +i({listen = L, Ref, _APid, T}) -> %% from old code + i({L, Ref, T}); + +i({listen, Ref, {Mod, Opts, Addrs}}) -> + [_] = diameter_config:subscribe(Ref, transport), %% assert existence {[LA, LP], Rest} = proplists:split(Opts, [ip, port]), LAddrOpt = get_addr(LA, Addrs), LPort = get_port(LP), {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)), LAddr = laddr(LAddrOpt, Mod, LSock), - true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}), + true = diameter_reg:add_new({?MODULE, listener, {Ref, {LAddr, LSock}}}), proc_lib:init_ack({ok, self(), {LAddr, LSock}}), - monitor(process, APid), #listener{socket = LSock}. laddr([], Mod, Sock) -> @@ -268,21 +276,22 @@ ssl_opts([{ssl_options, Opts}]) ssl_opts(T) -> ?ERROR({ssl_options, T}). -%% init/7 +%% init/8 %% Establish a TLS connection before capabilities exchange ... -init(Type, Ref, Mod, Pid, true, Opts, Addrs) -> - init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs); +init(Type, Ref, Mod, Pid, true, Opts, Addrs, SPid) -> + init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs, SPid); %% ... or not. -init(Type, Ref, Mod, Pid, _, Opts, Addrs) -> - init(Type, Ref, Mod, Pid, Opts, Addrs). +init(Type, Ref, Mod, Pid, _, Opts, Addrs, SPid) -> + init(Type, Ref, Mod, Pid, Opts, Addrs, SPid). -%% init/6 +%% init/7 -init(accept = T, Ref, Mod, Pid, Opts, Addrs) -> +init(accept = T, Ref, Mod, Pid, Opts, Addrs, SPid) -> {[Matches], Rest} = proplists:split(Opts, [accept]), - {LAddr, LSock} = listener(Ref, {Mod, Rest, Addrs}), + {ok, LPid, {LAddr, LSock}} = listener(Ref, {Mod, Rest, Addrs}), + ok = gen_server:call(LPid, {accept, SPid}, infinity), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(accept(Mod, LSock)), ok = accept_peer(Mod, Sock, accept(Matches)), @@ -290,7 +299,7 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs) -> diameter_peer:up(Pid), Sock; -init(connect = T, Ref, Mod, Pid, Opts, Addrs) -> +init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SPid) -> {[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]), LAddrOpt = get_addr(LA, Addrs), RAddr = get_addr(RA), @@ -344,24 +353,26 @@ accept(Opts) -> %% Accepting processes can be started concurrently: ensure only one %% listener is started. -listener(LRef, T) -> - diameter_sync:call({?MODULE, listener, LRef}, - {?MODULE, listener, [{LRef, T, self()}]}, +listener(Ref, T) -> + diameter_sync:call({?MODULE, listener, Ref}, + {?MODULE, listener, [{Ref, T, self()}]}, infinity, infinity). -listener({LRef, T, TPid}) -> - l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T, TPid). +%% listener/1 + +listener({Ref, T, _TPid}) -> + l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T). + +%% l/3 %% Existing listening process ... -l([{{?MODULE, listener, {_, AS}}, LPid}], _, _, TPid) -> - LPid ! {accept, TPid}, - AS; +l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) -> + {ok, LPid, AS}; %% ... or not. -l([], LRef, T, TPid) -> - {ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, TPid, T}), - AS. +l([], Ref, T) -> + diameter_tcp_sup:start_child({listen, Ref, T}). %% get_addr/1 @@ -440,6 +451,14 @@ portnr(Sock) -> %% # handle_call/3 %% --------------------------------------------------------------------------- +handle_call({accept, SPid}, _From, #listener{service = P} = S) -> + {reply, ok, if not is_pid(P), is_pid(SPid) -> + monitor(process, SPid), + S#listener{service = SPid}; + true -> + S + end}; + handle_call(_, _, State) -> {reply, nok, State}. @@ -507,19 +526,20 @@ m({'DOWN', _, process, Pid, _}, #monitor{parent = Pid, %% %% Transition listener state. -%% An accepting transport is attaching. -l({accept, TPid}, #listener{count = N} = S) -> - monitor(process, TPid), - S#listener{count = N+1}; - -%% Accepting process has died. -l({'DOWN', _, process, _, _}, #listener{count = N} = S) -> - S#listener{count = N-1}; +%% Service process has died. +l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid, + socket = Sock}) -> + gen_tcp:close(Sock), + x(T); %% Transport has been removed. l({transport, remove, _} = T, #listener{socket = Sock}) -> gen_tcp:close(Sock), - x(T). + x(T); + +%% Possibly death of an accepting process monitored in old code. +l(_, S) -> + S. %% t/2 %% diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index 37fcbbc267..cca28dd23c 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -195,13 +195,21 @@ unique_string() -> %% have_sctp/0 have_sctp() -> - case gen_sctp:open() of - {ok, Sock} -> - gen_sctp:close(Sock), - true; - {error, E} when E == eprotonosupport; - E == esocktnosupport -> %% fail on any other reason - false + case erlang:system_info(system_architecture) of + %% We do not support the sctp version present in solaris + %% version "sparc-sun-solaris2.10", that behaves differently + %% from later versions and linux + "sparc-sun-solaris2.10" -> + false; + _-> + case gen_sctp:open() of + {ok, Sock} -> + gen_sctp:close(Sock), + true; + {error, E} when E == eprotonosupport; + E == esocktnosupport -> %% fail on any other reason + false + end end. %% --------------------------------------------------------------------------- diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl index e7a4c36ca4..2d6cb04a6d 100644 --- a/lib/edoc/src/edoc_extract.erl +++ b/lib/edoc/src/edoc_extract.erl @@ -32,9 +32,9 @@ %% %% @headerfile "edoc.hrl" (disabled until it can be made private) -include("edoc.hrl"). -%% @type filename() = file:filename(). -%% @type proplist() = proplists:property(). -%% @type syntaxTree() = erl_syntax:syntaxTree(). +%% @type filename() = //kernel/file:filename(). +%% @type proplist() = //stdlib/proplists:property(). +%% @type syntaxTree() = //syntax_tools/erl_syntax:syntaxTree(). %% @spec source(File::filename(), Env::edoc_env(), Options::proplist()) %% -> {ModuleName, edoc:edoc_module()} @@ -639,11 +639,11 @@ file_macros(_Context, Env) -> %% %% The idea is to mimic how the @type tag works. %% Using @type: -%% @type t() = t1(). Some docs of t/0; -%% Further docs of t/0. +%%```@type t() = t1(). Some docs of t/0; +%% Further docs of t/0.''' %% The same thing using -type: -%% -type t() :: t1(). % Some docs of t/0; -%% Further docs of t/0. +%%```-type t() :: t1(). % Some docs of t/0; +%% Further docs of t/0.''' find_type_docs(Forms0, Comments, Env, File) -> Tree = erl_recomment:recomment_forms(Forms0, Comments), Forms = preprocess_forms(Tree), diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index dcc239f6b4..cc0a8d0b94 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -935,7 +935,7 @@ get_doc_env(Opts) -> %% Modules = [atom()] %% proplist() = [term()] %% -%% @type proplist() = proplists:property(). +%% @type proplist() = //stdlib/proplists:property(). %% @type edoc_env(). Environment information needed by EDoc for %% generating references. The data representation is not documented. %% diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl index 9e2e41e902..93f423b906 100644 --- a/lib/edoc/src/edoc_tags.erl +++ b/lib/edoc/src/edoc_tags.erl @@ -213,8 +213,10 @@ filter_tags([#tag{name = N, line = L} = T | Ts], Tags, Where, Ts1) -> true -> filter_tags(Ts, Tags, Where, [T | Ts1]); false -> - [warning(L, Where, "tag @~s not recognized.", [N]) || - Where =/= no], + case Where of + no -> ok; + _ -> warning(L, Where, "tag @~s not recognized.", [N]) + end, filter_tags(Ts, Tags, Where, Ts1) end; filter_tags([], _, _, Ts) -> @@ -451,7 +453,7 @@ check_type(#tag{line = L, data = Data}, P0, Ls, Ts) -> check_type(#t_def{type = Type}, P, Ls, Ts) -> check_type(Type, P, Ls, Ts); check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) -> - _ = check_used_type(Name, Args, P, Ls), + check_used_type(Name, Args, P, Ls), check_types3(Args++Ts, P, Ls); check_type(#t_var{}, P, Ls, Ts) -> check_types3(Ts, P, Ls); @@ -503,7 +505,8 @@ check_used_type(#t_name{name = N, module = Mod}=Name, Args, P, LocalTypes) -> false -> #parms{warn = W, line = L, file = File} = P, %% true = ets:insert(DT, TypeName), - [type_warning(L, File, "missing type", N, NArgs) || W] + _ = [type_warning(L, File, "missing type", N, NArgs) || W], + ok end. type_warning(Line, File, S, N, NArgs) -> diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript index ac4278bf22..4a418fe144 100755 --- a/lib/erl_docgen/priv/bin/xref_mod_app.escript +++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript @@ -84,7 +84,7 @@ preloaded(TopDir) -> %% It's OK if too much data is generated as long as all applications %% and all modules are mentioned. appmods(D) -> - ErlFiles = filelib:wildcard(filename:join([D,"src","*.erl"])), + ErlFiles = filelib:wildcard(filename:join([D,"src","**","*.erl"])), AppV = filename:basename(D), App = case string:rstr(AppV, "-") of 0 -> AppV; diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd index 835407520a..615b88b61a 100644 --- a/lib/erl_docgen/priv/dtd/erlref.dtd +++ b/lib/erl_docgen/priv/dtd/erlref.dtd @@ -29,7 +29,7 @@ <!-- `name' is used in common.refs.dtd and must therefore be defined in each *ref. dtd --> -<!ELEMENT name (#PCDATA) > +<!ELEMENT name (#PCDATA|seealso)* > <!ATTLIST name name CDATA #IMPLIED arity CDATA #IMPLIED clause_i CDATA #IMPLIED diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index fe6c7d3c28..edab8e1c7e 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -2007,10 +2007,10 @@ </xsl:variable> <xsl:choose> <xsl:when test="ancestor::datatype"> - <a name="type-{$fname}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/> + <a name="type-{$fname}"></a><span class="bold_code"><xsl:apply-templates/></span><br/> </xsl:when> <xsl:otherwise> - <a name="{$fname}-{$arity}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/> + <a name="{$fname}-{$arity}"></a><span class="bold_code"><xsl:apply-templates/></span><br/> </xsl:otherwise> </xsl:choose> </xsl:when> diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl index 0ac7985a48..4f3af1f767 100644 --- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl +++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2015. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -563,7 +563,14 @@ otp_xmlify_a_fileref(FileRef1, AppS) -> true -> case split(Marker0, "-") of [Func,Arity] -> - Func++"/"++Arity; + try list_to_integer(Arity) of + _ -> + Func++"/"++Arity + catch + _:_ -> + %% This is "type-"<a-type>. + Marker0 + end; _ -> Marker0 end @@ -831,19 +838,49 @@ local_types([]) -> []; local_types(Es) -> local_defs2(get_elem(localdef, Es)). +-define(LOCAL_TYPES, edoc_local_defs). + local_defs2([]) -> []; local_defs2(Es) -> + case collect_local_types(Es) of + [] -> local_defs3(Es); + LocalTypes -> + ?LOCAL_TYPES = ets:new(?LOCAL_TYPES, [named_table]), + true = ets:insert(?LOCAL_TYPES, LocalTypes), + try + local_defs3(Es) + after + ets:delete(?LOCAL_TYPES) + end + end. + +local_defs3(Es) -> {type,[?NL | [{v, localdef2(E)} || E <- Es]]}. +%% Does not work well for parametrized types. +collect_local_types(Es) -> + lists:append([collect_local_type(E) || E <- Es]). + +collect_local_type(#xmlElement{content = Es}) -> + case get_elem(typevar, Es) of + [] -> + [{t_abstype(get_content(abstype, Es))}]; + [_] -> + [] + end. + %% Like localdef/1, but does not use label_anchor/2 -- we don't want any %% markers or em tags in <v> tag, plain text only! +%% When used stand-alone, EDoc generates links to local types. An ETS +%% table holds local types, to avoid generating links to them. localdef2(#xmlElement{content = Es}) -> - case get_elem(typevar, Es) of - [] -> - t_utype(get_elem(type, Es)); - [V] -> - t_var(V) ++ [" = "] ++ t_utype(get_elem(type, Es)) - end. + Var = case get_elem(typevar, Es) of + [] -> + [t_abstype(get_content(abstype, Es))]; + [V] -> + t_var(V) + end, + Var ++ [" = "] ++ t_utype(get_elem(type, Es)). type_name(#xmlElement{content = Es}) -> t_name(get_elem(erlangName, get_content(typedef, Es))). @@ -855,10 +892,9 @@ types(Ts) -> typedecl(Name, #xmlElement{content = Es}) -> TypedefEs = get_content(typedef, Es), Id = "type-"++Name, - [{tag, typedef(TypedefEs)}, + [{tag, [{marker,[{id,Id}],[]}] ++ typedef(TypedefEs)}, ?NL, - {item, [{marker,[{id,Id}],[]} | - local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)]}, + {item, local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)}, ?NL]. typedef(Es) -> @@ -866,14 +902,14 @@ typedef(Es) -> ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), case get_elem(type, Es) of [] -> - [{tt, Name}]; + Name; Type -> - [{tt, Name ++ [" = "] ++ t_utype(Type)}] + Name ++ [" = "] ++ t_utype(Type) end. -local_defs([]) -> []; +local_defs([]) -> [{p,[]}]; local_defs(Es) -> - [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}]. + [?NL, {ul, [{li, [{p, localdef(E)}]} || E <- Es]}]. localdef(E = #xmlElement{content = Es}) -> Var = case get_elem(typevar, Es) of @@ -917,6 +953,7 @@ seealso_module(Es) -> Es1 -> {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]} end. + seealso_function(Es) -> case get_elem(see, Es) of [] -> []; @@ -988,7 +1025,14 @@ t_name([E]) -> end. t_utype([E]) -> - t_utype_elem(E). + flatten_type(t_utype_elem(E)). + +%% Make sure see also are top elements of lists. +flatten_type(T) -> + [case is_integer(E) of + true -> [E]; + false -> E + end || E <- lists:flatten(T)]. t_utype_elem(E=#xmlElement{content = Es}) -> case get_attrval(name, E) of @@ -1021,16 +1065,14 @@ t_type([#xmlElement{name = tuple, content = Es}]) -> t_tuple(Es); t_type([#xmlElement{name = 'fun', content = Es}]) -> t_fun(Es); -t_type([#xmlElement{name = abstype, content = Es}]) -> - t_abstype(Es); +t_type([E = #xmlElement{name = abstype, content = Es}]) -> + t_abstype(E, Es); t_type([#xmlElement{name = union, content = Es}]) -> t_union(Es); t_type([#xmlElement{name = record, content = Es}]) -> t_record(Es); t_type([#xmlElement{name = map, content = Es}]) -> - t_map(Es); -t_type([#xmlElement{name = map_field, content = Es}]) -> - t_map_field(Es). + t_map(Es). t_var(E) -> [get_attrval(name, E)]. @@ -1065,35 +1107,53 @@ t_fun(Es) -> t_record([E|Es]) -> ["#", get_attrval(value, E), "{"++ seq(fun t_field/1, Es) ++"}"]. + t_field(#xmlElement{name=field, content=[Atom,Type]}) -> [get_attrval(value, Atom), "="] ++ t_utype_elem(Type). t_map(Es) -> - ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). + ["#{"] ++ seq(fun t_map_field/1, Es, ["}"]). + +t_map_field(E = #xmlElement{name = map_field, content = [K,V]}) -> + KElem = t_utype_elem(K), + VElem = t_utype_elem(V), + AS = case get_attrval(assoc_type, E) of + "assoc" -> " => "; + "exact" -> " := " + end, + [KElem ++ AS ++ VElem]. -t_map_field([K,V]) -> - [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)]. +t_abstype(E, Es) -> + see_type(E, t_abstype(Es)). t_abstype(Es) -> - case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of - {Mod,Type} -> - [Type, "("] ++ - seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++ - [" (see module ", Mod, ")"]; - Type -> - [Type, "("] ++ - seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) + Name = t_name(get_elem(erlangName, Es)), + [Name, "("] ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"]). + +see_type(E, Es0) -> + case get_attrval(href, E) of + [] -> Es0; + Href0 -> + try + false = is_local_type(Es0), + %% Fails for parametrized types: + Text = #xmlText{value = lists:append(Es0)}, + {Href, Es} = otp_xmlify_a_href(Href0, [Text]), + [{seealso, [{marker, Href}], Es}] + catch + _:_ -> + Es0 + end end. -%% Split at one colon, but not at two (or more) -split_at_colon([$:,$:|_]=Rest,Acc) -> - lists:reverse(Acc)++Rest; -split_at_colon([$:|Type],Acc) -> - {lists:reverse(Acc),Type}; -split_at_colon([Char|Rest],Acc) -> - split_at_colon(Rest,[Char|Acc]); -split_at_colon([],Acc) -> - lists:reverse(Acc). +is_local_type(Es) -> + try + [_] = ets:lookup(?LOCAL_TYPES, Es), + true + catch + _:_-> + false + end. t_union(Es) -> seq(fun t_utype_elem/1, Es, " | ", []). diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 6dc51adee1..624100ad49 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -1708,28 +1708,36 @@ error: static int get_home(char *buf, int size) { - char* homedrive; - char* homepath; - #ifdef __WIN32__ - homedrive = getenv("HOMEDRIVE"); - homepath = getenv("HOMEPATH"); -#else - homedrive = ""; - homepath = getenv("HOME"); -#endif + char* homedrive = getenv("HOMEDRIVE"); + char* homepath = getenv("HOMEPATH"); - if (!homedrive || !homepath) { - buf[0] = '.'; - buf[1] = '\0'; - return 1; - } else if (strlen(homedrive)+strlen(homepath) < size-1) { + if (homedrive && homepath) { + if (strlen(homedrive)+strlen(homepath) >= size) + return 0; strcpy(buf, homedrive); strcat(buf, homepath); return 1; } - - return 0; + else { + int len = GetWindowsDirectory(buf, size); + if (len) { + return (len < size); + } + } +#else + char* homepath = getenv("HOME"); + if (homepath) { + if (strlen(homepath) >= size) + return 0; + strcpy(buf, homepath); + return 1; + } +#endif + + buf[0] = '.'; + buf[1] = '\0'; + return 1; } diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h index fbb61b0ccf..bded811a35 100644 --- a/lib/erl_interface/src/misc/ei_portio.h +++ b/lib/erl_interface/src/misc/ei_portio.h @@ -21,7 +21,7 @@ */ #ifndef _EI_PORTIO_H #define _EI_PORTIO_H -#if !defined(__WIN32__) || !defined(VXWORKS) +#if !defined(__WIN32__) && !defined(VXWORKS) #ifdef HAVE_WRITEV /* Declaration of struct iovec *iov should be visible in this scope. */ #include <sys/uio.h> diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile index b270bc84fe..a24ec77e31 100644 --- a/lib/gs/doc/src/Makefile +++ b/lib/gs/doc/src/Makefile @@ -148,7 +148,7 @@ release_docs_spec: docs $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" - (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc") $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" diff --git a/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl b/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl index 5451f1fe7d..b1f7bd7572 100644 --- a/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl +++ b/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl @@ -87,22 +87,29 @@ do_fp_unop(I, TempMap) -> %%% Fix an fmove op. do_fmove(I, TempMap) -> #fmove{src=Src,dst=Dst} = I, - case is_mem_opnd(Dst, TempMap) and is_mem_opnd(Src, TempMap) of + case + (is_mem_opnd(Src, TempMap) andalso is_mem_opnd(Dst, TempMap)) + orelse (is_mem_opnd(Src, TempMap) andalso (not is_float_temp(Dst))) + orelse ((not is_float_temp(Src)) andalso is_mem_opnd(Dst, TempMap)) + of true -> - Tmp = clone(Src), + Tmp = spill_temp(double), {[#fmove{src=Src, dst=Tmp},I#fmove{src=Tmp,dst=Dst}], true}; false -> {[I], false} end. +is_float_temp(#x86_temp{type=Type}) -> Type =:= double; +is_float_temp(#x86_mem{}) -> false. + %%% Check if an operand denotes a memory cell (mem or pseudo). is_mem_opnd(Opnd, TempMap) -> R = case Opnd of #x86_mem{} -> true; - #x86_temp{} -> + #x86_temp{type=double} -> Reg = hipe_x86:temp_reg(Opnd), case hipe_x86:temp_is_allocatable(Opnd) of true -> @@ -176,6 +183,9 @@ clone(Dst) -> #x86_mem{} -> hipe_x86:mem_type(Dst); #x86_temp{} -> hipe_x86:temp_type(Dst) end, + spill_temp(Type). + +spill_temp(Type) -> hipe_x86:mk_new_temp(Type). %%% Make a certain reg into a clone of Dst diff --git a/lib/hipe/arm/hipe_arm_registers.erl b/lib/hipe/arm/hipe_arm_registers.erl index 24cd929d41..dcf039676b 100644 --- a/lib/hipe/arm/hipe_arm_registers.erl +++ b/lib/hipe/arm/hipe_arm_registers.erl @@ -67,6 +67,8 @@ -define(R15, 15). -define(LAST_PRECOLOURED, 15). % must handle both GPR and FPR ranges +-define(LR, ?R14). + -define(ARG0, ?R1). -define(ARG1, ?R2). -define(ARG2, ?R3). @@ -114,7 +116,7 @@ stack_pointer() -> ?STACK_POINTER. proc_pointer() -> ?PROC_POINTER. -lr() -> ?R14. +lr() -> ?LR. pc() -> ?R15. @@ -198,7 +200,9 @@ call_clobbered() -> % does the RA strip the type or not? ]. tailcall_clobbered() -> % tailcall crapola needs one temp - [{?TEMP1,tagged},{?TEMP1,untagged}]. + [{?TEMP1,tagged},{?TEMP1,untagged} + ,{?LR,tagged},{?LR,untagged} + ]. live_at_return() -> [%%{?LR,untagged}, diff --git a/lib/hipe/arm/hipe_rtl_to_arm.erl b/lib/hipe/arm/hipe_rtl_to_arm.erl index ad5a559995..93342aba33 100644 --- a/lib/hipe/arm/hipe_rtl_to_arm.erl +++ b/lib/hipe/arm/hipe_rtl_to_arm.erl @@ -148,10 +148,11 @@ mk_shift_ir(S, Dst, Src1, ShiftOp, Src2) -> mk_li(Tmp, Src1, mk_shift_rr(S, Dst, Tmp, ShiftOp, Src2)). -mk_shift_ri(S, Dst, Src1, ShiftOp, Src2) when is_integer(Src2) -> - if Src2 >= 0, Src2 < 32 -> ok; - true -> io:format("~w: excessive immediate shift ~w\n", [?MODULE,Src2]) - end, +mk_shift_ri(S, Dst, Src1, ShiftOp, 0) + when ShiftOp =:= lsl; ShiftOp =:= lsr; ShiftOp =:= asr -> + [hipe_arm:mk_move(S, Dst, Src1)]; +mk_shift_ri(S, Dst, Src1, ShiftOp, Src2) + when is_integer(Src2), Src2 > 0, Src2 < 32 -> Am1 = {Src1,ShiftOp,Src2}, [hipe_arm:mk_move(S, Dst, Am1)]. diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 9453ca6c6f..230fce2e68 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -124,7 +124,7 @@ t_map_entries/2, t_map_put/3, t_map_update/3, - map_pairwise_merge/3 + t_map_pairwise_merge/4 ]). -ifdef(DO_ERL_BIF_TYPES_TEST). @@ -1689,10 +1689,10 @@ type(maps, merge, 2, Xs, Opaques) -> BDefK = t_map_def_key(MapB, Opaques), ADefV = t_map_def_val(MapA, Opaques), BDefV = t_map_def_val(MapB, Opaques), - t_map(map_pairwise_merge( + t_map(t_map_pairwise_merge( fun(K, _, _, mandatory, V) -> {K, mandatory, V}; (K, MNess, VA, optional, VB) -> {K, MNess, t_sup(VA,VB)} - end, MapA, MapB), + end, MapA, MapB, Opaques), t_sup(ADefK, BDefK), t_sup(ADefV, BDefV)) end, Opaques); type(maps, put, 3, Xs, Opaques) -> diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 7826dada9d..15f7b793a1 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -159,6 +159,7 @@ t_map_get/2, t_map_get/3, t_map_is_key/2, t_map_is_key/3, t_map_update/2, t_map_update/3, + t_map_pairwise_merge/4, t_map_put/2, t_map_put/3, t_matchstate/0, t_matchstate/2, @@ -219,8 +220,7 @@ is_erl_type/1, atom_to_string/1, var_table__new/0, - cache__new/0, - map_pairwise_merge/3 + cache__new/0 ]). %%-define(DO_ERL_TYPES_TEST, true). @@ -494,9 +494,9 @@ t_contains_opaque(?function(Domain, Range), Opaques) -> t_contains_opaque(Domain, Opaques) orelse t_contains_opaque(Range, Opaques); t_contains_opaque(?identifier(_Types), _Opaques) -> false; -t_contains_opaque(?integer(_Types), _Opaques) -> false; t_contains_opaque(?int_range(_From, _To), _Opaques) -> false; t_contains_opaque(?int_set(_Set), _Opaques) -> false; +t_contains_opaque(?integer(_Types), _Opaques) -> false; t_contains_opaque(?list(Type, Tail, _), Opaques) -> t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques); t_contains_opaque(?map(_, _, _) = Map, Opaques) -> @@ -1664,10 +1664,12 @@ t_map(Pairs0, DefK0, DefV0) -> %% define(DEBUG, true). try validate_map_elements(Pairs) - catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]); - error:{badarg, E} -> error({badarg, E}, [Pairs0,DefK0,DefV0]) + catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]) end, - ?map(Pairs, DefK, DefV). + case map_pairs_are_none(Pairs) of + true -> ?none; + false -> ?map(Pairs, DefK, DefV) + end. normalise_map_optionals([], _, _) -> []; normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) -> @@ -1684,7 +1686,6 @@ normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) -> normalise_map_optionals([E|T], DefK, DefV) -> [E|normalise_map_optionals(T, DefK, DefV)]. -validate_map_elements([{_,?mand,?none}|_]) -> error({badarg, none_in_mand}); validate_map_elements([{K1,_,_}|Rest=[{K2,_,_}|_]]) -> case is_singleton_type(K1) andalso K1 < K2 of false -> error(badarg); @@ -1697,6 +1698,10 @@ validate_map_elements([{K,_,_}]) -> end; validate_map_elements([]) -> true. +map_pairs_are_none([]) -> false; +map_pairs_are_none([{_,?mand,?none}|_]) -> true; +map_pairs_are_none([_|Ps]) -> map_pairs_are_none(Ps). + -spec t_is_map(erl_type()) -> boolean(). t_is_map(Type) -> @@ -1763,13 +1768,26 @@ mapdict_insert(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2 -> [E2|mapdict_insert(E1, T)]; mapdict_insert(E={_,_,_}, T) -> [E|T]. +-type map_pairwise_merge_fun() :: fun((erl_type(), + t_map_mandatoriness(), erl_type(), + t_map_mandatoriness(), erl_type()) + -> t_map_pair() | false). + +-spec t_map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type(), + opaques()) -> t_map_dict(). +t_map_pairwise_merge(F, MapA, MapB, Opaques) -> + do_opaque(MapA, Opaques, + fun(UMapA) -> + do_opaque(MapB, Opaques, + fun(UMapB) -> + map_pairwise_merge(F, UMapA, UMapB) + end) + end). + %% Merges the pairs of two maps together. Missing pairs become (?opt, DefV) or %% (?opt, ?none), depending on whether K \in DefK. --spec map_pairwise_merge(fun((erl_type(), - t_map_mandatoriness(), erl_type(), - t_map_mandatoriness(), erl_type()) - -> t_map_pair() | false), - erl_type(), erl_type()) -> t_map_dict(). +-spec map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type()) + -> t_map_dict(). map_pairwise_merge(F, ?map(APairs, ADefK, ADefV), ?map(BPairs, BDefK, BDefV)) -> map_pairwise_merge(F, APairs, ADefK, ADefV, BPairs, BDefK, BDefV). @@ -2833,12 +2851,7 @@ t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) -> %% becomes mandatory in the infinumum (K, _, V1, _, V2) -> {K, ?mand, t_inf(V1, V2)} end, A, B), - %% If the infinimum of any mandatory values is ?none, the entire map infinimum - %% is ?none. - case lists:any(fun({_,?mand,?none})->true; ({_,_,_}) -> false end, Pairs) of - true -> t_none(); - false -> t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV)) - end; + t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV)); t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) -> ?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2)); t_inf(?nil, ?nil, _Opaques) -> ?nil; @@ -4749,7 +4762,7 @@ type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, S, D, L, C) -> {Rep, L2, C2} = recur_limit(Fun, D, L1, TypeName, TypeNames), Rep1 = choose_opaque_type(Rep, Type), Rep2 = case cannot_have_opaque(Rep1, TypeName, TypeNames) of - true -> Rep1; + true -> Rep; false -> ArgTypes2 = subst_all_vars_to_any_list(ArgTypes), t_opaque(Module, Name, ArgTypes2, Rep1) @@ -4821,7 +4834,7 @@ remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, RemType, TypeNames, NewRep1 = choose_opaque_type(NewRep, Type), NewRep2 = case cannot_have_opaque(NewRep1, RemType, TypeNames) of - true -> NewRep1; + true -> NewRep; false -> ArgTypes2 = subst_all_vars_to_any_list(ArgTypes), t_opaque(Mod, Name, ArgTypes2, NewRep1) diff --git a/lib/hipe/ppc/hipe_ppc.erl b/lib/hipe/ppc/hipe_ppc.erl index 0fa96162f6..380e791bc1 100644 --- a/lib/hipe/ppc/hipe_ppc.erl +++ b/lib/hipe/ppc/hipe_ppc.erl @@ -167,8 +167,10 @@ temp_is_precoloured(#ppc_temp{reg=Reg,type=Type}) -> _ -> hipe_ppc_registers:is_precoloured_gpr(Reg) end. -mk_simm16(Value) -> #ppc_simm16{value=Value}. -mk_uimm16(Value) -> #ppc_uimm16{value=Value}. +mk_simm16(Value) when Value >= -(1 bsl 15), Value < (1 bsl 15) -> + #ppc_simm16{value=Value}. +mk_uimm16(Value) when Value >= 0, Value < (1 bsl 16) -> + #ppc_uimm16{value=Value}. mk_mfa(M, F, A) -> #ppc_mfa{m=M, f=F, a=A}. @@ -240,7 +242,11 @@ mk_li(Dst, Value, Tail) -> % Dst can be R0 Value =< 16#7FFFFFFF -> mk_li32(Dst, R0, Value, Tail); true -> - Highest = (Value bsr 48), % Value@highest + Highest = case (Value bsr 48) of % Value@highest + TopBitSet when TopBitSet >= (1 bsl 15) -> + TopBitSet - (1 bsl 16); % encoder needs it to be negative + FitsSimm16 -> FitsSimm16 + end, Higher = (Value bsr 32) band 16#FFFF, % Value@higher High = (Value bsr 16) band 16#FFFF, % Value@h Low = Value band 16#FFFF, % Value@l diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl index ff9da01b11..d89ff6235c 100644 --- a/lib/hipe/ppc/hipe_ppc_assemble.erl +++ b/lib/hipe/ppc/hipe_ppc_assemble.erl @@ -175,7 +175,8 @@ do_slwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 -> {Dst, Src1, {sh,N}, {mb,0}, {me,31-N}}. do_srwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 -> - {Dst, Src1, {sh,32-N}, {mb,N}, {me,31}}. + %% SH should be 0 (not 32) when N is 0 + {Dst, Src1, {sh,(32-N) band 31}, {mb,N}, {me,31}}. do_srawi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 32 -> {sh,N}. @@ -184,7 +185,8 @@ do_sldi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 64 -> {Dst, Src1, {sh6,N}, {me6,63-N}}. do_srdi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 64 -> - {Dst, Src1, {sh6,64-N}, {mb6,N}}. + %% SH should be 0 (not 64) when N is 0 + {Dst, Src1, {sh6,(64-N) band 63}, {mb6,N}}. do_sradi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 64 -> {sh6,N}. @@ -246,6 +248,7 @@ do_load(I) -> case LdOp of 'ld' -> do_disp_ds(Disp); 'ldu' -> do_disp_ds(Disp); + 'lwa' -> do_disp_ds(Disp); _ -> do_disp(Disp) end, NewBase = do_reg(Base), diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl index eef5ba8d96..f9c043eafe 100644 --- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl +++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl @@ -750,13 +750,25 @@ xaluop_commutes(XAluOp) -> xaluop_is_shift(XAluOp) -> case XAluOp of + 'add' -> false; + 'addcc' -> false; + 'and' -> false; + 'andcc' -> false; + 'cmpcc' -> false; + 'ldsb' -> false; + 'ldub' -> false; + 'lduw' -> false; + 'or' -> false; 'sll' -> true; - 'srl' -> true; + %% 'sllx' -> true; + 'smul' -> false; 'sra' -> true; - 'sllx' -> true; - 'srlx' -> true; - 'srax' -> true; - _ -> false + %% 'srax' -> true; + 'srl' -> true; + %% 'srlx' -> true; + 'sub' -> false; + 'subcc' -> false; + 'xor' -> false end. %%% Convert an extended SPARC AluOp back to a plain AluOp. @@ -764,9 +776,23 @@ xaluop_is_shift(XAluOp) -> xaluop_normalise(XAluOp) -> case XAluOp of - 'cmp' -> 'sub'; + 'add' -> 'add'; + 'addcc' -> 'addcc'; + 'and' -> 'and'; + 'andcc' -> 'andcc'; + %% 'cmp' -> 'sub'; 'cmpcc' -> 'subcc'; - _ -> XAluOp + 'ldsb' -> 'ldsb'; + 'ldub' -> 'ldub'; + 'lduw' -> 'lduw'; + 'or' -> 'or'; + 'sll' -> 'sll'; + 'smul' -> 'smul'; + 'sra' -> 'sra'; + 'srl' -> 'srl'; + 'sub' -> 'sub'; + 'subcc' -> 'subcc'; + 'xor' -> 'xor' end. %%% Convert an RTL condition code. diff --git a/lib/hipe/sparc/hipe_sparc_registers.erl b/lib/hipe/sparc/hipe_sparc_registers.erl index 884215702b..6681a10070 100644 --- a/lib/hipe/sparc/hipe_sparc_registers.erl +++ b/lib/hipe/sparc/hipe_sparc_registers.erl @@ -86,6 +86,8 @@ -define(I7, 31). -define(LAST_PRECOLOURED,31). % must handle both GRP and FPR ranges +-define(RA, ?O7). + -define(ARG0, ?O1). -define(ARG1, ?O2). -define(ARG2, ?O3). @@ -174,7 +176,7 @@ stack_pointer() -> ?STACK_POINTER. proc_pointer() -> ?PROC_POINTER. -return_address() -> ?O7. +return_address() -> ?RA. g0() -> ?G0. @@ -283,7 +285,9 @@ call_clobbered() -> % does the RA strip the type or not? ]. tailcall_clobbered() -> % tailcall crapola needs one temp - [{?TEMP1,tagged},{?TEMP1,untagged}]. + [{?TEMP1,tagged},{?TEMP1,untagged} + ,{?RA,tagged},{?RA,untagged} + ]. live_at_return() -> [{?HEAP_POINTER,untagged}, diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl index 03ec7adfd0..a3048d907e 100644 --- a/lib/hipe/test/hipe_testsuite_driver.erl +++ b/lib/hipe/test/hipe_testsuite_driver.erl @@ -99,7 +99,7 @@ write_suite(Suite) -> write_header(#suite{suitename = SuiteName, outputfile = OutputFile, testcases = TestCases}) -> Exports = format_export(TestCases), - TimeLimit = 3, %% with 1 or 2 it fails on some slow machines... + TimeLimit = 5, %% with 1 or 2 it fails on some slow machines... io:format(OutputFile, "%% ATTENTION!\n" "%% This is an automatically generated file. Do not edit.\n\n" diff --git a/lib/hipe/x86/hipe_x86_postpass.erl b/lib/hipe/x86/hipe_x86_postpass.erl index 939baeccec..4515822a34 100644 --- a/lib/hipe/x86/hipe_x86_postpass.erl +++ b/lib/hipe/x86/hipe_x86_postpass.erl @@ -95,7 +95,8 @@ peep([I=#move{src=#x86_temp{reg=Src}, dst=#x86_temp{reg=Dst}}, %% ElimBinALMDouble %% ---------------- -peep([Move=#move{src=Src, dst=Dst}, Alu=#alu{src=Src, dst=Dst}|Insns], Res, Lst) -> +peep([Move=#move{src=Src, dst=Dst}, Alu=#alu{src=Src, dst=Dst}|Insns], Res, Lst) + when not is_record(Dst, x86_mem) -> peep([Alu#alu{src=Dst}|Insns], [Move|Res], [elimBinALMDouble|Lst]); diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile index c9691df7af..19f12ac6b9 100644 --- a/lib/ic/doc/src/Makefile +++ b/lib/ic/doc/src/Makefile @@ -225,7 +225,7 @@ release_docs_spec: docs $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" - (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc") $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" diff --git a/lib/ic/src/ic_codegen.erl b/lib/ic/src/ic_codegen.erl index adad021da1..a3f141f606 100644 --- a/lib/ic/src/ic_codegen.erl +++ b/lib/ic/src/ic_codegen.erl @@ -245,8 +245,8 @@ emit_stub_head(G, F1, Name, java) -> stub_header(G, Name) -> ["Implementation stub file", "", - io_lib:format("Target: ~s", [Name]), - io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]), + io_lib:format("Target: ~ts", [Name]), + io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]), io_lib:format("IC vsn: ~s", [?COMPILERVSN]), "", "This file is automatically generated. DO NOT EDIT IT."]. @@ -298,8 +298,8 @@ emit_hrl_head(G, Fd, Name, c_server) -> hrl_header(G, Name) -> ["", - io_lib:format("Target: ~s", [Name]), - io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]), + io_lib:format("Target: ~ts", [Name]), + io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]), io_lib:format("IC vsn: ~s", [?COMPILERVSN]), "", "This file is automatically generated. DO NOT EDIT IT."]. diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml index aeda961714..65b3dcde95 100644 --- a/lib/inets/doc/src/http_server.xml +++ b/lib/inets/doc/src/http_server.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2015</year> + <year>2004</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -40,8 +40,8 @@ <item>Secure Sockets Layer (SSL)</item> <item>Erlang Scripting Interface (ESI)</item> <item>Common Gateway Interface (CGI)</item> - <item>User Authentication (using <c>Mnesia</c>, - <c>Dets</c> or plain text database)</item> + <item>User Authentication (using Mnesia, + Dets or plain text database)</item> <item>Common Logfile Format (with or without disk_log(3) support)</item> <item>URL Aliasing</item> <item>Action Mappings</item> @@ -563,7 +563,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[ <title>mod_auth - User Authentication</title> <p>The <seealso marker="mod_auth">mod_auth(3)</seealso> module provides for basic user authentication using - textual files, <c>Dets</c> databases as well as <c>Mnesia</c> databases.</p> + textual files, Dets databases as well as Mnesia databases.</p> <p>Uses the following Erlang Web Server API interaction data: </p> <list type="bulleted"> @@ -580,15 +580,15 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[ <section> <title>Mnesia As Authentication Database</title> - <p>If <c>Mnesia</c> is used as storage method, <c>Mnesia</c> must be - started before the HTTP server. The first time <c>Mnesia</c> is + <p>If Mnesia is used as storage method, Mnesia must be + started before the HTTP server. The first time Mnesia is started, the schema and the tables must be created before - <c>Mnesia</c> is started. A simple example of a module with two - functions that creates and start <c>Mnesia</c> is provided + Mnesia is started. A simple example of a module with two + functions that creates and start Mnesia is provided here. Function <c>first_start/0</c> is to be used the first time. It creates the schema and the tables. <c>start/0</c> is to be used in consecutive startups. - <c>start/0</c> starts <c>Mnesia</c> and waits for the tables to + <c>start/0</c> starts Mnesia and waits for the tables to be initiated. This function must only be used when the schema and the tables are already created.</p> @@ -616,25 +616,25 @@ start() -> mnesia:start(), mnesia:wait_for_tables([httpd_user, httpd_group], 60000). </code> - <p>To create the <c>Mnesia</c> tables, we use two records defined in + <p>To create the Mnesia tables, we use two records defined in <c>mod_auth.hrl</c>, so that file must be included. <c>first_start/0</c> creates a schema that specifies on which nodes the database is to reside. - Then it starts <c>Mnesia</c> and creates the tables. The first argument + Then it starts Mnesia and creates the tables. The first argument is the name of the tables, the second argument is a list of options of how to create the table, see - <seealso marker="mnesia:mnesia"><c>mnesia</c></seealso>, documentation for + <seealso marker="mnesia:mnesia"><c>mnesia(3)</c></seealso>, documentation for more information. As the implementation of the <c>mod_auth_mnesia</c> saves one row for each user, the type must be <c>bag</c>. When the schema and the tables are created, function <seealso marker="mnesia:mnesia#start-0">mnesia:start/0</seealso> - is used to start <c>Mnesia</c> and - waits for the tables to be loaded. <c>Mnesia</c> uses the + is used to start Mnesia and + waits for the tables to be loaded. Mnesia uses the directory specified as <c>mnesia_dir</c> at startup if specified, - otherwise <c>Mnesia</c> uses the current directory. For security - reasons, ensure that the <c>Mnesia</c> tables are stored outside + otherwise Mnesia uses the current directory. For security + reasons, ensure that the Mnesia tables are stored outside the document tree of the HTTP server. If they are placed in the directory which it protects, clients can download the tables. - Only the <c>Dets</c> and <c>Mnesia</c> storage + Only the Dets and Mnesia storage methods allow writing of dynamic user data to disk. <c>plain</c> is a read only method.</p> </section> @@ -669,7 +669,7 @@ start() -> <section> <title>mod_disk_log - Logging Using Disk_Log.</title> <p>Standard logging using the "Common Logfile Format" and - <seealso marker="kernel:disk_log">kernel:disk_log(3)</seealso>.</p> + <seealso marker="kernel:disk_log">disk_log(3)</seealso>.</p> <p>Uses the following Erlang Web Server API interaction data: </p> <list type="bulleted"> diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index ca9b268a03..13471aab2c 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2015</year> + <year>2004</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -72,7 +72,7 @@ <p><c>profile() = atom()</c></p> <p><c>path() = string()</c> representing a file path or directory path</p> <p><c>ip_address()</c> = See the - <seealso marker="kernel:inet">inet(3)</seealso> manual page in <c>Kernel</c>.</p> + <seealso marker="kernel:inet">inet(3)</seealso> manual page in Kernel.</p> <p><c>socket_opt()</c> = See the options used by <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> <c>gen_tcp(3)</c> and <seealso marker="ssl:ssl">ssl(3)</seealso> connect(s)</p> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 62b92b8356..d74635fc01 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2015</year> + <year>1997</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -455,7 +455,7 @@ text/plain asc txt</pre> directory. Several files can be given, in which case the server returns the first it finds, for example:</p> - <code>{directory_index, ["index.hml", "welcome.html"]}</code> + <code>{directory_index, ["index.html", "welcome.html"]}</code> <p>Access to http://your.server.org/docs/ would return http://your.server.org/docs/index.html or @@ -711,7 +711,7 @@ text/plain asc txt</pre> <item> <p>Sets the type of authentication database that is used for the directory. The key difference between the different methods is - that dynamic data can be saved when <c>Mnesia</c> and <c>Dets</c> + that dynamic data can be saved when Mnesia and Dets are used. This property is called <c>AuthDbType</c> in the Apache-like configuration files.</p> @@ -731,10 +731,10 @@ text/plain asc txt</pre> <code> ragnar:s7Xxv7 edward:wwjau8 </code> - <p>If the <c>Dets</c> storage method is used, the user database is - maintained by <c>Dets</c> and must not be edited by hand. Use the + <p>If the Dets storage method is used, the user database is + maintained by Dets and must not be edited by hand. Use the API functions in module <c>mod_auth</c> to create/edit the user - database. This directive is ignored if the <c>Mnesia</c> + database. This directive is ignored if the Mnesia storage method is used. For security reasons, ensure that <c>auth_user_file</c> is stored outside the document tree of the web server. If it is placed in the directory that it protects, @@ -753,10 +753,10 @@ text/plain asc txt</pre> <code>group1: bob joe ante</code> - <p>If the <c>Dets</c> storage method is used, the group database is - maintained by <c>Dets</c> and must not be edited by hand. Use the + <p>If the Dets storage method is used, the group database is + maintained by Dets and must not be edited by hand. Use the API for module <c>mod_auth</c> to create/edit the group database. - This directive is ignored if the <c>Mnesia</c> storage method is used. + This directive is ignored if the Mnesia storage method is used. For security reasons, ensure that the <c>auth_group_file</c> is stored outside the document tree of the web server. If it is placed in the directory that it protects, clients diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml index 4b7088b2c5..c4f844622b 100644 --- a/lib/inets/doc/src/mod_auth.xml +++ b/lib/inets/doc/src/mod_auth.xml @@ -33,7 +33,7 @@ <modulesummary>User authentication using text files, Dets, or Mnesia database.</modulesummary> <description> <p>This module provides for basic user authentication using - textual files, <c>Dets</c> databases, or <c>Mnesia</c> databases.</p> + textual files, Dets databases, or Mnesia databases.</p> </description> <funcs> diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index 8279fdc824..006fca1bdf 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -61,7 +61,7 @@ <tag><c>{server_port, integer()}</c></tag> <item><p>Servers port number.</p></item> - <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag> + <tag><c>{request_method, "GET | "PUT" | "DELETE" | "POST" | "PATCH"}</c></tag> <item><p>HTTP request method.</p></item> <tag><c>{remote_adress, inet:ip_address()} </c></tag> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5321203511..caa5a083a3 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,7 +33,38 @@ <file>notes.xml</file> </header> - <section><title>Inets 6.3</title> + <section><title>Inets 6.3.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + PUT and DELETE support has been added to mod_esi</p> + <p> + Own Id: OTP-13688 Aux Id: seq13149 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 6.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A debug message was accidently left enabled in the ftp + client.</p> + <p> + Own Id: OTP-13712 Aux Id: seq13143 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 6.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index bbf25f8e90..8bad91bf98 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -106,8 +106,8 @@ -type common_reason() :: 'econn' | 'eclosed' | term(). -type file_write_error_reason() :: term(). % See file:write for more info -%%-define(DBG(F,A), 'n/a'). --define(DBG(F,A), io:format(F,A)). +-define(DBG(F,A), 'n/a'). +%%-define(DBG(F,A), io:format(F,A)). %%%========================================================================= %%% API - CLIENT FUNCTIONS @@ -2099,7 +2099,7 @@ handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}} %%-------------------------------------------------------------------------- %% Default -handle_ctrl_result({Status, Lines}, #state{client = From} = State) +handle_ctrl_result({Status, _Lines}, #state{client = From} = State) when From =/= undefined -> ctrl_result_response(Status, State, {error, Status}). diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index e5182ca23c..9e54f2b2c5 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -395,7 +395,8 @@ validate_properties(Properties) -> %% That is, if property A depends on property B. %% The only sunch preperty at this time is bind_address that depends %% on ipfamily. -validate_properties2(Properties) -> +validate_properties2(Properties0) -> + Properties = fix_ipfamily(Properties0), case proplists:get_value(bind_address, Properties) of undefined -> case proplists:get_value(sock_type, Properties, ip_comm) of @@ -422,6 +423,15 @@ validate_properties2(Properties) -> end end. +fix_ipfamily(Properties) -> + case proplists:get_value(ipfamily, Properties) of + undefined -> + Properties; + IpFamily -> + NewProps = proplists:delete(ipfamily, Properties), + [{ipfamily, validate_ipfamily(IpFamily)} | NewProps] + end. + add_inet_defaults(Properties) -> case proplists:get_value(ipfamily, Properties) of undefined -> diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl index 424d269859..c893b10dca 100644 --- a/lib/inets/src/http_server/httpd_example.erl +++ b/lib/inets/src/http_server/httpd_example.erl @@ -20,7 +20,7 @@ %% -module(httpd_example). -export([print/1]). --export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]). +-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]). -export([newformat/3]). %% These are used by the inets test-suite @@ -59,6 +59,11 @@ get(_Env,[]) -> get(Env,Input) -> default(Env,Input). +put(Env,{Input,_Body}) -> + default(Env,Input); +put(Env,Input) -> + default(Env,Input). + get_bin(_Env,_Input) -> [list_to_binary(header()), list_to_binary(top("GET Example")), @@ -94,7 +99,7 @@ default(Env,Input) -> io_lib:format("~p",[httpd:parse_query(Input)]),"\n", footer()]. -peer(Env, Input) -> +peer(Env, _Input) -> Header = case proplists:get_value(peer_cert, Env) of undefined -> @@ -161,7 +166,7 @@ sleep(T) -> receive after T -> ok end. %% ------------------------------------------------------ -chunk_timeout(SessionID, _, StrInt) -> +chunk_timeout(SessionID, _, _StrInt) -> mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"), mod_esi:deliver(SessionID, top("Test chunk encoding timeout")), timer:sleep(20000), diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 2800250727..b21af1418c 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -241,7 +241,7 @@ alias_match_str(Alias, eval_script_alias) -> %%------------------------ Erl mechanism -------------------------------- erl(#mod{method = Method} = ModData, ESIBody, Modules) - when (Method =:= "GET") orelse (Method =:= "HEAD") -> + when (Method =:= "GET") orelse (Method =:= "HEAD") orelse (Method =:= "DELETE") -> ?hdrt("erl", [{method, Method}]), case httpd_util:split(ESIBody,":|%3A|/",2) of {ok, [ModuleName, FuncAndInput]} -> @@ -264,35 +264,32 @@ erl(#mod{method = Method} = ModData, ESIBody, Modules) {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]} end; -erl(#mod{request_uri = ReqUri, - method = "PUT", - http_version = Version, - data = Data}, _ESIBody, _Modules) -> - ?hdrt("erl", [{method, put}]), - {proceed, [{status,{501,{"PUT", ReqUri, Version}, - ?NICE("Erl mechanism doesn't support method PUT")}}| - Data]}; - -erl(#mod{request_uri = ReqUri, - method = "DELETE", - http_version = Version, - data = Data}, _ESIBody, _Modules) -> - ?hdrt("erl", [{method, delete}]), - {proceed,[{status,{501,{"DELETE", ReqUri, Version}, - ?NICE("Erl mechanism doesn't support method DELETE")}}| - Data]}; - -erl(#mod{request_uri = ReqUri, - method = "PATCH", - http_version = Version, - data = Data}, _ESIBody, _Modules) -> - ?hdrt("erl", [{method, patch}]), - {proceed, [{status,{501,{"PATCH", ReqUri, Version}, - ?NICE("Erl mechanism doesn't support method PATCH")}}| - Data]}; +erl(#mod{method = "PUT", entity_body = Body} = ModData, + ESIBody, Modules) -> + case httpd_util:split(ESIBody,":|%3A|/",2) of + {ok, [ModuleName, FuncAndInput]} -> + case httpd_util:split(FuncAndInput,"[\?/]",2) of + {ok, [FunctionName, Input]} -> + generate_webpage(ModData, ESIBody, Modules, + list_to_atom(ModuleName), + FunctionName, {Input,Body}, + [{entity_body, Body} | + script_elements(FuncAndInput, Input)]); + {ok, [FunctionName]} -> + generate_webpage(ModData, ESIBody, Modules, + list_to_atom(ModuleName), + FunctionName, {undefined,Body}, + [{entity_body, Body} | + script_elements(FuncAndInput, "")]); + {ok, BadRequest} -> + {proceed,[{status,{400,none, BadRequest}} | + ModData#mod.data]} + end; + {ok, BadRequest} -> + {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]} + end; -erl(#mod{method = "POST", - entity_body = Body} = ModData, ESIBody, Modules) -> +erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) -> ?hdrt("erl", [{method, post}]), case httpd_util:split(ESIBody,":|%3A|/",2) of {ok,[ModuleName, Function]} -> @@ -301,7 +298,16 @@ erl(#mod{method = "POST", Function, Body, [{entity_body, Body}]); {ok, BadRequest} -> {proceed,[{status, {400, none, BadRequest}} | ModData#mod.data]} - end. + end; + +erl(#mod{request_uri = ReqUri, + method = "PATCH", + http_version = Version, + data = Data}, _ESIBody, _Modules) -> + ?hdrt("erl", [{method, patch}]), + {proceed, [{status,{501,{"PATCH", ReqUri, Version}, + ?NICE("Erl mechanism doesn't support method PATCH")}}| + Data]}. generate_webpage(ModData, ESIBody, [all], Module, FunctionName, Input, ScriptElements) -> diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 3194b5ad3d..28e77151f2 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2015. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -119,8 +119,10 @@ groups() -> ]}, {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]}, {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code - {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test, - trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()}, + {http_1_1, [], + [host, chunked, expect, cgi, cgi_chunked_encoding_test, + trace, range, if_modified_since, mod_esi_chunk_timeout, + esi_put] ++ http_head() ++ http_get() ++ load()}, {http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()}, {http_0_9, [], http_head() ++ http_get() ++ load()} ]. @@ -283,20 +285,50 @@ init_per_testcase(Case, Config) when Case == host; Case == trace -> http_1_1 -> httpd_1_1 end, - [{version_cb, Cb} | proplists:delete(version_cb, Config)]; + dbg( + Case, + [{version_cb, Cb} | proplists:delete(version_cb, Config)], + init); init_per_testcase(range, Config) -> ct:timetrap({seconds, 20}), DocRoot = proplists:get_value(doc_root, Config), create_range_data(DocRoot), - Config; + dbg(range, Config, init); -init_per_testcase(_, Config) -> +init_per_testcase(Case, Config) -> ct:timetrap({seconds, 20}), - Config. - -end_per_testcase(_Case, _Config) -> - ok. + dbg(Case, Config, init). + +end_per_testcase(Case, Config) -> + dbg(Case, Config, 'end'). + + +dbg(Case, Config, Status) -> + Cases = [esi_put], + case lists:member(Case, Cases) of + true -> + case Status of + init -> + dbg:tracer(), + dbg:p(all, c), + dbg:tpl(httpd_example, cx), + dbg:tpl(mod_esi, generate_webpage, cx), + io:format("dbg: started~n"), + Config; + 'end' -> + io:format("dbg: stopped~n"), + dbg:stop_clear(), + ok + end; + false -> + case Status of + init -> + Config; + 'end' -> + ok + end + end. %%------------------------------------------------------------------------- %% Test cases starts here. @@ -765,6 +797,14 @@ esi(Config) when is_list(Config) -> ok = http_status("GET /cgi-bin/erl/httpd_example:peer ", Config, [{statuscode, 200}, {header, "peer-cert-exist", peer(Config)}]). + +%%------------------------------------------------------------------------- +esi_put() -> + [{doc, "Test mod_esi PUT"}]. + +esi_put(Config) when is_list(Config) -> + ok = http_status("PUT /cgi-bin/erl/httpd_example/put/123342234123 ", + Config, [{statuscode, 200}]). %%------------------------------------------------------------------------- mod_esi_chunk_timeout(Config) when is_list(Config) -> diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl index 5eaf3a28a0..38b8229389 100644 --- a/lib/inets/test/inets_SUITE.erl +++ b/lib/inets/test/inets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -212,11 +212,19 @@ start_httpd(Config) when is_list(Config) -> Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], true = lists:member(Pid0, Pids0), [_|_] = inets:services_info(), - inets:stop(httpd, Pid0), ct:sleep(500), + Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid0, Pids1), + {ok, Pid0b} = + inets:start(httpd, [{port, 0}, {ipfamily, inet6fb4} | HttpdConf]), + Pids0b = [ServicePid || {_, ServicePid} <- inets:services()], + true = lists:member(Pid0b, Pids0b), + [_|_] = inets:services_info(), + inets:stop(httpd, Pid0b), + ct:sleep(500), Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], - false = lists:member(Pid0, Pids1), + false = lists:member(Pid0b, Pids1), {ok, Pid1} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf], stand_alone), diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl index 5b8b1463c8..1e664337e6 100644 --- a/lib/inets/test/inets_sup_SUITE.erl +++ b/lib/inets/test/inets_sup_SUITE.erl @@ -33,7 +33,7 @@ suite() -> all() -> [default_tree, ftpc_worker, tftpd_worker, - httpd_subtree, httpd_subtree_profile, + httpd_config, httpd_subtree, httpd_subtree_profile, httpc_subtree]. groups() -> @@ -52,9 +52,32 @@ end_per_suite(_) -> inets:stop(), ok. -init_per_testcase(httpd_subtree, Config) -> +init_per_testcase(httpd_config = TC, Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + Dir = filename:join(PrivDir, TC), + ok = file:make_dir(Dir), + + FallbackConfig = [{port, 0}, + {server_name,"www.test"}, + {modules, [mod_get]}, + {server_root, Dir}, + {document_root, Dir}, + {bind_address, any}, + {ipfamily, inet6fb4}], + try + inets:stop(), + inets:start(), + inets:start(httpd, FallbackConfig), + Config + catch + _:Reason -> + inets:stop(), + exit({failed_starting_inets, Reason}) + end; + +init_per_testcase(httpd_subtree = TC, Config) -> PrivDir = proplists:get_value(priv_dir, Config), - Dir = filename:join(PrivDir, "root"), + Dir = filename:join(PrivDir, TC), ok = file:make_dir(Dir), SimpleConfig = [{port, 0}, @@ -75,9 +98,9 @@ init_per_testcase(httpd_subtree, Config) -> exit({failed_starting_inets, Reason}) end; -init_per_testcase(httpd_subtree_profile, Config) -> +init_per_testcase(httpd_subtree_profile = TC, Config) -> PrivDir = proplists:get_value(priv_dir, Config), - Dir = filename:join(PrivDir, "root"), + Dir = filename:join(PrivDir, TC), ok = file:make_dir(Dir), SimpleConfig = [{port, 0}, @@ -193,6 +216,11 @@ tftpd_worker(Config) when is_list(Config) -> [] = supervisor:which_children(tftp_sup), ok. +httpd_config() -> + [{doc, "Makes sure the httpd config works for inet6fb4."}]. +httpd_config(Config) when is_list(Config) -> + do_httpd_subtree(Config, default). + httpd_subtree() -> [{doc, "Makes sure the httpd sub tree is correct."}]. httpd_subtree(Config) when is_list(Config) -> diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 543e0d44fd..f668ef106c 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 6.3 +INETS_VSN = 6.3.3 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile index cd1e61a795..508c8e01b5 100644 --- a/lib/jinterface/doc/src/Makefile +++ b/lib/jinterface/doc/src/Makefile @@ -166,7 +166,7 @@ release_docs_spec: docs $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)" $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" - (/bin/cp -rf ../html "$(RELSYSDIR)/doc") + ($(CP) -rf ../html "$(RELSYSDIR)/doc") # $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \ # "$(RELSYSDIR)/doc/html" diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml index 5e0da409a3..d2e9390d7e 100644 --- a/lib/kernel/doc/src/app.xml +++ b/lib/kernel/doc/src/app.xml @@ -151,7 +151,7 @@ ApplicationVersion = string()</code> application is allowed to be started. <c>systools</c> uses this list to generate correct start scripts. Defaults to the empty list, but notice that all applications have - dependencies to (at least) <c>Kernel</c> and <c>STDLIB</c>.</p> + dependencies to (at least) Kernel and STDLIB.</p> </item> <tag><c>env</c></tag> <item> @@ -171,7 +171,7 @@ ApplicationVersion = string()</code> implemented as a supervision tree, otherwise the application controller does not know how to start it. <c>mod</c> can be omitted for applications without processes, typically - code libraries, for example, <c>STDLIB</c>.</p> + code libraries, for example, STDLIB.</p> </item> <tag><c>start_phases</c></tag> <item> @@ -236,7 +236,7 @@ ApplicationVersion = string()</code> <section> <title>See Also</title> <p><seealso marker="application"><c>application(3)</c></seealso>, - <seealso marker="sasl:systools"><c>sasl:systools(3)</c></seealso></p> + <seealso marker="sasl:systools"><c>systools(3)</c></seealso></p> </section> </fileref> diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 8d33aa86e7..886286b76d 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -200,7 +200,7 @@ <seealso marker="app"><c>app(4)</c></seealso>.</p> <p>If <c><anno>Distributed</anno> == {<anno>Application</anno>,[<anno>Time</anno>,]<anno>Nodes</anno>}</c>, the application becomes distributed. The argument overrides - the value for the application in the <c>Kernel</c> configuration + the value for the application in the Kernel configuration parameter <c>distributed</c>. <c><anno>Application</anno></c> must be the application name (same as in the first argument). If a node crashes and <c><anno>Time</anno></c> is specified, @@ -221,7 +221,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> the application is to be started at <c>cp2@cave</c> or <c>cp3@cave</c>.</p> <p>If <c>Distributed == default</c>, the value for - the application in the <c>Kernel</c> configuration parameter + the application in the Kernel configuration parameter <c>distributed</c> is used.</p> </desc> </func> @@ -267,7 +267,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> started, <c>Application</c> is started as well.</p> <p>By default, all applications are loaded with permission <c>true</c> on all nodes. The permission can be configured - using the <c>Kernel</c> configuration parameter <c>permissions</c>.</p> + using the Kernel configuration parameter <c>permissions</c>.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml index 03f983b96d..5901446960 100644 --- a/lib/kernel/doc/src/auth.xml +++ b/lib/kernel/doc/src/auth.xml @@ -47,7 +47,7 @@ <desc> <p>Use <seealso marker="erts:erlang#erlang:get_cookie/0"><c>erlang:get_cookie()</c></seealso> - in <c>ERTS</c> instead.</p> + in ERTS instead.</p> </desc> </func> <func> @@ -59,7 +59,7 @@ <desc> <p>Use <seealso marker="erts:erlang#erlang:set_cookie/2"><c>erlang:set_cookie(node(), <anno>Cookie</anno>)</c> - in <c>ERTS</c></seealso> instead.</p> + in ERTS</seealso> instead.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index d3611d6a03..4db377bcde 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -86,11 +86,11 @@ an <c>ebin</c> directory are ignored.</p> <p>All application directories found in the additional directories appears before the standard OTP applications, except for the - <c>Kernel</c> and <c>STDLIB</c> applications, which are placed before + Kernel and STDLIB applications, which are placed before any additional applications. In other words, modules found in any of the additional library directories override modules with - the same name in OTP, except for modules in <c>Kernel</c> and - <c>STDLIB</c>.</p> + the same name in OTP, except for modules in Kernel and + STDLIB.</p> <p>Environment variable <c>ERL_LIBS</c> (if defined) is to contain a colon-separated (for Unix-like systems) or semicolon-separated (for Windows) list of additional libraries.</p> @@ -151,7 +151,7 @@ zip:create("mnesia-4.4.7.ez", <c>$OTPROOT/lib/mnesia.ez/mnesia/ebin</c> or <c>$OTPROOT/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.</p> - <p>The code server uses module <c>erl_prim_loader</c> in <c>ERTS</c> + <p>The code server uses module <c>erl_prim_loader</c> in ERTS (possibly through <c>erl_boot_server</c>) to read code files from archives. However, the functions in <c>erl_prim_loader</c> can also be used by other applications to read files from archives. For @@ -651,6 +651,11 @@ ok = code:finish_loading(Prepared), <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old. If some processes still linger in the old code, these processes are killed before the code is removed.</p> + <note><p>As of ERTS version 9.0, a process is only considered + to be lingering in the code if it has direct references to the code. + For more information see documentation of + <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>, + which is used in order to determine this.</p></note> <p>Returns <c>true</c> if successful and any process is needed to be killed, otherwise <c>false</c>.</p> </desc> @@ -661,6 +666,11 @@ ok = code:finish_loading(Prepared), <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old, but only if no processes linger in it.</p> + <note><p>As of ERTS version 9.0, a process is only considered + to be lingering in the code if it has direct references to the code. + For more information see documentation of + <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>, + which is used in order to determine this.</p></note> <p>Returns <c>false</c> if the module cannot be purged because of processes lingering in old code, otherwise <c>true</c>.</p> </desc> @@ -678,9 +688,9 @@ ok = code:finish_loading(Prepared), <p>Normally, <c><anno>Loaded</anno></c> is the absolute filename <c>Filename</c> from which the code is obtained. If the module is preloaded (see - <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>), + <seealso marker="sasl:script"><c>script(4)</c></seealso>), <c>Loaded==preloaded</c>. If the module is Cover-compiled (see - <seealso marker="tools:cover"><c>tools:cover(3)</c></seealso>), + <seealso marker="tools:cover"><c>cover(3)</c></seealso>), <c>Loaded==cover_compiled</c>.</p> </desc> </func> diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml index 897365f9b9..4109251387 100644 --- a/lib/kernel/doc/src/erl_boot_server.xml +++ b/lib/kernel/doc/src/erl_boot_server.xml @@ -38,13 +38,13 @@ command-line flag <c>-loader inet</c>. All hosts specified with command-line flag <c>-hosts Host</c> must have one instance of this server running.</p> - <p>This server can be started with the <c>Kernel</c> configuration + <p>This server can be started with the Kernel configuration parameter <c>start_boot_server</c>.</p> <p>The <c>erl_boot_server</c> can read regular files and files in archives. See <seealso marker="code"><c>code(3)</c></seealso> and <seealso marker="erts:erl_prim_loader"><c>erl_prim_loader(3)</c></seealso> - in <c>ERTS</c>.</p> + in ERTS.</p> <warning><p>The support for loading code from archive files is experimental. It is released before it is ready to obtain early feedback. The file format, semantics, diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml index a5ce58ef3e..75114e015c 100644 --- a/lib/kernel/doc/src/erl_ddll.xml +++ b/lib/kernel/doc/src/erl_ddll.xml @@ -201,7 +201,7 @@ <desc> <p>Removes a driver monitor in much the same way as <seealso marker="erts:erlang#erlang:demonitor/1"><c>erlang:demonitor/1</c></seealso> - in <c>ERTS</c> + in ERTS does with process monitors. For details about how to create driver monitors, see <seealso marker="#monitor/2"><c>monitor/2</c></seealso>, @@ -431,7 +431,7 @@ <p>Creates a driver monitor and works in many ways as <seealso marker="erts:erlang#erlang:monitor/2"><c>erlang:monitor/2</c></seealso> - in <c>ERTS</c>, + in ERTS, does for processes. When a driver changes state, the monitor results in a monitor message that is sent to the calling process. <c><anno>MonitorRef</anno></c> returned by this function is @@ -745,7 +745,7 @@ <p>This parameter is the name of the driver to be used in subsequent calls to function <seealso marker="erts:erlang#open_port/2"><c>erlang:open_port</c></seealso> - in <c>ERTS</c>. + in ERTS. The name can be specified as an <c>iolist()</c> or an <c>atom()</c>. The name specified when loading is used to find the object file (with the help of <c><anno>Path</anno></c> diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index a8273e59e2..814e8eac46 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -33,7 +33,7 @@ <description> <p>The Erlang <em>error logger</em> is an event manager (see <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso> and - <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>), + <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>), registered as <c>error_logger</c>. Errors, warnings, and info events are sent to the error logger from the Erlang runtime system and the different Erlang/OTP applications. The events are, by default, @@ -44,12 +44,12 @@ executing.</p> <p>Initially, <c>error_logger</c> has only a primitive event handler, which buffers and prints the raw event messages. During - system startup, the <c>Kernel</c> application replaces this with a + system startup, the Kernel application replaces this with a <em>standard event handler</em>, by default one that writes - nicely formatted output to the terminal. <c>Kernel</c> can also be + nicely formatted output to the terminal. Kernel can also be configured so that events are logged to a file instead, or not logged at all, see <seealso marker="kernel_app"><c>kernel(6)</c></seealso>.</p> - <p>Also the <c>SASL</c> application, if started, adds its own event + <p>Also the SASL application, if started, adds its own event handler, which by default writes supervisor, crash, and progress reports to the terminal. See <seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso>.</p> @@ -58,9 +58,9 @@ User-defined event handlers can be added to handle application-specific events, see <seealso marker="#add_report_handler/1"><c>add_report_handler/1,2</c></seealso>. - Also, a useful event handler is provided in <c>STDLIB</c> for multi-file + Also, a useful event handler is provided in STDLIB for multi-file logging of events, see - <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso>.</p> + <seealso marker="stdlib:log_mf_h"><c>log_mf_h(3)</c></seealso>.</p> <p>Warning events were introduced in Erlang/OTP R9C and are enabled by default as from Erlang/OTP 18.0. To retain backwards compatibility with existing user-defined event handlers, the warning events can be @@ -82,7 +82,7 @@ <p>Adds a new event handler to the error logger. The event handler must be implemented as a <c>gen_event</c> callback module, see - <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p> + <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p> <p><c><anno>Handler</anno></c> is typically the name of the callback module and <c><anno>Args</anno></c> is an optional term (defaults to []) passed to the initialization callback function <c><anno>Handler</anno>:init/1</c>. @@ -97,7 +97,7 @@ <desc> <p>Deletes an event handler from the error logger by calling <c>gen_event:delete_handler(error_logger, <anno>Handler</anno>, [])</c>, - see <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p> + see <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p> </desc> </func> <func> @@ -110,7 +110,7 @@ The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as the arguments of <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> - in <c>STDLIB</c>. + in STDLIB. The event is handled by the standard event handler.</p> <p><em>Example:</em></p> <pre> @@ -171,7 +171,7 @@ ok</pre> The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as the arguments of <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> - in <c>STDLIB</c>. The event is handled by the standard event handler.</p> + in STDLIB. The event is handled by the standard event handler.</p> <p><em>Example:</em></p> <pre> 1> <input>error_logger:info_msg("Something happened in ~p~n", [a_module]).</input> @@ -235,7 +235,7 @@ ok</pre> <p>Enables or disables printout of standard events to a file.</p> <p>This is done by adding or deleting the standard event handler for output to file. Thus, calling this function overrides - the value of the <c>Kernel</c> <c>error_logger</c> configuration + the value of the Kernel <c>error_logger</c> configuration parameter.</p> <p>Enabling file logging can be used together with calling <c>tty(false)</c>, to have a silent system where @@ -274,7 +274,7 @@ ok</pre> to the terminal.</p> <p>This is done by adding or deleting the standard event handler for output to the terminal. Thus, calling this function overrides - the value of the <c>Kernel</c> <c>error_logger</c> configuration parameter.</p> + the value of the Kernel <c>error_logger</c> configuration parameter.</p> </desc> </func> <func> @@ -323,7 +323,7 @@ ok</pre> The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as the arguments of <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> - in <c>STDLIB</c>. + in STDLIB. The event is handled by the standard event handler. It is tagged as an error, warning, or info, see <seealso marker="#warning_map/0"><c>warning_map/0</c></seealso>.</p> @@ -416,8 +416,8 @@ ok</pre> </section> <section> <title>See Also</title> - <p><seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>, - <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso> + <p><seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>, + <seealso marker="stdlib:log_mf_h"><c>log_mf_h(3)</c></seealso> <seealso marker="kernel_app"><c>kernel(6)</c></seealso> <seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso></p> </section> diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 7d86c3ebcb..09497482cf 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -79,7 +79,7 @@ <seealso marker="#list_dir_all"><c>list_dir_all/1</c></seealso> and <seealso marker="#read_link_all"><c>read_link_all/1</c></seealso>.</p> - <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the <c>STDLIB</c> User´s Giude.</p> + <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the STDLIB User's Guide.</p> </description> @@ -277,7 +277,7 @@ f.txt: {person, "kalle", 25}. {ok,[{person,"kalle",25},{person,"pelle",30}]}</pre> <p>The encoding of <c><anno>Filename</anno></c> can be set by a comment, as described in - <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> </desc> </func> <func> @@ -445,7 +445,7 @@ f.txt: {person, "kalle", 25}. </taglist> <p>The encoding of <c><anno>Filename</anno></c> can be set by a comment, as described in - <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> </desc> </func> <func> @@ -455,7 +455,7 @@ f.txt: {person, "kalle", 25}. <p>The same as <c>eval/1</c>, but the variable bindings <c><anno>Bindings</anno></c> are used in the evaluation. For information about the variable bindings, see - <seealso marker="stdlib:erl_eval"><c>stdlib:erl_eval(3)</c></seealso>.</p> + <seealso marker="stdlib:erl_eval"><c>erl_eval(3)</c></seealso>.</p> </desc> </func> <func> @@ -830,7 +830,7 @@ f.txt: {person, "kalle", 25}. this module (<c>file</c>) for reading and writing data as the interfaces provided here work with byte-oriented data. Using other (Unicode) encodings makes the - <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> functions + <seealso marker="stdlib:io"><c>io(3)</c></seealso> functions <c>get_chars</c>, <c>get_line</c>, and <c>put_chars</c> more suitable, as they can work with the full Unicode range.</p> <p>If data is sent to an <c>io_device()</c> in a format that cannot be @@ -847,7 +847,7 @@ f.txt: {person, "kalle", 25}. that is, <seealso marker="#read/2"><c>read/2</c></seealso> are returned "as is". If module - <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is used for + <seealso marker="stdlib:io"><c>io(3)</c></seealso> is used for writing, the file can only cope with Unicode characters up to code point 255 (the ISO Latin-1 range).</p> </item> @@ -861,7 +861,7 @@ f.txt: {person, "kalle", 25}. the file lies beyond the ISO Latin-1 range (0..255), but failure occurs if the data contains Unicode code points beyond that range. The file is best read with the functions in the Unicode aware module - <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>.</p> + <seealso marker="stdlib:io"><c>io(3)</c></seealso>.</p> <p>Bytes written to the file by any means are translated to UTF-8 encoding before being stored on the disk file.</p> </item> @@ -891,7 +891,7 @@ f.txt: {person, "kalle", 25}. So a file can be analyzed in latin1 encoding for, for example, a BOM, positioned beyond the BOM and then be set for the right encoding before further reading. For functions identifying BOMs, see module - <seealso marker="stdlib:unicode"><c>stdlib:unicode(3)</c></seealso>. </p> + <seealso marker="stdlib:unicode"><c>unicode(3)</c></seealso>. </p> <p>This option is not allowed on <c>raw</c> files.</p> </item> <tag><c>ram</c></tag> @@ -932,7 +932,7 @@ f.txt: {person, "kalle", 25}. closed and the process itself is terminated. An <c><anno>IoDevice</anno></c> returned from this call can be used as an argument to the I/O functions (see - <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>).</p> + <seealso marker="stdlib:io"><c>io(3)</c></seealso>).</p> <note> <p>In previous versions of <c>file</c>, modes were specified as one of the atoms <c>read</c>, <c>write</c>, or @@ -1055,7 +1055,7 @@ f.txt: {person, "kalle", 25}. </taglist> <p>The encoding of <c><anno>Filename</anno></c> can be set by a comment as described in - <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> </desc> </func> <func> @@ -1128,7 +1128,7 @@ f.txt: {person, "kalle", 25}. </taglist> <p>The encoding of <c><anno>Filename</anno></c> can be set by a comment as described in - <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> </desc> </func> <func> @@ -1389,7 +1389,7 @@ f.txt: {person, "kalle", 25}. <c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise <c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c> is a record - <c>file_info</c>, defined in the <c>Kernel</c> include file + <c>file_info</c>, defined in the Kernel include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> <code type="none"> @@ -1552,7 +1552,7 @@ f.txt: {person, "kalle", 25}. raw line-oriented reading.</p> <p>If <c>encoding</c> is set to something else than <c>latin1</c>, the <c>read_line/1</c> call fails if the data contains characters larger than 255, - why module <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is to be + why module <seealso marker="stdlib:io"><c>io(3)</c></seealso> is to be preferred when reading such a file.</p> <p>The function returns:</p> <taglist> @@ -1970,7 +1970,7 @@ f.txt: {person, "kalle", 25}. <p>Changes file information. Returns <c>ok</c> if successful, otherwise <c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c> is a record - <c>file_info</c>, defined in the <c>Kernel</c> include file + <c>file_info</c>, defined in the Kernel include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> <code type="none"> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 83242c2df8..08454b9832 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -216,7 +216,7 @@ do_recv(Sock, Bs) -> time-out in milliseconds. Defaults to <c>infinity</c>.</p> <note> <p>The default values for options specified to <c>connect</c> can - be affected by the <c>Kernel</c> configuration parameter + be affected by the Kernel configuration parameter <c>inet_default_connect_options</c>. For details, see <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </note> @@ -293,7 +293,7 @@ do_recv(Sock, Bs) -> <seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p> <note> <p>The default values for options specified to <c>listen</c> can - be affected by the <c>Kernel</c> configuration parameter + be affected by the Kernel configuration parameter <c>inet_default_listen_options</c>. For details, see <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </note> @@ -307,7 +307,7 @@ do_recv(Sock, Bs) -> <type_desc variable="HttpPacket">See the description of <c>HttpPacket</c> in <seealso marker="erts:erlang#decode_packet/3"><c>erlang:decode_packet/3</c></seealso> - in <c>ERTS</c>. + in ERTS. </type_desc> <desc> <p>Receives a packet from a socket in passive diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 864f8facac..59a046bf4d 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -83,7 +83,7 @@ <c><![CDATA[SIGKILL]]></c>:</p> <pre> % <input>erl -heart -env HEART_KILL_SIGNAL SIGABRT ...</input></pre> - <p> If heart should <b>not</b> kill the Erlang runtime system, this can be indicated + <p> If heart should <em>not</em> kill the Erlang runtime system, this can be indicated using the environment variable <c><![CDATA[HEART_NO_KILL=TRUE]]></c>. This can be useful if the command executed by heart takes care of this, for example as part of a specific cleanup sequence. diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index c0dce2f50c..3f4e3684f4 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -36,7 +36,7 @@ <seealso marker="erts:inet_cfg">ERTS User's Guide: Inet Configuration</seealso> for more information about how to configure an Erlang runtime system for IP communication.</p> - <p>The following two <c>Kernel</c> configuration parameters affect the + <p>The following two Kernel configuration parameters affect the behavior of all sockets opened on an Erlang node:</p> <list type="bulleted"> <item><p><c>inet_default_connect_options</c> can contain a list of @@ -48,7 +48,7 @@ <p>When <c>accept</c> is issued, the values of the listening socket options are inherited. No such application variable is therefore needed for <c>accept</c>.</p> - <p>Using the <c>Kernel</c> configuration parameters above, one + <p>Using the Kernel configuration parameters above, one can set default options for all TCP sockets on a node, but use this with care. Options such as <c>{delay_send,true}</c> can be specified in this way. The following is an example of starting an Erlang @@ -95,7 +95,7 @@ fe80::204:acff:fe17:bf38 <datatype> <name name="hostent"/> <desc> - <p>The record is defined in the <c>Kernel</c> include file + <p>The record is defined in the Kernel include file <c>"inet.hrl"</c>.</p> <p>Add the following directive to the module:</p> <code> @@ -151,6 +151,12 @@ fe80::204:acff:fe17:bf38 <name name="socket_address"/> </datatype> <datatype> + <name name="socket_getopt"/> + </datatype> + <datatype> + <name name="socket_setopt"/> + </datatype> + <datatype> <name name="returned_non_ip_address"/> <desc> <p> @@ -327,8 +333,6 @@ fe80::204:acff:fe17:bf38 <func> <name name="getopts" arity="2"/> <fsummary>Get one or more options for a socket.</fsummary> - <type name="socket_getopt"/> - <type name="socket_setopt"/> <desc> <p>Gets one or more options for a socket. For a list of available options, see @@ -387,7 +391,7 @@ get_tcpi_sacked(Sock) -> <<_:28/binary,TcpiSacked:32/native,_/binary>> = Info, TcpiSacked.]]></code> <p>Preferably, you would check the machine type, the operating system, - and the <c>Kernel</c> version before executing anything similar to + and the Kernel version before executing anything similar to this code.</p> </desc> </func> @@ -580,7 +584,6 @@ get_tcpi_sacked(Sock) -> <func> <name name="setopts" arity="2"/> <fsummary>Set one or more options for a socket.</fsummary> - <type name="socket_setopt"/> <desc> <p>Sets one or more options for a socket.</p> <p>The following options are available:</p> @@ -658,7 +661,7 @@ get_tcpi_sacked(Sock) -> <p>The size of the user-level software buffer used by the driver. Not to be confused with options <c>sndbuf</c> and <c>recbuf</c>, which correspond to the - <c>Kernel</c> socket buffers. It is recommended + Kernel socket buffers. It is recommended to have <c>val(buffer) >= max(val(sndbuf),val(recbuf))</c> to avoid performance issues because of unnecessary copying. <c>val(buffer)</c> is automatically set to the above @@ -717,7 +720,7 @@ get_tcpi_sacked(Sock) -> <p>The socket message queue is set to a busy state when the amount of data on the message queue reaches this limit. Notice that this limit only - concerns data that has not yet reached the <c>ERTS</c> internal + concerns data that has not yet reached the ERTS internal socket implementation. Defaults to 8 kB.</p> <p>Senders of data to the socket are suspended if either the socket message queue is busy or the socket @@ -733,7 +736,7 @@ get_tcpi_sacked(Sock) -> <tag><c>{high_watermark, Size}</c> (TCP/IP sockets)</tag> <item> <p>The socket is set to a busy state when the amount - of data queued internally by the <c>ERTS</c> socket implementation + of data queued internally by the ERTS socket implementation reaches this limit. Defaults to 8 kB.</p> <p>Senders of data to the socket are suspended if either the socket message queue is busy or the socket @@ -813,7 +816,7 @@ get_tcpi_sacked(Sock) -> socket message queue is set in a not busy state when the amount of data queued in the message queue falls below this limit. Notice that this limit only concerns data - that has not yet reached the <c>ERTS</c> internal socket + that has not yet reached the ERTS internal socket implementation. Defaults to 4 kB.</p> <p>Senders that are suspended because of either a busy message queue or a busy socket are resumed @@ -831,7 +834,7 @@ get_tcpi_sacked(Sock) -> <item> <p>If the socket is in a busy state, the socket is set in a not busy state when the amount of data - queued internally by the <c>ERTS</c> socket implementation + queued internally by the ERTS socket implementation falls below this limit. Defaults to 4 kB.</p> <p>Senders that are suspended because of a busy message queue or a busy socket are resumed @@ -951,7 +954,7 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code> are returned with the format according to <c>HttpPacket</c> described in <seealso marker="erts:erlang#decode_packet/3"> - <c>erlang:decode_packet/3</c></seealso> in <c>ERTS</c>. + <c>erlang:decode_packet/3</c></seealso> in ERTS. A socket in passive mode returns <c>{ok, HttpPacket}</c> from <c>gen_tcp:recv</c> while an active socket sends messages like @@ -1127,7 +1130,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> can respond differently to this kind of option manipulation. Use with care.</p> <p>Notice that the default options for TCP/IP sockets can be - changed with the <c>Kernel</c> configuration parameters mentioned in + changed with the Kernel configuration parameters mentioned in the beginning of this manual page.</p> </desc> </func> diff --git a/lib/kernel/doc/src/init_stub.xml b/lib/kernel/doc/src/init_stub.xml index df89b174ca..1297c8264d 100644 --- a/lib/kernel/doc/src/init_stub.xml +++ b/lib/kernel/doc/src/init_stub.xml @@ -34,6 +34,6 @@ <modulesummary>Coordination of system startup.</modulesummary> <description> <p>This module is moved to the - <seealso marker="erts:init"><c>ERTS</c></seealso> application.</p> + <seealso marker="erts:init">ERTS</seealso> application.</p> </description> </erlref> diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 9e6fb60bb7..df681a505f 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -31,12 +31,12 @@ <app>kernel</app> <appsummary>The Kernel application.</appsummary> <description> - <p>The <c>Kernel</c> application has all the code necessary to run + <p>The Kernel application has all the code necessary to run the Erlang runtime system: file servers, code servers, and so on.</p> - <p>The <c>Kernel</c> application is the first application started. It is + <p>The Kernel application is the first application started. It is mandatory in the sense that the minimal system based on - Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>. <c>Kernel</c> + Erlang/OTP consists of Kernel and STDLIB. Kernel contains the following functional areas:</p> <list type="bulleted"> <item>Start, stop, supervision, configuration, and distribution of applications</item> @@ -53,13 +53,13 @@ <section> <title>Error Logger Event Handlers</title> <p>Two standard error logger event handlers are defined in - the <c>Kernel</c> application. These are described in + the Kernel application. These are described in <seealso marker="error_logger"><c>error_logger(3)</c></seealso>.</p> </section> <section> <title>Configuration</title> - <p>The following configuration parameters are defined for the <c>Kernel</c> + <p>The following configuration parameters are defined for the Kernel application. For more information about configuration parameters, see file <seealso marker="app"><c>app(4)</c></seealso>.</p> <taglist> @@ -162,8 +162,8 @@ depth to which terms are printed by the error logger event handlers included in OTP. This configuration parameter is used by the two event handlers - defined by the <c>Kernel</c> application and the two event - handlers in the <c>SASL</c> application. + defined by the Kernel application and the two event + handlers in the SASL application. (If you have implemented your own error handlers, this configuration parameter has no effect on them.)</p> @@ -173,7 +173,7 @@ <c>~P</c> and <c>~W</c>, respectively, and <c>Depth</c> is used as the depth parameter. For details, see <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> - in <c>STDLIB</c>.</p> + in STDLIB.</p> <note><p>A reasonable starting value for <c>Depth</c> is <c>30</c>. We recommend to test crashing various processes in your @@ -217,12 +217,14 @@ </item> <tag><c>{inet_dist_listen_options, Opts}</c></tag> <item> + <marker id="inet_dist_listen_options"></marker> <p>Defines a list of extra socket options to be used when opening the listening socket for a distributed Erlang node. See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p> </item> <tag><c>{inet_dist_connect_options, Opts}</c></tag> <item> + <marker id="inet_dist_connect_options"></marker> <p>Defines a list of extra socket options to be used when connecting to other distributed Erlang nodes. See <seealso marker="gen_tcp#connect/4"><c>gen_tcp:connect/4</c></seealso>.</p> @@ -239,7 +241,7 @@ <p>The name (string) of an Inet user configuration file. For details, see section <seealso marker="erts:inet_cfg"><c>Inet Configuration</c></seealso> - in the <c>ERTS</c> User's Guide.</p> + in the ERTS User's Guide.</p> </item> <tag><c>net_setuptime = SetupTime</c></tag> <item> @@ -358,7 +360,7 @@ MaxT = TickTime + TickTime / 4</code> <tag><c>start_timer = true | false</c></tag> <item> <p>Starts the <c>timer_server</c> if the parameter is - <c>true</c> (see <seealso marker="stdlib:timer"><c>stdlib:timer(3)</c></seealso>). + <c>true</c> (see <seealso marker="stdlib:timer"><c>timer(3)</c></seealso>). This parameter is to be set to <c>true</c> in an embedded system using this service.</p> <p>Defaults to <c>false</c>.</p> diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index f48a534d4f..4e2b0c69db 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -55,7 +55,7 @@ $ <input>erl -sname foobar</input></pre> <seealso marker="erts:erl"><c>erl</c></seealso>.</p> <p>Normally, connections are established automatically when another node is referenced. This functionality can be disabled - by setting <c>Kernel</c> configuration parameter + by setting Kernel configuration parameter <c>dist_auto_connect</c> to <c>false</c>, see <seealso marker="kernel_app"><c>kernel(6)</c></seealso>. In this case, connections must be established explicitly by calling @@ -116,6 +116,21 @@ $ <input>erl -sname foobar</input></pre> </func> <func> + <name name="getopts" arity="2"/> + <fsummary>Get distribution socket options.</fsummary> + <desc> + <p>Get one or more options for the distribution socket + connected to <c><anno>Node</anno></c>.</p> + <p>If <c><anno>Node</anno></c> is a connected node + the return value is the same as from + <seealso marker="inet#getopts/2"><c>inet:getopts(Sock, Options)</c></seealso> + where <c>Sock</c> is the distribution socket for <c><anno>Node</anno></c>.</p> + <p>Returns <c>ignored</c> if the local node is not alive or + <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not connected.</p> + </desc> + </func> + + <func> <name name="monitor_nodes" arity="1"/> <name name="monitor_nodes" arity="2"/> <fsummary>Subscribe to node status change messages.</fsummary> @@ -131,7 +146,7 @@ $ <input>erl -sname foobar</input></pre> are stopped. Two option lists are considered the same if they contain the same set of options.</p> - <p>As from <c>Kernel</c> version 2.11.4, and <c>ERTS</c> version + <p>As from Kernel version 2.11.4, and ERTS version 5.5.4, the following is guaranteed:</p> <list type="bulleted"> <item><p><c>nodeup</c> messages are delivered before delivery @@ -141,13 +156,13 @@ $ <input>erl -sname foobar</input></pre> messages from the remote node that have been passed through the connection have been delivered.</p></item> </list> - <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c> + <p>Notice that this is <em>not</em> guaranteed for Kernel versions before 2.11.4.</p> - <p>As from <c>Kernel</c> version 2.11.4, subscriptions can also be + <p>As from Kernel version 2.11.4, subscriptions can also be made before the <c>net_kernel</c> server is started, that is, <c>net_kernel:monitor_nodes/[1,2]</c> does not return <c>ignored</c>.</p> - <p>As from <c>Kernel</c> version 2.13, and <c>ERTS</c> version + <p>As from Kernel version 2.13, and ERTS version 5.7, the following is guaranteed:</p> <list type="bulleted"> <item><p><c>nodeup</c> messages are delivered after the @@ -157,7 +172,7 @@ $ <input>erl -sname foobar</input></pre> corresponding node has disappeared in results from <c>erlang:nodes/X</c>.</p></item> </list> - <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c> + <p>Notice that this is <em>not</em> guaranteed for Kernel versions before 2.13.</p> <p>The format of the node status change messages depends on <c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is @@ -289,6 +304,27 @@ $ <input>erl -sname foobar</input></pre> </func> <func> + <name name="setopts" arity="2"/> + <fsummary>Set distribution socket options.</fsummary> + <desc> + <p>Set one or more options for distribution sockets. + Argument <c><anno>Node</anno></c> can be either one node name + or the atom <c>new</c> to affect the distribution sockets of all + future connected nodes.</p> + <p>The return value is the same as from + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso> + or <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not + a connected node or <c>new</c>.</p> + <p>If <c><anno>Node</anno></c> is <c>new</c> the <c><anno>Options</anno></c> + will then also be added to kernel configration parameters + <seealso marker="kernel:kernel_app#inet_dist_listen_options">inet_dist_listen_options</seealso> + and + <seealso marker="kernel:kernel_app#inet_dist_connect_options">inet_dist_connect_options</seealso>.</p> + <p>Returns <c>ignored</c> if the local node is not alive.</p> + </desc> + </func> + + <func> <name>start([Name]) -> {ok, pid()} | {error, Reason}</name> <name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name> <name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index f37433110c..9e9be3f661 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,41 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 5.0.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + When calling os:cmd from a process that has set trap_exit + to true an 'EXIT' message would be left in the message + queue. This bug was introduced in kernel vsn 5.0.1.</p> + <p> + Own Id: OTP-13813</p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 5.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix a os:cmd bug where creating a background job using + & would cause os:cmd to hang until the background job + terminated or closed its stdout and stderr file + descriptors. This bug has existed from kernel 5.0.</p> + <p> + Own Id: OTP-13741</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 5.0</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -3447,7 +3482,7 @@ types (for instance, <c>ensure_loaded/1</c> now only accepts an atom as documented; it used to accept a string too).</p> - <p><c>Dialyzer</c> will generally emit warnings for any + <p>Dialyzer will generally emit warnings for any calls that use undocumented argument types. Even if the call happens to still work in R12B, you should correct your code. A future release will adhere to the diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index 8cad9fe4fc..5944e9321a 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -320,7 +320,7 @@ <fsummary>Information about a process.</fsummary> <desc> <p>Location transparent version of the BIF - <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in <c>ERTS</c>.</p> + <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in ERTS.</p> </desc> </func> @@ -330,7 +330,7 @@ <fsummary>Information about a process.</fsummary> <desc> <p>Location transparent version of the BIF - <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in <c>ERTS</c>.</p> + <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in ERTS.</p> </desc> </func> diff --git a/lib/kernel/doc/src/zlib_stub.xml b/lib/kernel/doc/src/zlib_stub.xml index b111581b10..9ab9c4eb62 100644 --- a/lib/kernel/doc/src/zlib_stub.xml +++ b/lib/kernel/doc/src/zlib_stub.xml @@ -34,6 +34,6 @@ <modulesummary>Zlib compression interface.</modulesummary> <description> <p>This module is moved to the - <seealso marker="erts:zlib"><c>ERTS</c></seealso> application.</p> + <seealso marker="erts:zlib">ERTS</seealso> application.</p> </description> </erlref> diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index 43e50d4325..320e916c04 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -63,7 +63,7 @@ f_getll, %% Get low level port or pid. f_address, %% The address of the "socket", %% generated from Socket,Node - %% These two are used in the tick loop, + %% These three are used in the tick loop, %% so they are not fun's to avoid holding old code. mf_tick, %% Takes the socket as parameters and %% sends a tick, this is no fun, it @@ -74,7 +74,11 @@ %% {ok, RecvCnt, SendCnt, SendPend} for %% a given socket. This is a {M,F}, %% returning {error, Reason on failure} - request_type = normal + request_type = normal, + + %% New in kernel-5.1 (OTP 19.1): + mf_setopts, %% netkernel:setopts on active connection + mf_getopts %% netkernel:getopts on active connection }). diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 6174136507..48541ec500 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) -> -spec call(term()) -> term(). call(Req) -> + Ref = erlang:monitor(process, ?MODULE), ?MODULE ! {code_call, self(), Req}, receive {?MODULE, Reply} -> - Reply + erlang:demonitor(Ref,[flush]), + Reply; + {'DOWN',Ref,process,_,_} -> + exit({'DOWN',code_server,Req}) end. reply(Pid, Res) -> @@ -933,14 +937,20 @@ del_ebin(Dir) -> filename:join(del_ebin_1(filename:split(Dir))). del_ebin_1([Parent,App,"ebin"]) -> - Ext = archive_extension(), - case filename:basename(Parent, Ext) of - Parent -> - %% Plain directory. + case filename:basename(Parent) of + [] -> + %% Parent is the root directory [Parent,App]; - Archive -> - %% Archive. - [Archive] + _ -> + Ext = archive_extension(), + case filename:basename(Parent, Ext) of + Parent -> + %% Plain directory. + [Parent,App]; + Archive -> + %% Archive. + [Archive] + end end; del_ebin_1([H|T]) -> [H|del_ebin_1(T)]; diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 47d0c1b861..8d2fc4d4b7 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -143,7 +143,11 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) -> ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie), send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)), ?debug({dist_util, self(), accept_connection, Node}), - connection(HSData). + connection(HSData); + +handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data -> + handshake_other_started(convert_old_hsdata(OldHsData)). + %% %% check if connecting node is allowed to connect @@ -330,7 +334,20 @@ handshake_we_started(#hs_data{request_type=ReqType, gen_digest(ChallengeA,HisCookie)), reset_timer(NewHSData#hs_data.timer), recv_challenge_ack(NewHSData, MyChallenge, MyCookie), - connection(NewHSData). + connection(NewHSData); + +handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data -> + handshake_we_started(convert_old_hsdata(OldHsData)). + +convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR, + FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) -> + #hs_data{ + kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T, + this_flags = TF, allowed = A, other_version = OV, other_flags = OF, + other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE, + f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA, + mf_tick = MFT, mf_getstat = MFG, request_type = RT}. + %% -------------------------------------------------------------- %% The connection has been established. @@ -350,15 +367,15 @@ connection(#hs_data{other_node = Node, mark_nodeup(HSData,Address), case FPostNodeup(Socket) of ok -> - con_loop(HSData#hs_data.kernel_pid, - Node, - Socket, - Address, - HSData#hs_data.this_node, - PType, - #tick{}, - HSData#hs_data.mf_tick, - HSData#hs_data.mf_getstat); + con_loop({HSData#hs_data.kernel_pid, + Node, + Socket, + PType, + HSData#hs_data.mf_tick, + HSData#hs_data.mf_getstat, + HSData#hs_data.mf_setopts, + HSData#hs_data.mf_getopts}, + #tick{}); _ -> ?shutdown2(Node, connection_setup_failed) end; @@ -454,8 +471,8 @@ mark_nodeup(#hs_data{kernel_pid = Kernel, ?shutdown(Node) end. -con_loop(Kernel, Node, Socket, TcpAddress, - MyNode, Type, Tick, MFTick, MFGetstat) -> +con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData, + Tick) -> receive {tcp_closed, Socket} -> ?shutdown2(Node, connection_closed); @@ -468,15 +485,12 @@ con_loop(Kernel, Node, Socket, TcpAddress, _ -> ignore_it end, - con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type, - Tick, MFTick, MFGetstat); + con_loop(ConData, Tick); {Kernel, tick} -> case send_tick(Socket, Tick, Type, MFTick, MFGetstat) of {ok, NewTick} -> - con_loop(Kernel, Node, Socket, TcpAddress, - MyNode, Type, NewTick, MFTick, - MFGetstat); + con_loop(ConData, NewTick); {error, not_responding} -> error_msg("** Node ~p not responding **~n" "** Removing (timedout) connection **~n", @@ -489,13 +503,24 @@ con_loop(Kernel, Node, Socket, TcpAddress, case MFGetstat(Socket) of {ok, Read, Write, _} -> From ! {self(), get_status, {ok, Read, Write}}, - con_loop(Kernel, Node, Socket, TcpAddress, - MyNode, - Type, Tick, - MFTick, MFGetstat); + con_loop(ConData, Tick); _ -> ?shutdown2(Node, get_status_failed) - end + end; + {From, Ref, {setopts, Opts}} -> + Ret = case MFSetOpts of + undefined -> {error, enotsup}; + _ -> MFSetOpts(Socket, Opts) + end, + From ! {Ref, Ret}, + con_loop(ConData, Tick); + {From, Ref, {getopts, Opts}} -> + Ret = case MFGetOpts of + undefined -> {error, enotsup}; + _ -> MFGetOpts(Socket, Opts) + end, + From ! {Ref, Ret}, + con_loop(ConData, Tick) end. diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index f8ef4a475d..7bc9e2ede3 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -103,6 +103,10 @@ names(EpmdAddr) -> register_node(Name, PortNo) -> register_node(Name, PortNo, inet). +register_node(Name, PortNo, inet_tcp) -> + register_node(Name, PortNo, inet); +register_node(Name, PortNo, inet6_tcp) -> + register_node(Name, PortNo, inet6); register_node(Name, PortNo, Family) -> gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity). @@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 -> 0; select_best_version(_L1, H1, L2, _H2) when L2 > H1 -> 0; -select_best_version(_L1, H1, L2, _H2) when L2 > H1 -> - 0; select_best_version(_L1, H1, _L2, H2) -> erlang:min(H1, H2). diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index a91a6ed517..75dd800c6b 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -75,6 +75,7 @@ -export_type([address_family/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, local_address/0, socket_address/0, returned_non_ip_address/0, + socket_setopt/0, socket_getopt/0, posix/0, socket/0, stat_option/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl index 3aa61973af..9b6c2745d5 100644 --- a/lib/kernel/src/inet6_tcp_dist.erl +++ b/lib/kernel/src/inet6_tcp_dist.erl @@ -24,6 +24,7 @@ -export([listen/1, accept/1, accept_connection/5, setup/5, close/1, select/1, is_node_name/1]). +-export([setopts/2, getopts/2]). %% ------------------------------------------------------------ %% Select this protocol based on node name @@ -72,3 +73,9 @@ close(Socket) -> is_node_name(Node) when is_atom(Node) -> inet_tcp_dist:is_node_name(Node). + +setopts(S, Opts) -> + inet_tcp_dist:setopts(S, Opts). + +getopts(S, Opts) -> + inet_tcp_dist:getopts(S, Opts). diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index f91d7ef7c3..3084bd599a 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -24,13 +24,16 @@ -export([listen/1, accept/1, accept_connection/5, setup/5, close/1, select/1, is_node_name/1]). +%% Optional +-export([setopts/2, getopts/2]). + %% Generalized dist API -export([gen_listen/2, gen_accept/2, gen_accept_connection/6, gen_setup/6, gen_select/2]). %% internal exports --export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]). +-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1,tick/2]). -import(error_logger,[error_msg/2]). @@ -74,7 +77,7 @@ gen_listen(Driver, Name) -> TcpAddress = get_tcp_address(Driver, Socket), {_,Port} = TcpAddress#net_address.address, ErlEpmd = net_kernel:epmd_module(), - case ErlEpmd:register_node(Name, Port) of + case ErlEpmd:register_node(Name, Port, Driver) of {ok, Creation} -> {ok, {Socket, TcpAddress, Creation}}; Error -> @@ -215,8 +218,10 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> inet:getll(S) end, f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end, - mf_tick = fun(S) -> tick(Driver, S) end, - mf_getstat = fun ?MODULE:getstat/1 + mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end, + mf_getstat = fun ?MODULE:getstat/1, + mf_setopts = fun ?MODULE:setopts/2, + mf_getopts = fun ?MODULE:getopts/2 }, dist_util:handshake_other_started(HSData); {false,IP} -> @@ -320,6 +325,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> {packet, 4}, nodelay()]) end, + f_getll = fun inet:getll/1, f_address = fun(_,_) -> @@ -329,9 +335,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> protocol = tcp, family = AddressFamily} end, - mf_tick = fun(S) -> tick(Driver, S) end, + mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end, mf_getstat = fun ?MODULE:getstat/1, - request_type = Type + request_type = Type, + mf_setopts = fun ?MODULE:setopts/2, + mf_getopts = fun ?MODULE:getopts/2 }, dist_util:handshake_we_started(HSData); _ -> @@ -492,3 +500,12 @@ split_stat([], R, W, P) -> {ok, R, W, P}. +setopts(S, Opts) -> + case [Opt || {K,_}=Opt <- Opts, + K =:= active orelse K =:= deliver orelse K =:= packet] of + [] -> inet:setopts(S,Opts); + Opts1 -> {error, {badopts,Opts1}} + end. + +getopts(S, Opts) -> + inet:getopts(S, Opts). diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index ac19f4935b..0c679e7349 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -59,6 +59,8 @@ connect_node/1, monitor_nodes/1, monitor_nodes/2, + setopts/2, + getopts/2, start/1, stop/0]). @@ -111,7 +113,7 @@ }). -record(listen, { - listen, %% listen pid + listen, %% listen socket accept, %% accepting pid address, %% #net_address module %% proto module @@ -384,7 +386,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) -> connections = ets:new(sys_dist,[named_table, protected, - {keypos, 2}]), + {keypos, #connection.node}]), listen = Listeners, allowed = [], verbose = 0 @@ -554,6 +556,38 @@ handle_call({new_ticktime,_T,_TP}, #state{tick = #tick_change{time = T}} = State) -> async_reply({reply, {ongoing_change_to, T}, State}, From); +handle_call({setopts, new, Opts}, From, State) -> + Ret = setopts_new(Opts, State), + async_reply({reply, Ret, State}, From); + +handle_call({setopts, Node, Opts}, From, State) -> + Return = + case ets:lookup(sys_dist, Node) of + [Conn] when Conn#connection.state =:= up -> + case call_owner(Conn#connection.owner, {setopts, Opts}) of + {ok, Ret} -> Ret; + _ -> {error, noconnection} + end; + + _ -> + {error, noconnection} + end, + async_reply({reply, Return, State}, From); + +handle_call({getopts, Node, Opts}, From, State) -> + Return = + case ets:lookup(sys_dist, Node) of + [Conn] when Conn#connection.state =:= up -> + case call_owner(Conn#connection.owner, {getopts, Opts}) of + {ok, Ret} -> Ret; + _ -> {error, noconnection} + end; + + _ -> + {error, noconnection} + end, + async_reply({reply, Return, State}, From); + handle_call(_Msg, _From, State) -> {noreply, State}. @@ -1608,3 +1642,93 @@ async_gen_server_reply(From, Msg) -> {'EXIT', _} -> ok end. + +call_owner(Owner, Msg) -> + Mref = monitor(process, Owner), + Owner ! {self(), Mref, Msg}, + receive + {Mref, Reply} -> + erlang:demonitor(Mref, [flush]), + {ok, Reply}; + {'DOWN', Mref, _, _, _} -> + error + end. + + +-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when + Node :: node() | new, + Options :: [inet:socket_setopt()], + Reason :: inet:posix() | noconnection. + +setopts(Node, Opts) when is_atom(Node), is_list(Opts) -> + request({setopts, Node, Opts}). + +setopts_new(Opts, State) -> + %% First try setopts on listening socket(s) + %% Bail out on failure. + %% If successful, we are pretty sure Opts are ok + %% and we continue with config params and pending connections. + case setopts_on_listen(Opts, State#state.listen) of + ok -> + setopts_new_1(Opts); + Fail -> Fail + end. + +setopts_on_listen(_, []) -> ok; +setopts_on_listen(Opts, [#listen {listen = LSocket, module = Mod} | T]) -> + try Mod:setopts(LSocket, Opts) of + ok -> + setopts_on_listen(Opts, T); + Fail -> Fail + catch + error:undef -> {error, enotsup} + end. + +setopts_new_1(Opts) -> + ConnectOpts = case application:get_env(kernel, inet_dist_connect_options) of + {ok, CO} -> CO; + _ -> [] + end, + application:set_env(kernel, inet_dist_connect_options, + merge_opts(Opts,ConnectOpts)), + ListenOpts = case application:get_env(kernel, inet_dist_listen_options) of + {ok, LO} -> LO; + _ -> [] + end, + application:set_env(kernel, inet_dist_listen_options, + merge_opts(Opts, ListenOpts)), + case lists:keyfind(nodelay, 1, Opts) of + {nodelay, ND} when is_boolean(ND) -> + application:set_env(kernel, dist_nodelay, ND); + _ -> ignore + end, + + %% Update any pending connections + PendingConns = ets:select(sys_dist, [{'_', + [{'=/=',{element,#connection.state,'$_'},up}], + ['$_']}]), + lists:foreach(fun(#connection{state = pending, owner = Owner}) -> + call_owner(Owner, {setopts, Opts}); + (#connection{state = up_pending, pending_owner = Owner}) -> + call_owner(Owner, {setopts, Opts}); + (_) -> ignore + end, PendingConns), + ok. + +merge_opts([], B) -> + B; +merge_opts([H|T], B0) -> + {Key, _} = H, + B1 = lists:filter(fun({K,_}) -> K =/= Key end, B0), + merge_opts(T, [H | B1]). + +-spec getopts(Node, Options) -> + {'ok', OptionValues} | {'error', Reason} | ignored when + Node :: node(), + Options :: [inet:socket_getopt()], + OptionValues :: [inet:socket_setopt()], + Reason :: inet:posix() | noconnection. + +getopts(Node, Opts) when is_atom(Node), is_list(Opts) -> + request({getopts, Node, Opts}). + diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index f0ad26b1f2..f8519d3a5e 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -226,11 +226,13 @@ extensions() -> Command :: atom() | io_lib:chars(). cmd(Cmd) -> validate(Cmd), - {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd), + {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd), Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout, - stream, in, eof, hide | SpawnOpts]), + stream, in, hide | SpawnOpts]), + MonRef = erlang:monitor(port, Port), true = port_command(Port, SpawnInput), - Bytes = get_data(Port, []), + Bytes = get_data(Port, MonRef, Eot, []), + demonitor(MonRef, [flush]), String = unicode:characters_to_list(Bytes), if %% Convert to unicode list if possible otherwise return bytes is_list(String) -> String; @@ -243,7 +245,7 @@ mk_cmd({win32,Wtype}, Cmd) -> {false,_} -> lists:concat(["cmd /c", Cmd]); {Cspec,_} -> lists:concat([Cspec," /c",Cmd]) end, - {Command, [], []}; + {Command, [], [], <<>>}; mk_cmd(OsType,Cmd) when is_atom(Cmd) -> mk_cmd(OsType, atom_to_list(Cmd)); mk_cmd(_,Cmd) -> @@ -252,7 +254,20 @@ mk_cmd(_,Cmd) -> {"/bin/sh -s unix:cmd", [out], %% We insert a new line after the command, in case the command %% contains a comment character. - ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}. + %% + %% The </dev/null closes stdin, which means that programs + %% that use a closed stdin as an termination indicator works. + %% An example of such a program is 'more'. + %% + %% The "echo ^D" is used to indicate that the program has executed + %% and we should return any output we have gotten. We cannot use + %% termination of the child or closing of stdin/stdout as then + %% starting background jobs from os:cmd will block os:cmd. + %% + %% I tried changing this to be "better", but got bombarded with + %% backwards incompatibility bug reports, so leave this as it is. + ["(", unicode:characters_to_binary(Cmd), "\n) </dev/null; echo \"\^D\"\n"], + <<$\^D>>}. validate(Atom) when is_atom(Atom) -> ok; @@ -267,21 +282,44 @@ validate1([List|Rest]) when is_list(List) -> validate1([]) -> ok. -get_data(Port, Sofar) -> +get_data(Port, MonRef, Eot, Sofar) -> receive {Port, {data, Bytes}} -> - get_data(Port, [Sofar,Bytes]); - {Port, eof} -> - Port ! {self(), close}, - receive - {Port, closed} -> - true - end, - receive - {'EXIT', Port, _} -> - ok - after 1 -> % force context switch - ok - end, + case eot(Bytes, Eot) of + more -> + get_data(Port, MonRef, Eot, [Sofar,Bytes]); + Last -> + Port ! {self(), close}, + flush_until_closed(Port), + flush_exit(Port), + iolist_to_binary([Sofar, Last]) + end; + {'DOWN', MonRef, _, _ , _} -> + flush_exit(Port), iolist_to_binary(Sofar) end. + +eot(_Bs, <<>>) -> + more; +eot(Bs, Eot) -> + case binary:match(Bs, Eot) of + nomatch -> more; + {Pos, _} -> + binary:part(Bs,{0, Pos}) + end. + +flush_until_closed(Port) -> + receive + {Port, {data, _Bytes}} -> + flush_until_closed(Port); + {Port, closed} -> + true + end. + +flush_exit(Port) -> + receive + {'EXIT', Port, _} -> + ok + after 1 -> % force context switch + ok + end. diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index eb58e92224..e43be77428 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -25,6 +25,7 @@ init_per_group/2,end_per_group/2]). -export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1, + setopts/1, table_waste/1, net_setuptime/1, inet_dist_options_options/1, @@ -42,6 +43,8 @@ -export([get_socket_priorities/0, tick_cli_test/1, tick_cli_test1/1, tick_serv_test/2, tick_serv_test1/1, + run_remote_test/1, + setopts_do/2, keep_conn/1, time_ping/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -66,6 +69,7 @@ suite() -> all() -> [tick, tick_change, illegal_nodenames, hidden_node, + setopts, table_waste, net_setuptime, inet_dist_options_options, {group, monitor_nodes}]. @@ -282,6 +286,165 @@ tick_cli_test1(Node) -> end end. +setopts(Config) when is_list(Config) -> + register(setopts_regname, self()), + [N1,N2,N3,N4] = get_nodenames(4, setopts), + + {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), "1", "ping"]), + 0 = wait_for_port_exit(Port1), + + {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), "2", "ping"]), + 0 = wait_for_port_exit(Port2), + + {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]), + {ok, LTcpPort} = inet:port(LSock), + + {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), + "1", integer_to_list(LTcpPort)]), + wait_and_connect(LSock, N3F, Port3), + 0 = wait_for_port_exit(Port3), + + {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), + "2", integer_to_list(LTcpPort)]), + wait_and_connect(LSock, N4F, Port4), + 0 = wait_for_port_exit(Port4), + + ok. + +wait_and_connect(LSock, NodeName, NodePort) -> + {ok, Sock} = gen_tcp:accept(LSock), + {ok, "Connect please"} = gen_tcp:recv(Sock, 0), + flush_from_port(NodePort), + pong = net_adm:ping(NodeName), + gen_tcp:send(Sock, "Connect done"), + gen_tcp:close(Sock). + + +flush_from_port(Port) -> + flush_from_port(Port, 10). + +flush_from_port(Port, Timeout) -> + receive + {Port,{data,String}} -> + io:format("~p: ~s\n", [Port, String]), + flush_from_port(Port, Timeout) + after Timeout -> + timeout + end. + +wait_for_port_exit(Port) -> + case (receive M -> M end) of + {Port,{exit_status,Status}} -> + Status; + {Port,{data,String}} -> + io:format("~p: ~s\n", [Port, String]), + wait_for_port_exit(Port) + end. + +run_remote_test([FuncStr, TestNodeStr | Args]) -> + Status = try + io:format("Node ~p started~n", [node()]), + TestNode = list_to_atom(TestNodeStr), + io:format("Node ~p spawning function ~p~n", [node(), FuncStr]), + {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]), + io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]), + receive + {'DOWN', Ref, process, Pid, normal} -> + 0; + Other -> + io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]), + 1 + end + catch + C:E -> + io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n", + [node(), C, E, erlang:get_stacktrace()]), + 2 + end, + io:format("Node ~p doing halt(~p).\n",[node(), Status]), + erlang:halt(Status). + +% Do the actual test on the remote node +setopts_do(TestNode, [OptNr, ConnectData]) -> + [] = nodes(), + {Opt, Val} = opt_from_nr(OptNr), + ok = net_kernel:setopts(new, [{Opt, Val}]), + + [] = nodes(), + {error, noconnection} = net_kernel:getopts(TestNode, [Opt]), + + case ConnectData of + "ping" -> % We connect + net_adm:ping(TestNode); + TcpPort -> % Other connect + {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort), + [{active,false},{packet,2}]), + ok = gen_tcp:send(Sock, "Connect please"), + {ok, "Connect done"} = gen_tcp:recv(Sock, 0), + gen_tcp:close(Sock) + end, + [TestNode] = nodes(), + {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), + {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]), + + NewVal = change_val(Val), + ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]), + {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]), + + ok = net_kernel:setopts(TestNode, [{Opt, Val}]), + {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), + + ok. + +opt_from_nr("1") -> {nodelay, true}; +opt_from_nr("2") -> {nodelay, false}. + +change_val(true) -> false; +change_val(false) -> true. + +start_node_unconnected(Name, Mod, Func, Args) -> + FullName = full_node_name(Name), + CmdLine = mk_node_cmdline(Name,Mod,Func,Args), + io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), + case open_port({spawn, CmdLine}, [exit_status]) of + Port when is_port(Port) -> + {FullName, Port}; + Error -> + exit({failed_to_start_node, FullName, Error}) + end. + +full_node_name(PreName) -> + HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, + atom_to_list(node())), + list_to_atom(atom_to_list(PreName) ++ HostSuffix). + +mk_node_cmdline(Name,Mod,Func,Args) -> + Static = "-noinput", + Pa = filename:dirname(code:which(?MODULE)), + Prog = case catch init:get_argument(progname) of + {ok,[[P]]} -> P; + _ -> exit(no_progname_argument_found) + end, + NameSw = case net_kernel:longnames() of + false -> "-sname "; + true -> "-name "; + _ -> exit(not_distributed_node) + end, + {ok, Pwd} = file:get_cwd(), + NameStr = atom_to_list(Name), + Prog ++ " " + ++ Static ++ " " + ++ NameSw ++ " " ++ NameStr + ++ " -pa " ++ Pa + ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr + ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) + ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func) + ++ " " ++ string:join(Args, " "). + %% OTP-4255. tick_change(Config) when is_list(Config) -> diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index f836b2aa94..620ab235a0 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) -> Stream = 0, Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, Loopback = {127,0,0,1}, + StatOpts = + [recv_avg,recv_cnt,recv_max,recv_oct, + send_avg,send_cnt,send_max,send_oct], {ok,Sb} = gen_sctp:open([{type,seqpacket}]), + {ok,SbStat1} = inet:getstat(Sb, StatOpts), {ok,Pb} = inet:port(Sb), ok = gen_sctp:listen(Sb, true), @@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) -> assoc_id=SbAssocId}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), ok = gen_sctp:close(Sa), + {ok,SbStat2} = inet:getstat(Sb, StatOpts), + [] = filter_stat_eq(SbStat1, SbStat2), ok = gen_sctp:close(Sb), receive @@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) -> end, ok. +filter_stat_eq([], []) -> + []; +filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) -> + if + Val1 == Val2 -> + [Stat|filter_stat_eq(SbStat1, SbStat2)]; + true -> + filter_stat_eq(SbStat1, SbStat2) + end. + + + %% Minimal data transfer in active mode. xfer_active(Config) when is_list(Config) -> Timeout = 2000, @@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) -> assoc_id=S2AssocId} = S2AssocChange = log_ok(gen_sctp:connect(S2, Loopback, P1, [])), ?LOGVAR(S2AssocChange), - case recv_event(log_ok(gen_sctp:recv(S1))) of - {Loopback,P2, - #sctp_assoc_change{ - state=comm_up, - error=0, - assoc_id=S1AssocId}} -> - ?LOGVAR(S1AssocId); - {Loopback,P2, - #sctp_paddr_change{ - state=addr_confirmed, - error=0, - assoc_id=S1AssocId}} -> - ?LOGVAR(S1AssocId), + S1AssocId = + case recv_event(log_ok(gen_sctp:recv(S1))) of {Loopback,P2, #sctp_assoc_change{ state=comm_up, error=0, - assoc_id=S1AssocId}} = - recv_event(log_ok(gen_sctp:recv(S1))) - end, + assoc_id=AssocId}} -> + AssocId; + {Loopback,P2, + #sctp_paddr_change{ + state=addr_confirmed, + error=0, + assoc_id=AssocId}} -> + {Loopback,P2, + #sctp_assoc_change{ + state=comm_up, + error=0, + assoc_id=AssocId}} = + recv_event(log_ok(gen_sctp:recv(S1))), + AssocId + end, + ?LOGVAR(S1AssocId), #sctp_sndrcvinfo{ ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} = @@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) -> Addr = {127,0,0,1}, Stream = 0, Timeout = 333, + StartTime = timestamp(), S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), ?LOGVAR(S1), P1 = socket_call(S1, get_port), @@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) -> state=comm_up, assoc_id=AssocId2}}} -> AssocId2 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, ?LOGVAR(S2Ai), S1Ai = @@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) -> state=comm_up, assoc_id=AssocId1}}} -> AssocId1 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, ?LOGVAR(S1Ai), %% @@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) -> receive {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}), receive {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout), @@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) -> receive {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok after Timeout -> - socket_bailout([S1,S2,S3]) + socket_bailout([S1,S2,S3], StartTime) end, socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}), receive {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok after Timeout -> - socket_bailout([S1,S2,S3]) + socket_bailout([S1,S2,S3], StartTime) end, %% inet:i(sctp), - socket_close_verbose(S1), - socket_close_verbose(S2), + socket_close_verbose(S1, StartTime), + socket_close_verbose(S2, StartTime), receive {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} -> match_unless_solaris(S3Ai, S3Ai_X) after Timeout -> - socket_bailout([S3]) + socket_bailout([S3], StartTime) end, receive {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp, assoc_id=S3Ai}}} -> ok after Timeout -> - socket_bailout([S3]) + socket_bailout([S3], StartTime) end, - socket_close_verbose(S3), + socket_close_verbose(S3, StartTime), [] = flush(), ok. @@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) -> Addr = {127,0,0,1}, Stream = 1, Timeout = 3333, + StartTime = timestamp(), S1 = socket_open([{ip,Addr}], Timeout), ?LOGVAR(S1), P1 = socket_call(S1, get_port), @@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) -> state=comm_up, assoc_id=AssocId2}}} -> AssocId2 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, S1Ai = receive @@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) -> state=comm_up, assoc_id=AssocId1}}} -> AssocId1 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% socket_call(S1, {setopts,[{recbuf,Limit}]}), @@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) -> receive {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% - socket_close_verbose(S1), + socket_close_verbose(S1, StartTime), receive {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok after Timeout -> - socket_bailout([S2]) + socket_bailout([S2], StartTime) end, receive {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp, assoc_id=S2Ai}}} -> ok after Timeout -> - socket_bailout([S2]) + socket_bailout([S2], StartTime) end, - socket_close_verbose(S2), + socket_close_verbose(S2, StartTime), [] = flush(), ok. @@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) -> end, s_start(Starter, Timeout). -socket_close_verbose(S) -> - History = socket_history(socket_close(S)), +socket_close_verbose(S, StartTime) -> + History = socket_history(socket_close(S), StartTime), io:format("socket_close ~p:~n ~p.~n", [S,History]), History. @@ -1535,19 +1557,19 @@ socket_call(S, Request) -> %% socket_get(S, Key) -> %% s_req(S, {get,Key}). -socket_bailout([S|Ss]) -> - History = socket_history(socket_close(S)), +socket_bailout([S|Ss], StartTime) -> + History = socket_history(socket_close(S), StartTime), io:format("bailout ~p:~n ~p.~n", [S,History]), - socket_bailout(Ss); -socket_bailout([]) -> + socket_bailout(Ss, StartTime); +socket_bailout([], _) -> io:format("flush: ~p.~n", [flush()]), ct:fail(socket_bailout). -socket_history({State,Flush}) -> +socket_history({State,Flush}, StartTime) -> {lists:keysort( 2, lists:flatten( - [[{Key,Val} || Val <- Vals] + [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals] || {Key,Vals} <- gb_trees:to_list(State)])), Flush}. @@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> {Parent,Ref,exit} -> ok = gen_sctp:close(Socket), Key = exit, - Val = {now(),Socket}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, Socket, State), Parent ! {self(),Ref,{NewState,flush()}}; {Parent,Ref,{Msg}} -> Result = Handler(Msg), Key = req, - Val = {now(),{Msg,Result}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Msg,Result}, State), Parent ! {self(),Ref,Result}, s_loop(Socket, Timeout, Parent, Handler, NewState); %% {Parent,Ref,{get,Key}} -> @@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}} when not is_tuple(Data) -> case gb_get({assoc_change,AssocId}, State) of - [{_,{Addr,Port, - #sctp_assoc_change{ - state=comm_up, - inbound_streams=Is}}}|_] + [{Addr,Port, + #sctp_assoc_change{ + state=comm_up, + inbound_streams=Is}}|_] when 0 =< Stream, Stream < Is-> ok; [] -> ok end, Key = {msg,AssocId,Stream}, - Val = {now(),{Addr,Port,SRI,Data}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SRI,Data}, State), Parent ! {self(),{Addr,Port,AssocId,Stream,Data}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, Key = {assoc_change,AssocId}, - Val = {now(),{Addr,Port,SAC}}, case {gb_get(Key, State),St} of {[],_} -> ok; - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} when St =:= comm_lost; St =:= shutdown_comp -> ok end, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SAC}, State), Parent ! {self(),{Addr,Port,SAC}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case {gb_get({assoc_change,AssocId}, State),St} of - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} when St =:= addr_available; St =:= addr_confirmed -> ok; {[],addr_confirmed} -> ok end, Key = {paddr_change,AssocId}, - Val = {now(),{Addr,Port,SPC}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SPC}, State), again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); {sctp,Socket,Addr,Port, @@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case gb_get({assoc_change,AssocId}, State) of - [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok; + [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok; [] -> ok end, Key = {shutdown_event,AssocId}, - Val = {now(),{Addr,Port}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port}, State), Parent ! {self(), {Addr,Port,SSE}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1707,11 +1723,12 @@ again(Socket) -> end. gb_push(Key, Val, GBT) -> + TS = timestamp(), case gb_trees:lookup(Key, GBT) of none -> - gb_trees:insert(Key, [Val], GBT); + gb_trees:insert(Key, [{TS,Val}], GBT); {value,V} -> - gb_trees:update(Key, [Val|V], GBT) + gb_trees:update(Key, [{TS,Val}|V], GBT) end. gb_get(Key, GBT) -> @@ -1719,7 +1736,7 @@ gb_get(Key, GBT) -> none -> []; {value,V} -> - V + [Val || {_TS,Val} <- V] end. match_unless_solaris(A, B) -> @@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) -> {unix,sunos} -> B; _ -> A = B end. + +timestamp() -> + erlang:monotonic_time(). diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 1370e23195..2b59eb2bfe 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -27,7 +27,8 @@ -export([get_arguments/1, get_argument/1, boot_var/1, restart/1, many_restarts/0, many_restarts/1, get_plain_arguments/1, - reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]). + reboot/1, stop_status/1, stop/1, get_status/1, script_id/1, + find_system_processes/0]). -export([boot1/1, boot2/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -355,12 +356,16 @@ wait_for(N,Node,EHPid) -> restart(Config) when is_list(Config) -> Args = args(), + Pa = " -pa " ++ filename:dirname(code:which(?MODULE)), + %% Currently test_server:start_node cannot be used. The restarted %% node immediately halts due to the implementation of %% test_server:start_node. - {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC), + {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC), %% Ok, the node is up, now the real test test begins. erlang:monitor_node(Node, true), + SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), + [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -375,6 +380,9 @@ restart(Config) when is_list(Config) -> end, ok = wait_restart(30, Node), + SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), + [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1, + %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), InitP = pid_to_list(InitPid), @@ -385,8 +393,24 @@ restart(Config) when is_list(Config) -> PurgerP = pid_to_list(PurgerPid), PurgerP = pid_to_list(PurgerPid1), + %% and same literal area collector process! + case LitCollectorPid of + undefined -> undefined = LitCollectorPid1; + _ -> + LitCollectorP = pid_to_list(LitCollectorPid), + LitCollectorP = pid_to_list(LitCollectorPid1) + end, + + %% and same dirty process code checker process! + case DirtyCodePid of + undefined -> undefined = DirtyCodePid1; + _ -> + DirtyCodeP = pid_to_list(DirtyCodePid), + DirtyCodeP = pid_to_list(DirtyCodePid1) + end, + NewProcs0 = rpc:call(Node, erlang, processes, []), - NewProcs = NewProcs0 -- [InitPid1, PurgerPid1], + NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of true -> ok; @@ -406,6 +430,37 @@ restart(Config) when is_list(Config) -> loose_node:stop(Node), ok. +-record(sys_procs, {init, + code_purger, + literal_collector, + dirty_proc_checker}). + +find_system_processes() -> + find_system_procs(processes(), #sys_procs{}). + +find_system_procs([], SysProcs) -> + [SysProcs#sys_procs.init, + SysProcs#sys_procs.code_purger, + SysProcs#sys_procs.literal_collector, + SysProcs#sys_procs.dirty_proc_checker]; +find_system_procs([P|Ps], SysProcs) -> + case process_info(P, initial_call) of + {initial_call,{otp_ring0,start,2}} -> + undefined = SysProcs#sys_procs.init, + find_system_procs(Ps, SysProcs#sys_procs{init = P}); + {initial_call,{erts_code_purger,start,0}} -> + undefined = SysProcs#sys_procs.code_purger, + find_system_procs(Ps, SysProcs#sys_procs{code_purger = P}); + {initial_call,{erts_literal_area_collector,start,0}} -> + undefined = SysProcs#sys_procs.literal_collector, + find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P}); + {initial_call,{erts_dirty_process_code_checker,start,0}} -> + undefined = SysProcs#sys_procs.dirty_proc_checker, + find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P}); + _ -> + find_system_procs(Ps, SysProcs) + end. + wait_restart(0, _Node) -> ct:fail(not_restarted); wait_restart(N, Node) -> diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 2a1e5016ec..e76d6ec482 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -24,7 +24,8 @@ init_per_testcase/2,end_per_testcase/2]). -export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1, find_executable/1, unix_comment_in_command/1, deep_list_command/1, - large_output_command/1, perf_counter_api/1]). + large_output_command/1, background_command/0, background_command/1, + message_leak/1, close_stdin/0, close_stdin/1, perf_counter_api/1]). -include_lib("common_test/include/ct.hrl"). @@ -35,7 +36,8 @@ suite() -> all() -> [space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command, find_executable, unix_comment_in_command, deep_list_command, - large_output_command, perf_counter_api]. + large_output_command, background_command, message_leak, + close_stdin, perf_counter_api]. groups() -> []. @@ -52,6 +54,14 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(TC, Config) + when TC =:= background_command; TC =:= close_stdin -> + case os:type() of + {win32, _} -> + {skip,"Should not work on windows"}; + _ -> + Config + end; init_per_testcase(_TC,Config) -> Config. @@ -261,13 +271,48 @@ deep_list_command(Config) when is_list(Config) -> %% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"]) ok. -%% Test to take sure that the correct data is +%% Test to make sure that the correct data is %% received when doing large commands. large_output_command(Config) when is_list(Config) -> %% Maximum allowed on windows is 8192, so we test well below that AAA = lists:duplicate(7000, $a), comp(AAA,os:cmd("echo " ++ AAA)). +%% Test that it is possible on unix to start a background task using os:cmd. +background_command() -> + [{timetrap, {seconds, 5}}]. +background_command(_Config) -> + %% This testcase fails when the os:cmd takes + %% longer then the 5 second timeout + os:cmd("sleep 10&"). + +%% Test that message does not leak to the calling process +message_leak(_Config) -> + process_flag(trap_exit, true), + + os:cmd("echo hello"), + [] = receive_all(), + + case os:type() of + {unix, _} -> + os:cmd("for i in $(seq 1 100); do echo hello; done&"), + [] = receive_all(); + _ -> + ok % Cannot background on non-unix + end, + + process_flag(trap_exit, false). + +%% Test that os:cmd closes stdin of the program that is executed +close_stdin() -> + [{timetrap, {seconds, 5}}]. +close_stdin(Config) -> + DataDir = proplists:get_value(data_dir, Config), + Fds = filename:join(DataDir, "my_fds"), + + "-1" = os:cmd(Fds). + + %% Test that the os:perf_counter api works as expected perf_counter_api(_Config) -> diff --git a/lib/kernel/test/os_SUITE_data/Makefile.src b/lib/kernel/test/os_SUITE_data/Makefile.src index 912d0cbcb1..f83f781411 100644 --- a/lib/kernel/test/os_SUITE_data/Makefile.src +++ b/lib/kernel/test/os_SUITE_data/Makefile.src @@ -3,7 +3,7 @@ LD = @LD@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ -PROGS = my_echo@exe@ +PROGS = my_echo@exe@ my_fds@exe@ all: $(PROGS) @@ -12,3 +12,9 @@ my_echo@exe@: my_echo@obj@ my_echo@obj@: my_echo.c $(CC) -c -o my_echo@obj@ $(CFLAGS) my_echo.c + +my_fds@exe@: my_fds@obj@ + $(LD) $(CROSSLDFLAGS) -o my_fds my_fds@obj@ @LIBS@ + +my_fds@obj@: my_fds.c + $(CC) -c -o my_fds@obj@ $(CFLAGS) my_fds.c diff --git a/lib/kernel/test/os_SUITE_data/my_fds.c b/lib/kernel/test/os_SUITE_data/my_fds.c new file mode 100644 index 0000000000..704a4d1e1d --- /dev/null +++ b/lib/kernel/test/os_SUITE_data/my_fds.c @@ -0,0 +1,9 @@ +#include <stdio.h> + +int +main(int argc, char** argv) +{ + char buff[1]; + int res = read(stdin, buff, 1); + printf("%d", res); +} diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index e7d422d03c..cdd200a234 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.0 +KERNEL_VSN = 5.0.2 diff --git a/lib/mnesia/doc/src/Mnesia_chap1.xml b/lib/mnesia/doc/src/Mnesia_chap1.xml index 9dfeb5efe4..035e934ed2 100644 --- a/lib/mnesia/doc/src/Mnesia_chap1.xml +++ b/lib/mnesia/doc/src/Mnesia_chap1.xml @@ -32,38 +32,38 @@ <rev>C</rev> <file>Mnesia_chap1.xml</file> </header> - <p>The <c>Mnesia</c> application provides a heavy duty real-time + <p>The Mnesia application provides a heavy duty real-time distributed database.</p> <section> <title>Scope</title> <p>This User's Guide describes how to - build <c>Mnesia</c> database applications, and how to integrate - and use the <c>Mnesia</c> database management system with + build Mnesia database applications, and how to integrate + and use the Mnesia database management system with OTP. Programming constructs are described, and numerous programming examples are included to illustrate the use of - <c>Mnesia</c>.</p> + Mnesia.</p> <p>This User's Guide is organized as follows:</p> <list type="bulleted"> <item><seealso marker="Mnesia_overview">Mnesia</seealso> provides an introduction to - <c>Mnesia</c>. + Mnesia. </item> <item><seealso marker="Mnesia_chap2">Getting Started</seealso> - introduces <c>Mnesia</c> with an example database. Examples + introduces Mnesia with an example database. Examples are included how to start an Erlang session, specify a - <c>Mnesia</c> database directory, initialize a database - schema, start <c>Mnesia</c>, and create tables. Initial + Mnesia database directory, initialize a database + schema, start Mnesia, and create tables. Initial prototyping of record definitions is also discussed. </item> <item><seealso marker="Mnesia_chap3">Build a Mnesia Database</seealso> more formally describes the steps - introduced in the previous section, namely the <c>Mnesia</c> - functions that define a database schema, start <c>Mnesia</c>, + introduced in the previous section, namely the Mnesia + functions that define a database schema, start Mnesia, and create the required tables. </item> <item><seealso marker="Mnesia_chap4">Transactions and Other Access Contexts</seealso> - describes the transactions properties that make <c>Mnesia</c> into + describes the transactions properties that make Mnesia into a fault tolerant, real-time distributed database management system. This section also describes the concept of locking to ensure consistency in tables, and "dirty @@ -76,16 +76,16 @@ features include indexing, checkpoints, distribution and fault tolerance, disc-less nodes, replication manipulation, local content tables, concurrency, and object-based programming in - <c>Mnesia</c>. + Mnesia. </item> <item><seealso marker="Mnesia_chap7">Mnesia System Information</seealso> describes the files contained in the - <c>Mnesia</c> database directory, database configuration data, + Mnesia database directory, database configuration data, core and table dumps, as well as the important subject of backup, fall-back, and disaster recovery principles. </item> <item><seealso marker="Mnesia_chap8">Combine Mnesia with - SNMP</seealso> is a short section that outlines <c>Mnesia</c> + SNMP</seealso> is a short section that outlines Mnesia integrated with SNMP. </item> <item><seealso marker="Mnesia_App_A">Appendix A: Backup diff --git a/lib/mnesia/doc/src/Mnesia_chap8.xml b/lib/mnesia/doc/src/Mnesia_chap8.xml index f1a469e315..4a2eed84d7 100644 --- a/lib/mnesia/doc/src/Mnesia_chap8.xml +++ b/lib/mnesia/doc/src/Mnesia_chap8.xml @@ -51,11 +51,11 @@ </item> </list> <p>All these approaches have different advantages and - disadvantages. <c>Mnesia</c> applications can easily be opened to + disadvantages. Mnesia applications can easily be opened to the SNMP protocol. A direct 1-to-1 mapping can be established - between <c>Mnesia</c> tables and SNMP tables. This means - that a <c>Mnesia</c> table can be configured to be <em>both</em> - a <c>Mnesia</c> table and an SNMP table. A number of functions to + between Mnesia tables and SNMP tables. This means + that a Mnesia table can be configured to be <em>both</em> + a Mnesia table and an SNMP table. A number of functions to control this behavior are described in the Reference Manual.</p> </section> </chapter> diff --git a/lib/mnesia/doc/src/Mnesia_overview.xml b/lib/mnesia/doc/src/Mnesia_overview.xml index d2d597b85d..63f2309284 100644 --- a/lib/mnesia/doc/src/Mnesia_overview.xml +++ b/lib/mnesia/doc/src/Mnesia_overview.xml @@ -39,14 +39,14 @@ high level of fault tolerance that is required in many nonstop systems, combined with requirements on the DBMS to run in the same address space as the application, have led us to implement a new - DBMS, called <c>Mnesia</c>.</p> - <p><c>Mnesia</c> is implemented in, and tightly connected to Erlang. + DBMS, called Mnesia.</p> + <p>Mnesia is implemented in, and tightly connected to Erlang. It provides the functionality that is necessary for the implementation of fault tolerant telecommunications systems.</p> - <p><c>Mnesia</c> is a multiuser distributed DBMS specially made for + <p>Mnesia is a multiuser distributed DBMS specially made for industrial telecommunications applications written in Erlang, which is also the intended target language. - <c>Mnesia</c> tries to address all the data + Mnesia tries to address all the data management issues required for typical telecommunications systems. It has a number of features that are not normally found in traditional databases.</p> @@ -54,7 +54,7 @@ from the features provided by traditional DBMSs. The applications now implemented in Erlang need a mixture of a broad range of features, which generally are not satisfied by traditional DBMSs. - <c>Mnesia</c> is designed with requirements like the following in + Mnesia is designed with requirements like the following in mind:</p> <list type="ordered"> <item>Fast real-time key/value lookup @@ -71,9 +71,9 @@ <item>Complex objects </item> </list> - <p><c>Mnesia</c> is designed with the typical data management problems - of telecommunications applications in mind. This sets <c>Mnesia</c> - apart from most other DBMS. Hence <c>Mnesia</c> + <p>Mnesia is designed with the typical data management problems + of telecommunications applications in mind. This sets Mnesia + apart from most other DBMS. Hence Mnesia combines many concepts found in traditional databases such as transactions and queries with concepts found in data management systems for telecommunications applications, for example:</p> @@ -86,7 +86,7 @@ suspending it. </item> </list> - <p><c>Mnesia</c> is also interesting because of its tight coupling to + <p>Mnesia is also interesting because of its tight coupling to Erlang, thus almost turning Erlang into a database programming language. This has many benefits, the foremost is that the impedance mismatch between the data format used by the DBMS @@ -97,7 +97,7 @@ <title>Mnesia Database Management System (DBMS)</title> <section> <title>Features</title> - <p><c>Mnesia</c> contains the following features that combine to + <p>Mnesia contains the following features that combine to produce a fault-tolerant, distributed DBMS written in Erlang: </p> <list type="bulleted"> @@ -118,7 +118,7 @@ functions can be called within one transaction. </item> <item>Several transactions can run concurrently, and their execution - is fully synchronized by the DBMS. <c>Mnesia</c> ensures that no + is fully synchronized by the DBMS. Mnesia ensures that no two processes manipulate data simultaneously. </item> <item>Transactions can be assigned the property of being executed on @@ -132,29 +132,29 @@ <section> <title>Add-On Application</title> - <p>Query List Comprehension (QLC) can be used with <c>Mnesia</c> + <p>Query List Comprehension (QLC) can be used with Mnesia to produce specialized functions that enhance the operational - ability of <c>Mnesia</c>. QLC has its own documentation as part + ability of Mnesia. QLC has its own documentation as part of the OTP documentation set. The main features of QLC - when used with <c>Mnesia</c> are as follows:</p> + when used with Mnesia are as follows:</p> <list type="bulleted"> - <item>QLC can optimize the query compiler for the <c>Mnesia</c> + <item>QLC can optimize the query compiler for the Mnesia DBMS, essentially making the DBMS more efficient. </item> <item>QLC can be used as a database programming - language for <c>Mnesia</c>. It includes a notation called "list + language for Mnesia. It includes a notation called "list comprehensions" and can be used to make complex database queries over a set of tables. </item> </list> <p>For information about QLC, see the <seealso marker="stdlib:qlc">qlc</seealso> manual page - in <c>STDLIB</c>.</p> + in STDLIB.</p> </section> <section> <title>When to Use Mnesia</title> - <p>Use <c>Mnesia</c> with the following types of applications:</p> + <p>Use Mnesia with the following types of applications:</p> <list type="bulleted"> <item>Applications that need to replicate data. </item> @@ -166,7 +166,7 @@ <item>Applications that use soft real-time characteristics. </item> </list> - <p><c>Mnesia</c> is not as appropriate with the + <p>Mnesia is not as appropriate with the following types of applications:</p> <list type="bulleted"> <item>Programs that process plain text or binary data files. @@ -176,14 +176,14 @@ library module <c>dets</c>, which is a disc-based version of the module <c>ets</c>. For information about <c>dets</c>, see the <seealso marker="stdlib:dets">dets</seealso> - manual page in <c>STDLIB</c>. + manual page in STDLIB. </item> <item>Applications that need disc logging facilities. Those applications can use the module <c>disk_log</c> by preference. For information about <c>disk_log</c>, see the <seealso marker="kernel:disk_log">disk_log</seealso> - manual page in <c>Kernel</c>. + manual page in Kernel. </item> <item>Hard real-time systems. </item> diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index b35214cde9..621b6047ee 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -37,7 +37,7 @@ <description> <p>The following are some of the most important and attractive - capabilities provided by <c>Mnesia</c>:</p> + capabilities provided by Mnesia:</p> <list type="bulleted"> <item>A relational/object hybrid data model that is suitable for telecommunications applications. @@ -62,15 +62,15 @@ reconfigured at runtime without stopping the system. </item> </list> - <p>This Reference Manual describes the <c>Mnesia</c> API. This - includes functions that define and manipulate <c>Mnesia</c> + <p>This Reference Manual describes the Mnesia API. This + includes functions that define and manipulate Mnesia tables.</p> <p>All functions in this Reference Manual can be used in any combination with queries using the list comprehension notation. For information about the query notation, see the <seealso marker="stdlib:qlc">qlc</seealso> - manual page in <c>STDLIB</c>.</p> - <p>Data in <c>Mnesia</c> is organized as a set of tables. Each table + manual page in STDLIB.</p> + <p>Data in Mnesia is organized as a set of tables. Each table has a name that must be an atom. Each table is made up of Erlang records. The user is responsible for the record definitions. Each table also has a set of properties. The @@ -121,14 +121,14 @@ <item> <p><c>index</c>. This is a list of attribute names, or integers, which specify the tuple positions on which - <c>Mnesia</c> is to build and maintain an extra index + Mnesia is to build and maintain an extra index table.</p> </item> <item> <p><c>local_content</c>. When an application requires tables whose contents are local to each node, <c>local_content</c> tables can be used. The table name - is known to all <c>Mnesia</c> nodes, but its content is + is known to all Mnesia nodes, but its content is unique on each node. This means that access to such a table must be done locally. Set field <c>local_content</c> to <c>true</c> to enable the <c>local_content</c> @@ -143,7 +143,7 @@ avoid inconsistencies because of network splits.</p> </item> <item> - <p><c>snmp</c>. Each (set-based) <c>Mnesia</c> table can be + <p><c>snmp</c>. Each (set-based) Mnesia table can be automatically turned into a Simple Network Management Protocol (SNMP) ordered table as well. This property specifies the types of the SNMP keys.</p> @@ -174,7 +174,7 @@ copy of each modified record during the transaction. During iteration, that is, <c>mnesia:fold[lr]/4</c>, <c>mnesia:next/2</c>, <c>mnesia:prev/2</c>, and - <c>mnesia:snmp_get_next_index/2</c>, <c>Mnesia</c> + <c>mnesia:snmp_get_next_index/2</c>, Mnesia compensates for every written or deleted record, which can reduce the performance.</p> <p>If possible, avoid writing or deleting records in the same @@ -188,7 +188,7 @@ <desc> <p>Makes the transaction silently return the tuple <c>{aborted, Reason}</c>. - Termination of a <c>Mnesia</c> transaction means that + Termination of a Mnesia transaction means that an exception is thrown to an enclosing <c>catch</c>. Thus, the expression <c>catch mnesia:abort(x)</c> does not terminate the transaction.</p> @@ -324,7 +324,7 @@ <c>mnesia:ets</c>. Argument <c>AccessMod</c> is the name of a callback module, which implements the <c>mnesia_access</c> behavior.</p> - <p><c>Mnesia</c> forwards calls to the following functions:</p> + <p>Mnesia forwards calls to the following functions:</p> <list type="bulleted"> <item>mnesia:lock/2 (read_lock_table/1, write_lock_table/1) </item> @@ -391,15 +391,15 @@ </item> </list> <p><c>ActivityId</c> is a record that represents the identity - of the enclosing <c>Mnesia</c> activity. The first field + of the enclosing Mnesia activity. The first field (obtained with <c>element(1, ActivityId)</c>) contains an atom, which can be interpreted as the activity type: <c>ets</c>, <c>async_dirty</c>, <c>sync_dirty</c>, or <c>tid</c>. <c>tid</c> means that the activity is a transaction. The structure of the rest of the identity - record is internal to <c>Mnesia</c>.</p> + record is internal to Mnesia.</p> <p><c>Opaque</c> is an opaque data structure that is internal - to <c>Mnesia</c>.</p> + to Mnesia.</p> </desc> </func> <func> @@ -458,7 +458,7 @@ mnesia:add_table_index(person, age)</code> <desc> <marker id="async_dirty"></marker> <p>Calls the <c>Fun</c> in a context that is not protected by - a transaction. The <c>Mnesia</c> function calls performed in + a transaction. The Mnesia function calls performed in the <c>Fun</c> are mapped to the corresponding dirty functions. This still involves logging, replication, and subscriptions, but there is no locking, local transaction @@ -467,7 +467,7 @@ mnesia:add_table_index(person, age)</code> for normal <c>mnesia:dirty_*</c> operations, the operations are performed semi-asynchronously. For details, see <c>mnesia:activity/4</c> and the User's Guide.</p> - <p>The <c>Mnesia</c> tables can be manipulated without + <p>The Mnesia tables can be manipulated without using transactions. This has some serious disadvantages, but is considerably faster, as the transaction manager is not involved and no locks are set. A dirty operation does, @@ -480,7 +480,7 @@ mnesia:add_table_index(person, age)</code> read records dirty than within a transaction.</p> <p>Depending on the application, it can be a good idea to use the dirty functions for certain operations. Almost all - <c>Mnesia</c> functions that can be called within + Mnesia functions that can be called within transactions have a dirty equivalent, which is much more efficient.</p> <p>However, notice that there is a risk that the database can @@ -497,7 +497,7 @@ mnesia:add_table_index(person, age)</code> <fsummary>Backs up all tables in the database.</fsummary> <desc> <marker id="backup"></marker> - <p>Activates a new checkpoint covering all <c>Mnesia</c> tables, + <p>Activates a new checkpoint covering all Mnesia tables, including the schema, with maximum degree of redundancy, and performs a backup using <c>backup_checkpoint/2/3</c>. The default value of the backup callback module <c>BackupMod</c> @@ -529,16 +529,16 @@ mnesia:add_table_index(person, age)</code> <taglist> <tag><c>extra_db_nodes</c></tag> <item> - <p><c>Value</c> is a list of nodes that <c>Mnesia</c> + <p><c>Value</c> is a list of nodes that Mnesia is to try to connect to. <c>ReturnValue</c> is those - nodes in <c>Value</c> that <c>Mnesia</c> is connected + nodes in <c>Value</c> that Mnesia is connected to.</p> <p>Notice that this function must only be used to connect to newly started RAM nodes (N.D.R.S.N.) with an empty schema. If, for example, this function is used after the network has been partitioned, it can lead to inconsistent tables.</p> - <p>Notice that <c>Mnesia</c> can be connected to other + <p>Notice that Mnesia can be connected to other nodes than those returned in <c>ReturnValue</c>.</p> </item> <tag><c>dc_dump_limit</c></tag> @@ -548,7 +548,7 @@ mnesia:add_table_index(person, age)</code> Configuration Parameters</seealso>. <c>ReturnValue</c> is the new value. Notice that this configuration parameter is not persistent. It is lost when - <c>Mnesia</c> has stopped.</p> + Mnesia has stopped.</p> </item> </taglist> </desc> @@ -562,9 +562,9 @@ mnesia:add_table_index(person, age)</code> <c>read_write</c> but it can also be set to the atom <c>read_only</c>. If <c>AccessMode</c> is set to <c>read_only</c>, updates to the table cannot be - performed. At startup, <c>Mnesia</c> always loads + performed. At startup, Mnesia always loads <c>read_only</c> tables locally regardless of when and if - <c>Mnesia</c> is terminated on other nodes.</p> + Mnesia is terminated on other nodes.</p> </desc> </func> <func> @@ -620,13 +620,13 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <desc> <marker id="create_schema"></marker> <p>Creates a new database on disc. Various files are - created in the local <c>Mnesia</c> directory of each node. + created in the local Mnesia directory of each node. Notice that the directory must be unique for each node. Two nodes must never share the same directory. If possible, use a local disc device to improve performance.</p> <p><c>mnesia:create_schema/1</c> fails if any of the Erlang nodes given as <c>DiscNodes</c> are not alive, if - <c>Mnesia</c> is running on any of the nodes, or if any + Mnesia is running on any of the nodes, or if any of the nodes already have a schema. Use <c>mnesia:delete_schema/1</c> to get rid of old faulty schemas.</p> @@ -638,10 +638,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </func> <func> <name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name> - <fsummary>Creates a <c>Mnesia</c> table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary> + <fsummary>Creates a Mnesia table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary> <desc> <marker id="create_table"></marker> - <p>Creates a <c>Mnesia</c> table called + <p>Creates a Mnesia table called <c>Name</c> according to argument <c>TabDef</c>. This list must be a list of <c>{Item, Value}</c> tuples, where the following values are allowed:</p> @@ -652,8 +652,8 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> set to the atom <c>read_only</c>. If <c>AccessMode</c> is set to <c>read_only</c>, updates to the table cannot be performed.</p> - <p>At startup, <c>Mnesia</c> always loads <c>read_only</c> - table locally regardless of when and if <c>Mnesia</c> is + <p>At startup, Mnesia always loads <c>read_only</c> + table locally regardless of when and if Mnesia is terminated on other nodes. This argument returns the access mode of the table. The access mode can be <c>read_only</c> or <c>read_write</c>.</p> @@ -693,7 +693,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <item> <p><c>{index, Intlist}</c>, where <c>Intlist</c> is a list of attribute names (atoms) or - record fields for which <c>Mnesia</c> is to build and + record fields for which Mnesia is to build and maintain an extra index table. The <c>qlc</c> query compiler <em>may</em> be able to optimize queries if there are indexes available.</p> @@ -735,10 +735,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <c>mnesia:create_table/2</c>, the table is immediately accessible by SNMP. Therefore applications that use SNMP to manipulate and control the system can be - designed easily, since <c>Mnesia</c> provides a + designed easily, since Mnesia provides a direct mapping between the logical tables that make up an SNMP control application and the physical data that - makes up a <c>Mnesia</c> table.</p> + makes up a Mnesia table.</p> </item> <item> <p><c>{storage_properties, [{Backend, Properties}]</c> @@ -746,7 +746,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <c>Backend</c> can currently be <c>ets</c> or <c>dets</c>. <c>Properties</c> is a list of options sent to the back end storage during table creation. <c>Properties</c> - cannot contain properties already used by <c>Mnesia</c>, + cannot contain properties already used by Mnesia, such as <c>type</c> or <c>named_table</c>.</p> <p>For example:</p> <code type="none"> @@ -776,7 +776,7 @@ mnesia:create_table(table, [{ram_copies, [node()]}, {disc_only_copies, nodes()}, mnesia:create_table(person, [{ram_copies, [N1, N2]}, {attributes, record_info(fields, person)}]).</code> - <p>If it is required that <c>Mnesia</c> must build and + <p>If it is required that Mnesia must build and maintain an extra index table on attribute <c>address</c> of all the <c>person</c> records that are inserted in the table, the following code would be issued:</p> @@ -819,8 +819,8 @@ mnesia:create_table(person, When the last replica is deleted with this function, the table disappears entirely.</p> <p>This function can also be used to delete a replica of - the table named <c>schema</c>. The <c>Mnesia</c> node is - then removed. Notice that <c>Mnesia</c> must be + the table named <c>schema</c>. The Mnesia node is + then removed. Notice that Mnesia must be stopped on the node first.</p> </desc> </func> @@ -891,9 +891,9 @@ mnesia:create_table(person, <c>mnesia:create_schema/1</c>. <c>mnesia:delete_schema/1</c> fails if any of the Erlang nodes given as <c>DiscNodes</c> are not alive, or if - <c>Mnesia</c> is running on any of the nodes.</p> + Mnesia is running on any of the nodes.</p> <p>After the database is deleted, it can still be possible - to start <c>Mnesia</c> as a disc-less node. This depends + to start Mnesia as a disc-less node. This depends on how configuration parameter <c>schema_location</c> is set.</p> <warning> @@ -1100,7 +1100,7 @@ mnesia:create_table(person, <name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name> <fsummary>Dirty update of a counter record.</fsummary> <desc> - <p><c>Mnesia</c> has no special counter records. However, + <p>Mnesia has no special counter records. However, records of the form <c>{Tab, Key, Integer}</c> can be used as (possibly disc-resident) counters when <c>Tab</c> is a <c>set</c>. This function updates a counter with a positive @@ -1147,7 +1147,7 @@ mnesia:create_table(person, <desc> <marker id="dump_log"></marker> <p>Performs a user-initiated dump of the local log file. - This is usually not necessary, as <c>Mnesia</c> by default + This is usually not necessary, as Mnesia by default manages this automatically. See configuration parameters <seealso marker="#dump_log_time_threshold">dump_log_time_threshold</seealso> and @@ -1172,7 +1172,7 @@ mnesia:create_table(person, <fsummary>Dumps local tables into a text file.</fsummary> <desc> <marker id="dump_to_textfile"></marker> - <p>Dumps all local tables of a <c>Mnesia</c> system into a + <p>Dumps all local tables of a Mnesia system into a text file, which can be edited (by a normal text editor) and then be reloaded with <c>mnesia:load_textfile/1</c>. Only use this function for @@ -1182,10 +1182,10 @@ mnesia:create_table(person, </func> <func> <name>error_description(Error) -> String</name> - <fsummary>Returns a string describing a particular <c>Mnesia</c> error.</fsummary> + <fsummary>Returns a string describing a particular Mnesia error.</fsummary> <desc> <marker id="error_description"></marker> - <p>All <c>Mnesia</c> transactions, including all the schema + <p>All Mnesia transactions, including all the schema update functions, either return value <c>{atomic, Val}</c> or the tuple <c>{aborted, Reason}</c>. <c>Reason</c> can be either of the atoms in the following list. The @@ -1264,8 +1264,8 @@ mnesia:create_table(person, <desc> <marker id="ets"></marker> <p>Calls the <c>Fun</c> in a raw context that is not protected by - a transaction. The <c>Mnesia</c> function call is performed in - the <c>Fun</c> and performed directly on the local <c>ets</c> + a transaction. The Mnesia function call is performed in + the <c>Fun</c> and performed directly on the local ETS tables on the assumption that the local storage type is <c>ram_copies</c> and the tables are not replicated to other nodes. Subscriptions are not triggered and checkpoints are @@ -1321,13 +1321,13 @@ mnesia:create_table(person, <fsummary>Forces a table to be loaded into the system.</fsummary> <desc> <marker id="force_load_table"></marker> - <p>The <c>Mnesia</c> algorithm for table load can lead to a + <p>The Mnesia algorithm for table load can lead to a situation where a table cannot be loaded. This situation - occurs when a node is started and <c>Mnesia</c> concludes, or + occurs when a node is started and Mnesia concludes, or suspects, that another copy of the table was active after this local copy became inactive because of a system crash.</p> <p>If this situation is not acceptable, this function can be - used to override the strategy of the <c>Mnesia</c> table + used to override the strategy of the Mnesia table load algorithm. This can lead to a situation where some transaction effects are lost with an inconsistent database as result, but for some applications high availability is more @@ -1402,9 +1402,9 @@ mnesia:create_table(person, <desc> <marker id="info"></marker> <p>Prints system information on the terminal. - This function can be used even if <c>Mnesia</c> is not + This function can be used even if Mnesia is not started. However, more information is displayed if - <c>Mnesia</c> is started.</p> + Mnesia is started.</p> </desc> </func> <func> @@ -1431,7 +1431,7 @@ mnesia:create_table(person, <p>Installs a backup as fallback. The fallback is used to restore the database at the next startup. Installation of fallbacks requires Erlang to be operational on all the - involved nodes, but it does not matter if <c>Mnesia</c> + involved nodes, but it does not matter if Mnesia is running or not. The installation of the fallback fails if the local node is not one of the disc-resident nodes in the backup.</p> @@ -1465,14 +1465,14 @@ mnesia:create_table(person, <p><c>{mnesia_dir, AlternateDir}</c>. This argument is only valid if the scope of the installation is <c>local</c>. Normally the installation - of a fallback is targeted to the <c>Mnesia</c> directory, + of a fallback is targeted to the Mnesia directory, as configured with configuration parameter <c>-mnesia dir</c>. But by explicitly supplying an <c>AlternateDir</c>, the fallback is installed there - regardless of the <c>Mnesia</c> directory configuration + regardless of the Mnesia directory configuration parameter setting. After installation of a fallback on - an alternative <c>Mnesia</c> directory, that directory - is fully prepared for use as an active <c>Mnesia</c> + an alternative Mnesia directory, that directory + is fully prepared for use as an active Mnesia directory.</p> <p>This is a dangerous feature that must be used with care. By unintentional mixing of directories, @@ -1509,7 +1509,7 @@ mnesia:create_table(person, <marker id="load_textfile"></marker> <p>Loads a series of definitions and data found in the text file (generated with <c>mnesia:dump_to_textfile/1</c>) - into <c>Mnesia</c>. This function also starts <c>Mnesia</c> + into Mnesia. This function also starts Mnesia and possibly creates a new schema. This function is intended for educational purposes only. It is recommended to use other functions to deal with real backups.</p> @@ -1558,7 +1558,7 @@ mnesia:create_table(person, </taglist> <p>Conflicting lock requests are automatically queued if there is no risk of a deadlock. Otherwise the transaction must be - terminated and executed again. <c>Mnesia</c> does this + terminated and executed again. Mnesia does this automatically as long as the upper limit of the maximum <c>retries</c> is not reached. For details, see <c>mnesia:transaction/3</c>.</p> @@ -1726,19 +1726,19 @@ mnesia:create_table(person, </func> <func> <name>report_event(Event) -> ok</name> - <fsummary>Reports a user event to the <c>Mnesia</c> event handler.</fsummary> + <fsummary>Reports a user event to the Mnesia event handler.</fsummary> <desc> <marker id="report_event"></marker> - <p>When tracing a system of <c>Mnesia</c> applications it is - useful to be able to interleave <c>Mnesia</c> own events with + <p>When tracing a system of Mnesia applications it is + useful to be able to interleave Mnesia own events with application-related events that give information about the application context.</p> <p>Whenever the application begins a - new and demanding <c>Mnesia</c> task, or if it enters a new + new and demanding Mnesia task, or if it enters a new interesting phase in its execution, it can be a good idea to use <c>mnesia:report_event/1</c>. <c>Event</c> can be any term and generates a <c>{mnesia_user, Event}</c> event - for any processes that subscribe to <c>Mnesia</c> system + for any processes that subscribe to Mnesia system events.</p> </desc> </func> @@ -1748,7 +1748,7 @@ mnesia:create_table(person, <desc> <marker id="restore"></marker> <p>With this function, tables can be restored online from a - backup without restarting <c>Mnesia</c>. + backup without restarting Mnesia. <c>Opaque</c> is forwarded to the backup module. <c>Args</c> is a list of the following tuples:</p> <list type="bulleted"> @@ -1873,7 +1873,7 @@ mnesia:create_table(person, <p>For a complete description of <c>select</c>, see the <seealso marker="erts:index">ERTS</seealso> User's Guide and the <seealso marker="stdlib:ets">ets</seealso> manual page in - <c>STDLIB</c>.</p> + STDLIB.</p> <p>For example, to find the names of all male persons older than 30 in table <c>Tab</c>:</p> <code type="none"> @@ -1920,10 +1920,10 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </func> <func> <name>set_debug_level(Level) -> OldLevel</name> - <fsummary>Changes the internal debug level of <c>Mnesia</c>.</fsummary> + <fsummary>Changes the internal debug level of Mnesia.</fsummary> <desc> <marker id="set_debug_level"></marker> - <p>Changes the internal debug level of <c>Mnesia</c>. + <p>Changes the internal debug level of Mnesia. For details, see <seealso marker="#configuration_parameters">Section Configuration Parameters</seealso>.</p> @@ -1934,7 +1934,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> <fsummary>Sets the master nodes for all tables.</fsummary> <desc> <marker id="set_master_nodes_1"></marker> - <p>For each table <c>Mnesia</c> determines its replica nodes + <p>For each table Mnesia determines its replica nodes (<c>TabNodes</c>) and starts <c>mnesia:set_master_nodes(Tab, TabMasterNodes)</c>. where <c>TabMasterNodes</c> is the intersection of @@ -1952,16 +1952,16 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> that can have caused an inconsistent database, it can use the function <c>mnesia:set_master_nodes(Tab, MasterNodes)</c> to define from which nodes each table is to be loaded. - At startup, the <c>Mnesia</c> normal table load algorithm is + At startup, the Mnesia normal table load algorithm is bypassed and the table is loaded from one of the master nodes - defined for the table, regardless of when and if <c>Mnesia</c> + defined for the table, regardless of when and if Mnesia terminated on other nodes. <c>MasterNodes</c> can only contain nodes where the table has a replica. If the <c>MasterNodes</c> list is empty, the master node recovery mechanism for the particular table is reset, and the normal load mechanism is used at the next restart.</p> <p>The master node setting is always local. It can be - changed regardless if <c>Mnesia</c> is started or not.</p> + changed regardless if Mnesia is started or not.</p> <p>The database can also become inconsistent if configuration parameter <c>max_wait_for_decision</c> is used or if <c>mnesia:force_load_table/1</c> is used.</p> @@ -1976,7 +1976,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </func> <func> <name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name> - <fsummary>Gets the corresponding <c>Mnesia</c> key from an SNMP index.</fsummary> + <fsummary>Gets the corresponding Mnesia key from an SNMP index.</fsummary> <type> <v>Tab ::= atom()</v> <v>RowIndex ::= [integer()]</v> @@ -1984,7 +1984,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> <v>key() ::= integer() | string() | [integer()]</v> </type> <desc> - <p>Transforms an SNMP index to the corresponding <c>Mnesia</c> + <p>Transforms an SNMP index to the corresponding Mnesia key. If the SNMP table has multiple keys, the key is a tuple of the key columns.</p> </desc> @@ -2020,7 +2020,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </func> <func> <name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name> - <fsummary>Organizes a <c>Mnesia</c> table as an SNMP table.</fsummary> + <fsummary>Organizes a Mnesia table as an SNMP table.</fsummary> <type> <v>Tab ::= atom()</v> <v>SnmpStruct ::= [{key, type()}]</v> @@ -2029,14 +2029,14 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </type> <desc> <p>A direct one-to-one mapping can be established between - <c>Mnesia</c> tables and SNMP tables. Many telecommunication + Mnesia tables and SNMP tables. Many telecommunication applications are controlled and monitored by the SNMP - protocol. This connection between <c>Mnesia</c> and SNMP + protocol. This connection between Mnesia and SNMP makes it simple and convenient to achieve this mapping.</p> <p>Argument <c>SnmpStruct</c> is a list of SNMP information. Currently, the only information needed is information about the key types in the table. Multiple - keys cannot be handled in <c>Mnesia</c>, but many SNMP + keys cannot be handled in Mnesia, but many SNMP tables have multiple keys. Therefore, the following convention is used: if a table has multiple keys, these must always be stored as a tuple of the keys. Information about @@ -2069,39 +2069,39 @@ mnesia:create_table(employee, <p>When a table is SNMP ordered, modifications are more expensive than usual, O(logN). Also, more memory is used.</p> <p>Notice that only the lexicographical SNMP ordering is - implemented in <c>Mnesia</c>, not the actual SNMP monitoring.</p> + implemented in Mnesia, not the actual SNMP monitoring.</p> </desc> </func> <func> <name>start() -> ok | {error, Reason}</name> - <fsummary>Starts a local <c>Mnesia</c> system.</fsummary> + <fsummary>Starts a local Mnesia system.</fsummary> <desc> <marker id="start"></marker> - <p>The startup procedure for a set of <c>Mnesia</c> nodes is a - fairly complicated operation. A <c>Mnesia</c> system consists - of a set of nodes, with <c>Mnesia</c> started locally on all + <p>The startup procedure for a set of Mnesia nodes is a + fairly complicated operation. A Mnesia system consists + of a set of nodes, with Mnesia started locally on all participating nodes. Normally, each node has a directory where - all the <c>Mnesia</c> files are written. This directory is - referred to as the <c>Mnesia</c> directory. <c>Mnesia</c> can + all the Mnesia files are written. This directory is + referred to as the Mnesia directory. Mnesia can also be started on disc-less nodes. For more information about disc-less nodes, see <c>mnesia:create_schema/1</c> and the User's Guide.</p> - <p>The set of nodes that makes up a <c>Mnesia</c> system is kept - in a schema. <c>Mnesia</c> nodes can be added to or removed + <p>The set of nodes that makes up a Mnesia system is kept + in a schema. Mnesia nodes can be added to or removed from the schema. The initial schema is normally created on disc with the function <c>mnesia:create_schema/1</c>. On disc-less nodes, a tiny default schema is generated each time - <c>Mnesia</c> is started. During the startup procedure, - <c>Mnesia</c> exchanges schema information between the nodes + Mnesia is started. During the startup procedure, + Mnesia exchanges schema information between the nodes to verify that the table definitions are compatible.</p> <p>Each schema has a unique cookie, which can be regarded as a unique schema identifier. The cookie must be the same on all - nodes where <c>Mnesia</c> is supposed to run. For details, + nodes where Mnesia is supposed to run. For details, see the User's Guide.</p> - <p>The schema file and all other files that <c>Mnesia</c> - needs are kept in the <c>Mnesia</c> directory. The + <p>The schema file and all other files that Mnesia + needs are kept in the Mnesia directory. The command-line option <c>-mnesia dir Dir</c> can be used to - specify the location of this directory to the <c>Mnesia</c> + specify the location of this directory to the Mnesia system. If no such command-line option is found, the name of the directory defaults to <c>Mnesia.Node</c>.</p> <p><c>application:start(mnesia)</c> can also be used.</p> @@ -2109,10 +2109,10 @@ mnesia:create_table(employee, </func> <func> <name>stop() -> stopped</name> - <fsummary>Stops <c>Mnesia</c> locally.</fsummary> + <fsummary>Stops Mnesia locally.</fsummary> <desc> <marker id="stop"></marker> - <p>Stops <c>Mnesia</c> locally on the current node.</p> + <p>Stops Mnesia locally on the current node.</p> <p><c>application:stop(mnesia)</c> can also be used.</p> </desc> </func> @@ -2132,7 +2132,7 @@ mnesia:create_table(employee, <desc> <marker id="sync_dirty"></marker> <p>Calls the <c>Fun</c> in a context that is not protected by - a transaction. The <c>Mnesia</c> function calls performed in + a transaction. The Mnesia function calls performed in the <c>Fun</c> are mapped to the corresponding dirty functions. It is performed in almost the same context as <c>mnesia:async_dirty/1,2</c>. The difference is that the @@ -2167,10 +2167,10 @@ mnesia:create_table(employee, </func> <func> <name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name> - <fsummary>Returns information about the <c>Mnesia</c> system.</fsummary> + <fsummary>Returns information about the Mnesia system.</fsummary> <desc> <marker id="system_info"></marker> - <p>Returns information about the <c>Mnesia</c> system, such as + <p>Returns information about the Mnesia system, such as transaction statistics, <c>db_nodes</c>, and configuration parameters. The valid keys are as follows:</p> <list type="bulleted"> @@ -2188,7 +2188,7 @@ mnesia:create_table(employee, </item> <item> <p><c>auto_repair</c>. Returns <c>true</c> or <c>false</c> - to indicate if <c>Mnesia</c> is configured to start the + to indicate if Mnesia is configured to start the auto-repair facility on corrupted disc files.</p> </item> <item> @@ -2209,20 +2209,20 @@ mnesia:create_table(employee, in the list of nodes if they explicitly have been added to the schema, for example, with <c>mnesia:add_table_copy/3</c>. The function can be - started even if <c>Mnesia</c> is not yet running.</p> + started even if Mnesia is not yet running.</p> </item> <item> <p><c>debug</c>. Returns the current debug level of - <c>Mnesia</c>.</p> + Mnesia.</p> </item> <item> - <p><c>directory</c>. Returns the name of the <c>Mnesia</c> - directory. It can be called even if <c>Mnesia</c> is + <p><c>directory</c>. Returns the name of the Mnesia + directory. It can be called even if Mnesia is not yet running.</p> </item> <item> <p><c>dump_log_load_regulation</c>. Returns a boolean that - tells if <c>Mnesia</c> is configured to regulate the + tells if Mnesia is configured to regulate the dumper process load.</p> <p>This feature is temporary and will be removed in future releases.</p> @@ -2233,9 +2233,9 @@ mnesia:create_table(employee, </item> <item> <p><c>dump_log_update_in_place</c>. Returns a boolean that - tells if <c>Mnesia</c> is configured to perform the - updates in the <c>dets</c> files directly, or if the - updates are to be performed in a copy of the <c>dets</c> + tells if Mnesia is configured to perform the + updates in the Dets files directly, or if the + updates are to be performed in a copy of the Dets files.</p> </item> <item> @@ -2253,13 +2253,13 @@ mnesia:create_table(employee, </item> <item> <p><c>held_locks</c>. Returns a list of all - locks held by the local <c>Mnesia</c> lock manager.</p> + locks held by the local Mnesia lock manager.</p> </item> <item> <p><c>is_running</c>. Returns <c>yes</c> or <c>no</c> to - indicate if <c>Mnesia</c> is running. It can + indicate if Mnesia is running. It can also return <c>starting</c> or <c>stopping</c>. Can be - called even if <c>Mnesia</c> is not yet running.</p> + called even if Mnesia is not yet running.</p> </item> <item> <p><c>local_tables</c>. Returns a list @@ -2272,7 +2272,7 @@ mnesia:create_table(employee, </item> <item> <p><c>log_version</c>. Returns the version - number of the <c>Mnesia</c> transaction log format.</p> + number of the Mnesia transaction log format.</p> </item> <item> <p><c>master_node_tables</c>. Returns a @@ -2280,25 +2280,25 @@ mnesia:create_table(employee, </item> <item> <p><c>protocol_version</c>. Returns the version number of - the <c>Mnesia</c> inter-process communication protocol.</p> + the Mnesia inter-process communication protocol.</p> </item> <item> <p><c>running_db_nodes</c>. Returns a list of nodes where - <c>Mnesia</c> currently is running. This function can be - called even if <c>Mnesia</c> is not yet running, but it + Mnesia currently is running. This function can be + called even if Mnesia is not yet running, but it then has slightly different semantics.</p> - <p>If <c>Mnesia</c> is down on the local node, the function + <p>If Mnesia is down on the local node, the function returns those other <c>db_nodes</c> and <c>extra_db_nodes</c> that for the moment are operational.</p> - <p>If <c>Mnesia</c> is started, the function returns - those nodes that <c>Mnesia</c> on the local node is fully - connected to. Only those nodes that <c>Mnesia</c> has + <p>If Mnesia is started, the function returns + those nodes that Mnesia on the local node is fully + connected to. Only those nodes that Mnesia has exchanged schema information with are included as <c>running_db_nodes</c>. After the merge of schemas, the - local <c>Mnesia</c> system is fully operable and + local Mnesia system is fully operable and applications can perform access of remote replicas. - Before the schema merge, <c>Mnesia</c> only operates + Before the schema merge, Mnesia only operates locally. Sometimes there are more nodes included in the <c>running_db_nodes</c> list than all <c>db_nodes</c> and <c>extra_db_nodes</c> together.</p> @@ -2322,17 +2322,17 @@ mnesia:create_table(employee, <item> <p><c>transaction_failures</c>. Returns a number that indicates how many transactions have - failed since <c>Mnesia</c> was started.</p> + failed since Mnesia was started.</p> </item> <item> <p><c>transaction_commits</c>. Returns a number that indicates how many transactions have - terminated successfully since <c>Mnesia</c> was started.</p> + terminated successfully since Mnesia was started.</p> </item> <item> <p><c>transaction_restarts</c>. Returns a number that indicates how many transactions have been - restarted since <c>Mnesia</c> was started.</p> + restarted since Mnesia was started.</p> </item> <item> <p><c>transaction_log_writes</c>. @@ -2342,12 +2342,12 @@ mnesia:create_table(employee, </item> <item> <p><c>use_dir</c>. Returns a boolean that indicates if - the <c>Mnesia</c> directory is used or not. Can be - started even if <c>Mnesia</c> is not yet running.</p> + the Mnesia directory is used or not. Can be + started even if Mnesia is not yet running.</p> </item> <item> <p><c>version</c>. Returns the current - version number of <c>Mnesia</c>.</p> + version number of Mnesia.</p> </item> </list> </desc> @@ -2359,13 +2359,13 @@ mnesia:create_table(employee, <marker id="table"></marker> <p>Returns a Query List Comprehension (QLC) query handle, see the <seealso marker="stdlib:qlc">qlc(3)</seealso> - manual page in <c>STDLIB</c>. The module <c>qlc</c> - implements a query language that can use <c>Mnesia</c> + manual page in STDLIB. The module <c>qlc</c> + implements a query language that can use Mnesia tables as sources of data. Calling <c>mnesia:table/1,2</c> is the means to make the <c>mnesia</c> table <c>Tab</c> usable to QLC.</p> - <p><c>Option</c> can contain <c>Mnesia</c> - options or QLC options. <c>Mnesia</c> recognizes the + <p><c>Option</c> can contain Mnesia + options or QLC options. Mnesia recognizes the following options (any other option is forwarded to QLC).</p> <list type="bulleted"> @@ -2374,7 +2374,7 @@ mnesia:create_table(employee, </item> <item><c>{n_objects,Number}</c>, where <c>n_objects</c> specifies (roughly) the number of objects returned - from <c>Mnesia</c> to QLC. Queries to remote tables + from Mnesia to QLC. Queries to remote tables can need a larger chunk to reduce network overhead. By default, <c>100</c> objects at a time are returned. </item> @@ -2413,7 +2413,7 @@ mnesia:create_table(employee, <desc> <marker id="table_info"></marker> <p>The <c>table_info/2</c> function takes two arguments. - The first is the name of a <c>Mnesia</c> table. + The first is the name of a Mnesia table. The second is one of the following keys:</p> <list type="bulleted"> <item> @@ -2465,7 +2465,7 @@ mnesia:create_table(employee, </item> <item> <p><c>load_node</c>. Returns the name of - the node that <c>Mnesia</c> loaded the table from. The + the node that Mnesia loaded the table from. The structure of the returned value is unspecified, but can be useful for debugging purposes.</p> </item> @@ -2476,7 +2476,7 @@ mnesia:create_table(employee, </item> <item> <p><c>load_reason</c>. Returns the - reason of why <c>Mnesia</c> decided to load the table. + reason of why Mnesia decided to load the table. The structure of the returned value is unspecified, but can be useful for debugging purposes.</p> </item> @@ -2620,7 +2620,7 @@ raise(Name, Amount) -> several processes running on different nodes can concurrently execute the function <c>raise/2</c> without interfering with each other.</p> - <p>Since <c>Mnesia</c> detects deadlocks, a transaction can be + <p>Since Mnesia detects deadlocks, a transaction can be restarted any number of times. This function attempts a restart as specified in <c>Retries</c>. <c>Retries</c> must be an integer greater than 0 or the atom <c>infinity</c>. @@ -2643,7 +2643,7 @@ raise(Name, Amount) -> <p><c>NewAttributeList</c> and <c>NewRecordName</c> specify the attributes and the new record type of the converted table. Table name always remains unchanged. If - <c>record_name</c> is changed, only the <c>Mnesia</c> + <c>record_name</c> is changed, only the Mnesia functions that use table identifiers work, for example, <c>mnesia:write/3</c> works, but not <c>mnesia:write/1</c>.</p> </desc> @@ -2706,7 +2706,7 @@ raise(Name, Amount) -> distributed operation that is either performed on all nodes with disc resident schema, or none. Uninstallation of fallbacks requires Erlang to be operational on all - involved nodes, but it does not matter if <c>Mnesia</c> is + involved nodes, but it does not matter if Mnesia is running or not. Which nodes that are considered as disc-resident nodes is determined from the schema information in the local fallback.</p> @@ -2792,28 +2792,28 @@ raise(Name, Amount) -> <section> <title>Configuration Parameters</title> <marker id="configuration_parameters"></marker> - <p><c>Mnesia</c> reads the following application configuration + <p>Mnesia reads the following application configuration parameters:</p> <list type="bulleted"> <item> <p><c>-mnesia access_module Module</c>. The name of the - <c>Mnesia</c> activity access callback module. Default is + Mnesia activity access callback module. Default is <c>mnesia</c>.</p> </item> <item> <p><c>-mnesia auto_repair true | false</c>. This flag - controls if <c>Mnesia</c> automatically tries to repair + controls if Mnesia automatically tries to repair files that have not been properly closed. Default is <c>true</c>.</p> </item> <item> <p><c>-mnesia backup_module Module</c>. The name of the - <c>Mnesia</c> backup callback module. Default is + Mnesia backup callback module. Default is <c>mnesia_backup</c>.</p> </item> <item> <p><c>-mnesia debug Level</c>. Controls the debug level - of <c>Mnesia</c>. The possible values are as follows:</p> + of Mnesia. The possible values are as follows:</p> <taglist> <tag><c>none</c></tag> <item> @@ -2825,7 +2825,7 @@ raise(Name, Amount) -> events generate <c>{mnesia_info, Format, Args}</c> system events. Processes can subscribe to these events with <c>mnesia:subscribe/1</c>. The events are always sent to - the <c>Mnesia</c> event handler.</p> + the Mnesia event handler.</p> </item> <tag><c>debug</c></tag> <item> @@ -2834,15 +2834,15 @@ raise(Name, Amount) -> <c>{mnesia_info, Format, Args}</c> system events. Processes can subscribe to these events with <c>mnesia:subscribe/1</c>. The events are always sent to - the <c>Mnesia</c> event handler. On this debug level, - the <c>Mnesia</c> event handler starts subscribing to + the Mnesia event handler. On this debug level, + the Mnesia event handler starts subscribing to updates in the schema table.</p> </item> <tag><c>trace</c></tag> <item> <p>Activates all events at the debug level. On this - level, the <c>Mnesia</c> event handler starts subscribing - to updates on all <c>Mnesia</c> tables. This level is + level, the Mnesia event handler starts subscribing + to updates on all Mnesia tables. This level is intended only for debugging small toy systems, as many large events can be generated.</p> </item> @@ -2856,7 +2856,7 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia core_dir Directory</c>. The name of the - directory where <c>Mnesia</c> core files is stored, or + directory where Mnesia core files is stored, or false. Setting it implies that also RAM-only nodes generate a core file if a crash occurs.</p> </item> @@ -2870,9 +2870,9 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia dir Directory</c>. The name of the directory - where all <c>Mnesia</c> data is stored. The directory name + where all Mnesia data is stored. The directory name must be unique for the current node. Two nodes must never - share the the same <c>Mnesia</c> directory. The results + share the the same Mnesia directory. The results are unpredictable.</p> </item> <item> @@ -2915,44 +2915,44 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia event_module Module</c>. The name of the - <c>Mnesia</c> event handler callback module. Default is + Mnesia event handler callback module. Default is <c>mnesia_event</c>.</p> </item> <item> <p><c>-mnesia extra_db_nodes Nodes</c> specifies a list of nodes, in addition to the ones found in the schema, with - which <c>Mnesia</c> is also to establish contact. Default + which Mnesia is also to establish contact. Default is <c>[]</c> (empty list).</p> </item> <item> <p><c>-mnesia fallback_error_function {UserModule, UserFunc}</c>. Specifies a user-supplied callback function, which is - called if a fallback is installed and <c>Mnesia</c> goes - down on another node. <c>Mnesia</c> calls the function + called if a fallback is installed and Mnesia goes + down on another node. Mnesia calls the function with one argument, the name of the dying node, for example, - <c>UserModule:UserFunc(DyingNode)</c>. <c>Mnesia</c> must + <c>UserModule:UserFunc(DyingNode)</c>. Mnesia must be restarted, otherwise the database can be inconsistent. - The default behavior is to terminate <c>Mnesia</c>.</p> + The default behavior is to terminate Mnesia.</p> </item> <item> <p><c>-mnesia max_wait_for_decision Timeout</c>. Specifies - how long <c>Mnesia</c> waits for other nodes to share their + how long Mnesia waits for other nodes to share their knowledge about the outcome of an unclear transaction. By default, <c>Timeout</c> is set to the atom <c>infinity</c>. - This implies that if <c>Mnesia</c> upon startup detects + This implies that if Mnesia upon startup detects a "heavyweight transaction" whose outcome is unclear, the - local <c>Mnesia</c> waits until <c>Mnesia</c> is started + local Mnesia waits until Mnesia is started on some (in the worst case all) of the other nodes that were involved in the interrupted transaction. This is a rare - situation, but if it occurs, <c>Mnesia</c> does not guess if + situation, but if it occurs, Mnesia does not guess if the transaction on the other nodes was committed or - terminated. <c>Mnesia</c> waits until it knows the outcome + terminated. Mnesia waits until it knows the outcome and then acts accordingly.</p> <p>If <c>Timeout</c> is set to an integer value in - milliseconds, <c>Mnesia</c> forces "heavyweight transactions" + milliseconds, Mnesia forces "heavyweight transactions" to be finished, even if the outcome of the transaction for the moment is unclear. After <c>Timeout</c> milliseconds, - <c>Mnesia</c> commits or terminates the transaction and + Mnesia commits or terminates the transaction and continues with the startup. This can lead to a situation where the transaction is committed on some nodes and terminated on other nodes. If the transaction is a @@ -2976,14 +2976,14 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia schema_location Loc</c>. Controls where - <c>Mnesia</c> looks for its schema. Parameter + Mnesia looks for its schema. Parameter <c>Loc</c> can be one of the following atoms:</p> <taglist> <tag><c>disc</c></tag> <item> <p>Mandatory disc. The schema is assumed to be located - in the <c>Mnesia</c> directory. If the schema cannot - be found, <c>Mnesia</c> refuses to start. This is the + in the Mnesia directory. If the schema cannot + be found, Mnesia refuses to start. This is the old behavior.</p> </item> <tag><c>ram</c></tag> @@ -3001,10 +3001,10 @@ raise(Name, Amount) -> <tag><c>opt_disc</c></tag> <item> <p>Optional disc. The schema can reside on disc or in - RAM. If the schema is found on disc, <c>Mnesia</c> + RAM. If the schema is found on disc, Mnesia starts as a disc-based node and the storage type of the schema table is <c>disc_copies</c>. If no schema is - found on disc, <c>Mnesia</c> starts as a disc-less node + found on disc, Mnesia starts as a disc-less node and the storage type of the schema table is <c>ram_copies</c>. Default value for the application parameter is <c>opt_disc</c>.</p> @@ -3012,7 +3012,7 @@ raise(Name, Amount) -> </taglist> </item> </list> - <p>First, the <c>SASL</c> application parameters are checked, + <p>First, the SASL application parameters are checked, then the command-line flags are checked, and finally, the default value is chosen.</p> </section> diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml index 95f5f8aa07..51b32129b6 100644 --- a/lib/mnesia/doc/src/mnesia_frag_hash.xml +++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml @@ -87,13 +87,13 @@ the new one.</p> <p><c>NewState</c> is stored as <c>hash_state</c> among the other <c>frag_properties</c>.</p> - <p>As a part of the <c>add_frag</c> procedure, <c>Mnesia</c> iterates + <p>As a part of the <c>add_frag</c> procedure, Mnesia iterates over all fragments corresponding to the <c>IterFrags</c> numbers and starts <c>key_to_frag_number(NewState,RecordKey)</c> for each record. If the new fragment differs from the old fragment, the record is moved to the new fragment.</p> <p>As the <c>add_frag</c> procedure is a part of a schema - transaction, <c>Mnesia</c> acquires write locks on the + transaction, Mnesia acquires write locks on the affected tables. That is, both the fragments corresponding to <c>IterFrags</c> and those corresponding to <c>AdditionalLockFrags</c>.</p> @@ -112,7 +112,7 @@ <desc> <p><c>NewState</c> is stored as <c>hash_state</c> among the other <c>frag_properties</c>.</p> - <p>As a part of the <c>del_frag</c> procedure, <c>Mnesia</c> iterates + <p>As a part of the <c>del_frag</c> procedure, Mnesia iterates over all fragments corresponding to the <c>IterFrags</c> numbers and starts <c>key_to_frag_number(NewState,RecordKey)</c> for each record. If the new fragment differs from the old @@ -120,7 +120,7 @@ <p>Notice that all records in the last fragment must be moved to another fragment, as the entire fragment is deleted.</p> <p>As the <c>del_frag</c> procedure is a part of a schema - transaction, <c>Mnesia</c> acquires write locks on the + transaction, Mnesia acquires write locks on the affected tables. That is, both the fragments corresponding to <c>IterFrags</c> and those corresponding to <c>AdditionalLockFrags</c>.</p> @@ -134,7 +134,7 @@ <v>Reason = term()</v> </type> <desc> - <p>Starts whenever <c>Mnesia</c> needs to determine + <p>Starts whenever Mnesia needs to determine which fragment a certain record belongs to. It is typically started at <c>read</c>, <c>write</c>, and <c>delete</c>.</p> </desc> @@ -149,7 +149,7 @@ <v>Reason = term()</v> </type> <desc> - <p>This function is called whenever <c>Mnesia</c> needs to determine + <p>This function is called whenever Mnesia needs to determine which fragments that need to be searched for a <c>MatchSpec</c>. It is typically called by <c>select</c> and <c>match_object</c>.</p> diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml index cd778ae072..a76f716981 100644 --- a/lib/mnesia/doc/src/mnesia_registry.xml +++ b/lib/mnesia/doc/src/mnesia_registry.xml @@ -38,20 +38,20 @@ <modulesummary>Dump support for registries in erl_interface.</modulesummary> <description> <p>This module is usually part of the <c>erl_interface</c> - application, but is currently part of the <c>Mnesia</c> + application, but is currently part of the Mnesia application.</p> <p>This module is mainly intended for internal use within OTP, but it has two functions that are exported for public use.</p> <p>On C-nodes, <c>erl_interface</c> has support for registry tables. These tables reside in RAM on the C-node, but can also - be dumped into <c>Mnesia</c> tables. By default, the dumping + be dumped into Mnesia tables. By default, the dumping of registry tables through <c>erl_interface</c> causes a - corresponding <c>Mnesia</c> table to be created with + corresponding Mnesia table to be created with <c>mnesia_registry:create_table/1</c>, if necessary.</p> <p>Tables that are created with these functions can be - administered as all other <c>Mnesia</c> tables. They can be + administered as all other Mnesia tables. They can be included in backups, replicas can be added, and so on. - The tables are normal <c>Mnesia</c> tables owned by the user + The tables are normal Mnesia tables owned by the user of the corresponding <c>erl_interface</c> registries.</p> </description> @@ -68,7 +68,7 @@ that is, <c>{ram_copies,[node()]}</c> or <c>{disc_copies,[node()]}</c>.</p> <p>This function is used by <c>erl_interface</c> to - create the <c>Mnesia</c> table if it does not already + create the Mnesia table if it does not already exist.</p> </desc> </func> diff --git a/lib/mnesia/doc/src/part.xml b/lib/mnesia/doc/src/part.xml index 101bdb29d4..d3ffe93937 100644 --- a/lib/mnesia/doc/src/part.xml +++ b/lib/mnesia/doc/src/part.xml @@ -30,7 +30,7 @@ <file>part.sgml</file> </header> <description> - <p>The <c>Mnesia</c> application is a distributed Database Management + <p>The Mnesia application is a distributed Database Management System (DBMS), appropriate for telecommunications applications and other Erlang applications, which require continuous operation and exhibit soft real-time properties.</p> diff --git a/lib/mnesia/doc/src/ref_man.xml b/lib/mnesia/doc/src/ref_man.xml index 662f0d61d6..7fb71b9c45 100644 --- a/lib/mnesia/doc/src/ref_man.xml +++ b/lib/mnesia/doc/src/ref_man.xml @@ -33,7 +33,7 @@ <file>refman.sgml</file> </header> <description> - <p>The <c>Mnesia</c> application is a distributed Database Management + <p>The Mnesia application is a distributed Database Management System (DBMS), appropriate for telecommunications applications and other Erlang applications, which require continuous operation and exhibit soft real-time properties.</p> diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml index 505d0dcc89..f79f75fead 100644 --- a/lib/observer/doc/src/notes.xml +++ b/lib/observer/doc/src/notes.xml @@ -32,6 +32,23 @@ <p>This document describes the changes made to the Observer application.</p> +<section><title>Observer 2.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed a crash happening when observing another node, who + have a different number of schedulers than the current + one.</p> + <p> + Own Id: OTP-13702 Aux Id: ERL-171 </p> + </item> + </list> + </section> + +</section> + <section><title>Observer 2.2</title> <section><title>Improvements and New Features</title> diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index 94ecef24b4..42b0fa1d8a 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -485,7 +485,7 @@ ttb:p(all, call).</input></pre> <p>For a description of the <c>match_spec()</c> syntax, see section <seealso marker="erts:match_spec"><c>Match Specifications in Erlang</c></seealso> - in <c>ERTS</c>, which explains the general match specification "language". + in ERTS, which explains the general match specification "language". </p> <note> <p>The <em>system tracer</em> for sequential tracing is diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 1010a6af4c..6a1aac92c7 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -235,12 +235,14 @@ terminate(_Event, #state{appmon=Pid}) -> code_change(_, _, State) -> State. -restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti}=State) -> +restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti, wins=Wins0}=State) -> catch Old ! exit, Me = self(), Pid = spawn_link(Node, observer_backend, fetch_stats, [Me, round(1000/Freq)]), wxWindow:refresh(Panel), - precalc(State#state{active=true, appmon=Pid, samples=reset_data(), time=Ti#ti{tick=0}}). + Wins = [W#win{state=undefined} || W <- Wins0], + precalc(State#state{active=true, appmon=Pid, samples=reset_data(), + wins=Wins, time=Ti#ti{tick=0}}). reset_data() -> {0, queue:new()}. @@ -253,18 +255,25 @@ add_data(Stats, {N, Q}, Wins, _, Active) -> add_data_1([#win{state={_,St}}|_]=Wins0, Last, N, {Drop, Q}, Active) when St /= undefined -> - {Wins, Stat} = - lists:mapfoldl(fun(Win0, Entry) -> - {Win1,Stat} = add_data_2(Win0, Last, Entry), - case Active of - true -> - Win = add_data_3(Win1, N, Drop, Stat, Q), - {Win, Stat}; - false -> - {Win1, Stat} - end - end, #{}, Wins0), - {Wins, {N,queue:in(Stat#{}, Q)}}; + try + {Wins, Stat} = + lists:mapfoldl(fun(Win0, Entry) -> + {Win1,Stat} = add_data_2(Win0, Last, Entry), + case Active of + true -> + Win = add_data_3(Win1, N, Drop, Stat, Q), + {Win, Stat}; + false -> + {Win1, Stat} + end + end, #{}, Wins0), + {Wins, {N,queue:in(Stat#{}, Q)}} + catch no_scheduler_change -> + {[Win#win{state=init_data(Id, Last), + info = info(Id, Last)} + || #win{name=Id}=Win <- Wins0], {0,queue:new()}} + end; + add_data_1(Wins, Stats, 1, {_, Q}, _) -> {[Win#win{state=init_data(Id, Stats), info = info(Id, Stats)} @@ -409,7 +418,8 @@ collect_data(utilz, MemInfo, Max) -> calc_delta([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) -> [100*(WN-WP) div (TN-TP)|calc_delta(Ss, Ps)]; -calc_delta([], []) -> []. +calc_delta([], []) -> []; +calc_delta(_, _) -> throw(no_scheduler_change). precalc(#state{samples=Data0, paint=Paint, time=Ti, wins=Wins0}=State) -> Wins = [precalc(Ti, Data0, Paint, Win) || Win <- Wins0], diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl index 59f6443551..968a7620aa 100644 --- a/lib/observer/src/observer_tv_wx.erl +++ b/lib/observer/src/observer_tv_wx.erl @@ -124,6 +124,8 @@ handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0}) end, case get_tables2(Node, Opt) of Error = {error, _} -> + Id =:= ?ID_MNESIA andalso + wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true), self() ! Error, {noreply, State}; Tables -> @@ -217,22 +219,32 @@ handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt, {noreply, State#state{tabs=Tabs}} end; -handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt, +handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0, timer=Timer0}) -> - Tables = get_tables(Node, Opt), + {Tables, Opt} = case Opt0#opt.type =:= mnesia andalso get_tables2(Node, Opt0) of + Ts when is_list(Ts) -> + {Ts, Opt0}; + _ -> % false or error getting mnesia tables + Opt1 = Opt0#opt{type=ets}, + {get_tables(Node, Opt1), Opt1} + end, Tabs = update_grid(Grid, Opt, Tables), wxWindow:setFocus(Grid), create_menus(Parent, Opt), Timer = observer_lib:start_timer(Timer0), - {noreply, State#state{node=Node, tabs=Tabs, timer=Timer}}; + {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt}}; handle_info(not_active, State = #state{timer = Timer0}) -> Timer = observer_lib:stop_timer(Timer0), {noreply, State#state{timer=Timer}}; -handle_info({error, Error}, State) -> +handle_info({error, Error}, #state{opt=Opt}=State) -> handle_error(Error), - {noreply, State}; + case Opt#opt.type of + mnesia -> wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true); + _ -> ok + end, + {noreply, State#state{opt=Opt#opt{type=ets}}}; handle_info(_Event, State) -> {noreply, State}. diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 301bb4b32f..5732c12006 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -21,7 +21,7 @@ -behaviour(wx_object). -export([start/0, stop/0]). --export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, +-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, get_menubar/0, set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, @@ -94,6 +94,9 @@ get_tracer() -> get_active_node() -> wx_object:call(observer, get_active_node). +get_menubar() -> + wx_object:call(observer, get_menubar). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(_Args) -> @@ -391,6 +394,9 @@ handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) -> handle_call(get_active_node, _From, State=#state{node=Node}) -> {reply, Node, State}; +handle_call(get_menubar, _From, State=#state{menubar=MenuBar}) -> + {reply, MenuBar, State}; + handle_call(stop, From, State) -> stop_servers(State), {noreply, State#state{reply_to=From}}; diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk index f214810199..9a7c6a546f 100644 --- a/lib/observer/vsn.mk +++ b/lib/observer/vsn.mk @@ -1 +1 @@ -OBSERVER_VSN = 2.2 +OBSERVER_VSN = 2.2.1 diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in index cb2b23d534..d26daa5eda 100644 --- a/lib/odbc/configure.in +++ b/lib/odbc/configure.in @@ -147,7 +147,7 @@ AC_SUBST(THR_LIBS) odbc_lib_link_success=no AC_SUBST(TARGET_FLAGS) case $host_os in - darwin1[[0-4]].*|darwin[[0-9]].*) + darwin1[[0-5]].*|darwin[[0-9]].*) TARGET_FLAGS="-DUNIX" if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then ODBC_LIB= -L"/usr/lib" diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml index d3dd39616f..40f9339028 100644 --- a/lib/odbc/doc/src/notes.xml +++ b/lib/odbc/doc/src/notes.xml @@ -585,7 +585,7 @@ also been extended. </item><item> The <c>configure</c> scripts of <c>erl_interface</c> and <c>odbc</c> now search for thread libraries and thread library quirks the - same way as <c>erts</c> do. </item><item> The + same way as ERTS do. </item><item> The <c>configure</c> script of the <c>odbc</c> application now also looks for odbc libraries in <c>lib64</c> and <c>lib/64</c> directories when building on a 64-bit diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl index 5727c1ca50..261dfc6f20 100644 --- a/lib/odbc/test/odbc_connect_SUITE.erl +++ b/lib/odbc/test/odbc_connect_SUITE.erl @@ -89,6 +89,7 @@ init_per_suite(Config) when is_list(Config) -> [{auto_commit, off}] ++ odbc_test_lib:platform_options()) of {ok, Ref} -> odbc:disconnect(Ref), + ct:timetrap(?default_timeout), [{tableName, odbc_test_lib:unique_table_name()} | Config]; _ -> {skip, "ODBC is not properly setup"} @@ -129,11 +130,8 @@ init_per_testcase(_TestCase, Config) -> init_per_testcase_common(Config). init_per_testcase_common(Config) -> - test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), - Dog = test_server:timetrap(?default_timeout), - Temp = lists:keydelete(connection_ref, 1, Config), - NewConfig = lists:keydelete(watchdog, 1, Temp), - [{watchdog, Dog} | NewConfig]. + ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), + lists:keydelete(connection_ref, 1, Config). %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ @@ -153,25 +151,22 @@ end_per_testcase(_TestCase, Config) -> end_per_testcase_common(Config). end_per_testcase_common(Config) -> - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table), io:format("Drop table: ~p ~p~n", [Table, Result]), - odbc:disconnect(Ref), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + odbc:disconnect(Ref). %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -commit(doc)-> - ["Test the use of explicit commit"]; -commit(suite) -> []; +commit()-> + [{doc,"Test the use of explicit commit"}]. commit(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -205,14 +200,13 @@ commit(Config) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -rollback(doc)-> - ["Test the use of explicit rollback"]; -rollback(suite) -> []; +rollback()-> + [{doc,"Test the use of explicit rollback"}]. rollback(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), @@ -245,9 +239,8 @@ rollback(Config) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -not_explicit_commit(doc) -> - ["Test what happens if you try using commit on a auto_commit connection."]; -not_explicit_commit(suite) -> []; +not_explicit_commit() -> + [{doc,"Test what happens if you try using commit on a auto_commit connection."}]. not_explicit_commit(_Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}] ++ @@ -256,19 +249,17 @@ not_explicit_commit(_Config) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -not_exist_db(doc) -> - ["Tests valid data format but invalid data in the connection parameters."]; -not_exist_db(suite) -> []; +not_exist_db() -> + [{doc,"Tests valid data format but invalid data in the connection parameters."}]. not_exist_db(_Config) -> {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", odbc_test_lib:platform_options()), %% So that the odbc control server can be stoped "in the correct way" - test_server:sleep(100). + ct:sleep(100). %%------------------------------------------------------------------------- -no_c_executable(doc) -> - "Test what happens if the port-program can not be found"; -no_c_executable(suite) -> []; +no_c_executable() -> + [{doc,"Test what happens if the port-program can not be found"}]. no_c_executable(_Config) -> process_flag(trap_exit, true), Dir = filename:nativename(filename:join(code:priv_dir(odbc), @@ -293,9 +284,8 @@ no_c_executable(_Config) -> end. %%------------------------------------------------------------------------ -port_dies(doc) -> - "Tests what happens if the port program dies"; -port_dies(suite) -> []; +port_dies() -> + [{doc,"Tests what happens if the port program dies"}]. port_dies(_Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), {status, _} = process_info(Ref, status), @@ -307,7 +297,7 @@ port_dies(_Config) -> %% Wait for exit_status from port 5000 ms (will not get a exit %% status in this case), then wait a little longer to make sure %% the port and the controlprocess has had time to terminate. - test_server:sleep(10000), + ct:sleep(10000), undefined = process_info(Ref, status); [] -> ct:fail([erlang:port_info(P, name) || P <- erlang:ports()]) @@ -315,9 +305,8 @@ port_dies(_Config) -> %%------------------------------------------------------------------------- -control_process_dies(doc) -> - "Tests what happens if the Erlang control process dies"; -control_process_dies(suite) -> []; +control_process_dies() -> + [{doc,"Tests what happens if the Erlang control process dies"}]. control_process_dies(_Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), process_flag(trap_exit, true), @@ -326,7 +315,7 @@ control_process_dies(_Config) -> [Port] -> {connected, Ref} = erlang:port_info(Port, connected), exit(Ref, kill), - test_server:sleep(500), + ct:sleep(500), undefined = erlang:port_info(Port, connected); %% Check for c-program still running, how? [] -> @@ -334,9 +323,8 @@ control_process_dies(_Config) -> end. %%------------------------------------------------------------------------- -client_dies_normal(doc) -> - ["Client dies with reason normal."]; -client_dies_normal(suite) -> []; +client_dies_normal() -> + [{doc,"Client dies with reason normal."}]. client_dies_normal(Config) when is_list(Config) -> Pid = spawn(?MODULE, client_normal, [self()]), @@ -352,7 +340,7 @@ client_dies_normal(Config) when is_list(Config) -> {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> - test_server:fail(control_process_not_stopped) + ct:fail(control_process_not_stopped) end. client_normal(Pid) -> @@ -366,9 +354,8 @@ client_normal(Pid) -> %%------------------------------------------------------------------------- -client_dies_timeout(doc) -> - ["Client dies with reason timeout."]; -client_dies_timeout(suite) -> []; +client_dies_timeout() -> + [{doc,"Client dies with reason timeout."}]. client_dies_timeout(Config) when is_list(Config) -> Pid = spawn(?MODULE, client_timeout, [self()]), @@ -384,7 +371,7 @@ client_dies_timeout(Config) when is_list(Config) -> {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> - test_server:fail(control_process_not_stopped) + ct:fail(control_process_not_stopped) end. client_timeout(Pid) -> @@ -398,9 +385,8 @@ client_timeout(Pid) -> %%------------------------------------------------------------------------- -client_dies_error(doc) -> - ["Client dies with reason error."]; -client_dies_error(suite) -> []; +client_dies_error() -> + [{doc,"Client dies with reason error."}]. client_dies_error(Config) when is_list(Config) -> Pid = spawn(?MODULE, client_error, [self()]), @@ -416,7 +402,7 @@ client_dies_error(Config) when is_list(Config) -> {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> - test_server:fail(control_process_not_stopped) + ct:fail(control_process_not_stopped) end. client_error(Pid) -> @@ -430,9 +416,8 @@ client_error(Pid) -> %%------------------------------------------------------------------------- -connect_timeout(doc) -> - ["Test the timeout for the connect function."]; -connect_timeout(suite) -> []; +connect_timeout() -> + [{doc,"Test the timeout for the connect function."}]. connect_timeout(Config) when is_list(Config) -> {'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(), [{timeout, 0}] ++ @@ -442,10 +427,9 @@ connect_timeout(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -connect_port_timeout(doc) -> - ["Test the timeout for the port program to connect back to the odbc " - "application within the connect function."]; -connect_port_timeout(suite) -> []; +connect_port_timeout() -> + [{"Test the timeout for the port program to connect back to the odbc " + "application within the connect function."}]. connect_port_timeout(Config) when is_list(Config) -> %% Application environment var 'port_timeout' has been set to 0 by %% init_per_testcase/2. @@ -453,15 +437,14 @@ connect_port_timeout(Config) when is_list(Config) -> odbc_test_lib:platform_options()). %%------------------------------------------------------------------------- -timeout(doc) -> - ["Test that timeouts don't cause unwanted behavior sush as receiving" - " an anwser to a previously tiemed out query."]; -timeout(suite) -> []; +timeout() -> + [{"Test that timeouts don't cause unwanted behavior sush as receiving" + " an anwser to a previously tiemed out query."}]. timeout(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}]), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), @@ -512,7 +495,7 @@ update_table_timeout(Table, TimeOut, Pid) -> {'EXIT', timeout} -> Pid ! timout_occurred; {updated, 1} -> - test_server:fail(database_locker_failed) + ct:fail(database_locker_failed) end, receive @@ -537,15 +520,14 @@ update_table_timeout(Table, TimeOut, Pid) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -many_timeouts(doc) -> - ["Tests that many consecutive timeouts lead to that the connection " - "is shutdown."]; -many_timeouts(suite) -> []; +many_timeouts() -> + [{doc, "Tests that many consecutive timeouts lead to that the connection " + "is shutdown."}]. many_timeouts(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -592,19 +574,18 @@ loop_many_timouts(Ref, UpdateQuery, TimeOut) -> {'EXIT',timeout} -> loop_many_timouts(Ref, UpdateQuery, TimeOut); {updated, 1} -> - test_server:fail(database_locker_failed); + ct:fail(database_locker_failed); {error, connection_closed} -> ok end. %%------------------------------------------------------------------------- -timeout_reset(doc) -> - ["Check that the number of consecutive timouts is reset to 0 when " - "a successful call to the database is made."]; -timeout_reset(suite) -> []; +timeout_reset() -> + [{doc, "Check that the number of consecutive timouts is reset to 0 when " + "a successful call to the database is made."}]. timeout_reset(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -688,21 +669,20 @@ loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) -> loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts - 1); {updated, 1} -> - test_server:fail(database_locker_failed); + ct:fail(database_locker_failed); {error, connection_closed} -> - test_server:fail(connection_closed_premature) + ct:fail(connection_closed_premature) end. %%------------------------------------------------------------------------- -disconnect_on_timeout(doc) -> - ["Check that disconnect after a time out works properly"]; -disconnect_on_timeout(suite) -> []; +disconnect_on_timeout() -> + [{doc,"Check that disconnect after a time out works properly"}]. disconnect_on_timeout(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -726,7 +706,7 @@ disconnect_on_timeout(Config) when is_list(Config) -> ok -> ok = odbc:commit(Ref, commit); nok -> - test_server:fail(database_locker_failed) + ct:fail(database_locker_failed) end. update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> @@ -744,14 +724,13 @@ update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> end. %%------------------------------------------------------------------------- -connection_closed(doc) -> - ["Checks that you get an appropriate error message if you try to" - " use a connection that has been closed"]; -connection_closed(suite) -> []; +connection_closed() -> + [{doc, "Checks that you get an appropriate error message if you try to" + " use a connection that has been closed"}]. connection_closed(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -771,14 +750,13 @@ connection_closed(Config) when is_list(Config) -> {error, connection_closed} = odbc:commit(Ref, commit). %%------------------------------------------------------------------------- -disable_scrollable_cursors(doc) -> - ["Test disabling of scrollable cursors."]; -disable_scrollable_cursors(suite) -> []; +disable_scrollable_cursors() -> + [{doc,"Test disabling of scrollable cursors."}]. disable_scrollable_cursors(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{scrollable_cursors, off}]), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -792,10 +770,10 @@ disable_scrollable_cursors(Config) when is_list(Config) -> NextResult = ?RDBMS:selected_ID(1, next), - test_server:format("Expected: ~p~n", [NextResult]), + ct:pal("Expected: ~p~n", [NextResult]), Result = odbc:next(Ref), - test_server:format("Got: ~p~n", [Result]), + ct:pal("Got: ~p~n", [Result]), NextResult = Result, {error, scrollable_cursors_disabled} = odbc:first(Ref), @@ -809,15 +787,14 @@ disable_scrollable_cursors(Config) when is_list(Config) -> {selected, _ColNames,[]} = odbc:select(Ref, next, 1). %%------------------------------------------------------------------------- -return_rows_as_lists(doc)-> - ["Test the option that a row may be returned as a list instead " - "of a tuple. Too be somewhat backward compatible."]; -return_rows_as_lists(suite) -> []; +return_rows_as_lists()-> + [{doc,"Test the option that a row may be returned as a list instead " + "of a tuple. Too be somewhat backward compatible."}]. return_rows_as_lists(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{tuple_row, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -854,29 +831,28 @@ return_rows_as_lists(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -api_missuse(doc)-> - ["Test that behaviour of the control process if the api is abused"]; -api_missuse(suite) -> []; +api_missuse()-> + [{doc,"Test that behaviour of the control process if the api is abused"}]. api_missuse(Config) when is_list(Config)-> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Serious programming fault, connetion will be shut down gen_server:call(Ref, {self(), foobar, 10}, infinity), - test_server:sleep(10), + ct:sleep(10), undefined = process_info(Ref, status), {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Serious programming fault, connetion will be shut down gen_server:cast(Ref2, {self(), foobar, 10}), - test_server:sleep(10), + ct:sleep(10), undefined = process_info(Ref2, status), {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Could be an innocent misstake the connection lives. Ref3 ! foobar, - test_server:sleep(10), + ct:sleep(10), {status, _} = process_info(Ref3, status). transaction_support_str(mysql) -> @@ -886,13 +862,13 @@ transaction_support_str(_) -> %%------------------------------------------------------------------------- -extended_errors(doc)-> - ["Test the extended errors connection option: When off; the old behaviour of just an error " - "string is returned on error. When on, the error string is replaced by a 3 element tuple " - "that also exposes underlying ODBC provider error codes."]; -extended_errors(suite) -> []; +extended_errors()-> + [{doc, + "Test the extended errors connection option: When off; the old behaviour of just an error " + "string is returned on error. When on, the error string is replaced by a 3 element tuple " + "that also exposes underlying ODBC provider error codes."}]. extended_errors(Config) when is_list(Config)-> - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), {updated, _} = odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))"), diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl index c88c00725e..a3a4bc78eb 100644 --- a/lib/odbc/test/odbc_data_type_SUITE.erl +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -120,6 +120,7 @@ init_per_suite(Config) when is_list(Config) -> false -> case (catch odbc:start()) of ok -> + ct:timetrap(?default_timeout), [{tableName, odbc_test_lib:unique_table_name()}| Config]; _ -> {skip, "ODBC not startable"} @@ -191,23 +192,22 @@ init_per_testcase(Case, Config) -> common_init_per_testcase(Case, Config) -> PlatformOptions = odbc_test_lib:platform_options(), - case atom_to_list(Case) of - "binary" ++ _ -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{binary_strings, on}] ++ PlatformOptions); - LCase when LCase == "utf8"; - LCase == "nchar"; - LCase == "nvarchar" -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{binary_strings, on}] ++ PlatformOptions); - _ -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), PlatformOptions) - end, + {ok, Ref} = + case atom_to_list(Case) of + "binary" ++ _ -> + odbc:connect(?RDBMS:connection_string(), + [{binary_strings, on}] ++ PlatformOptions); + LCase when LCase == "utf8"; + LCase == "nchar"; + LCase == "nvarchar" -> + odbc:connect(?RDBMS:connection_string(), + [{binary_strings, on}] ++ PlatformOptions); + _ -> + odbc:connect(?RDBMS:connection_string(), PlatformOptions) + end, odbc_test_lib:strict(Ref, ?RDBMS), - Dog = test_server:timetrap(?default_timeout), - Temp = lists:keydelete(connection_ref, 1, Config), - NewConfig = lists:keydelete(watchdog, 1, Temp), - [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. + NewConfig = lists:keydelete(connection_ref, 1, Config), + [{connection_ref, Ref} | NewConfig]. is_fixed_upper_limit(mysql) -> false; @@ -231,28 +231,23 @@ is_supported_bit(_) -> %% Description: Cleanup after each test case %%-------------------------------------------------------------------- end_per_testcase(_TestCase, Config) -> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), ok = odbc:disconnect(Ref), %% Clean up if needed - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc:sql_query(NewRef, "DROP TABLE " ++ Table), - odbc:disconnect(NewRef), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. + odbc:disconnect(NewRef). %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -char_fixed_lower_limit(doc) -> - ["Tests fixed length char data type lower boundaries."]; -char_fixed_lower_limit(suite) -> - []; +char_fixed_lower_limit() -> + [{doc,"Tests fixed length char data type lower boundaries."}]. char_fixed_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -287,18 +282,16 @@ char_fixed_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -char_fixed_upper_limit(doc) -> - ["Tests fixed length char data type upper boundaries."]; -char_fixed_upper_limit(suite) -> - []; +char_fixed_upper_limit() -> + [{doc,"Tests fixed length char data type upper boundaries."}]. char_fixed_upper_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Limit unknown"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! @@ -337,14 +330,12 @@ char_fixed_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -char_fixed_padding(doc) -> - ["Tests that data that is shorter than the given size is padded " - "with blanks."]; -char_fixed_padding(suite) -> - []; +char_fixed_padding() -> + [{doc, "Tests that data that is shorter than the given size is padded " + "with blanks."}]. char_fixed_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -365,13 +356,11 @@ char_fixed_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -varchar_lower_limit(doc) -> - ["Tests variable length char data type lower boundaries."]; -varchar_lower_limit(suite) -> - []; +varchar_lower_limit() -> + [{doc,"Tests variable length char data type lower boundaries."}]. varchar_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -405,13 +394,11 @@ varchar_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -varchar_upper_limit(doc) -> - ["Tests variable length char data type upper boundaries."]; -varchar_upper_limit(suite) -> - []; +varchar_upper_limit() -> + [{doc,"Tests variable length char data type upper boundaries."}]. varchar_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> @@ -455,14 +442,12 @@ varchar_upper_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -varchar_no_padding(doc) -> - ["Tests that data that is shorter than the given max size is not padded " - "with blanks."]; -varchar_no_padding(suite) -> - []; +varchar_no_padding() -> + [{doc, "Tests that data that is shorter than the given max size is not padded " + "with blanks."}]. varchar_no_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should NOT be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -481,13 +466,11 @@ varchar_no_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -text_lower_limit(doc) -> - ["Tests 'long' char data type lower boundaries."]; -text_lower_limit(suite) -> - []; +text_lower_limit() -> + [{doc,"Tests 'long' char data type lower boundaries."}]. text_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -504,15 +487,13 @@ text_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -text_upper_limit(doc) -> - []; -text_upper_limit(suite) -> - []; +text_upper_limit() -> + [{doc,"Tests 'text' char data type upper boundaries."}]. text_upper_limit(Config) when is_list(Config) -> {skip,"Consumes too much resources" }. -%% Ref = ?config(connection_ref, Config), -%% Table = ?config(tableName, Config), +%% Ref = proplists:get_value(connection_ref, Config), +%% Table = proplists:get_value(tableName, Config), %% {updated, _} = % Value == 0 || -1 driver dependent! %% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -534,13 +515,11 @@ text_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_char_fixed_lower_limit(doc) -> - ["Tests fixed length char data type lower boundaries."]; -binary_char_fixed_lower_limit(suite) -> - []; +binary_char_fixed_lower_limit() -> + [{doc,"Tests fixed length char data type lower boundaries."}]. binary_char_fixed_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -579,18 +558,16 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) -> ++ "')"). %%------------------------------------------------------------------------- -binary_char_fixed_upper_limit(doc) -> - ["Tests fixed length char data type upper boundaries."]; -binary_char_fixed_upper_limit(suite) -> - []; +binary_char_fixed_upper_limit() -> + [{doc,"Tests fixed length char data type upper boundaries."}]. binary_char_fixed_upper_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Limit unknown"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! @@ -630,14 +607,12 @@ binary_char_fixed_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_char_fixed_padding(doc) -> - ["Tests that data that is shorter than the given size is padded " - "with blanks."]; -binary_char_fixed_padding(suite) -> - []; +binary_char_fixed_padding() -> + [{doc, "Tests that data that is shorter than the given size is padded " + "with blanks."}]. binary_char_fixed_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -658,13 +633,11 @@ binary_char_fixed_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_varchar_lower_limit(doc) -> - ["Tests variable length char data type lower boundaries."]; -binary_varchar_lower_limit(suite) -> - []; +binary_varchar_lower_limit() -> + [{doc,"Tests variable length char data type lower boundaries."}]. binary_varchar_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -701,13 +674,11 @@ binary_varchar_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_varchar_upper_limit(doc) -> - ["Tests variable length char data type upper boundaries."]; -binary_varchar_upper_limit(suite) -> - []; +binary_varchar_upper_limit() -> + [{doc,"Tests variable length char data type upper boundaries."}]. binary_varchar_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> @@ -750,14 +721,12 @@ binary_varchar_upper_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -binary_varchar_no_padding(doc) -> - ["Tests that data that is shorter than the given max size is not padded " - "with blanks."]; -binary_varchar_no_padding(suite) -> - []; +binary_varchar_no_padding() -> + [{doc,"Tests that data that is shorter than the given max size is not padded " + "with blanks."}]. binary_varchar_no_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should NOT be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -776,13 +745,11 @@ binary_varchar_no_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_text_lower_limit(doc) -> - ["Tests 'long' char data type lower boundaries."]; -binary_text_lower_limit(suite) -> - []; +binary_text_lower_limit() -> + [{doc,"Tests 'long' char data type lower boundaries."}]. binary_text_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -799,15 +766,13 @@ binary_text_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_text_upper_limit(doc) -> - []; -binary_text_upper_limit(suite) -> - []; +binary_text_upper_limit() -> + [{doc,"Tests text char data type upper boundaries."}]. binary_text_upper_limit(Config) when is_list(Config) -> {skip,"Consumes too much resources" }. -%% Ref = ?config(connection_ref, Config), -%% Table = ?config(tableName, Config), +%% Ref = proplists:get_value(connection_ref, Config), +%% Table = proplists:get_value(tableName, Config), %% {updated, _} = % Value == 0 || -1 driver dependent! %% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -830,17 +795,15 @@ binary_text_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -tiny_int_lower_limit(doc) -> - ["Tests integer of type tinyint."]; -tiny_int_lower_limit(suite) -> - []; +tiny_int_lower_limit() -> + [{doc,"Tests integer of type tinyint."}]. tiny_int_lower_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Type tiniyint not supported"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -864,17 +827,15 @@ tiny_int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -tiny_int_upper_limit(doc) -> - ["Tests integer of type tinyint."]; -tiny_int_upper_limit(suite) -> - []; +tiny_int_upper_limit() -> + [{doc,"Tests integer of type tinyint."}]. tiny_int_upper_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Type tiniyint not supported"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -898,13 +859,11 @@ tiny_int_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -small_int_lower_limit(doc) -> - ["Tests integer of type smallint."]; -small_int_lower_limit(suite) -> - []; +small_int_lower_limit() -> + [{doc,"Tests integer of type smallint."}]. small_int_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -927,13 +886,11 @@ small_int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -small_int_upper_limit(doc) -> - ["Tests integer of type smallint."]; -small_int_upper_limit(suite) -> - []; +small_int_upper_limit() -> + [{doc,"Tests integer of type smallint."}]. small_int_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -955,13 +912,11 @@ small_int_upper_limit(Config) when is_list(Config) -> ++ "')"). %%------------------------------------------------------------------------- -int_lower_limit(doc) -> - ["Tests integer of type int."]; -int_lower_limit(suite) -> - []; +int_lower_limit() -> + [{doc,"Tests integer of type int."}]. int_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -983,13 +938,11 @@ int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -int_upper_limit(doc) -> - ["Tests integer of type int."]; -int_upper_limit(suite) -> - []; +int_upper_limit() -> + [{doc,"Tests integer of type int."}]. int_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1011,13 +964,11 @@ int_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -big_int_lower_limit(doc) -> - ["Tests integer of type bigint"]; -big_int_lower_limit(suite) -> - []; +big_int_lower_limit() -> + [{doc,"Tests integer of type bigint"}]. big_int_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1040,13 +991,11 @@ big_int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -big_int_upper_limit(doc) -> - ["Tests integer of type bigint."]; -big_int_upper_limit(suite) -> - []; +big_int_upper_limit() -> + [{doc,"Tests integer of type bigint."}]. big_int_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1068,17 +1017,13 @@ big_int_upper_limit(Config) when is_list(Config) -> ++ "')"). %%------------------------------------------------------------------------- -bit_false(doc) -> - [""]; -bit_false(suite) -> - []; bit_false(Config) when is_list(Config) -> case ?RDBMS of oracle -> {skip, "Not supported by driver"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1102,17 +1047,13 @@ bit_false(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -bit_true(doc) -> - [""]; -bit_true(suite) -> - []; bit_true(Config) when is_list(Config) -> case ?RDBMS of oracle -> {skip, "Not supported by driver"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! @@ -1136,14 +1077,11 @@ bit_true(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -float_lower_limit(doc) -> - [""]; -float_lower_limit(suite) -> - []; + float_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of mysql -> @@ -1186,13 +1124,10 @@ float_lower_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -float_upper_limit(doc) -> - [""]; -float_upper_limit(suite) -> - []; + float_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of mysql -> @@ -1218,13 +1153,11 @@ float_upper_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -float_zero(doc) -> - ["Test the float value zero."]; -float_zero(suite) -> - []; +float_zero() -> + [{doc,"Test the float value zero."}]. float_zero(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1237,13 +1170,11 @@ float_zero(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table). %%------------------------------------------------------------------------- -real_zero(doc) -> - ["Test the real value zero."]; -real_zero(suite) -> - []; +real_zero() -> + [{doc,"Test the real value zero."}]. real_zero(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> @@ -1262,13 +1193,11 @@ real_zero(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table) end. %%------------------------------------------------------------------------ -dec_long(doc) -> - [""]; dec_long(suit) -> []; dec_long(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1281,13 +1210,11 @@ dec_long(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------ -dec_double(doc) -> - [""]; dec_double(suit) -> []; dec_double(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1329,13 +1256,11 @@ dec_double(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ -dec_bignum(doc) -> - [""]; dec_bignum(suit) -> []; dec_bignum(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1361,13 +1286,11 @@ dec_bignum(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ -num_long(doc) -> - [""]; num_long(suit) -> []; num_long(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1380,13 +1303,11 @@ num_long(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------ -num_double(doc) -> - [""]; num_double(suit) -> []; num_double(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1426,13 +1347,11 @@ num_double(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ -num_bignum(doc) -> - [""]; num_bignum(suit) -> []; num_bignum(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1459,13 +1378,13 @@ num_bignum(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ -utf8(doc) -> - ["Test unicode support"]; +utf8() -> + [{doc,"Test unicode support"}]. utf8(suit) -> []; utf8(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"), @@ -1487,30 +1406,30 @@ utf8(Config) when is_list(Config) -> end, Latin1Data), - test_server:format("UnicodeIn: ~p ~n",[UnicodeIn]), + ct:pal("UnicodeIn: ~p ~n",[UnicodeIn]), {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)", [{{sql_varchar,50}, UnicodeIn}]), {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), - test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + ct:pal("UnicodeOut: ~p~n", [UnicodeOut]), Result = lists:map(fun({Char}) -> unicode:characters_to_list(Char,utf8) end, UnicodeOut), - test_server:format("Result: ~p ~n", [Result]), + ct:pal("Result: ~p ~n", [Result]), Latin1Data = Result. %%------------------------------------------------------------------------ -nchar(doc) -> - ["Test unicode nchar support in sqlserver"]; +nchar() -> + [{doc,"Test unicode nchar support in sqlserver"}]. nchar(suit) -> []; nchar(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1520,13 +1439,13 @@ nchar(Config) when is_list(Config) -> %%------------------------------------------------------------------------ -nvarchar(doc) -> - ["Test 'unicode' nvarchar support"]; +nvarchar() -> + [{doc,"Test 'unicode' nvarchar support"}]. nvarchar(suit) -> []; nvarchar(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1535,13 +1454,11 @@ nvarchar(Config) when is_list(Config) -> w_char_support(Ref, Table, sql_wlongvarchar, 50). %%------------------------------------------------------------------------ -timestamp(doc) -> - [""]; timestamp(suit) -> []; timestamp(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1582,21 +1499,21 @@ w_char_support(Ref, Table, CharType, Size) -> end, Latin1Data), - test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), + ct:pal("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", [{{CharType, Size},UnicodeIn}]), {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), - test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + ct:pal("UnicodeOut: ~p~n", [UnicodeOut]), PadResult = lists:map(fun({Unicode}) -> unicode:characters_to_list(Unicode,{utf16,little}) end, UnicodeOut), - test_server:format("Result: ~p~n", [PadResult]), + ct:pal("Result: ~p~n", [PadResult]), Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult), diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl index 5f719b7287..c283872965 100644 --- a/lib/odbc/test/odbc_query_SUITE.erl +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -113,6 +113,7 @@ init_per_suite(Config) when is_list(Config) -> false -> case (catch odbc:start()) of ok -> + ct:timetrap(?default_timeout), [{tableName, odbc_test_lib:unique_table_name()}| Config]; _ -> {skip, "ODBC not startable"} @@ -144,10 +145,10 @@ end_per_suite(_Config) -> init_per_testcase(_Case, Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc_test_lib:strict(Ref, ?RDBMS), - Dog = test_server:timetrap(?default_timeout), - Temp = lists:keydelete(connection_ref, 1, Config), - NewConfig = lists:keydelete(watchdog, 1, Temp), - [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. + + NewConfig = lists:keydelete(connection_ref, 1, Config), + + [{connection_ref, Ref} | NewConfig]. %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ @@ -158,27 +159,23 @@ init_per_testcase(_Case, Config) -> %% Description: Cleanup after each test case %%-------------------------------------------------------------------- end_per_testcase(_Case, Config) -> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), ok = odbc:disconnect(Ref), %% Clean up if needed - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc:sql_query(NewRef, "DROP TABLE " ++ Table), - odbc:disconnect(NewRef), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. + odbc:disconnect(NewRef). %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -stored_proc(doc)-> - ["Test stored proc with OUT param"]; -stored_proc(suite) -> []; +stored_proc()-> + [{doc, "Test stored proc with OUT param"}]. stored_proc(Config) when is_list(Config) -> case ?RDBMS of X when X == oracle; X == postgres-> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), {updated, _} = odbc:sql_query(Ref, ?RDBMS:stored_proc_integer_out()), @@ -192,12 +189,11 @@ stored_proc(Config) when is_list(Config) -> {skip, "stored proc not yet supported"} end. -sql_query(doc)-> - ["Test the common cases"]; -sql_query(suite) -> []; +sql_query()-> + [{doc, "Test the common cases"}]. sql_query(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -235,14 +231,14 @@ sql_query(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_count(doc) -> - ["Tests select_count/[2,3]'s timeout, " - " select_count's functionality will be better tested by other tests " - " such as first."]; +select_count() -> + [{doc, "Tests select_count/[2,3]'s timeout, " + " select_count's functionality will be better tested by other tests " + " such as first."}]. select_count(sute) -> []; select_count(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -257,12 +253,11 @@ select_count(Config) when is_list(Config) -> (catch odbc:select_count(Ref, "SELECT * FROM ", -1)), ok. %%------------------------------------------------------------------------- -first(doc) -> - ["Tests first/[1,2]"]; -first(suite) -> []; +first() -> + [doc, {"Tests first/[1,2]"}]. first(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -284,12 +279,11 @@ first(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -last(doc) -> - ["Tests last/[1,2]"]; -last(suite) -> []; +last() -> + [{doc, "Tests last/[1,2]"}]. last(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -311,12 +305,11 @@ last(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -next(doc) -> - ["Tests next/[1,2]"]; -next(suite) -> []; +next() -> + [{doc, "Tests next/[1,2]"}]. next(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -337,12 +330,11 @@ next(Config) when is_list(Config) -> {'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)), ok. %%------------------------------------------------------------------------- -prev(doc) -> - ["Tests prev/[1,2]"]; -prev(suite) -> []; +prev() -> + [{doc, "Tests prev/[1,2]"}]. prev(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -366,12 +358,12 @@ prev(Config) when is_list(Config) -> {'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)), ok. %%------------------------------------------------------------------------- -select_next(doc) -> - ["Tests select/[4,5] with CursorRelation = next "]; +select_next() -> + [{doc, "Tests select/[4,5] with CursorRelation = next "}]. select_next(suit) -> []; select_next(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -407,12 +399,12 @@ select_next(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_relative(doc) -> - ["Tests select/[4,5] with CursorRelation = relative "]; +select_relative() -> + [{doc, "Tests select/[4,5] with CursorRelation = relative "}]. select_relative(suit) -> []; select_relative(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -448,12 +440,12 @@ select_relative(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_absolute(doc) -> - ["Tests select/[4,5] with CursorRelation = absolute "]; +select_absolute() -> + [{doc, "Tests select/[4,5] with CursorRelation = absolute "}]. select_absolute(suit) -> []; select_absolute(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -482,12 +474,11 @@ select_absolute(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -create_table_twice(doc) -> - ["Test what happens if you try to create the same table twice."]; -create_table_twice(suite) -> []; +create_table_twice() -> + [{doc, "Test what happens if you try to create the same table twice."}]. create_table_twice(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -501,12 +492,11 @@ create_table_twice(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -delete_table_twice(doc) -> - ["Test what happens if you try to delete the same table twice."]; -delete_table_twice(suite) -> []; +delete_table_twice() -> + [{doc, "Test what happens if you try to delete the same table twice."}]. delete_table_twice(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -518,12 +508,12 @@ delete_table_twice(Config) when is_list(Config) -> ok. %------------------------------------------------------------------------- -duplicate_key(doc) -> - ["Test what happens if you try to use the same key twice"]; +duplicate_key() -> + [{doc, "Test what happens if you try to use the same key twice"}]. duplicate_key(suit) -> []; duplicate_key(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -539,13 +529,12 @@ duplicate_key(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -not_connection_owner(doc) -> - ["Test what happens if a process that did not start the connection" - " tries to acess it."]; -not_connection_owner(suite) -> []; +not_connection_owner() -> + [{doc, "Test what happens if a process that did not start the connection" + " tries to acess it."}]. not_connection_owner(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), spawn_link(?MODULE, not_owner, [self(), Ref, Table]), @@ -564,12 +553,11 @@ not_owner(Pid, Ref, Table) -> Pid ! continue. %%------------------------------------------------------------------------- -no_result_set(doc) -> - ["Tests what happens if you try to use a function that needs an " - "associated result set when there is none."]; -no_result_set(suite) -> []; +no_result_set() -> + [{doc, "Tests what happens if you try to use a function that needs an " + "associated result set when there is none."}]. no_result_set(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), {error, result_set_does_not_exist} = odbc:first(Ref), {error, result_set_does_not_exist} = odbc:last(Ref), @@ -582,13 +570,11 @@ no_result_set(Config) when is_list(Config) -> odbc:select(Ref, {relative, 2}, 1), ok. %%------------------------------------------------------------------------- -query_error(doc) -> - ["Test what happens if there is an error in the query."]; -query_error(suite) -> - []; +query_error() -> + [{doc, "Test what happens if there is an error in the query."}]. query_error(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -605,15 +591,13 @@ query_error(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -multiple_select_result_sets(doc) -> - ["Test what happens if you have a batch of select queries."]; -multiple_select_result_sets(suite) -> - []; +multiple_select_result_sets() -> + [{doc, "Test what happens if you have a batch of select queries."}]. multiple_select_result_sets(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -640,16 +624,14 @@ multiple_select_result_sets(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -multiple_mix_result_sets(doc) -> - ["Test what happens if you have a batch of select and other type of" - " queries."]; -multiple_mix_result_sets(suite) -> - []; +multiple_mix_result_sets() -> + [{doc, "Test what happens if you have a batch of select and other type of" + " queries."}]. multiple_mix_result_sets(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -674,15 +656,13 @@ multiple_mix_result_sets(Config) when is_list(Config) -> {skip, "multiple result_set not supported"} end. %%------------------------------------------------------------------------- -multiple_result_sets_error(doc) -> - ["Test what happens if one of the batched queries fails."]; -multiple_result_sets_error(suite) -> - []; +multiple_result_sets_error() -> + [{doc, "Test what happens if one of the batched queries fails."}]. multiple_result_sets_error(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -709,15 +689,13 @@ multiple_result_sets_error(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -param_insert_tiny_int(doc)-> - ["Test insertion of tiny ints by parameterized queries."]; -param_insert_tiny_int(suite) -> - []; +param_insert_tiny_int()-> + [{doc,"Test insertion of tiny ints by parameterized queries."}]. param_insert_tiny_int(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -746,13 +724,11 @@ param_insert_tiny_int(Config) when is_list(Config) -> {skip, "Type tiniyint not supported"} end. %%------------------------------------------------------------------------- -param_insert_small_int(doc)-> - ["Test insertion of small ints by parameterized queries."]; -param_insert_small_int(suite) -> - []; +param_insert_small_int()-> + [{doc,"Test insertion of small ints by parameterized queries."}]. param_insert_small_int(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -778,13 +754,11 @@ param_insert_small_int(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_int(doc)-> - ["Test insertion of ints by parameterized queries."]; -param_insert_int(suite) -> - []; +param_insert_int()-> + [{doc,"Test insertion of ints by parameterized queries."}]. param_insert_int(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -810,13 +784,11 @@ param_insert_int(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_integer(doc)-> - ["Test insertion of integers by parameterized queries."]; -param_insert_integer(suite) -> - []; +param_insert_integer()-> + [{doc,"Test insertion of integers by parameterized queries."}]. param_insert_integer(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -842,13 +814,11 @@ param_insert_integer(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_decimal(doc)-> - ["Test insertion of decimal numbers by parameterized queries."]; -param_insert_decimal(suite) -> - []; +param_insert_decimal()-> + [{doc,"Test insertion of decimal numbers by parameterized queries."}]. param_insert_decimal(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -893,13 +863,11 @@ param_insert_decimal(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_numeric(doc)-> - ["Test insertion of numeric numbers by parameterized queries."]; -param_insert_numeric(suite) -> - []; +param_insert_numeric()-> + [{doc,"Test insertion of numeric numbers by parameterized queries."}]. param_insert_numeric(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -944,13 +912,11 @@ param_insert_numeric(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_char(doc)-> - ["Test insertion of fixed length string by parameterized queries."]; -param_insert_char(suite) -> - []; +param_insert_char()-> + [{doc,"Test insertion of fixed length string by parameterized queries."}]. param_insert_char(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -980,13 +946,11 @@ param_insert_char(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_character(doc)-> - ["Test insertion of fixed length string by parameterized queries."]; -param_insert_character(suite) -> - []; +param_insert_character()-> + [{doc,"Test insertion of fixed length string by parameterized queries."}]. param_insert_character(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1017,13 +981,11 @@ param_insert_character(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------ -param_insert_char_varying(doc)-> - ["Test insertion of variable length strings by parameterized queries."]; -param_insert_char_varying(suite) -> - []; +param_insert_char_varying()-> + [{doc,"Test insertion of variable length strings by parameterized queries."}]. param_insert_char_varying(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1054,13 +1016,11 @@ param_insert_char_varying(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_character_varying(doc)-> - ["Test insertion of variable length strings by parameterized queries."]; -param_insert_character_varying(suite) -> - []; +param_insert_character_varying()-> + [{doc,"Test insertion of variable length strings by parameterized queries."}]. param_insert_character_varying(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1091,13 +1051,11 @@ param_insert_character_varying(Config) when is_list(Config) -> [{{sql_varchar, 10}, ["1", 2]}])), ok. %%------------------------------------------------------------------------- -param_insert_float(doc)-> - ["Test insertion of floats by parameterized queries."]; -param_insert_float(suite) -> - []; +param_insert_float()-> + [{doc,"Test insertion of floats by parameterized queries."}]. param_insert_float(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1120,7 +1078,7 @@ param_insert_float(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail(float_numbers_do_not_match) + ct:fail(float_numbers_do_not_match) end, {'EXIT',{badarg,odbc,param_query,'Params'}} = @@ -1130,13 +1088,11 @@ param_insert_float(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_real(doc)-> - ["Test insertion of real numbers by parameterized queries."]; -param_insert_real(suite) -> - []; +param_insert_real()-> + [{doc,"Test insertion of real numbers by parameterized queries."}]. param_insert_real(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1161,7 +1117,7 @@ param_insert_real(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail(real_numbers_do_not_match) + ct:fail(real_numbers_do_not_match) end, {'EXIT',{badarg,odbc,param_query,'Params'}} = @@ -1171,13 +1127,11 @@ param_insert_real(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_double(doc)-> - ["Test insertion of doubles by parameterized queries."]; -param_insert_double(suite) -> - []; +param_insert_double()-> + [{doc,"Test insertion of doubles by parameterized queries."}]. param_insert_double(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1200,7 +1154,7 @@ param_insert_double(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail(double_numbers_do_not_match) + ct:fail(double_numbers_do_not_match) end, {'EXIT',{badarg,odbc,param_query,'Params'}} = @@ -1210,13 +1164,11 @@ param_insert_double(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_mix(doc)-> - ["Test insertion of a mixture of datatypes by parameterized queries."]; -param_insert_mix(suite) -> - []; +param_insert_mix()-> + [{doc,"Test insertion of a mixture of datatypes by parameterized queries."}]. param_insert_mix(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1237,13 +1189,11 @@ param_insert_mix(Config) when is_list(Config) -> odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ok. %%------------------------------------------------------------------------- -param_update(doc)-> - ["Test parameterized update query."]; -param_update(suite) -> - []; +param_update()-> + [{doc,"Test parameterized update query."}]. param_update(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1272,12 +1222,12 @@ param_update(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -delete_nonexisting_row(doc) -> % OTP-5759 - ["Make a delete...where with false conditions (0 rows deleted). ", - "This used to give an error message (see ticket OTP-5759)."]; +delete_nonexisting_row() -> % OTP-5759 + [{doc, "Make a delete...where with false conditions (0 rows deleted). ", + "This used to give an error message (see ticket OTP-5759)."}]. delete_nonexisting_row(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table @@ -1301,13 +1251,11 @@ delete_nonexisting_row(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_delete(doc) -> - ["Test parameterized delete query."]; -param_delete(suite) -> - []; +param_delete() -> + [{doc,"Test parameterized delete query."}]. param_delete(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1336,13 +1284,11 @@ param_delete(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -param_select(doc) -> - ["Test parameterized select query."]; -param_select(suite) -> - []; +param_select() -> + [{doc,"Test parameterized select query."}]. param_select(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1366,13 +1312,11 @@ param_select(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_select_empty_params(doc) -> - ["Test parameterized select query with no parameters."]; -param_select_empty_params(suite) -> - []; +param_select_empty_params() -> + [{doc,"Test parameterized select query with no parameters."}]. param_select_empty_params(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1396,13 +1340,11 @@ param_select_empty_params(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_delete_empty_params(doc) -> - ["Test parameterized delete query with no parameters."]; -param_delete_empty_params(suite) -> - []; +param_delete_empty_params() -> + [{doc,"Test parameterized delete query with no parameters."}]. param_delete_empty_params(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1430,13 +1372,11 @@ param_delete_empty_params(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_integer(doc) -> - ["Test describe_table/[2,3] for integer columns."]; -describe_integer(suite) -> - []; +describe_integer() -> + [{doc,"Test describe_table/[2,3] for integer columns."}]. describe_integer(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1449,13 +1389,11 @@ describe_integer(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_string(doc) -> - ["Test describe_table/[2,3] for string columns."]; -describe_string(suite) -> - []; +describe_string() -> + [{doc,"Test describe_table/[2,3] for string columns."}]. describe_string(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1470,13 +1408,11 @@ describe_string(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_floating(doc) -> - ["Test describe_table/[2,3] for floting columns."]; -describe_floating(suite) -> - []; +describe_floating() -> + [{doc,"Test describe_table/[2,3] for floting columns."}]. describe_floating(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1490,14 +1426,12 @@ describe_floating(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_dec_num(doc) -> - ["Test describe_table/[2,3] for decimal and numerical columns"]; -describe_dec_num(suite) -> - []; +describe_dec_num() -> + [{doc,"Test describe_table/[2,3] for decimal and numerical columns"}]. describe_dec_num(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1511,14 +1445,12 @@ describe_dec_num(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -describe_timestamp(doc) -> - ["Test describe_table/[2,3] for tinmestap columns"]; -describe_timestamp(suite) -> - []; +describe_timestamp() -> + [{doc,"Test describe_table/[2,3] for tinmestap columns"}]. describe_timestamp(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1530,14 +1462,12 @@ describe_timestamp(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_no_such_table(doc) -> - ["Test what happens if you try to describe a table that does not exist."]; -describe_no_such_table(suite) -> - []; +describe_no_such_table() -> + [{doc,"Test what happens if you try to describe a table that does not exist."}]. describe_no_such_table(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {error, _ } = odbc:describe_table(Ref, Table), ok. @@ -1549,10 +1479,10 @@ describe_no_such_table(Config) when is_list(Config) -> is_driver_error(Error) -> case is_list(Error) of true -> - test_server:format("Driver error ~p~n", [Error]), + ct:pal("Driver error ~p~n", [Error]), ok; false -> - test_server:fail(Error) + ct:fail(Error) end. is_supported_multiple_resultsets(sqlserver) -> true; diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl index f055eeb60e..310b92ca29 100644 --- a/lib/odbc/test/odbc_start_SUITE.erl +++ b/lib/odbc/test/odbc_start_SUITE.erl @@ -49,6 +49,7 @@ init_per_suite(Config) -> _ -> %% Make sure odbc is not already started odbc:stop(), + ct:timetrap(?TIMEOUT), [{tableName, odbc_test_lib:unique_table_name()} | Config] end end. @@ -74,11 +75,9 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. +init_per_testcase(_TestCase, Config) -> + ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), + Config. %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ @@ -88,15 +87,8 @@ init_per_testcase(_TestCase, Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - +end_per_testcase(_TestCase, _Config) -> + ok. %%-------------------------------------------------------------------- %% Function: all(Clause) -> TestCases %% Clause - atom() - suite | doc @@ -135,10 +127,8 @@ app(Config) when is_list(Config) -> appup(Config) when is_list(Config) -> ok = ?t:appup_test(odbc). -start(doc) -> - ["Test start/stop of odbc"]; -start(suite) -> - []; +start() -> + [{doc,"Test start/stop of odbc"}]. start(Config) when is_list(Config) -> PlatformOptions = odbc_test_lib:platform_options(), {error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(), @@ -153,9 +143,9 @@ start(Config) when is_list(Config) -> start_odbc(transient), start_odbc(permanent); {error, odbc_not_started} -> - test_server:fail(start_failed); + ct:fail(start_failed); Error -> - test_server:format("Connection failed: ~p~n", [Error]), + ct:pal("Connection failed: ~p~n", [Error]), {skip, "ODBC is not properly setup"} end. @@ -166,13 +156,12 @@ start_odbc(Type) -> ok = odbc:disconnect(Ref), odbc:stop(); {error, odbc_not_started} -> - test_server:fail(start_failed) + ct:fail(start_failed) end. -long_connection_line(doc)-> - ["Test a connection line longer than 127 characters"]; -long_connection_line(suite) -> []; +long_connection_line()-> + [{doc,"Test a connection line longer than 127 characters"}]. long_connection_line(_Config) -> odbc:start(), String133 = "unknown_odbc_parameter=01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl index 37c2249303..cf82d4d32a 100644 --- a/lib/odbc/test/odbc_test_lib.erl +++ b/lib/odbc/test/odbc_test_lib.erl @@ -61,13 +61,13 @@ odbc_check() -> end. check_row_count(Count, Count) -> - test_server:format("Correct row count Count: ~p~n", [Count]), + ct:pal("Correct row count Count: ~p~n", [Count]), true; check_row_count(_, undefined) -> - test_server:format("Undefined row count ~n", []), + ct:pal("Undefined row count ~n", []), true; check_row_count(Expected, Count) -> - test_server:format("Incorrect row count Expected ~p Got ~p~n", + ct:pal("Incorrect row count Expected ~p Got ~p~n", [Expected, Count]), false. diff --git a/lib/parsetools/doc/src/yecc.xml b/lib/parsetools/doc/src/yecc.xml index 6ec9fb5f0e..9188bd2a22 100644 --- a/lib/parsetools/doc/src/yecc.xml +++ b/lib/parsetools/doc/src/yecc.xml @@ -201,7 +201,7 @@ <p>The grammar starts with an optional <c>header</c> section. The header is put first in the generated file, before the module declaration. The purpose of the header is to provide a means to - make the documentation generated by <c>EDoc</c> look nicer. Each + make the documentation generated by EDoc look nicer. Each header line should be enclosed in double quotes, and newlines will be inserted between the lines. For example:</p> <code> diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml index 6df4924d0a..25d983afd3 100644 --- a/lib/reltool/doc/src/notes.xml +++ b/lib/reltool/doc/src/notes.xml @@ -546,8 +546,8 @@ <p>The handling of applications included in releases has been improved. Applications that are required to be started before other applications in a release are now - automatically included in the release. The <c>kernel</c> - and <c>stdlib</c> applications are always included as + automatically included in the release. The Kernel + and STDLIB applications are always included as they are mandatory.</p> <p>Applications that are (explicitly or implicitly) included in a release are now automatically included as diff --git a/lib/reltool/doc/src/part.xml b/lib/reltool/doc/src/part.xml index 5904084879..e608d548ea 100644 --- a/lib/reltool/doc/src/part.xml +++ b/lib/reltool/doc/src/part.xml @@ -31,7 +31,7 @@ <rev>%VSN%</rev> </header> <description> - <p><c>Reltool</c> is a release management tool. It analyses a given + <p>Reltool is a release management tool. It analyses a given Erlang/OTP installation and determines various dependencies between applications. The <c>graphical</c> frontend depicts the dependencies and enables interactive customization of a diff --git a/lib/reltool/doc/src/ref_man.xml b/lib/reltool/doc/src/ref_man.xml index 38f270b79a..1684f075ff 100644 --- a/lib/reltool/doc/src/ref_man.xml +++ b/lib/reltool/doc/src/ref_man.xml @@ -31,7 +31,7 @@ <rev>%VSN%</rev> </header> <description> - <p><c>Reltool</c> is a release management tool. It analyses a given + <p>Reltool is a release management tool. It analyses a given Erlang/OTP installation and determines various dependencies between applications. The <c>graphical</c> frontend depicts the dependencies and enables interactive customization of a diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 38448e7961..5bfbee966b 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -47,7 +47,7 @@ <p>The tool uses an installed Erlang/OTP system as input. <c>root_dir</c> is the root directory of the analysed system and - it defaults to the system executing <c>reltool</c>. Applications + it defaults to the system executing Reltool. Applications may also be located outside <c>root_dir</c>. <c>lib_dirs</c> defines library directories where additional applications may reside and it defaults to the directories @@ -56,7 +56,7 @@ <p>An application directory <c>AppDir</c> under a library directory is recognized by the existence of an <c>AppDir/ebin</c> - directory. If this does not exist, <c>reltool</c> will not + directory. If this does not exist, Reltool will not consider <c>AppDir</c> at all when looking for applications.</p> <p>It is recommended that application directories are named as the @@ -81,14 +81,14 @@ <tag><c>config</c></tag> <item> <p>This is the main option and it controls the configuration - of <c>reltool</c>. It can either be a <c>sys</c> tuple or + of Reltool. It can either be a <c>sys</c> tuple or a name of a <c>file</c> containing a sys tuple.</p> </item> <tag><c>trap_exit</c></tag> <item> <p>This option controls the error handling behavior of - <c>reltool</c>. By default the window processes traps + Reltool. By default the window processes traps exit, but this behavior can altered by setting <c>trap_exit</c> to <c>false</c>.</p> </item> diff --git a/lib/reltool/doc/src/reltool_intro.xml b/lib/reltool/doc/src/reltool_intro.xml index 8e232b8838..2980ad7977 100644 --- a/lib/reltool/doc/src/reltool_intro.xml +++ b/lib/reltool/doc/src/reltool_intro.xml @@ -34,7 +34,7 @@ <rev>%VSN%</rev> <file>reltool_intro.xml</file> </header> - <p><c>Reltool</c> is a release management tool. It analyses a given + <p>Reltool is a release management tool. It analyses a given Erlang/OTP installation and determines various dependencies between applications. The <c>graphical</c> frontend depicts the dependencies and enables interactive customization of a target system. The backend provides a @@ -82,7 +82,7 @@ and about the Erlang/OTP development system:</p> <list type="bulleted"> <item> - <p>the Reference Manual of <c>Reltool</c></p> + <p>the Reference Manual of Reltool</p> </item> <item> <p>the Erlang/OTP <c>System Principles</c></p> diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 9ac22b9450..89b5f3a997 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -154,19 +154,19 @@ -record(app_info, { - description = "" :: string(), - id = "" :: string(), - vsn = "" :: app_vsn(), - modules = [] :: [mod_name()], - maxP = infinity :: integer() | infinity, - maxT = infinity :: integer() | infinity, - registered = [] :: [atom()], - incl_apps = [] :: [app_name()], - applications = [] :: [app_name()], - env = [] :: [{atom(), term()}], - mod = undefined :: {mod_name(), [term()]} | undefined, - start_phases = undefined :: [{atom(), term()}] | undefined, - runtime_dependencies = [] :: [string()] + description = "" :: '_' | string(), + id = "" :: '_' | string(), + vsn = "" :: '_' | app_vsn(), + modules = [] :: '_' | [mod_name()], + maxP = infinity :: '_' | integer() | infinity, + maxT = infinity :: '_' | integer() | infinity, + registered = [] :: '_' | [atom()], + incl_apps = [] :: '_' | '$3' | [app_name()], + applications = [] :: '_' | '$2' | [app_name()], + env = [] :: '_' | [{atom(), term()}], + mod = undefined :: '_' | {mod_name(), [term()]} | undefined, + start_phases = undefined :: '_' | [{atom(), term()}] | undefined, + runtime_dependencies = [] :: '_' | [string()] }). -record(regexp, {source, compiled}). diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 21a4485f94..89e90670cf 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -530,9 +530,14 @@ analyse(#state{sys=Sys} = S, Apps, Status) -> %% Write all #app to app_tab and all #mod to mod_tab. Status2 = apps_init_is_included(S, Apps, RelApps, Status), + %% For each application that is not (directly or indirectly) part + %% of a release, but still has #app.is_included==true, propagate + %% is_included to the dependencies specified in the .app files. + app_propagate_is_included(S), + %% For each module that has #mod.is_included==true, propagate %% is_included to the modules it uses. - propagate_is_included(S), + mod_propagate_is_included(S), %% Insert reverse dependencies - i.e. for each %% #mod{name=Mod, uses_mods=[UsedMod]}, @@ -565,31 +570,34 @@ apps_in_rels(Rels, Apps) -> apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) -> Mandatory = [{RelName, kernel}, {RelName, stdlib}], - Other = + Explicit0 = [{RelName, AppName} || #rel_app{name=AppName} <- RelApps], + Explicit = Mandatory ++ Explicit0, + Deps = [{RelName, AppName} || RA <- RelApps, - AppName <- [RA#rel_app.name | + AppName <- + case lists:keyfind(RA#rel_app.name, + #app.name, + Apps) of + App=#app{info = #app_info{applications = AA}} -> %% Included applications in rel shall overwrite included %% applications in .app. I.e. included applications in %% .app shall only be used if it is not defined in rel. - case RA#rel_app.incl_apps of - undefined -> - case lists:keyfind(RA#rel_app.name, - #app.name, - Apps) of - #app{info = #app_info{incl_apps = IA}} -> - IA; - false -> - reltool_utils:throw_error( - "Release ~tp uses non existing " - "application ~w", - [RelName,RA#rel_app.name]) - end; - IA -> - IA - end], - not lists:keymember(AppName, 2, Mandatory)], - more_apps_in_rels(Mandatory ++ Other, Apps, []). + IA = case RA#rel_app.incl_apps of + undefined -> + (App#app.info)#app_info.incl_apps; + RelIA -> + RelIA + end, + AA ++ IA; + false -> + reltool_utils:throw_error( + "Release ~tp uses non existing " + "application ~w", + [RelName,RA#rel_app.name]) + end, + not lists:keymember(AppName, 2, Explicit)], + more_apps_in_rels(Deps, Apps, Explicit). more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> case lists:member(RA, Acc) of @@ -597,8 +605,8 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> more_apps_in_rels(RelApps, Apps, Acc); false -> case lists:keyfind(AppName, #app.name, Apps) of - #app{info = #app_info{applications = InfoApps}} -> - Extra = [{RelName, N} || N <- InfoApps], + #app{info = #app_info{applications = AA, incl_apps=IA}} -> + Extra = [{RelName, N} || N <- AA++IA], Acc2 = more_apps_in_rels(Extra, Apps, [RA | Acc]), more_apps_in_rels(RelApps, Apps, Acc2); false -> @@ -610,7 +618,6 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> more_apps_in_rels([], _Apps, Acc) -> Acc. - apps_init_is_included(S, Apps, RelApps, Status) -> lists:foldl(fun(App, AccStatus) -> app_init_is_included(S, App, RelApps, AccStatus) @@ -745,6 +752,100 @@ false_to_undefined(Bool) -> _ -> Bool end. +get_no_rel_apps_and_dependencies(S) -> + ets:select(S#state.app_tab, [{#app{name='$1', + is_included=true, + info=#app_info{applications='$2', + incl_apps='$3', + _='_'}, + rels=[], + _='_'}, + [], + [{{'$1','$2','$3'}}]}]). + +app_propagate_is_included(S) -> + lists:foreach( + fun({AppName,DepNames1,DepNames2}) -> + app_mark_is_included(S,AppName,DepNames1++DepNames2) + end, + get_no_rel_apps_and_dependencies(S)). + +app_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys}=S,UsedByName,[AppName|AppNames]) -> + case ets:lookup(AppTab, AppName) of + [A] -> + case A#app.is_included of + undefined -> + %% Not yet marked => mark and propagate + A2 = + case A#app.incl_cond of + include -> + A#app{is_pre_included = true, + is_included = true}; + exclude -> + A#app{is_pre_included = false, + is_included = false}; + AppInclCond when AppInclCond==undefined; + AppInclCond==derived -> + A#app{is_included = true} + end, + ets:insert(AppTab, A2), + + ModCond = + case A#app.mod_cond of + undefined -> Sys#sys.mod_cond; + _ -> A#app.mod_cond + end, + Filter = + fun(M) -> + case ModCond of + all -> true; + app -> M#mod.is_app_mod; + ebin -> M#mod.is_ebin_mod; + derived -> false; + none -> false + end + end, + Mods = lists:filter(Filter, A#app.mods), + %% Mark the modules of this app, but no need to go + %% recursive on modules since this is done in + %% mod_mark_is_included. + [case M#mod.is_included of + undefined -> + M2 = + case M#mod.incl_cond of + include -> + M#mod{is_pre_included = true, + is_included = true}; + exclude -> + M#mod{is_pre_included = false, + is_included = false}; + ModInclCond when ModInclCond==undefined; + ModInclCond==derived -> + M#mod{is_included = true} + end, + ets:insert(ModTab, M2); + _ -> + ok + end || M <- Mods], + + %% Go recursive on dependencies + #app{info=#app_info{applications=DepNames1, + incl_apps=DepNames2}} = A, + app_mark_is_included(S,AppName,DepNames1++DepNames2); + _ -> + %% Already marked + ok + end; + [] -> + %% Missing app + reltool_utils:throw_error( + "Application ~tp uses non existing application ~w", + [UsedByName,AppName]) + end, + app_mark_is_included(S, UsedByName, AppNames); +app_mark_is_included(_S, _UsedByName, []) -> + ok. + %% Return the list for {ModName, UsesModNames} for all modules where %% #mod.is_included==true. get_all_mods_and_dependencies(S) -> @@ -755,7 +856,7 @@ get_all_mods_and_dependencies(S) -> [], [{{'$1','$2'}}]}]). -propagate_is_included(S) -> +mod_propagate_is_included(S) -> case lists:flatmap( fun({ModName,UsesModNames}) -> mod_mark_is_included(S,ModName,UsesModNames,[]) diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index e9bd46fb27..e8dfea94da 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -142,6 +142,7 @@ all() -> save_config, dependencies, mod_incl_cond_derived, + dep_in_app_not_xref, use_selected_vsn, use_selected_vsn_relative_path, non_standard_vsn_id, @@ -408,7 +409,6 @@ create_release_sort(Config) -> {app,tools,[{mod_cond,app},{incl_cond,include}]} ]}, %% Generate release - ?msym({ok, {release, {RelName1, RelVsn}, {erts, _}, [{kernel, _}, @@ -2304,6 +2304,7 @@ dependencies(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test that incl_cond on mod level overwrites mod_cond on app level %% Uses same test applications as dependencies/1 above mod_incl_cond_derived(Config) -> @@ -2346,6 +2347,40 @@ mod_incl_cond_derived(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% ERL-167, OTP-11993: For applications that are not included in a +%% release spec ('rel'), dependencies in the .app files are not +%% considered - only those found with xref. +dep_in_app_not_xref(Config) -> + RelName = "Just testing...", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs,[filename:join(datadir(Config),"dep_in_app_not_xref")]}, + {incl_cond,exclude}, + {incl_archive_filters,[]}, + {erts,[{incl_cond,exclude}]}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]} + ]}, + + TargetDir = filename:join([?WORK_DIR, "target_dep_in_app_not_xref"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + ?log("~p~n",[file:list_dir(filename:join([TargetDir,"lib"]))]), + + ?m(true, filelib:is_dir(filename:join([TargetDir,"lib","y-1.0"]))), + ?m(true, filelib:is_dir(filename:join([TargetDir,"lib","z-1.0"]))), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% use_selected_vsn(Config) -> LibDir1 = filename:join(datadir(Config),"use_selected_vsn"), B1Dir = filename:join(LibDir1,"b-1.0"), diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app new file mode 100644 index 0000000000..fe922e3a41 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1]}, + {registered, []}, + {applications, [kernel, stdlib, y]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app new file mode 100644 index 0000000000..a21cfe6c21 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app @@ -0,0 +1,8 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1]}, + {registered, []}, + {applications, [kernel, stdlib]}, + {included_applications, [z]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl new file mode 100644 index 0000000000..902a7e21f3 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index 14a81b2293..db04bfdf7b 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -78,7 +78,7 @@ <p>Pseudo function that by means of a <c>parse_transform</c> translates the <em>literal</em><c>fun()</c> typed as parameter in the function call to a match specification as described in - the <c>match_spec</c> manual of <c>ERTS</c> users guide. + the <c>match_spec</c> manual of ERTS users guide. (with literal I mean that the <c>fun()</c> needs to textually be written as the parameter of the function, it cannot be held in a variable which in turn is passed to the diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml index 6bec7cb9ca..b6dfb2dd28 100644 --- a/lib/runtime_tools/doc/src/notes.xml +++ b/lib/runtime_tools/doc/src/notes.xml @@ -668,7 +668,7 @@ also been extended. </item><item> The <c>configure</c> scripts of <c>erl_interface</c> and <c>odbc</c> now search for thread libraries and thread library quirks the - same way as <c>erts</c> do. </item><item> The + same way as ERTS do. </item><item> The <c>configure</c> script of the <c>odbc</c> application now also looks for odbc libraries in <c>lib64</c> and <c>lib/64</c> directories when building on a 64-bit diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml index 8550a88b28..4160757164 100644 --- a/lib/sasl/doc/src/alarm_handler.xml +++ b/lib/sasl/doc/src/alarm_handler.xml @@ -54,7 +54,7 @@ format is defined by the user. For example, an event handler for SNMP can be defined, together with an alarm Management Information Base (MIB).</p> - <p>The alarm handler is part of the <c>SASL</c> application.</p> + <p>The alarm handler is part of the SASL application.</p> <p>When writing new event handlers for the alarm handler, the following events must be handled:</p> <taglist> @@ -76,7 +76,7 @@ {NewHandler, Args})</c>. <c>NewHandler:init({Args, {alarm_handler, Alarms}})</c> is called. For more details, see <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso> - in <c>STDLIB</c>.</p> + in STDLIB.</p> </description> <funcs> diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml index 6fbdcb9f5b..a43a966dcb 100644 --- a/lib/sasl/doc/src/appup.xml +++ b/lib/sasl/doc/src/appup.xml @@ -362,12 +362,12 @@ point_of_no_return</pre> system is restarted.</p> <pre> restart_new_emulator</pre> - <p>This instruction is used when the application <c>ERTS</c>, - <c>Kernel</c>, <c>STDLIB</c>, or <c>SASL</c> is + <p>This instruction is used when the application ERTS, + Kernel, STDLIB, or SASL is upgraded. It shuts down the current emulator and starts a new one. All processes are terminated gracefully, and the new - version of <c>ERTS</c>, <c>Kernel</c>, <c>STDLIB</c>, and - <c>SASL</c> are used when the emulator restarts. + version of ERTS, Kernel, STDLIB, and + SASL are used when the emulator restarts. Only one <c>restart_new_emulator</c> instruction is allowed in the <c>relup</c> file, and it must be placed first. <seealso marker="systools#make_relup/3"><c>systools:make_relup/3,4</c></seealso> @@ -385,7 +385,7 @@ restart_new_emulator</pre> <warning> <p>As stated earlier, instruction <c>restart_new_emulator</c> causes the emulator to be restarted with new versions of - <c>ERTS</c>, <c>Kernel</c>, <c>STDLIB</c>, and <c>SASL</c>. + ERTS>, Kernel, STDLIB, and SASL. However, all other applications do at startup run their old versions in this new emulator. This is usually no problem, but every now and then incompatible changes occur to the diff --git a/lib/sasl/doc/src/error_logging.xml b/lib/sasl/doc/src/error_logging.xml index 8464a41ff9..4b2c960bbb 100644 --- a/lib/sasl/doc/src/error_logging.xml +++ b/lib/sasl/doc/src/error_logging.xml @@ -32,20 +32,20 @@ <rev>B</rev> <file>error_logging.xml</file> </header> - <p>The <c>SASL</c> application introduces three types of reports:</p> + <p>The SASL application introduces three types of reports:</p> <list type="bulleted"> <item>Supervisor report</item> <item>Progress report</item> <item>Crash report</item> </list> - <p>When the <c>SASL</c> application is started, it adds a handler that + <p>When the SASL application is started, it adds a handler that formats and writes these reports, as specified in the configuration - parameters for <c>SASL</c>, that is, the environment variables - in the <c>SASL</c> application specification, which is found in the - <c>.app</c> file of <c>SASL</c>. For details, see the + parameters for SASL, that is, the environment variables + in the SASL application specification, which is found in the + <c>.app</c> file of SASL. For details, see the <seealso marker="sasl_app"><c>sasl(6)</c></seealso> application in the Reference Manual and the <seealso marker="kernel:app"><c>app(4)</c></seealso> - file in the <c>Kernel</c> Reference Manual.</p> + file in the Kernel Reference Manual.</p> <section> <title>Supervisor Report</title> @@ -180,14 +180,14 @@ <p>The report browser is used to browse and format error reports written by the error logger handler <seealso marker="stdlib:log_mf_h"><c>log_mf_h</c></seealso> - defined in <c>STDLIB</c>.</p> + defined in STDLIB.</p> <p>The <c>log_mf_h</c> handler writes all reports to a report logging directory, which is specified when - configuring the <c>SASL</c> application.</p> + configuring the SASL application.</p> <p>If the report browser is used offline, the reports can be copied to another directory specified when starting the browser. If no such directory - is specified, the browser reads reports from the <c>SASL</c> + is specified, the browser reads reports from the SASL <c>error_logger_mf_dir</c>.</p> <section> diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml index dae73f8b23..bd713af932 100644 --- a/lib/sasl/doc/src/notes.xml +++ b/lib/sasl/doc/src/notes.xml @@ -824,7 +824,7 @@ <list> <item> <p>Use an infinity timeout in all calls to - <c>gen_server:call()</c> in the <c>sasl</c> + <c>gen_server:call()</c> in the SASL application.</p> <p> Own Id: OTP-8506 Aux Id: seq11509 </p> diff --git a/lib/sasl/doc/src/part.xml b/lib/sasl/doc/src/part.xml index f531ed2dea..659710487e 100644 --- a/lib/sasl/doc/src/part.xml +++ b/lib/sasl/doc/src/part.xml @@ -30,7 +30,7 @@ <file>part.xml</file> </header> <description> - <p>The System Architecture Support Libraries <c>SASL</c> application + <p>The System Architecture Support Libraries SASL application provides support for alarm handling, release handling, and related functions.</p> </description> diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml index 1ce19046eb..d5df4fd345 100644 --- a/lib/sasl/doc/src/rb.xml +++ b/lib/sasl/doc/src/rb.xml @@ -38,7 +38,7 @@ <p>The Report Browser (RB) tool is used to browse and format error reports written by the error logger handler <seealso marker="stdlib:log_mf_h"><c>log_mf_h</c></seealso> - in <c>STDLIB</c>.</p> + in STDLIB.</p> </description> <funcs> @@ -62,7 +62,7 @@ reports that match that filter.</p> <p>The reports are matched using the <seealso marker="stdlib:proplists"><c>proplists</c></seealso> - module in <c>STDLIB</c>. The report must be a proplist + module in STDLIB. The report must be a proplist to be matched against any of the filters.</p> <p>If the filter has the form <c>{Key, RegExp, re}</c>, the report must contain an element with key equal to <c>Key</c> and @@ -102,7 +102,7 @@ </list> <p>For a definition of valid regular expressions and options, see the <seealso marker="stdlib:re"><c>re</c></seealso> module in - <c>STDLIB</c> and in particular function <c>re:run/3</c>.</p> + STDLIB and in particular function <c>re:run/3</c>.</p> <p>For details about data type <c>mp()</c>, see <seealso marker="stdlib:re#type-mp"><c>re:mp()</c></seealso>.</p> </desc> diff --git a/lib/sasl/doc/src/ref_man.xml b/lib/sasl/doc/src/ref_man.xml index 42045df5ec..78cf1eb26e 100644 --- a/lib/sasl/doc/src/ref_man.xml +++ b/lib/sasl/doc/src/ref_man.xml @@ -30,7 +30,7 @@ <file>application.xml</file> </header> <description> - <p>The <c>SASL</c> application provides support for alarm handling, + <p>The SASL application provides support for alarm handling, release handling, and related functions.</p> </description> <xi:include href="sasl_app.xml"/> diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml index d6558c06b4..9356b2cd47 100644 --- a/lib/sasl/doc/src/rel.xml +++ b/lib/sasl/doc/src/rel.xml @@ -59,7 +59,7 @@ <tag><c>Vsn = string()</c></tag> <item><p>Release version.</p></item> <tag><c>EVsn = string()</c></tag> - <item><p><c>ERTS</c> version the release is intended for.</p></item> + <item><p>ERTS version the release is intended for.</p></item> <tag><c>Application = atom()</c></tag> <item><p>Name of an application included in the release.</p></item> <tag><c>AppVsn = string()</c></tag> @@ -82,8 +82,8 @@ to the same value as in the application resource file.</p></item> </taglist> <note> - <p>The list of applications must contain the <c>Kernel</c> and - <c>STDLIB</c> applications.</p> + <p>The list of applications must contain the Kernel and + STDLIB applications.</p> </note> </section> diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml index bcbc5f5339..8f073807fb 100644 --- a/lib/sasl/doc/src/release_handler.xml +++ b/lib/sasl/doc/src/release_handler.xml @@ -31,7 +31,7 @@ <module>release_handler</module> <modulesummary>Unpacking and Installation of Release Packages</modulesummary> <description> - <p>The <em>release handler</em> process belongs to the <c>SASL</c> + <p>The <em>release handler</em> process belongs to the SASL application, which is responsible for <em>release handling</em>, that is, unpacking, installation, and removal of release packages.</p> <p>An introduction to release handling and an example is provided in @@ -44,7 +44,7 @@ directory of the previous version of the release, where <c>$ROOT</c> is the installation root directory, <seealso marker="kernel:code#root_dir/0"><c>code:root_dir()</c></seealso>. - Another <c>releases</c> directory can be specified using the <c>SASL</c> + Another <c>releases</c> directory can be specified using the SASL configuration parameter <c>releases_dir</c> or the OS environment variable <c>RELDIR</c>. The release handler must have write access to this directory to install the new release. @@ -56,7 +56,7 @@ <item>A boot script, <c>Name.boot</c></item> </list> <p>The <c>.rel</c> file contains information about the release: its name, - version, and which <c>ERTS</c> and application versions it uses.</p> + version, and which ERTS and application versions it uses.</p> <p>A release package can also contain:</p> <list type="bulleted"> <item>A release upgrade file, <c>relup</c></item> @@ -115,7 +115,7 @@ <em>System Documentation</em>. In this case, the system configuration file <c>sys.config</c> is mandatory.</p> <p>The installation of a new release can restart the system. Which - program to use is specified by the <c>SASL</c> configuration + program to use is specified by the SASL configuration parameter <c>start_prg</c>, which defaults to <c>$ROOT/bin/start</c>.</p> <p>The emulator restart on Windows NT expects that the system is @@ -132,7 +132,7 @@ is made permanent.</p> <p>The release handler at a node running on a diskless machine, or with a read-only file system, must be configured accordingly - using the following <c>SASL</c> configuration parameters (for + using the following SASL configuration parameters (for details, see <seealso marker="sasl_app">sasl(6)</seealso>):</p> <taglist> <tag><c>masters</c></tag> @@ -287,7 +287,7 @@ returned, the emulator is restarted before the upgrade instructions are executed. This occurs if the emulator or any of the applications - <c>Kernel</c>, <c>STDLIB</c>, or <c>SASL</c> + Kernel, STDLIB, or SASL are updated. The new emulator version and these core applications execute after the restart. For all other applications the old versions are @@ -310,13 +310,13 @@ <tag><c>code_change_timeout</c></tag> <item><p>Defines the time-out for all calls to - <seealso marker="stdlib:sys#change_code/4"><c>stdlib:sys:change_code</c></seealso>. + <seealso marker="stdlib:sys#change_code/4"><c>sys:change_code</c></seealso>. If no value is specified or <c>default</c> is specified, the default value defined in <c>sys</c> is used.</p></item> <tag><c>suspend_timeout</c></tag> <item><p>Defines the time-out for all calls to - <seealso marker="stdlib:sys#suspend/1"><c>stdlib:sys:suspend</c></seealso>. + <seealso marker="stdlib:sys#suspend/1"><c>sys:suspend</c></seealso>. If no value is specified, the values defined by the <c>Timeout</c> parameter of the <c>upgrade</c> or <c>suspend</c> instructions are used. If <c>default</c> is specified, the default value defined in @@ -342,7 +342,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). => {ok,NewVsn}</code> <p>If <c>NewVsn</c> is installed with option <c>{update_paths,true}</c>, then - <seealso marker="kernel:code#lib_dir/1"><c>kernel:code:lib_dir(myapp)</c></seealso> + <seealso marker="kernel:code#lib_dir/1"><c>code:lib_dir(myapp)</c></seealso> returns <c>/home/user/myapp-1.0</c>.</p></item> </taglist> <note> @@ -801,7 +801,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). is an atom named from the Posix error codes, such as <c>enoent</c>, <c>eacces</c>, or <c>eisdir</c>. See <seealso marker="kernel:file"><c>file(3)</c></seealso> - in <c>Kernel</c>.</p></item> + in Kernel.</p></item> <tag><c>Posix</c></tag> <item><p>Some file operation failed, as for the previous item in the list.</p></item> diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml index 52f449c2a8..0576397f9b 100644 --- a/lib/sasl/doc/src/sasl_app.xml +++ b/lib/sasl/doc/src/sasl_app.xml @@ -31,17 +31,17 @@ <app>sasl</app> <appsummary>The SASL application</appsummary> <description> - <p>The <c>SASL</c> application provides the following services:</p> + <p>The SASL application provides the following services:</p> <list type="bulleted"> <item><c>alarm_handler</c></item> <item><c>rb</c></item> <item><c>release_handler</c></item> <item><c>systools</c></item> </list> - <p>The <c>SASL</c> application also includes <c>error_logger</c> event - handlers for formatting <c>SASL</c> error and crash reports.</p> + <p>The SASL application also includes <c>error_logger</c> event + handlers for formatting SASL error and crash reports.</p> <note> - <p>The <c>SASL</c> application in OTP has nothing to do with + <p>The SASL application in OTP has nothing to do with "Simple Authentication and Security Layer" (RFC 4422).</p> </note> </description> @@ -49,7 +49,7 @@ <section> <title>Error Logger Event Handlers</title> <p>The following error logger event handlers are used by - the <c>SASL</c> application.</p> + the SASL application.</p> <taglist> <tag><c>sasl_report_tty_h</c></tag> <item> @@ -57,7 +57,7 @@ reports</em>, and <em>progress reports</em> to <c>stdio</c>. This error logger event handler uses <seealso marker="kernel:kernel_app#error_logger_format_depth">error_logger_format_depth</seealso> - in the <c>Kernel</c> application to limit how much detail is + in the Kernel application to limit how much detail is printed in crash and supervisor reports.</p> </item> <tag><c>sasl_report_file_h</c></tag> @@ -66,7 +66,7 @@ report</em>, and <em>progress report</em> to a single file. This error logger event handler uses <seealso marker="kernel:kernel_app#error_logger_format_depth">error_logger_format_depth</seealso> - in the <c>Kernel</c> application to limit the details + in the Kernel application to limit the details printed in crash and supervisor reports.</p> </item> <tag><c>log_mf_h</c></tag> @@ -75,9 +75,9 @@ error logger to disk. Multiple files and log rotation are used. For efficiency reasons, each event is written as a binary. For more information about this handler, - see <seealso marker="stdlib:log_mf_h">the <c>STDLIB</c> Reference + see <seealso marker="stdlib:log_mf_h">the STDLIB Reference Manual</seealso>.</p> - <p>To activate this event handler, three <c>SASL</c> + <p>To activate this event handler, three SASL configuration parameters must be set, <c>error_logger_mf_dir</c>, <c>error_logger_mf_maxbytes</c>, and <c>error_logger_mf_maxfiles</c>. The next section provides @@ -88,9 +88,9 @@ <section> <title>Configuration</title> - <p>The following configuration parameters are defined for the <c>SASL</c> + <p>The following configuration parameters are defined for the SASL application. For more information about configuration parameters, see - <seealso marker="kernel:app"><c>app(4)</c></seealso> in <c>Kernel</c>.</p> + <seealso marker="kernel:app"><c>app(4)</c></seealso> in Kernel.</p> <p>All configuration parameters are optional.</p> <taglist> <tag><c><![CDATA[sasl_error_logger = Value ]]></c></tag> @@ -112,7 +112,7 @@ Use <c>[append]</c> to have the <c>FileName</c> open in append mode. <c>FileName</c> is a string.</p></item> <tag><c>false</c></tag> - <item><p>No <c>SASL</c> error logger handler is installed.</p></item> + <item><p>No SASL error logger handler is installed.</p></item> </taglist> </item> <tag><c><![CDATA[errlog_type = error | progress | all ]]></c></tag> diff --git a/lib/sasl/doc/src/sasl_intro.xml b/lib/sasl/doc/src/sasl_intro.xml index b71dafb192..f74a7c1db8 100644 --- a/lib/sasl/doc/src/sasl_intro.xml +++ b/lib/sasl/doc/src/sasl_intro.xml @@ -32,7 +32,7 @@ <section> <title>Scope</title> - <p>The <c>SASL</c> application provides support for:</p> + <p>The SASL application provides support for:</p> <list type="bulleted"> <item>Error logging</item> <item>Alarm handling</item> diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml index 7446762de4..fa503fa573 100644 --- a/lib/sasl/doc/src/systools.xml +++ b/lib/sasl/doc/src/systools.xml @@ -85,7 +85,7 @@ </item> <item> <p>If the emulator needs to be restarted after upgrading or - downgrading, that is, if the <c>ERTS</c> version differs + downgrading, that is, if the ERTS version differs between <c>Name.rel</c> and <c>Name2.rel</c></p> </item> </list> @@ -201,10 +201,10 @@ between the applications. Where there are no dependencies, the order in the <c>.rel</c> file is kept.</p> <p>The function fails if the mandatory - applications <c>Kernel</c> and <c>STDLIB</c> are not + applications Kernel and STDLIB are not included in the <c>.rel</c> file and have start type <c>permanent</c> (which is default).</p> - <p>If <c>SASL</c> is not included as an application in + <p>If SASL is not included as an application in the <c>.rel</c> file, a warning is issued because such a release cannot be used in an upgrade. To turn off this warning, add option <c>no_warn_sasl</c>.</p> diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src index 4ee8a7d6c8..633cdfa070 100644 --- a/lib/sasl/src/sasl.app.src +++ b/lib/sasl/src/sasl.app.src @@ -46,5 +46,5 @@ {errlog_type, all}]}, {mod, {sasl, []}}, {runtime_dependencies, ["tools-2.6.14","stdlib-3.0","kernel-5.0", - "erts-8.0"]}]}. + "erts-8.1"]}]}. diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 352e4984df..efe6cc9eb4 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1491,8 +1491,9 @@ mandatory_modules() -> preloaded() -> %% Sorted [erl_prim_loader,erl_tracer,erlang, - erts_code_purger, - erts_internal,init,otp_ring0,prim_eval,prim_file, + erts_code_purger,erts_dirty_process_code_checker, + erts_internal,erts_literal_area_collector, + init,otp_ring0,prim_eval,prim_file, prim_inet,prim_zip,zlib]. %%______________________________________________________________________ diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index ad58bb6b7b..10d2539b7f 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -1063,6 +1063,12 @@ otp_9395_check_and_purge(cleanup,_Conf) -> %% OTP-9395 - performance problems when there are MANY processes %% Upgrade which updates many modules (brutal_purge) otp_9395_update_many_mods(Conf) when is_list(Conf) -> + + %% "nain" is very slow - it fails this test quite often due to a + %% long sys call + %% /proc/cpuinfo: "clock: 1249MHz" + inet:gethostname() == {ok,"nain"} andalso throw({skip,"slow test host"}), + %% Set some paths PrivDir = priv_dir(Conf), Dir = filename:join(PrivDir,"otp_9395_update_many_mods"), @@ -1162,6 +1168,12 @@ otp_9395_update_many_mods(cleanup,_Conf) -> %% OTP-9395 - performance problems when there are MANY processes %% Upgrade which removes many modules (brutal_purge) otp_9395_rm_many_mods(Conf) when is_list(Conf) -> + + %% "nain" is very slow - it fails this test quite often due to a + %% long sys call + %% /proc/cpuinfo: "clock: 1249MHz" + inet:gethostname() == {ok,"nain"} andalso throw({skip,"slow test host"}), + %% Set some paths PrivDir = priv_dir(Conf), Dir = filename:join(PrivDir,"otp_9395_rm_many_mods"), @@ -1920,7 +1932,7 @@ wait_nodes_up(Nodes, Tag) -> wait_nodes_up(Nodes0, Tag, Apps) -> ?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]), Nodes = fix_nodes(Nodes0), - wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30). + wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 60). fix_nodes([{Node,InitPid}|Nodes]) -> [{Node,InitPid} | fix_nodes(Nodes)]; @@ -1962,7 +1974,7 @@ wait_nodes_up(Nodes, Tag, Apps, N) -> ?t:format("",[]), ok; _ -> - timer:sleep(1000), + timer:sleep(2000), wait_nodes_up(Pang, Tag, Apps, N-1) end. diff --git a/lib/sasl/test/rh_test_lib.erl b/lib/sasl/test/rh_test_lib.erl index 11935496d8..dacd8b6b9f 100644 --- a/lib/sasl/test/rh_test_lib.erl +++ b/lib/sasl/test/rh_test_lib.erl @@ -18,7 +18,7 @@ cmd(Cmd,Args,Env) -> case open_port({spawn_executable, Cmd}, [{args,Args},{env,Env}]) of Port when is_port(Port) -> unlink(Port), - erlang:port_close(Port), + catch erlang:port_close(Port), % migth already be closed, so catching ok; Error -> Error diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index 94325a8ed6..fc5116dac9 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -154,7 +154,7 @@ do_write_agent_conf(Fd, {intAgentMaxPacketSize = Tag, Val} ) -> do_write_agent_conf(Fd, {snmpEngineMaxMessageSize = Tag, Val} ) -> io:format(Fd, "{~w, ~w}.~n", [Tag, Val]); do_write_agent_conf(Fd, {snmpEngineID = Tag, Val} ) -> - io:format(Fd, "{~w, \"~s\"}.~n", [Tag, Val]); + io:format(Fd, "{~w, ~p}.~n", [Tag, Val]); do_write_agent_conf(_Fd, Crap) -> error({bad_agent_config, Crap}). @@ -758,9 +758,9 @@ do_write_usm_conf( PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}) -> io:format(Fd, "{", []), - io:format(Fd, "\"~s\", ", [EngineID]), - io:format(Fd, "\"~s\", ", [UserName]), - io:format(Fd, "\"~s\", ", [SecName]), + io:format(Fd, "~p, ", [EngineID]), + io:format(Fd, "~p, ", [UserName]), + io:format(Fd, "~p, ", [SecName]), io:format(Fd, "~w, ", [Clone]), io:format(Fd, "~w, ", [AuthP]), do_write_usm2(Fd, AuthKeyC, ", "), @@ -859,15 +859,15 @@ do_write_vacm_conf( {vacmSecurityToGroup, SecModel, SecName, GroupName}) -> io:format( - Fd, "{vacmSecurityToGroup, ~w, \"~s\", \"~s\"}.~n", + Fd, "{vacmSecurityToGroup, ~w, ~p, ~p}.~n", [SecModel, SecName, GroupName]); do_write_vacm_conf( Fd, {vacmAccess, GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}) -> io:format( - Fd, "{vacmAccess, \"~s\", \"~s\", ~w, ~w, ~w, " - "\"~s\", \"~s\", \"~s\"}.~n", + Fd, "{vacmAccess, ~p, ~p, ~w, ~w, ~w, " + "~p, ~p, ~p}.~n", [GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV]); do_write_vacm_conf( @@ -875,7 +875,7 @@ do_write_vacm_conf( {vacmViewTreeFamily, ViewIndex, ViewSubtree, ViewStatus, ViewMask}) -> io:format( - Fd, "{vacmViewTreeFamily, \"~s\", ~w, ~w, ~w}.~n", + Fd, "{vacmViewTreeFamily, ~p, ~w, ~w, ~w}.~n", [ViewIndex, ViewSubtree, ViewStatus, ViewMask]); do_write_vacm_conf(_Fd, Crap) -> error({bad_vacm_config, Crap}). diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index b593e9ea84..d25f66f44a 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -23,12 +23,12 @@ {vsn, "%VSN%"}, {modules, [ %% Compiler modules (not in the runtime part of the app) -% snmpc, -% snmpc_lib, -% snmpc_mib_gram, -% snmpc_mib_to_hrl, -% snmpc_misc, -% snmpc_tok, + snmpc, + snmpc_lib, + snmpc_mib_gram, + snmpc_mib_to_hrl, + snmpc_misc, + snmpc_tok, %% Application modules snmp, diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl index db1f9ee61b..d86692aaf6 100644 --- a/lib/snmp/src/compile/snmpc.erl +++ b/lib/snmp/src/compile/snmpc.erl @@ -64,7 +64,7 @@ compile(Input, _Output, Options) -> {ok, _} -> ok; {error, Reason} -> - io:format("~p", [Reason]), + io:format("~tp", [Reason]), error end. @@ -126,7 +126,14 @@ compile(FileName) -> %%---------------------------------------------------------------------- compile(FileName, Options) when is_list(FileName) -> - true = snmpc_misc:is_string(FileName), + case snmpc_misc:check_file(FileName) of + true -> + compile_1(FileName, Options); + false -> + {error, {invalid_file, FileName}} + end. + +compile_1(FileName, Options) -> DefOpts = [{deprecated, true}, {group_check, true}, {i, ["./"]}, diff --git a/lib/snmp/src/compile/snmpc_misc.erl b/lib/snmp/src/compile/snmpc_misc.erl index 933d629746..312074f2e7 100644 --- a/lib/snmp/src/compile/snmpc_misc.erl +++ b/lib/snmp/src/compile/snmpc_misc.erl @@ -29,7 +29,7 @@ bits_to_int/2, ensure_trailing_dir_delimiter/1, foreach/3, - is_string/1, + check_file/1, read_mib/1, read_noexit/2, strip_extension_from_filename/2, @@ -86,21 +86,21 @@ to_upper([C|Cs]) -> [C|to_upper(Cs)]; to_upper([]) -> []. -is_string([]) -> true; -is_string([Tkn | Str]) - when is_integer(Tkn) andalso (Tkn >= 0) andalso (Tkn =< 255) -> - is_string(Str); -is_string(_) -> false. - - +check_file(FileName) -> + case filename:extension(FileName) of + ".mib" -> + filelib:is_regular(FileName); + _ -> + filelib:is_regular(FileName ++ ".mib") + end. + + foreach(Function, ExtraArgs, [H | T]) -> apply(Function, [H | ExtraArgs]), foreach(Function, ExtraArgs, T); foreach(_Function, _ExtraArgs, []) -> true. - - %%---------------------------------------------------------------------- %% Returns: {ok, Mib}|{error, Reason} %% The reason for having the function if this module is: diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk index 87539f88f7..0f54e67c65 100644 --- a/lib/snmp/test/modules.mk +++ b/lib/snmp/test/modules.mk @@ -31,6 +31,7 @@ SUITE_MODULES = \ snmp_agent_mibs_test \ snmp_agent_nfilter_test \ snmp_agent_test \ + snmp_agent_conf_test \ snmp_agent_test_lib \ snmp_manager_config_test \ snmp_manager_user \ diff --git a/lib/snmp/test/snmp_SUITE.erl b/lib/snmp/test/snmp_SUITE.erl index 3b9219739b..05bd86253b 100644 --- a/lib/snmp/test/snmp_SUITE.erl +++ b/lib/snmp/test/snmp_SUITE.erl @@ -81,7 +81,8 @@ groups() -> {group, note_store_test}]}, {agent, [], [{group, mibs_test}, {group, nfilter_test}, - {group, agent_test}, + {group, agent_test}, + {group, agent_conf_test}, {group, snmpnet_test}]}, {manager, [], [{group, manager_config_test}, {group, manager_user_test}, @@ -97,6 +98,7 @@ groups() -> {mibs_test, [], [{snmp_agent_mibs_test, all}]}, {nfilter_test, [], [{snmp_agent_nfilter_test, all}]}, {agent_test, [], [{snmp_agent_test, all}]}, + {agent_conf_test, [], [{snmp_agent_conf_test, all}]}, {snmpnet_test, [], [{snmp_to_snmpnet_SUITE, all}]}, {manager_config_test, [], [{snmp_manager_config_test, all}]}, {manager_user_test, [], [{snmp_manager_user_test, all}]}, diff --git a/lib/snmp/test/snmp_agent_conf_test.erl b/lib/snmp/test/snmp_agent_conf_test.erl new file mode 100644 index 0000000000..0a22bd47d1 --- /dev/null +++ b/lib/snmp/test/snmp_agent_conf_test.erl @@ -0,0 +1,210 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(snmp_agent_conf_test). + +%%---------------------------------------------------------------------- +%% Include files +%%---------------------------------------------------------------------- + +%-include_lib("test_server/include/test_server.hrl"). +%-include("snmp_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-export([ + all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + + check_agent/1, + check_usm/1, + check_vacm/1 + ]). + + +all() -> [ + check_agent, + check_usm, + check_vacm + ]. + + +groups() -> + []. + + +init_per_suite(Config) -> + PrivDir = ?config(priv_dir, Config), + PrivSubdir = filename:join(PrivDir, "snmp_agent_conf_test"), + ok = filelib:ensure_dir(filename:join(PrivSubdir, "dummy")), + [{priv_subdir, PrivSubdir} | Config]. + +end_per_suite(_Config) -> + ok. + +%%====================================================================== +%% Test data +%%====================================================================== + +engine_ids() -> [ + "plain eid", + "here\"eid", + "comes\neid", + "trouble\0eid", + binary_to_list(<<"ä¸å›½å¼•æ“Žæ ‡è¯†ç¬¦"/utf8>>) +]. + +snmp_admin_strings() -> [ + "plain string", + "heres\"eid", + "trouble\neid", + binary_to_list(<<"ä¸å›½å¼•æ“Žæ ‡è¯†ç¬¦"/utf8>>) +]. + + +%%====================================================================== +%% Test functions +%%====================================================================== + + +check_agent(Config) -> + Dir = ?config(priv_subdir, Config), + lists:foreach( + fun(EngineId) -> check_agent_by_engineid(Dir, EngineId) end, + engine_ids() + ), + ok. + +check_agent_by_engineid(Dir, EngineId) -> + WEntries = [ + snmpa_conf:agent_entry(intAgentIpAddress, {0,0,0,0}), + snmpa_conf:agent_entry(intAgentUDPPort, 161), + snmpa_conf:agent_entry(snmpEngineMaxMessageSize, 484), + snmpa_conf:agent_entry(snmpEngineID, EngineId) + ], + + ok = snmpa_conf:write_agent_config(Dir, WEntries), + {ok, REntries} = snmpa_conf:read_agent_config(Dir), + + true = is_subset(WEntries, REntries), + ok. + +%%====================================================================== + +check_usm(Config) -> + Dir = ?config(priv_subdir, Config), + EngineId = hd(engine_ids()), + UserName = hd(snmp_admin_strings()), + SecName = hd(snmp_admin_strings()), + + %% vary engine id + lists:foreach( + fun(EngineId_) -> check_usm_by_params(Dir, EngineId_, UserName, SecName) end, + engine_ids() + ), + + %% vary user name + lists:foreach( + fun(UserName_) -> check_usm_by_params(Dir, EngineId, UserName_, SecName) end, + snmp_admin_strings() + ), + + %% vary sec name + lists:foreach( + fun(SecName_) -> check_usm_by_params(Dir, EngineId, UserName, SecName_) end, + snmp_admin_strings() + ), + + ok. + +check_usm_by_params(Dir, EngineId, UserName, SecName) -> + WEntries = [ + snmpa_conf:usm_entry( + EngineId, + UserName, + SecName, + zeroDotZero, + usmNoAuthProtocol, % authproto + "", "", + usmNoPrivProtocol, % privproto + "", "", "", + [], %AuthKey + []) %PrivKey + ], + + ok = snmpa_conf:write_usm_config(Dir, WEntries), + {ok, REntries} = snmpa_conf:read_usm_config(Dir), + + true = is_subset(WEntries, REntries), + ok. + +%%====================================================================== + +check_vacm(Config) -> + Dir = ?config(priv_subdir, Config), + + %% vary sec name + lists:foreach( + fun(SecName_) -> check_vacm_by_params(Dir, SecName_) end, + snmp_admin_strings() + ), + + ok. + + +check_vacm_by_params(Dir, SecName) -> + WEntries = [ + %% SecModel, SecName, GroupName + snmpa_conf:vacm_s2g_entry(usm, SecName, SecName), + %% GroupName,Prefix,SecModel,SecLevel,Match,ReadView,WriteView,NotifyView + snmpa_conf:vacm_acc_entry(SecName, "", any, noAuthNoPriv, exact, "all", "all", "all") + ], + + ok = snmpa_conf:write_vacm_config(Dir, WEntries), + {ok, REntries} = snmpa_conf:read_vacm_config(Dir), + + true = is_subset(WEntries, REntries), + ok. + + + +%%====================================================================== + + +%% additional tests needed: +% check_context() +% check_community() +% check_standard() +% check_target_addr() +% check_target_params() +% check_notify() + + +%%====================================================================== +%% Local utility functions +%%====================================================================== + +is_subset(List1, List2) -> + io:format("Check ~p is subset of ~p\n", [List1, List2]), + sets:is_subset( + sets:from_list(List1), + sets:from_list(List2) + ). diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 8ae495bb1b..3c1a6f2afd 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -647,22 +647,22 @@ init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). init_per_group_ipv6(GroupName, Config, Init) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), - Init( - snmp_test_lib:init_group_top_dir( - GroupName, - [{ipfamily, inet6}, - {ip, ?LOCALHOST(inet6)} - | lists:keydelete(ip, 1, Config)])); - {error, _} -> - {skip, "Host seems to not support IPv6"} - end; + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> + Init( + snmp_test_lib:init_group_top_dir( + GroupName, + [{ipfamily, inet6}, + {ip, ?LOCALHOST(inet6)} + | lists:keydelete(ip, 1, Config)])); + false -> + {skip, "Host does not support IPV6"} + end; _ -> - {skip, "Host does not support IPV6"} + {skip, "Test config ipv6_hosts is missing"} end. end_per_group(all_tcs, Config) -> diff --git a/lib/snmp/test/snmp_app_test.erl b/lib/snmp/test/snmp_app_test.erl index 6e7e85d3b4..5e69866f9a 100644 --- a/lib/snmp/test/snmp_app_test.erl +++ b/lib/snmp/test/snmp_app_test.erl @@ -23,366 +23,29 @@ %%---------------------------------------------------------------------- -module(snmp_app_test). --export([ - all/0, groups/0, - init_per_group/2, end_per_group/2, - init_per_suite/1, end_per_suite/1, - init_per_testcase/2, end_per_testcase/2, +%% Note: This directive should only be used in test suites. +-compile(export_all). - fields/1, - modules/1, - exportall/1, - app_depend/1, - - start_and_stop_empty/1, - start_and_stop_with_agent/1, - start_and_stop_with_manager/1, - start_and_stop_with_agent_and_manager/1, - start_epmty_and_then_agent_and_manager_and_stop/1, - start_with_agent_and_then_manager_and_stop/1, - start_with_manager_and_then_agent_and_stop/1 - ]). - - --include_lib("kernel/include/file.hrl"). -include_lib("common_test/include/ct.hrl"). --include("snmp_test_lib.hrl"). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- all() -> - Cases = - [ - fields, - modules, - exportall, - app_depend, - {group, start_and_stop} - ], - Cases. - -groups() -> - [{start_and_stop, [], - [start_and_stop_empty, - start_and_stop_with_agent, - start_and_stop_with_manager, - start_and_stop_with_agent_and_manager, - start_epmty_and_then_agent_and_manager_and_stop, - start_with_agent_and_then_manager_and_stop, - start_with_manager_and_then_agent_and_stop]}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_suite(Config) when is_list(Config) -> - ?DISPLAY_SUITE_INFO(), - - %% Note that part of this stuff (the suite top dir creation) - %% may already be done (if we run the entire snmp suite). - - PrivDir = ?config(priv_dir, Config), - TopDir = filename:join(PrivDir, app), - case file:make_dir(TopDir) of - ok -> - ok; - {error, eexist} -> - ok; - Error -> - fail({failed_creating_subsuite_top_dir, Error}) - end, - AppFile = - case is_app() of - {ok, File} -> - io:format("File: ~n~p~n", [File]), - snmp:print_version_info(), - File; - {error, Reason} -> - fail(Reason) - end, - [{app_topdir, TopDir}, {app_file, AppFile} | Config]. - - -is_app() -> - is_app(?APPLICATION). - -is_app(App) -> - LibDir = code:lib_dir(App), - File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]), - case file:consult(File) of - {ok, [{application, App, AppFile}]} -> - {ok, AppFile}; - Error -> - {error, {invalid_format, Error}} - end. - -end_per_suite(suite) -> []; -end_per_suite(doc) -> []; -end_per_suite(Config) when is_list(Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% Test server callbacks -init_per_testcase(_Case, Config) -> - Config. - -end_per_testcase(_Case, Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -fields(suite) -> - []; -fields(doc) -> - []; -fields(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Fields = [vsn, description, modules, registered, applications], - case check_fields(Fields, AppFile, []) of - [] -> - ok; - Missing -> - fail({missing_fields, Missing}) - end. - -check_fields([], _AppFile, Missing) -> - Missing; -check_fields([Field|Fields], AppFile, Missing) -> - check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)). - -check_field(Name, AppFile, Missing) -> - io:format("checking field: ~p~n", [Name]), - case lists:keymember(Name, 1, AppFile) of - true -> - Missing; - false -> - [Name|Missing] - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -modules(suite) -> - []; -modules(doc) -> - []; -modules(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Mods = key1search(modules, AppFile), - EbinList = get_ebin_mods(snmp), - case missing_modules(Mods, EbinList, []) of - [] -> - ok; - Missing -> - fail({missing_modules, Missing}) - end, - Allowed = [snmpc, - snmpc_lib, - snmpc_misc, - snmpc_mib_gram, - snmpc_mib_to_hrl, - snmpc_tok], - case extra_modules(Mods, EbinList, Allowed, []) of - [] -> - ok; - Extra -> - fail({extra_modules, Extra}) - end, - {ok, Mods}. - -get_ebin_mods(App) -> - LibDir = code:lib_dir(App), - EbinDir = filename:join([LibDir,"ebin"]), - {ok, Files0} = file:list_dir(EbinDir), - Files1 = [lists:reverse(File) || File <- Files0], - [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1]. - - -missing_modules([], _Ebins, Missing) -> - Missing; -missing_modules([Mod|Mods], Ebins, Missing) -> - case lists:member(Mod, Ebins) of - true -> - missing_modules(Mods, Ebins, Missing); - false -> - io:format("missing module: ~p~n", [Mod]), - missing_modules(Mods, Ebins, [Mod|Missing]) - end. - - -extra_modules(_Mods, [], Allowed, Extra) -> - Extra--Allowed; -extra_modules(Mods, [Mod|Ebins], Allowed, Extra) -> - case lists:member(Mod, Mods) of - true -> - extra_modules(Mods, Ebins, Allowed, Extra); - false -> - io:format("superfluous module: ~p~n", [Mod]), - extra_modules(Mods, Ebins, Allowed, [Mod|Extra]) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -exportall(suite) -> - []; -exportall(doc) -> - []; -exportall(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Mods = key1search(modules, AppFile), - check_export_all(Mods). - - -check_export_all([]) -> - ok; -check_export_all([Mod|Mods]) -> - case (catch apply(Mod, module_info, [compile])) of - {'EXIT', {undef, _}} -> - check_export_all(Mods); - O -> - case lists:keysearch(options, 1, O) of - false -> - check_export_all(Mods); - {value, {options, List}} -> - case lists:member(export_all, List) of - true -> - fail({export_all, Mod}); - false -> - check_export_all(Mods) - end - end - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -app_depend(suite) -> - []; -app_depend(doc) -> - []; -app_depend(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Apps = key1search(applications, AppFile), - check_apps(Apps). - - -check_apps([]) -> - ok; -check_apps([App|Apps]) -> - case is_app(App) of - {ok, _} -> - check_apps(Apps); - Error -> - throw({error, {missing_app, {App, Error}}}) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_and_stop_empty(suite) -> - []; -start_and_stop_empty(doc) -> - ["Start and stop the application empty (no configured components)"]; -start_and_stop_empty(Config) when is_list(Config) -> - ?line false = ?IS_SNMP_RUNNING(), - - ?line ok = snmp:start(), - - ?line true = ?IS_SNMP_RUNNING(), - - ?line ok = snmp:stop(), - - ?line false = ?IS_SNMP_RUNNING(), - - ok. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_and_stop_with_agent(suite) -> - []; -start_and_stop_with_agent(doc) -> - ["Start and stop the application with the agent pre-configured"]; -start_and_stop_with_agent(Config) when is_list(Config) -> - ?SKIP(not_implemented_yet). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_and_stop_with_manager(suite) -> - []; -start_and_stop_with_manager(doc) -> - ["Start and stop the application with the manager pre-configured"]; -start_and_stop_with_manager(Config) when is_list(Config) -> - ?SKIP(not_implemented_yet). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_and_stop_with_agent_and_manager(suite) -> - []; -start_and_stop_with_agent_and_manager(doc) -> - ["Start and stop the application with both the agent " - "and the manager pre-configured"]; -start_and_stop_with_agent_and_manager(Config) when is_list(Config) -> - ?SKIP(not_implemented_yet). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_epmty_and_then_agent_and_manager_and_stop(suite) -> - []; -start_epmty_and_then_agent_and_manager_and_stop(doc) -> - ["Start the application empty, then start the agent and then " - "the manager and then stop the application"]; -start_epmty_and_then_agent_and_manager_and_stop(Config) when is_list(Config) -> - ?SKIP(not_implemented_yet). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_with_agent_and_then_manager_and_stop(suite) -> - []; -start_with_agent_and_then_manager_and_stop(doc) -> - ["Start the application with the agent pre-configured, " - "then start the manager and then stop the application"]; -start_with_agent_and_then_manager_and_stop(Config) when is_list(Config) -> - ?SKIP(not_implemented_yet). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -start_with_manager_and_then_agent_and_stop(suite) -> - []; -start_with_manager_and_then_agent_and_stop(doc) -> - ["Start the application with the manager pre-configured, " - "then start the agent and then stop the application"]; -start_with_manager_and_then_agent_and_stop(Config) when is_list(Config) -> - ?SKIP(not_implemented_yet). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. + [ + app, + appup + ]. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +app() -> + [{doc, "Test that the snmp app file is ok"}]. +app(Config) when is_list(Config) -> + ok = test_server:app_test(snmp). +%%-------------------------------------------------------------------- +appup() -> + [{doc, "Test that the snmp appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = test_server:appup_test(snmp). diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index d17882e765..71f4017d8b 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -583,38 +583,38 @@ init_per_group(event_tests_mt = GroupName, Config) -> GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6_mt = GroupName, Config) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> ipv6_init( snmp_test_lib:init_group_top_dir( GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config])); - {error, _} -> - {skip, "Host seems to not support IPv6"} + false -> + {skip, "Host does not support IPv6"} end; _ -> - {skip, "Host does not support IPV6"} + {skip, "Test config ipv6_hosts is missing"} end; init_per_group(ipv6 = GroupName, Config) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); - {error, _} -> - {skip, "Host seems to not support IPv6"} + false -> + {skip, "Host does not support IPv6"} end; _ -> - {skip, "Host does not support IPV6"} + {skip, "Test config ipv6_hosts is missing"} end; init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). - + end_per_group(_GroupName, Config) -> %% Do we really need to do this? lists:keydelete(snmp_group_top_dir, 1, Config). diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl index ac9e37bc8b..24c14d86ea 100644 --- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl +++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl @@ -121,14 +121,14 @@ init_per_group(_, Config) -> Config. init_per_group_ipv6(Families, Config) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> init_per_group_ip(Families, Config); - {error, _} -> - {skip, "Host seems to not support IPv6"} + false -> + {skip, "Host does not support IPv6"} end; _ -> {skip, "Test config ipv6_hosts is missing"} diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index fd15c334a3..f9d11b2a60 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,30 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SSH client does not any longer retry a bad password given + as option to ssh:connect et al.</p> + <p> + Own Id: OTP-13674 Aux Id: TR-HU92273 </p> + </item> + <item> + <p> + Removed possible hanging risk for a certain timing + sequence when communicating client and server executes on + the same node.</p> + <p> + Own Id: OTP-13715</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.3</title> <section><title>Improvements and New Features</title> @@ -108,6 +132,22 @@ </section> +<section><title>Ssh 4.2.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SSH client does not any longer retry a bad password given + as option to ssh:connect et al.</p> + <p> + Own Id: OTP-13674 Aux Id: TR-HU92273 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.2.2</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 e6c54d27bf..ef9f7cbd9b 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2015</year> + <year>2004</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -756,7 +756,7 @@ <p>Utility function that starts the applications <c>crypto</c>, <c>public_key</c>, and <c>ssh</c>. Default type is <c>temporary</c>. For more information, see the <seealso marker="kernel:application">application(3)</seealso> - manual page in <c>kernel</c>.</p> + manual page in Kernel.</p> </desc> </func> @@ -769,7 +769,7 @@ <desc> <p>Stops the <c>ssh</c> application. For more information, see the <seealso marker="kernel:application">application(3)</seealso> - manual page in <c>kernel</c>.</p> + manual page in Kernel.</p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml index f6ce44c015..5cc4c24889 100644 --- a/lib/ssh/doc/src/ssh_app.xml +++ b/lib/ssh/doc/src/ssh_app.xml @@ -4,7 +4,7 @@ <appref> <header> <copyright> - <year>2012</year><year>2015</year> + <year>2012</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -203,7 +203,7 @@ <section> <title>Unicode support</title> <p>Unicode filenames are supported 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> for information about this subject. + <seealso marker="kernel:file">file</seealso> manual page in Kernel for information about this subject. </p> <p>The shell and the cli both support unicode. </p> diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml index 907b0b3bec..7b598494f7 100644 --- a/lib/ssh/doc/src/ssh_channel.xml +++ b/lib/ssh/doc/src/ssh_channel.xml @@ -139,7 +139,7 @@ enters the <c>ssh_channel</c> process receive loop and become an <c>ssh_channel process</c>. The process must have been started using one of the start functions in <c>proc_lib</c>, see the <seealso - marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in <c>stdlib</c>. + marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in STDLIB. The user is responsible for any initialization of the process and must call <seealso marker = "#init-1">ssh_channel:init/1</seealso>. </p> diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 3245ba5197..76b7d8cd55 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -45,7 +45,7 @@ "erts-6.0", "kernel-3.0", "public_key-1.1", - "stdlib-3.0" + "stdlib-3.1" ]}]}. diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 0570853a9b..1d7be3547b 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -52,16 +52,15 @@ %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> - application:start(crypto), - application:start(asn1), - application:start(public_key), - application:start(ssh). + start(temporary). start(Type) -> - application:start(crypto, Type), - application:start(asn1), - application:start(public_key, Type), - application:start(ssh, Type). + case application:ensure_all_started(ssh, Type) of + {ok, _} -> + ok; + Other -> + Other + end. %%-------------------------------------------------------------------- -spec stop() -> ok | {error, term()}. @@ -90,7 +89,7 @@ connect(Socket, Options, Timeout) when is_port(Socket) -> {error, Error}; {_SocketOptions, SshOptions} -> case valid_socket_to_use(Socket, Options) of - ok -> + ok -> {ok, {Host,_Port}} = inet:sockname(Socket), Opts = [{user_pid,self()}, {host,fmt_host(Host)} | SshOptions], ssh_connection_handler:start_connection(client, Socket, Opts, Timeout); @@ -128,23 +127,23 @@ connect(Host, Port, Options, Timeout) -> -spec close(pid()) -> ok. %% %% Description: Closes an ssh connection. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- close(ConnectionRef) -> ssh_connection_handler:stop(ConnectionRef). %%-------------------------------------------------------------------- -spec connection_info(pid(), [atom()]) -> [{atom(), term()}]. %% -%% Description: Retrieves information about a connection. -%%-------------------------------------------------------------------- +%% Description: Retrieves information about a connection. +%%-------------------------------------------------------------------- connection_info(ConnectionRef, Options) -> ssh_connection_handler:connection_info(ConnectionRef, Options). %%-------------------------------------------------------------------- -spec channel_info(pid(), channel_id(), [atom()]) -> [{atom(), term()}]. %% -%% Description: Retrieves information about a connection. -%%-------------------------------------------------------------------- +%% Description: Retrieves information about a connection. +%%-------------------------------------------------------------------- channel_info(ConnectionRef, ChannelId, Options) -> ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options). @@ -153,9 +152,9 @@ channel_info(ConnectionRef, ChannelId, Options) -> -spec daemon(integer()|port(), proplists:proplist()) -> {ok, pid()} | {error, term()}. -spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}. -%% Description: Starts a server listening for SSH connections +%% Description: Starts a server listening for SSH connections %% on the given port. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- daemon(Port) -> daemon(Port, []). @@ -188,9 +187,9 @@ daemon_info(Pid) -> -spec stop_listener(pid()) -> ok. -spec stop_listener(inet:ip_address(), integer()) -> ok. %% -%% Description: Stops the listener, but leaves +%% Description: Stops the listener, but leaves %% existing connections started by the listener up and running. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- stop_listener(SysSup) -> ssh_system_sup:stop_listener(SysSup). stop_listener(Address, Port) -> @@ -202,9 +201,9 @@ stop_listener(Address, Port, Profile) -> -spec stop_daemon(pid()) -> ok. -spec stop_daemon(inet:ip_address(), integer()) -> ok. %% -%% Description: Stops the listener and all connections started by +%% Description: Stops the listener and all connections started by %% the listener. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- stop_daemon(SysSup) -> ssh_system_sup:stop_system(SysSup). stop_daemon(Address, Port) -> @@ -243,7 +242,7 @@ start_shell({ok, ConnectionRef}) -> case ssh_connection:session_channel(ConnectionRef, infinity) of {ok,ChannelId} -> success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []), - Args = [{channel_cb, ssh_shell}, + Args = [{channel_cb, ssh_shell}, {init_args,[ConnectionRef, ChannelId]}, {cm, ConnectionRef}, {channel_id, ChannelId}], {ok, State} = ssh_channel:init([Args]), @@ -256,7 +255,7 @@ start_shell(Error) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- -default_algorithms() -> +default_algorithms() -> ssh_transport:default_algorithms(). %%-------------------------------------------------------------------- @@ -296,13 +295,13 @@ daemon_shell_opt(Options) -> daemon_host_inet_opt(HostAddr, Options1) -> case HostAddr of any -> - {ok, Host0} = inet:gethostname(), + {ok, Host0} = inet:gethostname(), {Host0, proplists:get_value(inet, Options1, inet), Options1}; {_,_,_,_} -> - {HostAddr, inet, + {HostAddr, inet, [{ip, HostAddr} | Options1]}; {_,_,_,_,_,_,_,_} -> - {HostAddr, inet6, + {HostAddr, inet6, [{ip, HostAddr} | Options1]} end. @@ -313,8 +312,8 @@ start_daemon(Socket, Options) -> {error, Error}; {SocketOptions, SshOptions} -> case valid_socket_to_use(Socket, Options) of - ok -> - try + ok -> + try do_start_daemon(Socket, [{role,server}|SshOptions], SocketOptions) catch throw:bad_fd -> {error,bad_fd}; @@ -330,16 +329,16 @@ start_daemon(Host, Port, Options, Inet) -> {error, _Reason} = Error -> Error; {SocketOptions, SshOptions}-> - try + try do_start_daemon(Host, Port, [{role,server}|SshOptions] , [Inet|SocketOptions]) catch throw:bad_fd -> {error,bad_fd}; _C:_E -> {error,{cannot_start_daemon,_C,_E}} end end. - + do_start_daemon(Socket, SshOptions, SocketOptions) -> - {ok, {IP,Port}} = + {ok, {IP,Port}} = try {ok,_} = inet:sockname(Socket) catch _:_ -> throw(bad_socket) @@ -351,7 +350,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) -> {address, Host}, {port, Port}, {role, server}, - {socket_opts, SocketOptions}, + {socket_opts, SocketOptions}, {ssh_opts, SshOptions}], {_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}), case ssh_system_sup:system_supervisor(Host, Port, Profile) of @@ -385,7 +384,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) -> end. do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> - {Host,Port1} = + {Host,Port1} = try case proplists:get_value(fd, SocketOptions) of undefined -> @@ -402,21 +401,21 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> {Port, WaitRequestControl, Opts0} = case Port1 of 0 -> %% Allocate the socket here to get the port number... - {_, Callback, _} = + {_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}), {ok,LSock} = ssh_acceptor:callback_listen(Callback, 0, SocketOptions), {ok,{_,LPort}} = inet:sockname(LSock), - {LPort, - {LSock,Callback}, + {LPort, + {LSock,Callback}, [{lsocket,LSock},{lsock_owner,self()}] }; _ -> {Port1, false, []} end, - Opts = [{address, Host}, + Opts = [{address, Host}, {port, Port}, {role, server}, - {socket_opts, SocketOptions}, + {socket_opts, SocketOptions}, {ssh_opts, SshOptions} | Opts0], case ssh_system_sup:system_supervisor(Host, Port, Profile) of undefined -> @@ -465,7 +464,7 @@ find_hostport(Fd) -> {ok, HostPort} = inet:sockname(S), ok = inet:close(S), HostPort. - + handle_options(Opts) -> try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of @@ -480,9 +479,9 @@ handle_options(Opts) -> algs_compatibility(Os0) -> %% Take care of old options 'public_key_alg' and 'pref_public_key_algs' case proplists:get_value(public_key_alg, Os0) of - undefined -> + undefined -> Os0; - A when is_atom(A) -> + A when is_atom(A) -> %% Skip public_key_alg if pref_public_key_algs is defined: Os = lists:keydelete(public_key_alg, 1, Os0), case proplists:get_value(pref_public_key_algs,Os) of @@ -492,7 +491,7 @@ algs_compatibility(Os0) -> [{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os]; undefined -> throw({error, {eoptions, {public_key_alg,A} }}); - _ -> + _ -> Os end; V -> @@ -620,7 +619,7 @@ 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({preferred_algorithms,[_|_]} = Opt) -> +handle_ssh_option({preferred_algorithms,[_|_]} = Opt) -> handle_pref_algs(Opt); handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> @@ -629,7 +628,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> lists:foldl( fun({N,G,P}, Acc) when is_integer(N),N>0, is_integer(G),G>0, - is_integer(P),P>0 -> + is_integer(P),P>0 -> [{N,{G,P}} | Acc]; ({N,{G,P}}, Acc) when is_integer(N),N>0, is_integer(G),G>0, @@ -637,7 +636,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> [{N,{G,P}} | Acc]; ({N,GPs}, Acc) when is_list(GPs) -> lists:foldr(fun({Gi,Pi}, Acci) when is_integer(Gi),Gi>0, - is_integer(Pi),Pi>0 -> + is_integer(Pi),Pi>0 -> [{N,{Gi,Pi}} | Acci] end, Acc, GPs) end, [], L0))}; @@ -647,7 +646,7 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0, Tag == ssh_moduli_file -> {ok,GroupDefs} = case Tag of - file -> + file -> file:consult(File); ssh_moduli_file -> case file:open(File,[read]) of @@ -672,14 +671,14 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0, catch _:_ -> throw({error, {{eoptions, Opt}, "Bad format in file: "++File}}) - end; - + end; + -handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0, +handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0, is_integer(Max), Max>=Min -> %% Server Opt; -handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0, +handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0, is_integer(I), I>=Min, is_integer(Max), Max>=I -> %% Client @@ -724,7 +723,7 @@ handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3 Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> Opt; -handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), +handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), is_atom(Function) -> Opt; handle_ssh_option({exec, Function} = Opt) when is_function(Function) -> @@ -772,7 +771,7 @@ handle_ssh_option({quiet_mode, Value} = Opt) when is_boolean(Value) -> Opt; handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 -> Opt; -handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) -> +handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) -> Opt; handle_ssh_option({id_string, random}) -> {id_string, {random,2,5}}; %% 2 - 5 random characters @@ -814,11 +813,11 @@ handle_pref_algs({preferred_algorithms,Algs}) -> of DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs) catch - _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}}, + _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}}, "Bad preferred_algorithms key"}}) end || {Key,Vals} <- Algs] }; - + Dups -> throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}}) catch @@ -857,13 +856,13 @@ handle_pref_alg(Key, ) -> handle_pref_alg(Key, lists:reverse(Vs), Sup); -handle_pref_alg(Key, +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, +handle_pref_alg(Key, Vs=[V|_], Sup=[S|_] ) when is_atom(V), is_atom(S) -> @@ -878,14 +877,14 @@ chk_alg_vs(OptKey, Values, SupportedValues) -> [] -> 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 -> Inet; false -> case lists:member(inet6, Inet) of - true -> + true -> Inet; false -> [inet | Inet] @@ -916,8 +915,8 @@ directory_exist_readable(Dir) -> {error, Error} -> {error, Error} end. - - + + collect_per_size(L) -> lists:foldr( @@ -948,7 +947,7 @@ read_moduli_file(D, I, Acc) -> read_moduli_file(D, I+1, Acc) end end. - + handle_user_pref_pubkey_algs([], Acc) -> {true, lists:reverse(Acc)}; handle_user_pref_pubkey_algs([H|T], Acc) -> @@ -963,7 +962,7 @@ handle_user_pref_pubkey_algs([H|T], Acc) -> false end. -fmt_host({A,B,C,D}) -> +fmt_host({A,B,C,D}) -> lists:concat([A,".",B,".",C,".",D]); -fmt_host(T={_,_,_,_,_,_,_,_}) -> +fmt_host(T={_,_,_,_,_,_,_,_}) -> lists:flatten(string:join([io_lib:format("~.16B",[A]) || A <- tuple_to_list(T)], ":")). diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 868f3a9181..4cd91177f6 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -127,7 +127,7 @@ recv_sequence = 0, keyex_key, keyex_info, - random_length_padding = 255, % From RFC 4253 section 6. + random_length_padding = 15, % From RFC 4253 section 6. %% User auth user, diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 49eec8072f..ac35b70209 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -31,24 +31,123 @@ -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/2, handle_userauth_info_response/2 ]). %%-------------------------------------------------------------------- %%% Internal application API %%-------------------------------------------------------------------- +%%%---------------------------------------------------------------- +userauth_request_msg(#ssh{userauth_methods = ServerMethods, + userauth_supported_methods = UserPrefMethods, % Note: this is not documented as supported for clients + userauth_preference = ClientMethods0 + } = Ssh0) -> + case sort_select_mthds(ClientMethods0, UserPrefMethods, ServerMethods) of + [] -> + Msg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, + description = "Unable to connect using the available authentication methods", + language = "en"}, + {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh0)}; + + [{Pref,Module,Function,Args} | Prefs] -> + Ssh = case Pref of + "keyboard-interactive" -> Ssh0; + _ -> Ssh0#ssh{userauth_preference = Prefs} + end, + case Module:Function(Args ++ [Ssh]) of + {not_ok, Ssh1} -> + userauth_request_msg(Ssh1#ssh{userauth_preference = Prefs}); + Result -> + {Pref,Result} + end + end. + + + +sort_select_mthds(Clients, undefined, Servers) -> + %% User has not expressed an opinion via option "auth_methods", use the server's prefs + sort_select_mthds1(Clients, Servers, string:tokens(?SUPPORTED_AUTH_METHODS,",")); + +sort_select_mthds(Clients, Users0, Servers0) -> + %% The User has an opinion, use the intersection of that and the Servers whishes but + %% in the Users order + sort_select_mthds1(Clients, string:tokens(Users0,","), Servers0). + + +sort_select_mthds1(Clients, Users0, Servers0) -> + Servers = unique(Servers0), + Users = unique(Users0), + [C || Key <- Users, + lists:member(Key, Servers), + C <- Clients, + element(1,C) == Key]. + +unique(L) -> + lists:reverse( + lists:foldl(fun(E,Acc) -> + case lists:member(E,Acc) of + true -> Acc; + false -> [E|Acc] + end + end, [], L)). + + +%%%---- userauth_request_msg "callbacks" +password_msg([#ssh{opts = Opts, io_cb = IoCb, + user = User, service = Service} = Ssh0]) -> + {Password,Ssh} = + case proplists:get_value(password, Opts) of + undefined when IoCb == ssh_no_io -> + {not_ok, Ssh0}; + undefined -> + {IoCb:read_password("ssh password: ",Ssh0), Ssh0}; + PW -> + %% If "password" option is given it should not be tried again + {PW, Ssh0#ssh{opts = lists:keyreplace(password,1,Opts,{password,not_ok})}} + end, + case Password of + not_ok -> + {not_ok, Ssh}; + _ -> + ssh_transport:ssh_packet( + #ssh_msg_userauth_request{user = User, + service = Service, + method = "password", + data = + <<?BOOLEAN(?FALSE), + ?STRING(unicode:characters_to_binary(Password))>>}, + Ssh) + end. + +%% See RFC 4256 for info on keyboard-interactive +keyboard_interactive_msg([#ssh{user = User, + opts = Opts, + service = Service} = Ssh]) -> + case proplists:get_value(password, Opts) of + not_ok -> + {not_ok,Ssh}; % No need to use a failed pwd once more + _ -> + ssh_transport:ssh_packet( + #ssh_msg_userauth_request{user = User, + service = Service, + method = "keyboard-interactive", + data = << ?STRING(<<"">>), + ?STRING(<<>>) >> }, + Ssh) + end. + publickey_msg([Alg, #ssh{user = User, session_id = SessionId, service = Service, opts = Opts} = Ssh]) -> - Hash = sha, %% Maybe option?! + Hash = ssh_transport:sha(Alg), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), case KeyCb:user_key(Alg, Opts) of {ok, PrivKey} -> StrAlgo = atom_to_list(Alg), case encode_public_key(StrAlgo, ssh_transport:extract_public_key(PrivKey)) of not_ok -> - not_ok; + {not_ok, Ssh}; PubKeyBlob -> SigData = build_sig_data(SessionId, User, Service, PubKeyBlob, StrAlgo), @@ -65,52 +164,15 @@ publickey_msg([Alg, #ssh{user = User, Ssh) end; _Error -> - not_ok - end. - -password_msg([#ssh{opts = Opts, io_cb = IoCb, - user = User, service = Service} = Ssh]) -> - Password = case proplists:get_value(password, Opts) of - undefined -> - user_interaction(IoCb, Ssh); - PW -> - PW - end, - case Password of - not_ok -> - not_ok; - _ -> - ssh_transport:ssh_packet( - #ssh_msg_userauth_request{user = User, - service = Service, - method = "password", - data = - <<?BOOLEAN(?FALSE), - ?STRING(unicode:characters_to_binary(Password))>>}, - Ssh) + {not_ok, Ssh} end. -user_interaction(ssh_no_io, _) -> - not_ok; -user_interaction(IoCb, Ssh) -> - IoCb:read_password("ssh password: ", Ssh). - - -%% See RFC 4256 for info on keyboard-interactive -keyboard_interactive_msg([#ssh{user = User, - service = Service} = Ssh]) -> - ssh_transport:ssh_packet( - #ssh_msg_userauth_request{user = User, - service = Service, - method = "keyboard-interactive", - data = << ?STRING(<<"">>), - ?STRING(<<>>) >> }, - Ssh). - +%%%---------------------------------------------------------------- service_request_msg(Ssh) -> ssh_transport:ssh_packet(#ssh_msg_service_request{name = "ssh-userauth"}, Ssh#ssh{service = "ssh-userauth"}). +%%%---------------------------------------------------------------- init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> case user_name(Opts) of {ok, User} -> @@ -140,34 +202,9 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> description = ErrStr}) end. -userauth_request_msg(#ssh{userauth_preference = []} = Ssh) -> - Msg = #ssh_msg_disconnect{code = - ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, - description = "Unable to connect using the available" - " authentication methods", - language = "en"}, - {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh)}; - -userauth_request_msg(#ssh{userauth_methods = Methods, - userauth_preference = [{Pref, Module, - Function, Args} | Prefs]} - = Ssh0) -> - Ssh = Ssh0#ssh{userauth_preference = Prefs}, - case lists:member(Pref, Methods) of - true -> - case Module:Function(Args ++ [Ssh]) of - not_ok -> - userauth_request_msg(Ssh); - Result -> - {Pref,Result} - end; - false -> - userauth_request_msg(Ssh) - end. - - -handle_userauth_request(#ssh_msg_service_request{name = - Name = "ssh-userauth"}, +%%%---------------------------------------------------------------- +%%% called by server +handle_userauth_request(#ssh_msg_service_request{name = Name = "ssh-userauth"}, _, Ssh) -> {ok, ssh_transport:ssh_packet(#ssh_msg_service_accept{name = Name}, Ssh#ssh{service = "ssh-connection"})}; @@ -223,32 +260,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "publickey", - data = Data}, - SessionId, + data = <<?BYTE(?FALSE), + ?UINT32(ALen), BAlg:ALen/binary, + ?UINT32(KLen), KeyBlob:KLen/binary, + _/binary + >> + }, + _SessionId, #ssh{opts = Opts, userauth_supported_methods = Methods} = Ssh) -> - <<?BYTE(HaveSig), ?UINT32(ALen), BAlg:ALen/binary, - ?UINT32(KLen), KeyBlob:KLen/binary, SigWLen/binary>> = Data, - Alg = binary_to_list(BAlg), - case HaveSig of - ?TRUE -> - case verify_sig(SessionId, User, "ssh-connection", Alg, - KeyBlob, SigWLen, Opts) of - true -> - {authorized, User, - ssh_transport:ssh_packet( - #ssh_msg_userauth_success{}, Ssh)}; - false -> - {not_authorized, {User, undefined}, - ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications = Methods, - partial_success = false}, Ssh)} - end; - ?FALSE -> + + case pre_verify_sig(User, binary_to_list(BAlg), + KeyBlob, Opts) of + true -> {not_authorized, {User, undefined}, ssh_transport:ssh_packet( - #ssh_msg_userauth_pk_ok{algorithm_name = Alg, - key_blob = KeyBlob}, Ssh)} + #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), + key_blob = KeyBlob}, Ssh)}; + false -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = Methods, + partial_success = false}, Ssh)} + end; + +handle_userauth_request(#ssh_msg_userauth_request{user = User, + service = "ssh-connection", + method = "publickey", + data = <<?BYTE(?TRUE), + ?UINT32(ALen), BAlg:ALen/binary, + ?UINT32(KLen), KeyBlob:KLen/binary, + SigWLen/binary>> + }, + SessionId, + #ssh{opts = Opts, + userauth_supported_methods = Methods} = Ssh) -> + + case verify_sig(SessionId, User, "ssh-connection", + binary_to_list(BAlg), + KeyBlob, SigWLen, Opts) of + true -> + {authorized, User, + ssh_transport:ssh_packet( + #ssh_msg_userauth_success{}, Ssh)}; + false -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = Methods, + partial_success = false}, Ssh)} end; handle_userauth_request(#ssh_msg_userauth_request{user = User, @@ -319,31 +378,50 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, partial_success = false}, Ssh)}. - -handle_userauth_info_request( - #ssh_msg_userauth_info_request{name = Name, - instruction = Instr, - num_prompts = NumPrompts, - data = Data}, IoCb, - #ssh{opts = Opts} = Ssh) -> +%%%---------------------------------------------------------------- +%%% keyboard-interactive client +handle_userauth_info_request(#ssh_msg_userauth_info_request{name = Name, + instruction = Instr, + num_prompts = NumPrompts, + data = Data}, + #ssh{opts = Opts, + io_cb = IoCb + } = Ssh) -> PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data), - Responses = keyboard_interact_get_responses(IoCb, Opts, - Name, Instr, PromptInfos), - {ok, - ssh_transport:ssh_packet( - #ssh_msg_userauth_info_response{num_responses = NumPrompts, - data = Responses}, Ssh)}. + case keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) of + not_ok -> + not_ok; + Responses -> + {ok, + ssh_transport:ssh_packet( + #ssh_msg_userauth_info_response{num_responses = NumPrompts, + data = Responses}, Ssh)} + end. +%%%---------------------------------------------------------------- +%%% keyboard-interactive server handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, data = <<?UINT32(Sz), Password:Sz/binary>>}, #ssh{opts = Opts, kb_tries_left = KbTriesLeft, user = User, userauth_supported_methods = Methods} = Ssh) -> + SendOneEmpty = proplists:get_value(tstflg, Opts) == one_empty, case check_password(User, unicode:characters_to_list(Password), Opts, Ssh) of + {true,Ssh1} when SendOneEmpty==true -> + Msg = #ssh_msg_userauth_info_request{name = "", + instruction = "", + language_tag = "", + num_prompts = 0, + data = <<?BOOLEAN(?FALSE)>> + }, + {authorized_but_one_more, User, + ssh_transport:ssh_packet(Msg, Ssh1)}; + {true,Ssh1} -> {authorized, User, ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh1)}; + {false,Ssh1} -> {not_authorized, {User, {error,"Bad user or password"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ @@ -353,6 +431,11 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, )} end; +handle_userauth_info_response({extra,#ssh_msg_userauth_info_response{}}, + #ssh{user = User} = Ssh) -> + {authorized, User, + ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)}; + handle_userauth_info_response(#ssh_msg_userauth_info_response{}, _Auth) -> ssh_connection_handler:disconnect( @@ -369,11 +452,6 @@ method_preference(Algs) -> [{"publickey", ?MODULE, publickey_msg, [A]} | Acc] end, [{"password", ?MODULE, password_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} ], Algs). @@ -434,19 +512,34 @@ get_password_option(Opts, User) -> false -> proplists:get_value(password, Opts, false) end. -verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> - {ok, Key} = decode_public_key_v2(KeyBlob, Alg), - KeyCb = proplists:get_value(key_cb, Opts, ssh_file), +pre_verify_sig(User, Alg, KeyBlob, Opts) -> + try + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + KeyCb:is_auth_key(Key, User, Opts) + catch + _:_ -> + false + end. - case KeyCb:is_auth_key(Key, User, Opts) of - true -> - PlainText = build_sig_data(SessionId, User, - Service, KeyBlob, Alg), - <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen, - <<?UINT32(AlgLen), _Alg:AlgLen/binary, - ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, - ssh_transport:verify(PlainText, sha, Sig, Key); - false -> +verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> + try + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:is_auth_key(Key, User, Opts) of + true -> + PlainText = build_sig_data(SessionId, User, + Service, KeyBlob, Alg), + <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen, + <<?UINT32(AlgLen), _Alg:AlgLen/binary, + ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, + ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key); + false -> + false + end + catch + _:_ -> false end. @@ -473,6 +566,9 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) -> proplists:get_value(password, Opts, undefined), IoCb, Name, Instr, PromptInfos, Opts, NumPrompts). + +keyboard_interact_get_responses(_, _, not_ok, _, _, _, _, _, _) -> + not_ok; keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _, 1) when Password =/= undefined -> [Password]; %% Password auth implemented with keyboard-interaction and passwd is known @@ -486,17 +582,18 @@ keyboard_interact_get_responses(true, Fun, _Pwd, _IoCb, Name, Instr, PromptInfos keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts). keyboard_interact(IoCb, Name, Instr, Prompts, Opts) -> - if Name /= "" -> IoCb:format("~s~n", [Name]); - true -> ok - end, - if Instr /= "" -> IoCb:format("~s~n", [Instr]); - true -> ok - end, + write_if_nonempty(IoCb, Name), + write_if_nonempty(IoCb, Instr), lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts); ({Prompt, false}) -> IoCb:read_password(Prompt, Opts) end, Prompts). +write_if_nonempty(_, "") -> ok; +write_if_nonempty(_, <<>>) -> ok; +write_if_nonempty(IoCb, Text) -> IoCb:format("~s~n",[Text]). + + keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) -> Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end, PromptInfos), diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index e952a333ff..facf6b561a 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -60,7 +60,8 @@ ]). %%% Behaviour callbacks --export([handle_event/4, terminate/3, format_status/2, code_change/4]). +-export([callback_mode/0, handle_event/4, terminate/3, + format_status/2, code_change/4]). %%% Exports not intended to be used :). They are used for spawning and tests -export([init_connection_handler/3, % proc_lib:spawn needs this @@ -338,6 +339,7 @@ renegotiate_data(ConnectionHandler) -> ssh_params :: #ssh{} | undefined, socket :: inet:socket(), + sender :: pid() | undefined, decrypted_data_buffer = <<>> :: binary(), encrypted_data_buffer = <<>> :: binary(), undecrypted_packet_length :: undefined | non_neg_integer(), @@ -366,22 +368,21 @@ init_connection_handler(Role, Socket, Opts) -> {Protocol, Callback, CloseTag} = proplists:get_value(transport, Opts, ?DefaultTransport), S0#data{ssh_params = init_ssh_record(Role, Socket, Opts), - transport_protocol = Protocol, - transport_cb = Callback, - transport_close_tag = CloseTag + sender = spawn_link(fun() -> nonblocking_sender(Socket, Callback) end), + transport_protocol = Protocol, + transport_cb = Callback, + transport_close_tag = CloseTag } of S -> gen_statem:enter_loop(?MODULE, [], %%[{debug,[trace,log,statistics,debug]} || Role==server], - handle_event_function, {hello,Role}, S) catch _:Error -> gen_statem:enter_loop(?MODULE, [], - handle_event_function, {init_error,Error}, S0) end. @@ -428,7 +429,12 @@ init_connection(server, C = #connection{}, Opts) -> init_ssh_record(Role, Socket, Opts) -> {ok, PeerAddr} = inet:peername(Socket), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), - AuthMethods = proplists:get_value(auth_methods, Opts, ?SUPPORTED_AUTH_METHODS), + AuthMethods = proplists:get_value(auth_methods, + Opts, + case Role of + server -> ?SUPPORTED_AUTH_METHODS; + client -> undefined + end), S0 = #ssh{role = Role, key_cb = KeyCb, opts = Opts, @@ -499,6 +505,9 @@ init_ssh_record(Role, Socket, Opts) -> %%% ######## Error in the initialisation #### +callback_mode() -> + handle_event_function. + handle_event(_, _Event, {init_error,Error}, _) -> case Error of {badmatch,{error,enotconn}} -> @@ -518,7 +527,7 @@ handle_event(_, _Event, {init_error,Error}, _) -> %% The very first event that is sent when the we are set as controlling process of Socket handle_event(_, socket_control, {hello,_}, D) -> VsnMsg = ssh_transport:hello_version_msg(string_version(D#data.ssh_params)), - ok = send_bytes(VsnMsg, D), + send_bytes(VsnMsg, D), case inet:getopts(Socket=D#data.socket, [recbuf]) of {ok, [{recbuf,Size}]} -> %% Set the socket to the hello text line handling mode: @@ -543,7 +552,7 @@ handle_event(_, {info_line,_Line}, {hello,Role}, D) -> server -> %% But the client may NOT send them to the server. Openssh answers with cleartext, %% and so do we - ok = send_bytes("Protocol mismatch.", D), + send_bytes("Protocol mismatch.", D), {stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}} end; @@ -558,7 +567,7 @@ handle_event(_, {version_exchange,Version}, {hello,Role}, D) -> {active, once}, {recbuf, D#data.inet_initial_recbuf_size}]), {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1), - ok = send_bytes(SshPacket, D), + send_bytes(SshPacket, D), {next_state, {kexinit,Role,init}, D#data{ssh_params = Ssh, key_exchange_init_msg = KeyInitMsg}}; not_supported -> @@ -576,7 +585,7 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg}, Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload), Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of {ok, NextKexMsg, Ssh2} when Role==client -> - ok = send_bytes(NextKexMsg, D), + send_bytes(NextKexMsg, D), Ssh2; {ok, Ssh2} when Role==server -> Ssh2 @@ -589,43 +598,43 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg}, %%%---- diffie-hellman handle_event(_, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params), - ok = send_bytes(KexdhReply, D), + send_bytes(KexdhReply, D), {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) -> {ok, NewKeys, Ssh} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}}; %%%---- diffie-hellman group exchange handle_event(_, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params), - ok = send_bytes(GexGroup, D), + send_bytes(GexGroup, D), {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params), - ok = send_bytes(GexGroup, D), + send_bytes(GexGroup, D), {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) -> {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params), - ok = send_bytes(KexGexInit, D), + send_bytes(KexGexInit, D), {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}}; %%%---- elliptic curve diffie-hellman handle_event(_, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params), - ok = send_bytes(KexEcdhReply, D), + send_bytes(KexEcdhReply, D), {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) -> {ok, NewKeys, Ssh} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}}; @@ -633,9 +642,9 @@ handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) -> {ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params), - ok = send_bytes(KexGexReply, D), + send_bytes(KexGexReply, D), {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}}; @@ -643,7 +652,7 @@ handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,serv handle_event(_, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) -> {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh1}}; @@ -655,7 +664,7 @@ handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,init}, D) -> Ssh = case Role of client -> {MsgReq, Ssh2} = ssh_auth:service_request_msg(Ssh1), - ok = send_bytes(MsgReq, D), + send_bytes(MsgReq, D), Ssh2; server -> Ssh1 @@ -673,7 +682,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s "ssh-userauth" -> Ssh0 = #ssh{session_id=SessionId} = D#data.ssh_params, {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {next_state, {userauth,server}, D#data{ssh_params = Ssh}}; _ -> @@ -685,7 +694,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s handle_event(_, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client}, #data{ssh_params = #ssh{service="ssh-userauth"} = Ssh0} = State) -> {Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0), - ok = send_bytes(Msg, State), + send_bytes(Msg, State), {next_state, {userauth,client}, State#data{auth_user = Ssh#ssh.user, ssh_params = Ssh}}; @@ -702,7 +711,7 @@ handle_event(_, %% Probably the very first userauth_request but we deny unauthorized login {not_authorized, _, {Reply,Ssh}} = ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {keep_state, D#data{ssh_params = Ssh}}; {"ssh-connection", "ssh-connection", Method} -> @@ -712,7 +721,7 @@ handle_event(_, %% Yepp! we support this method case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of {authorized, User, {Reply, Ssh}} -> - ok = send_bytes(Reply, D), + send_bytes(Reply, D), D#data.starter ! ssh_connected, connected_fun(User, Method, D), {next_state, {connected,server}, @@ -720,11 +729,11 @@ handle_event(_, ssh_params = Ssh#ssh{authenticated = true}}}; {not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" -> retry_fun(User, Reason, D), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {next_state, {userauth_keyboard_interactive,server}, D#data{ssh_params = Ssh}}; {not_authorized, {User, Reason}, {Reply, Ssh}} -> retry_fun(User, Reason, D), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {keep_state, D#data{ssh_params = Ssh}} end; false -> @@ -794,9 +803,13 @@ handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) - handle_event(_, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client}, #data{ssh_params = Ssh0} = D) -> - {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_request(Msg, Ssh0#ssh.io_cb, Ssh0), - send_bytes(Reply, D), - {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}}; + case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of + {ok, {Reply, Ssh}} -> + send_bytes(Reply, D), + {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}}; + not_ok -> + {next_state, {userauth,client}, D, [{next_event, internal, Msg}]} + end; handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D) -> case ssh_auth:handle_userauth_info_response(Msg, D#data.ssh_params) of @@ -809,9 +822,21 @@ handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_inte {not_authorized, {User, Reason}, {Reply, Ssh}} -> retry_fun(User, Reason, D), send_bytes(Reply, D), - {next_state, {userauth,server}, D#data{ssh_params = Ssh}} + {next_state, {userauth,server}, D#data{ssh_params = Ssh}}; + + {authorized_but_one_more, _User, {Reply, Ssh}} -> + send_bytes(Reply, D), + {next_state, {userauth_keyboard_interactive_extra,server}, D#data{ssh_params = Ssh}} end; +handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D) -> + {authorized, User, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response({extra,Msg}, D#data.ssh_params), + send_bytes(Reply, D), + D#data.starter ! ssh_connected, + connected_fun(User, "keyboard-interactive", D), + {next_state, {connected,server}, D#data{auth_user = User, + ssh_params = Ssh#ssh{authenticated = true}}}; + handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client}, #data{ssh_params = Ssh0} = D0) -> Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference, @@ -819,7 +844,18 @@ handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactiv D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}}, {next_state, {userauth,client}, D, [{next_event, internal, Msg}]}; -handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, D) -> +handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, + #data{ssh_params = Ssh0} = D0) -> + Opts = Ssh0#ssh.opts, + D = case proplists:get_value(password, Opts) of + undefined -> + D0; + _ -> + D0#data{ssh_params = + Ssh0#ssh{opts = + lists:keyreplace(password,1,Opts, + {password,not_ok})}} % FIXME:intermodule dependency + end, {next_state, {userauth,client}, D, [{next_event, internal, Msg}]}; handle_event(_, Msg=#ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) -> @@ -1006,13 +1042,13 @@ handle_event({call,From}, get_print_info, StateName, D) -> {keep_state_and_data, [{reply,From,Reply}]}; handle_event({call,From}, {connection_info, Options}, _, D) -> - Info = ssh_info(Options, D, []), + Info = fold_keys(Options, fun conn_info/2, D), {keep_state_and_data, [{reply,From,Info}]}; handle_event({call,From}, {channel_info,ChannelId,Options}, _, D) -> case ssh_channel:cache_lookup(cache(D), ChannelId) of #channel{} = Channel -> - Info = ssh_channel_info(Options, Channel, []), + Info = fold_keys(Options, fun chann_info/2, Channel), {keep_state_and_data, [{reply,From,Info}]}; undefined -> {keep_state_and_data, [{reply,From,[]}]} @@ -1206,16 +1242,20 @@ handle_event(internal, prepare_next_packet, _, D) -> Sz when Sz >= Enough -> self() ! {D#data.transport_protocol, D#data.socket, <<>>}; _ -> - inet:setopts(D#data.socket, [{active, once}]) + ok end, + inet:setopts(D#data.socket, [{active, once}]), keep_state_and_data; handle_event(info, {CloseTag,Socket}, StateName, D = #data{socket = Socket, transport_close_tag = CloseTag}) -> - disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, - description = "Connection closed"}, - StateName, D); + %% Simulate a disconnect from the peer + handle_event(info, + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, + description = "Connection closed"}, + StateName, + D); handle_event(info, {timeout, {_, From} = Request}, _, #data{connection_state = #connection{requests = Requests} = C0} = D) -> @@ -1315,12 +1355,10 @@ terminate(shutdown, StateName, State0) -> State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, description = "Application shutdown"}, State0), -timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead finalize_termination(StateName, State); %% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)-> %% State = send_msg(Msg, State0), -%% timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead %% finalize_termination(StateName, Msg, State); terminate({shutdown,_R}, StateName, State) -> @@ -1382,12 +1420,12 @@ fmt_stat_rec(FieldNames, Rec, Exclude) -> state_name(), #data{}, term() - ) -> {gen_statem:callback_mode(), state_name(), #data{}}. + ) -> {ok, state_name(), #data{}}. %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . code_change(_OldVsn, StateName, State, _Extra) -> - {handle_event_function, StateName, State}. + {ok, StateName, State}. %%==================================================================== @@ -1409,18 +1447,15 @@ start_the_connection_child(UserPid, Role, Socket, Options) -> %% Stopping -type finalize_termination_result() :: ok . -finalize_termination(_StateName, #data{transport_cb = Transport, - connection_state = Connection, - socket = Socket}) -> - case Connection of +finalize_termination(_StateName, D) -> + case D#data.connection_state of #connection{system_supervisor = SysSup, sub_system_supervisor = SubSysSup} when is_pid(SubSysSup) -> ssh_system_sup:stop_subsystem(SysSup, SubSysSup); _ -> do_nothing end, - (catch Transport:close(Socket)), - ok. + close_transport(D). %%-------------------------------------------------------------------- %% "Invert" the Role @@ -1475,8 +1510,33 @@ send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) -> send_bytes(Bytes, State), State#data{ssh_params=Ssh}. -send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) -> - Transport:send(Socket, Bytes). +send_bytes(Bytes, #data{sender = Sender}) -> + Sender ! {send,Bytes}, + ok. + +close_transport(D) -> + D#data.sender ! close, + ok. + + +nonblocking_sender(Socket, Callback) -> + receive + {send, Bytes} -> + case Callback:send(Socket, Bytes) of + ok -> + nonblocking_sender(Socket, Callback); + E = {error,_} -> + exit({shutdown,E}) + end; + + close -> + case Callback:close(Socket) of + ok -> + ok; + E = {error,_} -> + exit({shutdown,E}) + end + end. handle_version({2, 0} = NumVsn, StrVsn, Ssh0) -> Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0), @@ -1635,7 +1695,6 @@ new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} = disconnect(Msg=#ssh_msg_disconnect{description=Description}, _StateName, State0) -> State = send_msg(Msg, State0), disconnect_fun(Description, State), -timer:sleep(400), {stop, {shutdown,Description}, State}. %%%---------------------------------------------------------------- @@ -1644,43 +1703,43 @@ counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) -> counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) -> Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}. -ssh_info([], _State, Acc) -> - Acc; -ssh_info([client_version | Rest], #data{ssh_params = #ssh{c_vsn = IntVsn, - c_version = StringVsn}} = State, Acc) -> - ssh_info(Rest, State, [{client_version, {IntVsn, StringVsn}} | Acc]); - -ssh_info([server_version | Rest], #data{ssh_params =#ssh{s_vsn = IntVsn, - s_version = StringVsn}} = State, Acc) -> - ssh_info(Rest, State, [{server_version, {IntVsn, StringVsn}} | Acc]); -ssh_info([peer | Rest], #data{ssh_params = #ssh{peer = Peer}} = State, Acc) -> - ssh_info(Rest, State, [{peer, Peer} | Acc]); -ssh_info([sockname | Rest], #data{socket = Socket} = State, Acc) -> - {ok, SockName} = inet:sockname(Socket), - ssh_info(Rest, State, [{sockname, SockName}|Acc]); -ssh_info([user | Rest], #data{auth_user = User} = State, Acc) -> - ssh_info(Rest, State, [{user, User}|Acc]); -ssh_info([ _ | Rest], State, Acc) -> - ssh_info(Rest, State, Acc). - - -ssh_channel_info([], _, Acc) -> - Acc; +%%%---------------------------------------------------------------- +conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version}; +conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version}; +conn_info(peer, #data{ssh_params=S}) -> S#ssh.peer; +conn_info(user, D) -> D#data.auth_user; +conn_info(sockname, D) -> {ok, SockName} = inet:sockname(D#data.socket), + SockName; +%% dbg options ( = not documented): +conn_info(socket, D) -> D#data.socket; +conn_info(chan_ids, D) -> + ssh_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) -> + [Id | Acc] + end, [], cache(D)). -ssh_channel_info([recv_window | Rest], #channel{recv_window_size = WinSize, - recv_packet_size = Packsize - } = Channel, Acc) -> - ssh_channel_info(Rest, Channel, [{recv_window, {{win_size, WinSize}, - {packet_size, Packsize}}} | Acc]); -ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize, - send_packet_size = Packsize - } = Channel, Acc) -> - ssh_channel_info(Rest, Channel, [{send_window, {{win_size, WinSize}, - {packet_size, Packsize}}} | Acc]); -ssh_channel_info([ _ | Rest], Channel, Acc) -> - ssh_channel_info(Rest, Channel, Acc). +%%%---------------------------------------------------------------- +chann_info(recv_window, C) -> + {{win_size, C#channel.recv_window_size}, + {packet_size, C#channel.recv_packet_size}}; +chann_info(send_window, C) -> + {{win_size, C#channel.send_window_size}, + {packet_size, C#channel.send_packet_size}}; +%% dbg options ( = not documented): +chann_info(pid, C) -> + C#channel.user. +%%%---------------------------------------------------------------- +%% Assisting meta function for the *_info functions +fold_keys(Keys, Fun, Extra) -> + lists:foldr(fun(Key, Acc) -> + try Fun(Key, Extra) of + Value -> [{Key,Value}|Acc] + catch + _:_ -> Acc + end + end, [], Keys). +%%%---------------------------------------------------------------- log_error(Reason) -> Report = io_lib:format("Erlang ssh connection handler failed with reason:~n" " ~p~n" @@ -1689,7 +1748,6 @@ log_error(Reason) -> [Reason, erlang:get_stacktrace()]), error_logger:error_report(Report). - %%%---------------------------------------------------------------- not_connected_filter({connection_reply, _Data}) -> true; not_connected_filter(_) -> false. diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl index 480795cfc7..bd6bc0335b 100644 --- a/lib/ssh/src/ssh_dbg.erl +++ b/lib/ssh/src/ssh_dbg.erl @@ -24,6 +24,7 @@ -export([messages/0, messages/1, + messages/2, stop/0 ]). @@ -36,12 +37,16 @@ writer, acc = []}). %%%================================================================ -messages() -> messages(fun(String,_D) -> io:format(String) end). -%% messages() -> messages(fun(String,Acc) -> [String|Acc] end) +messages() -> + messages(fun(String,_D) -> io:format(String) end). messages(Write) when is_function(Write,2) -> + messages(Write, fun(X) -> X end). + +messages(Write, MangleArg) when is_function(Write,2), + is_function(MangleArg,1) -> catch dbg:start(), - setup_tracer(Write), + setup_tracer(Write, MangleArg), dbg:p(new,c), dbg_ssh_messages(). @@ -63,18 +68,30 @@ msg_formater({trace,_Pid,return_from,{ssh_message,encode,1},_Res}, D) -> msg_formater({trace,_Pid,call,{ssh_message,decode,_}}, D) -> D; msg_formater({trace,Pid,return_from,{ssh_message,decode,1},Msg}, D) -> - fmt("~nRECV ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D); + fmt("~n~p RECV ~s~n", [Pid,wr_record(shrink_bin(Msg))], D); msg_formater({trace,_Pid,call,{ssh_transport,select_algorithm,_}}, D) -> D; msg_formater({trace,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg}}, D) -> - fmt("~nALGORITHMS ~p~n~s~n", [Pid, wr_record(Alg)], D); + fmt("~n~p ALGORITHMS~n~s~n", [Pid, wr_record(Alg)], D); + + +msg_formater({trace,Pid,send,{tcp,Sock,Bytes},Pid}, D) -> + fmt("~n~p TCP SEND on ~p~n ~p~n", [Pid,Sock, shrink_bin(Bytes)], D); + +msg_formater({trace,Pid,send,{tcp,Sock,Bytes},Dest}, D) -> + fmt("~n~p TCP SEND from ~p TO ~p~n ~p~n", [Pid,Sock,Dest, shrink_bin(Bytes)], D); msg_formater({trace,Pid,send,ErlangMsg,Dest}, D) -> - fmt("~nERL MSG ~p SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D); + fmt("~n~p ERL MSG SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D); + + +msg_formater({trace,Pid,'receive',{tcp,Sock,Bytes}}, D) -> + fmt("~n~p TCP RECEIVE on ~p~n ~p~n", [Pid,Sock,shrink_bin(Bytes)], D); msg_formater({trace,Pid,'receive',ErlangMsg}, D) -> - fmt("~nERL MSG ~p RECIEVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D); + fmt("~n~p ERL MSG RECEIVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D); + msg_formater(M, D) -> fmt("~nDBG ~n~p~n", [shrink_bin(M)], D). @@ -87,8 +104,10 @@ fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) -> D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}. %%%---------------------------------------------------------------- -setup_tracer(Write) -> - Handler = fun msg_formater/2, +setup_tracer(Write, MangleArg) -> + Handler = fun(Arg, D) -> + msg_formater(MangleArg(Arg), D) + end, InitialData = #data{writer = Write}, {ok,_} = dbg:tracer(process, {Handler, InitialData}), ok. diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl index 026d0f6151..1d8f370884 100644 --- a/lib/ssh/src/ssh_io.erl +++ b/lib/ssh/src/ssh_io.erl @@ -31,56 +31,55 @@ read_line(Prompt, Ssh) -> format("~s", [listify(Prompt)]), proplists:get_value(user_pid, Ssh) ! {self(), question}, receive - Answer -> + Answer when is_list(Answer) -> Answer end. yes_no(Prompt, Ssh) -> - io:format("~s [y/n]?", [Prompt]), + format("~s [y/n]?", [Prompt]), proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), question}, receive - Answer -> + %% I can't see that the atoms y and n are ever received, but it must + %% be investigated before removing + y -> yes; + n -> no; + + Answer when is_list(Answer) -> case trim(Answer) of "y" -> yes; "n" -> no; "Y" -> yes; "N" -> no; - y -> yes; - n -> no; _ -> - io:format("please answer y or n\n"), + format("please answer y or n\n",[]), yes_no(Prompt, Ssh) end end. -read_password(Prompt, Ssh) -> +read_password(Prompt, #ssh{opts=Opts}) -> read_password(Prompt, Opts); +read_password(Prompt, Opts) when is_list(Opts) -> format("~s", [listify(Prompt)]), - case is_list(Ssh) of - false -> - proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), user_password}; - _ -> - proplists:get_value(user_pid, Ssh) ! {self(), user_password} - end, + proplists:get_value(user_pid, Opts) ! {self(), user_password}, receive - Answer -> - case Answer of - "" -> - read_password(Prompt, Ssh); - Pass -> Pass - end + Answer when is_list(Answer) -> + case trim(Answer) of + "" -> + read_password(Prompt, Opts); + Pwd -> + Pwd + end end. -listify(A) when is_atom(A) -> - atom_to_list(A); -listify(L) when is_list(L) -> - L; -listify(B) when is_binary(B) -> - binary_to_list(B). format(Fmt, Args) -> io:format(Fmt, Args). +%%%================================================================ +listify(A) when is_atom(A) -> atom_to_list(A); +listify(L) when is_list(L) -> L; +listify(B) when is_binary(B) -> binary_to_list(B). + trim(Line) when is_list(Line) -> lists:reverse(trim1(lists:reverse(trim1(Line)))); @@ -93,6 +92,3 @@ trim1([$\r|Cs]) -> trim(Cs); trim1([$\n|Cs]) -> trim(Cs); trim1([$\t|Cs]) -> trim(Cs); trim1(Cs) -> Cs. - - - diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 7cb3b75ac0..15b80de30a 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -46,7 +46,7 @@ handle_kex_ecdh_reply/2, extract_public_key/1, ssh_packet/2, pack/2, - sign/3, verify/4]). + sha/1, sign/3, verify/4]). %%% For test suites -export([pack/3]). @@ -1619,6 +1619,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) -> crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L). +sha('ssh-rsa') -> sha; +sha('ssh-dss') -> sha; +sha('ecdsa-sha2-nistp256') -> sha(secp256r1); +sha('ecdsa-sha2-nistp384') -> sha(secp384r1); +sha('ecdsa-sha2-nistp521') -> sha(secp521r1); sha(secp256r1) -> sha256; sha(secp384r1) -> sha384; sha(secp521r1) -> sha512; diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index 0f68130a05..8b2db0e1a8 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -180,21 +180,19 @@ simple_exec(Config) -> %%-------------------------------------------------------------------- %% Testing if no group matches simple_exec_groups_no_match_too_small(Config) -> - try simple_exec_group({400,500,600}, Config) - of - _ -> ct:fail("Exec though no group available") - catch - error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> - ok - end. + try_exec_simple_group({400,500,600}, Config). simple_exec_groups_no_match_too_large(Config) -> - try simple_exec_group({9200,9500,9700}, Config) + try_exec_simple_group({9200,9500,9700}, Config). + + +try_exec_simple_group(Group, Config) -> + try simple_exec_group(Group, Config) of _ -> ct:fail("Exec though no group available") catch - error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> - ok + error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> ok; + error:{badmatch,{error,"Connection closed"}} -> ok end. %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 733414e23a..d52d453007 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -50,7 +50,12 @@ inet6_option/1, inet_option/1, internal_error/1, - known_hosts/1, + known_hosts/1, + login_bad_pwd_no_retry1/1, + login_bad_pwd_no_retry2/1, + login_bad_pwd_no_retry3/1, + login_bad_pwd_no_retry4/1, + login_bad_pwd_no_retry5/1, misc_ssh_options/1, openssh_zlib_basic_test/1, packet_size_zero/1, @@ -100,7 +105,8 @@ all() -> daemon_opt_fd, multi_daemon_opt_fd, packet_size_zero, - ssh_info_print + ssh_info_print, + {group, login_bad_pwd_no_retry} ]. groups() -> @@ -116,7 +122,13 @@ groups() -> {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {key_cb, [], [key_callback, key_callback_options]}, - {internal_error, [], [internal_error]} + {internal_error, [], [internal_error]}, + {login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1, + login_bad_pwd_no_retry2, + login_bad_pwd_no_retry3, + login_bad_pwd_no_retry4, + login_bad_pwd_no_retry5 + ]} ]. @@ -1090,6 +1102,72 @@ ssh_info_print(Config) -> %%-------------------------------------------------------------------- +%% Check that a basd pwd is not tried more times. Could cause lock-out +%% on server + +login_bad_pwd_no_retry1(Config) -> + login_bad_pwd_no_retry(Config, "keyboard-interactive,password"). + +login_bad_pwd_no_retry2(Config) -> + login_bad_pwd_no_retry(Config, "password,keyboard-interactive"). + +login_bad_pwd_no_retry3(Config) -> + login_bad_pwd_no_retry(Config, "password,publickey,keyboard-interactive"). + +login_bad_pwd_no_retry4(Config) -> + login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive"). + +login_bad_pwd_no_retry5(Config) -> + login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive,password,password"). + + + + + +login_bad_pwd_no_retry(Config, AuthMethods) -> + PrivDir = proplists:get_value(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = proplists:get_value(data_dir, Config), + + Parent = self(), + PwdFun = fun(_, _, _, undefined) -> {false, 1}; + (_, _, _, _) -> Parent ! retry_bad_pwd, + false + end, + + {DaemonRef, _Host, Port} = + ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {auth_methods, AuthMethods}, + {user_passwords, [{"foo","somepwd"}]}, + {pwdfun, PwdFun} + ]), + + ConnRes = ssh:connect("localhost", Port, + [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "badpwd"}, + {user_dir, UserDir}, + {user_interaction, false}]), + + receive + retry_bad_pwd -> + ssh:stop_daemon(DaemonRef), + {fail, "Retry bad password"} + after 0 -> + case ConnRes of + {error,"Unable to connect using the available authentication methods"} -> + ssh:stop_daemon(DaemonRef), + ok; + {ok,Conn} -> + ssh:close(Conn), + ssh:stop_daemon(DaemonRef), + {fail, "Connect erroneosly succeded"} + end + end. + +%%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- %% Due to timing the error message may or may not be delivered to diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index b165928877..212b99c695 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.3 +SSH_VSN = 4.3.2 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 3b6f988a2d..9d68ee0eee 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -28,6 +28,26 @@ <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 8.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The TLS/SSL protocol version selection for the SSL server + has been corrected to follow RFC 5246 Appendix E.1 + especially in case where the list of supported versions + has gaps. Now the server selects the highest protocol + version it supports that is not higher than what the + client supports.</p> + <p> + Own Id: OTP-13753 Aux Id: seq13150 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 8.0</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index a66e947bc1..f317dfded4 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -141,6 +141,16 @@ marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso> </item> + + <tag><c><![CDATA[bypass_pem_cache = boolean() <optional>]]></c></tag> + <item> + <p>Introduced in ssl-8.0.2. Disables the PEM-cache. + The PEM cache has proven to be a bottleneck, until the + implementation has been improved this can be used as + a workaround. Defaults to false. + </p> + </item> + <tag><c><![CDATA[alert_timeout = integer() <optional>]]></c></tag> <item> <p> diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml index 4bd5f67202..1150043e76 100644 --- a/lib/ssl/doc/src/ssl_distribution.xml +++ b/lib/ssl/doc/src/ssl_distribution.xml @@ -71,8 +71,8 @@ <section> <title>Building Boot Scripts Including the ssl Application</title> <p>Boot scripts are built using the <c>systools</c> utility in the - <c>sasl</c> application. For more information on <c>systools</c>, - see the <c>sasl</c> documentation. This is only an example of + SASL application. For more information on <c>systools</c>, + see the SASL documentation. This is only an example of what can be done.</p> <p>The simplest boot script possible includes only the Kernel diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index b8be686b99..479f68f4bb 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -42,22 +42,17 @@ -export([next_record/1, next_event/3]). %% Handshake handling --export([%%renegotiate/2, - send_handshake/2, queue_handshake/2, queue_change_cipher/2]). +-export([renegotiate/2, + reinit_handshake_data/1, + send_handshake/2, queue_handshake/2, queue_change_cipher/2, + select_sni_extension/1]). %% Alert and close handling --export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3, - handle_normal_shutdown/3 %%, close/5 - %%alert_user/6, alert_user/9 - ]). +-export([send_alert/2, close/5]). %% Data handling --export([%%write_application_data/3, - read_application_data/2, - passive_receive/2, next_record_if_active/1%, - %%handle_common_event/4, - %handle_packet/3 +-export([passive_receive/2, next_record_if_active/1, handle_common_event/4 ]). %% gen_statem state functions @@ -65,9 +60,7 @@ hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states connection/3]). %% gen_statem callbacks --export([terminate/3, code_change/4, format_status/2]). - --define(GEN_STATEM_CB_MODE, state_functions). +-export([callback_mode/0, terminate/3, code_change/4, format_status/2]). %%==================================================================== %% Internal application API @@ -104,10 +97,11 @@ send_handshake(Handshake, State) -> send_handshake_flight(queue_handshake(Handshake, State)). queue_flight_buffer(Msg, #state{negotiated_version = Version, - connection_states = #connection_states{ - current_write = - #connection_state{epoch = Epoch}}, + connection_states = ConnectionStates, flight_buffer = Flight} = State) -> + ConnectionState = + ssl_record:current_connection_state(ConnectionStates, write), + Epoch = maps:get(epoch, ConnectionState), State#state{flight_buffer = Flight ++ [{Version, Epoch, Msg}]}. queue_handshake(Handshake, #state{negotiated_version = Version, @@ -141,6 +135,25 @@ send_alert(Alert, #state{negotiated_version = Version, Transport:send(Socket, BinMsg), State0#state{connection_states = ConnectionStates}. +close(downgrade, _,_,_,_) -> + ok; +%% Other +close(_, Socket, Transport, _,_) -> + Transport:close(Socket). + +reinit_handshake_data(#state{protocol_buffers = Buffers} = State) -> + State#state{premaster_secret = undefined, + public_key_info = undefined, + tls_handshake_history = ssl_handshake:init_handshake_history(), + protocol_buffers = + Buffers#protocol_buffers{dtls_fragment_state = + dtls_handshake:dtls_handshake_new_flight(0)}}. + +select_sni_extension(#client_hello{extensions = HelloExtensions}) -> + HelloExtensions#hello_extensions.sni; +select_sni_extension(_) -> + undefined. + %%==================================================================== %% tls_connection_sup API %%==================================================================== @@ -161,12 +174,15 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), try State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0), - gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State) + gen_statem:enter_loop(?MODULE, [], init, State) catch throw:Error -> - gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error,State0}) + gen_statem:enter_loop(?MODULE, [], error, {Error,State0}) end. +callback_mode() -> + state_functions. + %%-------------------------------------------------------------------- %% State functionsconnection/2 %%-------------------------------------------------------------------- @@ -231,7 +247,7 @@ hello(internal, #client_hello{client_version = ClientVersion, case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State); + ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State); {Version, {Type, Session}, ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> Protocol = case Protocol0 of @@ -255,7 +271,7 @@ hello(internal, #server_hello{} = Hello, ssl_options = SslOptions} = State) -> case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> - handle_own_alert(Alert, ReqVersion, hello, State); + ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State); {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, Version, NewId, ConnectionStates, ProtoExt, Protocol, State) @@ -334,7 +350,7 @@ handle_info({Protocol, _, Data}, StateName, {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> - handle_normal_shutdown(Alert, StateName, State0), + ssl_connection:handle_normal_shutdown(Alert, StateName, State0), {stop, {shutdown, own_alert}} end; handle_info({CloseTag, Socket}, StateName, @@ -354,7 +370,7 @@ handle_info({CloseTag, Socket}, StateName, %%invalidate_session(Role, Host, Port, Session) ok end, - handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), {stop, {shutdown, transport_closed}}; handle_info(Msg, StateName, State) -> ssl_connection:handle_info(Msg, StateName, State). @@ -362,6 +378,51 @@ handle_info(Msg, StateName, State) -> handle_call(Event, From, StateName, State) -> ssl_connection:handle_call(Event, From, StateName, State, ?MODULE). +handle_common_event(internal, #alert{} = Alert, StateName, + #state{negotiated_version = Version} = State) -> + ssl_connection:handle_own_alert(Alert, Version, StateName, State); + +%%% DTLS record protocol level handshake messages +handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE} = Record, + StateName, + #state{protocol_buffers = + #protocol_buffers{dtls_packets = Packets0, + dtls_fragment_state = HsState0} = Buffers, + negotiated_version = Version} = State0) -> + try + {Packets1, HsState} = dtls_handshake:get_dtls_handshake(Record, HsState0), + State = + State0#state{protocol_buffers = + Buffers#protocol_buffers{dtls_fragment_state = HsState}}, + Events = dtls_handshake_events(Packets0 ++ Packets1), + case StateName of + connection -> + ssl_connection:hibernate_after(StateName, State, Events); + _ -> + {next_state, StateName, State, Events} + end + catch throw:#alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, Version, StateName, State0) + end; +%%% DTLS record protocol level application data messages +handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) -> + {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]}; +%%% DTLS record protocol level change cipher messages +handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> + {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]}; +%%% DTLS record protocol level Alert messages +handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, + #state{negotiated_version = Version} = State) -> + case decode_alerts(EncAlerts) of + Alerts = [_|_] -> + handle_alerts(Alerts, {next_state, StateName, State}); + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, Version, StateName, State) + end; +%% Ignore unknown TLS record level protocol messages +handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) -> + {next_state, StateName, State}. + %%-------------------------------------------------------------------- %% Description:This function is called by a gen_fsm when it is about %% to terminate. It should be the opposite of Module:init/1 and do any @@ -376,7 +437,7 @@ terminate(Reason, StateName, State) -> %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, StateName, State, _Extra) -> - {?GEN_STATEM_CB_MODE, StateName, State}. + {ok, StateName, State}. format_status(Type, Data) -> ssl_connection:format_status(Type, Data). @@ -384,10 +445,21 @@ format_status(Type, Data) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- + +dtls_handshake_events([]) -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake)); +dtls_handshake_events(Packets) -> + lists:map(fun(Packet) -> + {next_event, internal, {handshake, Packet}} + end, Packets). + + encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> {Seq, ConnectionStates} = sequence(ConnectionStates0), {EncHandshake, Frag} = dtls_handshake:encode_handshake(Handshake, Version, Seq), - Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake), + %% DTLS does not have an equivalent version to SSLv2. So v2 hello compatibility + %% will always be false + Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake, false), {Frag, ConnectionStates, Hist}. encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> @@ -423,12 +495,12 @@ encode_handshake_record(_Version, _Epoch, _Space, _MsgType, _MsgSeq, _Len, <<>>, encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin, Offset, MRS, Encoded0, CS0) -> MaxFragmentLen = Space - 25, - case Bin of - <<BinFragment:MaxFragmentLen/bytes, Rest/binary>> -> - ok; + {BinFragment, Rest} = + case Bin of + <<BinFragment0:MaxFragmentLen/bytes, Rest0/binary>> -> + {BinFragment0, Rest0}; _ -> - BinFragment = Bin, - Rest = <<>> + {Bin, <<>>} end, FragLength = byte_size(BinFragment), Frag = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragLength), BinFragment], @@ -459,13 +531,13 @@ finish_pack_records({[], Acc}) -> finish_pack_records({Buf, Acc}) -> lists:reverse([lists:reverse(Buf)|Acc]). -%% decode_alerts(Bin) -> -%% ssl_alert:decode(Bin). +decode_alerts(Bin) -> + ssl_alert:decode(Bin). initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, - ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), + ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> @@ -548,7 +620,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> {Record, State} = next_record(State0), next_event(StateName, Record, State); _ -> - {Record, State} = read_application_data(<<>>, State0), + {Record, State} = ssl_connection:read_application_data(<<>>, State0), next_event(StateName, Record, State) end. @@ -560,7 +632,7 @@ next_event(connection = StateName, no_record, State0, Actions) -> {no_record, State} -> ssl_connection:hibernate_after(StateName, State, Actions); {#ssl_tls{} = Record, State} -> - {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]}; + {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; {#alert{} = Alert, State} -> {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} end; @@ -569,20 +641,11 @@ next_event(StateName, Record, State, Actions) -> no_record -> {next_state, StateName, State, Actions}; #ssl_tls{} = Record -> - {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]}; + {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; #alert{} = Alert -> {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} end. -read_application_data(_,State) -> - {#ssl_tls{fragment = <<"place holder">>}, State}. - -handle_own_alert(_,_,_, State) -> %% Place holder - {stop, {shutdown, own_alert}, State}. - -handle_normal_shutdown(_, _, _State) -> %% Place holder - ok. - %% TODO This generates dialyzer warnings, has to be handled differently. %% handle_packet(Address, Port, Packet) -> %% try dtls_record:get_dtls_records(Packet, <<>>) of @@ -631,5 +694,34 @@ handle_normal_shutdown(_, _, _State) -> %% Place holder %% address_to_bin({A,B,C,D,E,F,G,H}, Port) -> %% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>. -sequence(#connection_states{dtls_write_msg_seq = Seq} = CS) -> - {Seq, CS#connection_states{dtls_write_msg_seq = Seq + 1}}. +sequence(#{write_msg_seq := Seq} = ConnectionState) -> + {Seq, ConnectionState#{write_msg_seq => Seq + 1}}. + +renegotiate(#state{role = client} = State, Actions) -> + %% Handle same way as if server requested + %% the renegotiation + Hs0 = ssl_handshake:init_handshake_history(), + {next_state, connection, State#state{tls_handshake_history = Hs0, + protocol_buffers = #protocol_buffers{}}, + [{next_event, internal, #hello_request{}} | Actions]}; + +renegotiate(#state{role = server, + connection_states = CS0} = State0, Actions) -> + HelloRequest = ssl_handshake:hello_request(), + CS = CS0#{write_msg_seq => 0}, + State1 = send_handshake(HelloRequest, + State0#state{connection_states = + CS}), + Hs0 = ssl_handshake:init_handshake_history(), + {Record, State} = next_record(State1#state{tls_handshake_history = Hs0, + protocol_buffers = #protocol_buffers{}}), + next_event(hello, Record, State, Actions). + +handle_alerts([], Result) -> + Result; +handle_alerts(_, {stop,_} = Stop) -> + Stop; +handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> + handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)); +handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> + handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)). diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 5a799cf441..c6535d5928 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -35,7 +35,7 @@ %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- --spec client_hello(host(), inet:port_number(), #connection_states{}, +-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(), #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% @@ -48,7 +48,7 @@ client_hello(Host, Port, ConnectionStates, SslOpts, Cache, CacheCb, Renegotiation, OwnCert). %%-------------------------------------------------------------------- --spec client_hello(host(), inet:port_number(), term(), #connection_states{}, +-spec client_hello(host(), inet:port_number(), term(), ssl_record:connection_states(), #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% @@ -61,7 +61,7 @@ client_hello(Host, Port, Cookie, ConnectionStates, Cache, CacheCb, Renegotiation, OwnCert) -> Version = dtls_record:highest_protocol_version(Versions), Pending = ssl_record:pending_connection_state(ConnectionStates, read), - SecParams = Pending#connection_state.security_parameters, + SecParams = maps:get(security_parameters, Pending), CipherSuites = ssl_handshake:available_suites(UserSuites, Version), Extensions = ssl_handshake:client_hello_extensions(Host, dtls_v1:corresponding_tls_version(Version), CipherSuites, @@ -445,29 +445,23 @@ enc_handshake(#client_hello{client_version = {Major, Minor}, cookie = Cookie, cipher_suites = CipherSuites, compression_methods = CompMethods, - extensions = HelloExtensions}, Version) -> + extensions = HelloExtensions}, _Version) -> SIDLength = byte_size(SessionID), - BinCookie = enc_client_hello_cookie(Version, Cookie), + CookieLength = byte_size(Cookie), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions), - + {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, - BinCookie/binary, + ?BYTE(CookieLength), Cookie/binary, ?UINT16(CsLength), BinCipherSuites/binary, ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>}; enc_handshake(HandshakeMsg, Version) -> ssl_handshake:encode_handshake(HandshakeMsg, Version). -enc_client_hello_cookie(_, <<>>) -> - <<>>; -enc_client_hello_cookie(_, Cookie) -> - CookieLength = byte_size(Cookie), - <<?BYTE(CookieLength), Cookie/binary>>. - decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, ?BYTE(Cookie_length), Cookie:Cookie_length/binary, diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 5387fcafa8..8a6e2d315c 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -30,7 +30,7 @@ -include("ssl_cipher.hrl"). %% Handling of incoming data --export([get_dtls_records/2]). +-export([get_dtls_records/2, init_connection_states/2]). %% Decoding -export([decode_cipher_text/2]). @@ -58,7 +58,26 @@ %%==================================================================== %% Internal application API %%==================================================================== - +%%-------------------------------------------------------------------- +-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) -> + ssl_record:connection_states(). +%% % + % +%% Description: Creates a connection_states record with appropriate +%% values for the initial SSL connection setup. +%%-------------------------------------------------------------------- +init_connection_states(Role, BeastMitigation) -> + ConnectionEnd = ssl_record:record_protocol_role(Role), + Current = initial_connection_state(ConnectionEnd, BeastMitigation), + Pending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation), + #{write_msg_seq => 0, + prvious_read => undefined, + current_read => Current, + pending_read => Pending, + prvious_write => undefined, + current_write => Current, + pending_write => Pending}. + %%-------------------------------------------------------------------- -spec get_dtls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}. %% @@ -122,63 +141,59 @@ get_dtls_records_aux(Data, Acc) -> end. encode_plain_text(Type, Version, Data, - #connection_states{current_write = - #connection_state{ - epoch = Epoch, - sequence_number = Seq, - compression_state=CompS0, - security_parameters= - #security_parameters{ - cipher_type = ?AEAD, - compression_algorithm=CompAlg} - }= WriteState0} = ConnectionStates) -> + #{current_write := + #{epoch := Epoch, + sequence_number := Seq, + compression_state := CompS0, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + compression_algorithm = CompAlg} + }= WriteState0} = ConnectionStates) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - WriteState1 = WriteState0#connection_state{compression_state = CompS1}, + WriteState1 = WriteState0#{compression_state => CompS1}, AAD = calc_aad(Type, Version, Epoch, Seq), {CipherFragment, WriteState} = ssl_record:cipher_aead(dtls_v1:corresponding_tls_version(Version), Comp, WriteState1, AAD), CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment), - {CipherText, ConnectionStates#connection_states{current_write = - WriteState#connection_state{sequence_number = Seq +1}}}; + {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}}; encode_plain_text(Type, Version, Data, - #connection_states{current_write=#connection_state{ - epoch = Epoch, - sequence_number = Seq, - compression_state=CompS0, - security_parameters= - #security_parameters{compression_algorithm=CompAlg} - }= WriteState0} = ConnectionStates) -> + #{current_write := + #{epoch := Epoch, + sequence_number := Seq, + compression_state := CompS0, + security_parameters := + #security_parameters{compression_algorithm = CompAlg} + }= WriteState0} = ConnectionStates) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - WriteState1 = WriteState0#connection_state{compression_state = CompS1}, + WriteState1 = WriteState0#{compression_state => CompS1}, MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp), {CipherFragment, WriteState} = ssl_record:cipher(dtls_v1:corresponding_tls_version(Version), Comp, WriteState1, MacHash), CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment), - {CipherText, ConnectionStates#connection_states{current_write = - WriteState#connection_state{sequence_number = Seq +1}}}. + {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}}. decode_cipher_text(#ssl_tls{type = Type, version = Version, epoch = Epoch, sequence_number = Seq, fragment = CipherFragment} = CipherText, - #connection_states{current_read = - #connection_state{ - compression_state = CompressionS0, - security_parameters= - #security_parameters{ - cipher_type = ?AEAD, - compression_algorithm=CompAlg} - } = ReadState0}= ConnnectionStates0) -> + #{current_read := + #{compression_state := CompressionS0, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + compression_algorithm = CompAlg} + } = ReadState0} = ConnnectionStates0) -> AAD = calc_aad(Type, Version, Epoch, Seq), case ssl_record:decipher_aead(dtls_v1:corresponding_tls_version(Version), CipherFragment, ReadState0, AAD) of {PlainFragment, ReadState1} -> {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - compression_state = CompressionS1}}, + ConnnectionStates = ConnnectionStates0#{ + current_read => ReadState1#{ + compression_state => CompressionS1}}, {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; #alert{} = Alert -> Alert @@ -188,13 +203,12 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, epoch = Epoch, sequence_number = Seq, fragment = CipherFragment} = CipherText, - #connection_states{current_read = - #connection_state{ - compression_state = CompressionS0, - security_parameters= - #security_parameters{ - compression_algorithm=CompAlg} - } = ReadState0}= ConnnectionStates0) -> + #{current_read := + #{compression_state := CompressionS0, + security_parameters := + #security_parameters{ + compression_algorithm = CompAlg} + } = ReadState0}= ConnnectionStates0) -> {PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version), CipherFragment, ReadState0, true), MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment), @@ -202,17 +216,17 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, true -> {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - compression_state = CompressionS1}}, + ConnnectionStates = ConnnectionStates0#{ + current_read => ReadState1#{ + compression_state => CompressionS1}}, {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end. %%-------------------------------------------------------------------- --spec encode_change_cipher_spec(dtls_version(), #connection_states{}) -> - {iolist(), #connection_states{}}. +-spec encode_change_cipher_spec(dtls_version(), ssl_record:connection_states()) -> + {iolist(), ssl_record:connection_states()}. %% %% Description: Encodes a change_cipher_spec-message to send on the ssl socket. %%-------------------------------------------------------------------- @@ -352,92 +366,87 @@ is_acceptable_version(Version, Versions) -> %%-------------------------------------------------------------------- --spec init_connection_state_seq(dtls_version(), #connection_states{}) -> - #connection_state{}. +-spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) -> + ssl_record:connection_state(). %% %% Description: Copy the read sequence number to the write sequence number %% This is only valid for DTLS in the first client_hello %%-------------------------------------------------------------------- init_connection_state_seq({254, _}, - #connection_states{ - current_read = Read = #connection_state{epoch = 0}, - current_write = Write = #connection_state{epoch = 0}} = CS0) -> - CS0#connection_states{current_write = - Write#connection_state{ - sequence_number = Read#connection_state.sequence_number}}; + #{current_read := #{epoch := 0} = Read, + current_write := #{epoch := 0} = Write} = CS0) -> + Seq = maps:get(sequence_number, Read), + CS0#{current_write => Write#{sequence_number => Seq}}; init_connection_state_seq(_, CS) -> CS. %%-------------------------------------------------------- --spec current_connection_state_epoch(#connection_states{}, read | write) -> +-spec current_connection_state_epoch(ssl_record:connection_states(), read | write) -> integer(). %% %% Description: Returns the epoch the connection_state record %% that is currently defined as the current conection state. %%-------------------------------------------------------------------- -current_connection_state_epoch(#connection_states{current_read = Current}, +current_connection_state_epoch(#{current_read := Current}, read) -> - Current#connection_state.epoch; -current_connection_state_epoch(#connection_states{current_write = Current}, + maps:get(epoch, Current); +current_connection_state_epoch(#{current_write := Current}, write) -> - Current#connection_state.epoch. + maps:get(epoch, Current). %%-------------------------------------------------------------------- --spec connection_state_by_epoch(#connection_states{}, integer(), read | write) -> - #connection_state{}. +-spec connection_state_by_epoch(ssl_record:connection_states(), integer(), read | write) -> + ssl_record:connection_state(). %% %% Description: Returns the instance of the connection_state record %% that is defined by the Epoch. %%-------------------------------------------------------------------- -connection_state_by_epoch(#connection_states{current_read = CS}, Epoch, read) - when CS#connection_state.epoch == Epoch -> +connection_state_by_epoch(#{current_read := #{epoch := Epoch}} = CS, Epoch, read) -> CS; -connection_state_by_epoch(#connection_states{pending_read = CS}, Epoch, read) - when CS#connection_state.epoch == Epoch -> +connection_state_by_epoch(#{pending_read := #{epoch := Epoch}} = CS, Epoch, read) -> CS; -connection_state_by_epoch(#connection_states{current_write = CS}, Epoch, write) - when CS#connection_state.epoch == Epoch -> +connection_state_by_epoch(#{current_write := #{epoch := Epoch}} = CS, Epoch, write) -> CS; -connection_state_by_epoch(#connection_states{pending_write = CS}, Epoch, write) - when CS#connection_state.epoch == Epoch -> +connection_state_by_epoch(#{pending_write := #{epoch := Epoch}} = CS, Epoch, write) -> CS. %%-------------------------------------------------------------------- --spec set_connection_state_by_epoch(#connection_states{}, - #connection_state{}, read | write) - -> #connection_states{}. +-spec set_connection_state_by_epoch(ssl_record:connection_states(), + ssl_record:connection_state(), read | write) + -> ssl_record:connection_states(). %% %% Description: Returns the instance of the connection_state record %% that is defined by the Epoch. %%-------------------------------------------------------------------- -set_connection_state_by_epoch(ConnectionStates0 = - #connection_states{current_read = CS}, - NewCS = #connection_state{epoch = Epoch}, read) - when CS#connection_state.epoch == Epoch -> - ConnectionStates0#connection_states{current_read = NewCS}; - -set_connection_state_by_epoch(ConnectionStates0 = - #connection_states{pending_read = CS}, - NewCS = #connection_state{epoch = Epoch}, read) - when CS#connection_state.epoch == Epoch -> - ConnectionStates0#connection_states{pending_read = NewCS}; - -set_connection_state_by_epoch(ConnectionStates0 = - #connection_states{current_write = CS}, - NewCS = #connection_state{epoch = Epoch}, write) - when CS#connection_state.epoch == Epoch -> - ConnectionStates0#connection_states{current_write = NewCS}; - -set_connection_state_by_epoch(ConnectionStates0 = - #connection_states{pending_write = CS}, - NewCS = #connection_state{epoch = Epoch}, write) - when CS#connection_state.epoch == Epoch -> - ConnectionStates0#connection_states{pending_write = NewCS}. +set_connection_state_by_epoch(#{current_read := #{epoch := Epoch}} = ConnectionStates0, + NewCS = #{epoch := Epoch}, read) -> + ConnectionStates0#{current_read => NewCS}; +set_connection_state_by_epoch(#{pending_read := #{epoch := Epoch}} = ConnectionStates0, + NewCS = #{epoch := Epoch}, read) -> + ConnectionStates0#{pending_read => NewCS}; +set_connection_state_by_epoch(#{current_write := #{epoch := Epoch}} = ConnectionStates0, + NewCS = #{epoch := Epoch}, write) -> + ConnectionStates0#{current_write => NewCS}; +set_connection_state_by_epoch(#{pending_write := #{epoch := Epoch}} = ConnectionStates0, +NewCS = #{epoch := Epoch}, write) -> + ConnectionStates0#{pending_write => NewCS}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - +initial_connection_state(ConnectionEnd, BeastMitigation) -> + #{security_parameters => + ssl_record:initial_security_params(ConnectionEnd), + epoch => 0, + sequence_number => 1, + beast_mitigation => BeastMitigation, + compression_state => undefined, + cipher_state => undefined, + mac_secret => undefined, + secure_renegotiation => undefined, + client_verify_data => undefined, + server_verify_data => undefined + }. lowest_list_protocol_version(Ver, []) -> Ver; @@ -454,8 +463,8 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Epoch, Seq, Fragment) -> [<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(Seq), ?UINT16(Length)>>, Fragment]. -calc_mac_hash(#connection_state{mac_secret = MacSecret, - security_parameters = #security_parameters{mac_algorithm = MacAlg}}, +calc_mac_hash(#{mac_secret := MacSecret, + security_parameters := #security_parameters{mac_algorithm = MacAlg}}, Type, Version, Epoch, SeqNo, Fragment) -> Length = erlang:iolist_size(Fragment), NewSeq = (Epoch bsl 48) + SeqNo, diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index b26efbd88f..00b0513891 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -55,7 +55,7 @@ {applications, [crypto, public_key, kernel, stdlib]}, {env, []}, {mod, {ssl_app, []}}, - {runtime_dependencies, ["stdlib-3.0","public_key-1.2","kernel-3.0", + {runtime_dependencies, ["stdlib-3.1","public_key-1.2","kernel-3.0", "erts-7.0","crypto-3.3", "inets-5.10.7"]}]}. diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 11728128c4..32252386b4 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,18 +1,11 @@ %% -*- erlang -*- {"%VSN%", [ - {<<"7\\..*">>, [{restart_application, ssl}]}, - {<<"6\\..*">>, [{restart_application, ssl}]}, - {<<"5\\..*">>, [{restart_application, ssl}]}, - {<<"4\\..*">>, [{restart_application, ssl}]}, - {<<"3\\..*">>, [{restart_application, ssl}]} + {<<"^8[.]0([.][0-9]+)?$">>, [{restart_application, ssl}]}, + {<<"^[3-7][.][^.].*">>, [{restart_application, ssl}]} ], [ - {<<"7\\..*">>, [{restart_application, ssl}]}, - {<<"6\\..*">>, [{restart_application, ssl}]}, - {<<"5\\..*">>, [{restart_application, ssl}]}, - {<<"4\\..*">>, [{restart_application, ssl}]}, - {<<"3\\..*">>, [{restart_application, ssl}]} - ] + {<<"^8[.]0([.][0-9]+)?$">>, [{restart_application, ssl}]}, + {<<"^[3-7][.][^.].*">>, [{restart_application, ssl}]} + ] }. - diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index d2aeb3258f..27b753af2e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -43,7 +43,7 @@ renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1, connection_information/1, connection_information/2]). %% Misc --export([handle_options/2]). +-export([handle_options/2, tls_version/1]). -deprecated({negotiated_next_protocol, 1, next_major_release}). -deprecated({connection_info, 1, next_major_release}). @@ -607,6 +607,11 @@ format_error(Error) -> Other end. +tls_version({3, _} = Version) -> + Version; +tls_version({254, _} = Version) -> + dtls_v1:corresponding_tls_version(Version). + %%%-------------------------------------------------------------- %%% Internal functions %%%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index db71b16d80..05dfb4c1b3 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -39,8 +39,8 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec encode(#alert{}, ssl_record:ssl_version(), #connection_states{}) -> - {iolist(), #connection_states{}}. +-spec encode(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) -> + {iolist(), ssl_record:connection_states()}. %% %% Description: Encodes an alert %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 3ec3f50e05..f359655d85 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -64,7 +64,7 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) - {ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self), {self, IssuerId}; false -> - other_issuer(OtpCert, BinCert, CertDbHandle) + other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) end, case SignedAndIssuerID of @@ -200,7 +200,7 @@ certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) -> {_, true = SelfSigned} -> certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned); {{error, issuer_not_found}, SelfSigned} -> - case find_issuer(OtpCert, BinCert, CertDbHandle) of + case find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) of {ok, {SerialNr, Issuer}} -> certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned); @@ -232,7 +232,7 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned {ok, undefined, lists:reverse(Chain)} end. -find_issuer(OtpCert, BinCert, CertDbHandle) -> +find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) -> IsIssuerFun = fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) -> case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of @@ -250,12 +250,24 @@ find_issuer(OtpCert, BinCert, CertDbHandle) -> Acc end, - try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of - issuer_not_found -> - {error, issuer_not_found} - catch - {ok, _IssuerId} = Return -> - Return + if is_reference(CertsDbRef) -> % actual DB exists + try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _IssuerId} = Return -> + Return + end; + is_tuple(CertsDbRef), element(1,CertsDbRef) =:= extracted -> % cache bypass byproduct + {extracted, CertsData} = CertsDbRef, + DB = [Entry || {decoded, Entry} <- CertsData], + try lists:foldl(IsIssuerFun, issuer_not_found, DB) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _IssuerId} = Return -> + Return + end end. is_valid_extkey_usage(KeyUse, client) -> @@ -281,12 +293,12 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith subjectPublicKey = Key}) -> {Key, Params}. -other_issuer(OtpCert, BinCert, CertDbHandle) -> +other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) -> case public_key:pkix_issuer_id(OtpCert, other) of {ok, IssuerId} -> {other, IssuerId}; {error, issuer_not_found} -> - case find_issuer(OtpCert, BinCert, CertDbHandle) of + case find_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) of {ok, IssuerId} -> {other, IssuerId}; Other -> diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 53282998d0..304d1706f5 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -58,6 +58,13 @@ -export([handle_info/3, handle_call/5, handle_session/7, ssl_config/3, prepare_connection/2, hibernate_after/3]). +%% Alert and close handling +-export([handle_own_alert/4,handle_alert/3, + handle_normal_shutdown/3 + ]). + +%% Data handling +-export([write_application_data/3, read_application_data/2]). %%==================================================================== %% Internal application API @@ -264,7 +271,7 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> %%-------------------------------------------------------------------- -spec handle_session(#server_hello{}, ssl_record:ssl_version(), - binary(), #connection_states{}, _,_, #state{}) -> + binary(), ssl_record:connection_states(), _,_, #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- handle_session(#server_hello{cipher_suite = CipherSuite, @@ -272,19 +279,21 @@ handle_session(#server_hello{cipher_suite = CipherSuite, Version, NewId, ConnectionStates, ProtoExt, Protocol0, #state{session = #session{session_id = OldId}, negotiated_version = ReqVersion, - negotiated_protocol = CurrentProtocol} = State0) -> + negotiated_protocol = CurrentProtocol} = State0) -> {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - {ExpectNPN, Protocol} = case Protocol0 of - undefined -> {false, CurrentProtocol}; - _ -> {ProtoExt =:= npn, Protocol0} - end, + {ExpectNPN, Protocol} = case Protocol0 of + undefined -> + {false, CurrentProtocol}; + _ -> + {ProtoExt =:= npn, Protocol0} + end, State = State0#state{key_algorithm = KeyAlgorithm, - negotiated_version = Version, + negotiated_version = Version, connection_states = ConnectionStates, premaster_secret = PremasterSecret, expecting_next_protocol_negotiation = ExpectNPN, @@ -382,7 +391,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished, session = #session{master_secret = MasterSecret}, connection_states = ConnectionStates0} = State0, Connection) -> - case ssl_handshake:verify_connection(Version, Finished, client, + case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, client, get_current_prf(ConnectionStates0, write), MasterSecret, Handshake) of verified -> @@ -392,7 +401,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished, expecting_finished = false}, Connection), Connection:next_event(connection, Record, State); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, abbreviated, State0) + handle_own_alert(Alert, Version, abbreviated, State0) end; abbreviated(internal, #finished{verify_data = Data} = Finished, @@ -400,7 +409,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished, session = #session{master_secret = MasterSecret}, negotiated_version = Version, connection_states = ConnectionStates0} = State0, Connection) -> - case ssl_handshake:verify_connection(Version, Finished, server, + case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, server, get_pending_prf(ConnectionStates0, write), MasterSecret, Handshake0) of verified -> @@ -412,7 +421,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished, {Record, State} = prepare_connection(State1#state{expecting_finished = false}, Connection), Connection:next_event(connection, Record, State); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, abbreviated, State0) + handle_own_alert(Alert, Version, abbreviated, State0) end; %% only allowed to send next_protocol message after change cipher spec @@ -452,9 +461,9 @@ certify(internal, #certificate{asn1_certificates = []}, #state{role = server, negotiated_version = Version, ssl_options = #ssl_options{verify = verify_peer, fail_if_no_peer_cert = true}} = - State, Connection) -> + State, _Connection) -> Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), - Connection:handle_own_alert(Alert, Version, certify, State); + handle_own_alert(Alert, Version, certify, State); certify(internal, #certificate{asn1_certificates = []}, #state{role = server, @@ -469,9 +478,9 @@ certify(internal, #certificate{}, #state{role = server, negotiated_version = Version, ssl_options = #ssl_options{verify = verify_none}} = - State, Connection) -> + State, _Connection) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate), - Connection:handle_own_alert(Alert, Version, certify, State); + handle_own_alert(Alert, Version, certify, State); certify(internal, #certificate{} = Cert, #state{negotiated_version = Version, @@ -492,7 +501,7 @@ certify(internal, #certificate{} = Cert, handle_peer_cert(Role, PeerCert, PublicKeyInfo, State#state{client_certificate_requested = false}, Connection); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State) + handle_own_alert(Alert, Version, certify, State) end; certify(internal, #server_key_exchange{exchange_keys = Keys}, @@ -506,10 +515,10 @@ certify(internal, #server_key_exchange{exchange_keys = Keys}, Alg == psk; Alg == dhe_psk; Alg == rsa_psk; Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> - Params = ssl_handshake:decode_server_key(Keys, Alg, Version), + Params = ssl_handshake:decode_server_key(Keys, Alg, ssl:tls_version(Version)), %% Use negotiated value if TLS-1.2 otherwhise return default - HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, Version), + HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, ssl:tls_version(Version)), case is_anonymous(Alg) of true -> @@ -517,26 +526,25 @@ certify(internal, #server_key_exchange{exchange_keys = Keys}, State#state{hashsign_algorithm = HashSign}, Connection); false -> case ssl_handshake:verify_server_key(Params, HashSign, - ConnectionStates, Version, PubKeyInfo) of + ConnectionStates, ssl:tls_version(Version), PubKeyInfo) of true -> calculate_secret(Params#server_key_params.params, State#state{hashsign_algorithm = HashSign}, Connection); false -> - Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR), + handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR), Version, certify, State) end end; -certify(internal, #certificate_request{hashsign_algorithms = HashSigns}, +certify(internal, #certificate_request{} = CertRequest, #state{session = #session{own_certificate = Cert}, - key_algorithm = KeyExAlg, + role = client, ssl_options = #ssl_options{signature_algs = SupportedHashSigns}, negotiated_version = Version} = State0, Connection) -> - - case ssl_handshake:select_hashsign(HashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of + case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of #alert {} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0); + handle_own_alert(Alert, Version, certify, State0); NegotiatedHashSign -> {Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}), Connection:next_event(certify, Record, @@ -555,7 +563,7 @@ certify(internal, #server_hello_done{}, when Alg == psk -> case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup) of #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0); + handle_own_alert(Alert, Version, certify, State0); PremasterSecret -> State = master_secret(PremasterSecret, State0#state{premaster_secret = PremasterSecret}), @@ -576,7 +584,7 @@ certify(internal, #server_hello_done{}, case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup, RSAPremasterSecret) of #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0); + handle_own_alert(Alert, Version, certify, State0); PremasterSecret -> State = master_secret(PremasterSecret, State0#state{premaster_secret = RSAPremasterSecret}), @@ -590,13 +598,13 @@ certify(internal, #server_hello_done{}, negotiated_version = Version, premaster_secret = undefined, role = client} = State0, Connection) -> - case ssl_handshake:master_secret(record_cb(Connection), Version, Session, + case ssl_handshake:master_secret(ssl:tls_version(Version), Session, ConnectionStates0, client) of {MasterSecret, ConnectionStates} -> State = State0#state{connection_states = ConnectionStates}, client_certify_and_key_exchange(State, Connection); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0) + handle_own_alert(Alert, Version, certify, State0) end; %% Master secret is calculated from premaster_secret @@ -606,7 +614,7 @@ certify(internal, #server_hello_done{}, negotiated_version = Version, premaster_secret = PremasterSecret, role = client} = State0, Connection) -> - case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret, + case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret, ConnectionStates0, client) of {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, @@ -614,7 +622,7 @@ certify(internal, #server_hello_done{}, session = Session}, client_certify_and_key_exchange(State, Connection); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0) + handle_own_alert(Alert, Version, certify, State0) end; certify(internal = Type, #client_key_exchange{} = Msg, @@ -628,11 +636,11 @@ certify(internal = Type, #client_key_exchange{} = Msg, certify(internal, #client_key_exchange{exchange_keys = Keys}, State = #state{key_algorithm = KeyAlg, negotiated_version = Version}, Connection) -> try - certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), + certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, ssl:tls_version(Version)), State, Connection) catch #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State) + handle_own_alert(Alert, Version, certify, State) end; certify(Type, Msg, State, Connection) -> @@ -663,21 +671,21 @@ cipher(internal, #certificate_verify{signature = Signature, %% Use negotiated value if TLS-1.2 otherwhise return default HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, Version), case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, HashSign, MasterSecret, Handshake) of + ssl:tls_version(Version), HashSign, MasterSecret, Handshake) of valid -> {Record, State} = Connection:next_record(State0), Connection:next_event(cipher, Record, State#state{cert_hashsign_algorithm = HashSign}); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, cipher, State0) + handle_own_alert(Alert, Version, cipher, State0) end; %% client must send a next protocol message if we are expecting it cipher(internal, #finished{}, #state{role = server, expecting_next_protocol_negotiation = true, negotiated_protocol = undefined, negotiated_version = Version} = State0, - Connection) -> - Connection:handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0); + _Connection) -> + handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0); cipher(internal, #finished{verify_data = Data} = Finished, #state{negotiated_version = Version, @@ -689,7 +697,7 @@ cipher(internal, #finished{verify_data = Data} = Finished, = Session0, connection_states = ConnectionStates0, tls_handshake_history = Handshake0} = State, Connection) -> - case ssl_handshake:verify_connection(Version, Finished, + case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, opposite_role(Role), get_current_prf(ConnectionStates0, read), MasterSecret, Handshake0) of @@ -698,7 +706,7 @@ cipher(internal, #finished{verify_data = Data} = Finished, cipher_role(Role, Data, Session, State#state{expecting_finished = false}, Connection); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, cipher, State) + handle_own_alert(Alert, Version, cipher, State) end; %% only allowed to send next_protocol message after change cipher spec @@ -731,7 +739,7 @@ connection({call, From}, {application_data, Data}, %% parallize send and receive decoding and not block the receiver %% if sending is overloading the socket. try - Connection:write_application_data(Data, From, State) + write_application_data(Data, From, State) catch throw:Error -> hibernate_after(connection, State, [{reply, From, Error}]) end; @@ -802,35 +810,37 @@ handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #st when StateName =/= connection -> {keep_state_and_data}; handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName, - #state{tls_handshake_history = Hs0} = State0, Connection) -> + #state{tls_handshake_history = Hs0, + ssl_options = #ssl_options{v2_hello_compatible = V2HComp}} = State0, + Connection) -> + + PossibleSNI = Connection:select_sni_extension(Handshake), %% This function handles client SNI hello extension when Handshake is %% a client_hello, which needs to be determined by the connection callback. %% In other cases this is a noop - State = Connection:handle_sni_extension(Handshake, State0), - HsHist = ssl_handshake:update_handshake_history(Hs0, Raw), + State = handle_sni_extension(PossibleSNI, State0), + HsHist = ssl_handshake:update_handshake_history(Hs0, Raw, V2HComp), {next_state, StateName, State#state{tls_handshake_history = HsHist}, [{next_event, internal, Handshake}]}; -handle_common_event(internal, {tls_record, TLSRecord}, StateName, State, Connection) -> - Connection:handle_common_event(internal, TLSRecord, StateName, State); +handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, State, Connection) -> + Connection:handle_common_event(internal, TLSorDTLSRecord, StateName, State); handle_common_event(timeout, hibernate, _, _, _) -> {keep_state_and_data, [hibernate]}; handle_common_event(internal, {application_data, Data}, StateName, State0, Connection) -> - case Connection:read_application_data(Data, State0) of + case read_application_data(Data, State0) of {stop, Reason, State} -> {stop, Reason, State}; {Record, State} -> Connection:next_event(StateName, Record, State) end; handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName, - #state{negotiated_version = Version} = State, Connection) -> - Connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, + #state{negotiated_version = Version} = State, _) -> + handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, StateName, State); -handle_common_event(internal, _, _, _, _) -> - {keep_state_and_data, [postpone]}; handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State, - Connection) -> + _) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), - Connection:handle_own_alert(Alert, Version, {StateName, Msg}, State). + handle_own_alert(Alert, Version, {StateName, Msg}, State). handle_call({application_data, _Data}, _, _, _, _) -> %% In renegotiation priorities handshake, send data when handshake is finished @@ -908,9 +918,8 @@ handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection -> handle_call({prf, Secret, Label, Seed, WantedLength}, From, _, #state{connection_states = ConnectionStates, negotiated_version = Version}, _) -> - ConnectionState = + #{security_parameters := SecParams} = ssl_record:current_connection_state(ConnectionStates, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{master_secret = MasterSecret, client_random = ClientRandom, server_random = ServerRandom, @@ -925,7 +934,7 @@ handle_call({prf, Secret, Label, Seed, WantedLength}, From, _, (client_random, Acc) -> [ClientRandom|Acc]; (server_random, Acc) -> [ServerRandom|Acc] end, [], Seed)), - ssl_handshake:prf(Version, PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength) + ssl_handshake:prf(ssl:tls_version(Version), PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength) catch exit:_ -> {error, badarg}; error:Reason -> {error, Reason} @@ -936,20 +945,19 @@ handle_call(_,_,_,_,_) -> handle_info({ErrorTag, Socket, econnaborted}, StateName, #state{socket = Socket, transport_cb = Transport, - start_or_recv_from = StartFrom, role = Role, protocol_cb = Connection, + start_or_recv_from = StartFrom, role = Role, error_tag = ErrorTag, tracker = Tracker} = State) when StateName =/= connection -> - Connection:alert_user(Transport, Tracker,Socket, - StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role), + alert_user(Transport, Tracker,Socket, + StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection), {stop, normal, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, - protocol_cb = Connection, error_tag = ErrorTag} = State) -> Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]), error_logger:info_report(Report), - Connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), {stop, normal, State}; handle_info({'DOWN', MonitorRef, _, _, _}, _, @@ -1052,13 +1060,124 @@ format_status(terminate, [_, StateName, State]) -> ssl_options = NewOptions, flight_buffer = ?SECRET_PRINTOUT} }}]}]. + +%%-------------------------------------------------------------------- +%%% +%%-------------------------------------------------------------------- +write_application_data(Data0, From, + #state{socket = Socket, + negotiated_version = Version, + protocol_cb = Connection, + transport_cb = Transport, + connection_states = ConnectionStates0, + socket_options = SockOpts, + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) -> + Data = encode_packet(Data0, SockOpts), + + case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of + true -> + Connection:renegotiate(State#state{renegotiation = {true, internal}}, + [{next_event, {call, From}, {application_data, Data0}}]); + false -> + {Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0), + Result = Transport:send(Socket, Msgs), + ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates}, + [{reply, From, Result}]) + end. + +read_application_data(Data, #state{user_application = {_Mon, Pid}, + socket = Socket, + protocol_cb = Connection, + transport_cb = Transport, + socket_options = SOpts, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + timer = Timer, + user_data_buffer = Buffer0, + tracker = Tracker} = State0) -> + Buffer1 = if + Buffer0 =:= <<>> -> Data; + Data =:= <<>> -> Buffer0; + true -> <<Buffer0/binary, Data/binary>> + end, + case get_data(SOpts, BytesToRead, Buffer1) of + {ok, ClientData, Buffer} -> % Send data + SocketOpt = deliver_app_data(Transport, Socket, SOpts, + ClientData, Pid, RecvFrom, Tracker, Connection), + cancel_timer(Timer), + State = State0#state{user_data_buffer = Buffer, + start_or_recv_from = undefined, + timer = undefined, + bytes_to_read = undefined, + socket_options = SocketOpt + }, + if + SocketOpt#socket_options.active =:= false; Buffer =:= <<>> -> + %% Passive mode, wait for active once or recv + %% Active and empty, get more data + Connection:next_record_if_active(State); + true -> %% We have more data + read_application_data(<<>>, State) + end; + {more, Buffer} -> % no reply, we need more data + Connection:next_record(State0#state{user_data_buffer = Buffer}); + {passive, Buffer} -> + Connection:next_record_if_active(State0#state{user_data_buffer = Buffer}); + {error,_Reason} -> %% Invalid packet in packet mode + deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection), + {stop, normal, State0} + end. +%%-------------------------------------------------------------------- +%%% +%%-------------------------------------------------------------------- +handle_alert(#alert{level = ?FATAL} = Alert, StateName, + #state{socket = Socket, transport_cb = Transport, + protocol_cb = Connection, + ssl_options = SslOpts, start_or_recv_from = From, host = Host, + port = Port, session = Session, user_application = {_Mon, Pid}, + role = Role, socket_options = Opts, tracker = Tracker}) -> + invalidate_session(Role, Host, Port, Session), + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), + alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection), + {stop, normal}; + +handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, + StateName, State) -> + handle_normal_shutdown(Alert, StateName, State), + {stop, {shutdown, peer_close}}; + +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) -> + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), + handle_normal_shutdown(Alert, StateName, State), + {stop, {shutdown, peer_close}}; + +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{ssl_options = SslOpts, renegotiation = {true, From}, + protocol_cb = Connection} = State0) -> + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), + gen_statem:reply(From, {error, renegotiation_rejected}), + {Record, State} = Connection:next_record(State0), + %% Go back to connection! + Connection:next_event(connection, Record, State); + +%% Gracefully log and ignore all other warning alerts +handle_alert(#alert{level = ?WARNING} = Alert, StateName, + #state{ssl_options = SslOpts, protocol_cb = Connection} = State0) -> + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), + {Record, State} = Connection:next_record(State0), + Connection:next_event(StateName, Record, State). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- connection_info(#state{sni_hostname = SNIHostname, session = #session{cipher_suite = CipherSuite}, - negotiated_version = Version, ssl_options = Opts}) -> - [{protocol, tls_record:protocol_version(Version)}, + protocol_cb = Connection, + negotiated_version = {_,_} = Version, + ssl_options = Opts}) -> + RecordCB = record_cb(Connection), + [{protocol, RecordCB:protocol_version(Version)}, {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)}, {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts). @@ -1070,7 +1189,7 @@ do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocol = State0, Connection) when is_atom(Type) -> ServerHello = - ssl_handshake:server_hello(SessId, Version, ConnectionStates0, ServerHelloExt), + ssl_handshake:server_hello(SessId, ssl:tls_version(Version), ConnectionStates0, ServerHelloExt), State = server_hello(ServerHello, State0#state{expecting_next_protocol_negotiation = NextProtocols =/= undefined}, Connection), @@ -1097,14 +1216,14 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, Connection:next_event(certify, Record, State) catch #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, hello, State0) + handle_own_alert(Alert, Version, hello, State0) end. resumed_server_hello(#state{session = Session, connection_states = ConnectionStates0, negotiated_version = Version} = State0, Connection) -> - case ssl_handshake:master_secret(record_cb(Connection), Version, Session, + case ssl_handshake:master_secret(ssl:tls_version(Version), Session, ConnectionStates0, server) of {_, ConnectionStates1} -> State1 = State0#state{connection_states = ConnectionStates1, @@ -1114,7 +1233,7 @@ resumed_server_hello(#state{session = Session, {Record, State} = Connection:next_record(State2), Connection:next_event(abbreviated, Record, State); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, hello, State0) + handle_own_alert(Alert, Version, hello, State0) end. server_hello(ServerHello, State0, Connection) -> @@ -1180,7 +1299,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, tls_handshake_history = Handshake0} = State, Connection) -> case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, HashSign, PrivateKey, Handshake0) of + ssl:tls_version(Version), HashSign, PrivateKey, Handshake0) of #certificate_verify{} = Verified -> Connection:queue_handshake(Verified, State); ignore -> @@ -1203,7 +1322,7 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = Connection:next_event(cipher, Record, State) catch throw:#alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0) + handle_own_alert(Alert, Version, certify, State0) end. do_client_certify_and_key_exchange(State0, Connection) -> @@ -1294,12 +1413,11 @@ key_exchange(#state{role = server, key_algorithm = Algo, Algo == dhe_rsa; Algo == dh_anon -> DHKeys = public_key:generate_key(Params), - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dh, DHKeys, Params, + Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), {dh, DHKeys, Params, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1319,15 +1437,15 @@ key_exchange(#state{role = server, key_algorithm = Algo, Algo == ecdh_anon -> ECDHKeys = public_key:generate_key(select_curve(State0)), - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKeys, - HashSignAlgo, ClientRandom, - ServerRandom, - PrivateKey}), + Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), + {ecdh, ECDHKeys, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), State = Connection:queue_handshake(Msg, State0), State#state{diffie_hellman_keys = ECDHKeys}; @@ -1341,14 +1459,14 @@ key_exchange(#state{role = server, key_algorithm = psk, connection_states = ConnectionStates0, negotiated_version = Version } = State0, Connection) -> - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint, - HashSignAlgo, ClientRandom, - ServerRandom, + Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), + {psk, PskIdentityHint, + HashSignAlgo, ClientRandom, + ServerRandom, PrivateKey}), Connection:queue_handshake(Msg, State0); @@ -1361,16 +1479,16 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk, negotiated_version = Version } = State0, Connection) -> DHKeys = public_key:generate_key(Params), - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, - PskIdentityHint, DHKeys, Params, - HashSignAlgo, ClientRandom, - ServerRandom, - PrivateKey}), + Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), + {dhe_psk, + PskIdentityHint, DHKeys, Params, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), State = Connection:queue_handshake(Msg, State0), State#state{diffie_hellman_keys = DHKeys}; @@ -1384,15 +1502,15 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk, connection_states = ConnectionStates0, negotiated_version = Version } = State0, Connection) -> - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint, - HashSignAlgo, ClientRandom, - ServerRandom, - PrivateKey}), + Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), + {psk, PskIdentityHint, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), Connection:queue_handshake(Msg, State0); key_exchange(#state{role = server, key_algorithm = Algo, @@ -1413,15 +1531,15 @@ key_exchange(#state{role = server, key_algorithm = Algo, Keys0 = {_,_} -> Keys0 end, - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {srp, Keys, SrpParams, - HashSignAlgo, ClientRandom, - ServerRandom, - PrivateKey}), + Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), + {srp, Keys, SrpParams, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), State = Connection:queue_handshake(Msg, State0), State#state{srp_params = SrpParams, srp_keys = Keys}; @@ -1431,7 +1549,7 @@ key_exchange(#state{role = client, public_key_info = PublicKeyInfo, negotiated_version = Version, premaster_secret = PremasterSecret} = State0, Connection) -> - Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo), + Msg = rsa_key_exchange(ssl:tls_version(Version), PremasterSecret, PublicKeyInfo), Connection:queue_handshake(Msg, State0); key_exchange(#state{role = client, @@ -1442,7 +1560,7 @@ key_exchange(#state{role = client, when Algorithm == dhe_dss; Algorithm == dhe_rsa; Algorithm == dh_anon -> - Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}), + Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dh, DhPubKey}), Connection:queue_handshake(Msg, State0); key_exchange(#state{role = client, @@ -1452,14 +1570,14 @@ key_exchange(#state{role = client, when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa; Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa; Algorithm == ecdh_anon -> - Msg = ssl_handshake:key_exchange(client, Version, {ecdh, Keys}), + Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdh, Keys}), Connection:queue_handshake(Msg, State0); key_exchange(#state{role = client, ssl_options = SslOpts, key_algorithm = psk, negotiated_version = Version} = State0, Connection) -> - Msg = ssl_handshake:key_exchange(client, Version, + Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {psk, SslOpts#ssl_options.psk_identity}), Connection:queue_handshake(Msg, State0); @@ -1468,7 +1586,7 @@ key_exchange(#state{role = client, key_algorithm = dhe_psk, negotiated_version = Version, diffie_hellman_keys = {DhPubKey, _}} = State0, Connection) -> - Msg = ssl_handshake:key_exchange(client, Version, + Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}), Connection:queue_handshake(Msg, State0); @@ -1479,7 +1597,7 @@ key_exchange(#state{role = client, negotiated_version = Version, premaster_secret = PremasterSecret} = State0, Connection) -> - Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity, + Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity, PremasterSecret, PublicKeyInfo), Connection:queue_handshake(Msg, State0); @@ -1491,7 +1609,7 @@ key_exchange(#state{role = client, when Algorithm == srp_dss; Algorithm == srp_rsa; Algorithm == srp_anon -> - Msg = ssl_handshake:key_exchange(client, Version, {srp, ClientPubKey}), + Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {srp, ClientPubKey}), Connection:queue_handshake(Msg, State0). rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) @@ -1504,7 +1622,7 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) Algorithm == ?sha384WithRSAEncryption; Algorithm == ?sha512WithRSAEncryption -> - ssl_handshake:key_exchange(client, Version, + ssl_handshake:key_exchange(client, ssl:tls_version(Version), {premaster_secret, PremasterSecret, PublicKeyInfo}); rsa_key_exchange(_, _, _) -> @@ -1521,7 +1639,7 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, Algorithm == ?sha384WithRSAEncryption; Algorithm == ?sha512WithRSAEncryption -> - ssl_handshake:key_exchange(client, Version, + ssl_handshake:key_exchange(client, ssl:tls_version(Version), {psk_premaster_secret, PskIdentity, PremasterSecret, PublicKeyInfo}); rsa_psk_key_exchange(_, _, _, _) -> @@ -1533,12 +1651,14 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer, cert_db = CertDbHandle, cert_db_ref = CertDbRef, negotiated_version = Version} = State0, Connection) -> - #connection_state{security_parameters = - #security_parameters{cipher_suite = CipherSuite}} = + #{security_parameters := + #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates0, read), - HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, Version, [Version]), + TLSVersion = ssl:tls_version(Version), + HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, + TLSVersion, [TLSVersion]), Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef, - HashSigns, Version), + HashSigns, TLSVersion), State = Connection:queue_handshake(Msg, State0), State#state{client_certificate_requested = true}; @@ -1551,7 +1671,7 @@ calculate_master_secret(PremasterSecret, connection_states = ConnectionStates0, session = Session0} = State0, Connection, _Current, Next) -> - case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret, + case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret, ConnectionStates0, server) of {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, @@ -1560,7 +1680,7 @@ calculate_master_secret(PremasterSecret, {Record, State} = Connection:next_record(State1), Connection:next_event(Next, Record, State); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, certify, State0) + handle_own_alert(Alert, Version, certify, State0) end. finalize_handshake(State0, StateName, Connection) -> @@ -1593,7 +1713,7 @@ finished(#state{role = Role, negotiated_version = Version, connection_states = ConnectionStates0, tls_handshake_history = Handshake0} = State0, StateName, Connection) -> MasterSecret = Session#session.master_secret, - Finished = ssl_handshake:finished(Version, Role, + Finished = ssl_handshake:finished(ssl:tls_version(Version), Role, get_current_prf(ConnectionStates0, write), MasterSecret, Handshake0), ConnectionStates = save_verify_data(Role, Finished, ConnectionStates0, StateName), @@ -1658,7 +1778,7 @@ master_secret(#alert{} = Alert, _) -> master_secret(PremasterSecret, #state{session = Session, negotiated_version = Version, role = Role, connection_states = ConnectionStates0} = State) -> - case ssl_handshake:master_secret(tls_record, Version, PremasterSecret, + case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret, ConnectionStates0, Role) of {MasterSecret, ConnectionStates} -> State#state{ @@ -1741,11 +1861,11 @@ is_anonymous(_) -> false. get_current_prf(CStates, Direction) -> - CS = ssl_record:current_connection_state(CStates, Direction), - CS#connection_state.security_parameters#security_parameters.prf_algorithm. + #{security_parameters := SecParams} = ssl_record:current_connection_state(CStates, Direction), + SecParams#security_parameters.prf_algorithm. get_pending_prf(CStates, Direction) -> - CS = ssl_record:pending_connection_state(CStates, Direction), - CS#connection_state.security_parameters#security_parameters.prf_algorithm. + #{security_parameters := SecParams} = ssl_record:pending_connection_state(CStates, Direction), + SecParams#security_parameters.prf_algorithm. opposite_role(client) -> server; @@ -1967,7 +2087,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, session_cache = Cache, session_cache_cb = CacheCb} = State0) -> Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), - case ssl_handshake:master_secret(tls_record, Version, Session, + case ssl_handshake:master_secret(ssl:tls_version(Version), Session, ConnectionStates0, client) of {_, ConnectionStates} -> {Record, State} = @@ -1976,7 +2096,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, session = Session}), Connection:next_event(abbreviated, Record, State); #alert{} = Alert -> - Connection:handle_own_alert(Alert, Version, hello, State0) + handle_own_alert(Alert, Version, hello, State0) end. make_premaster_secret({MajVer, MinVer}, rsa) -> @@ -2043,7 +2163,7 @@ handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = <<>>} = %% user_data_buffer =/= <<>> handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection} = State0) -> - case Connection:read_application_data(<<>>, State0) of + case read_application_data(<<>>, State0) of {stop, Reason, State} -> {stop, Reason, State}; {Record, State1} -> @@ -2057,3 +2177,264 @@ handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection} Stop end end. + +encode_packet(Data, #socket_options{packet=Packet}) -> + case Packet of + 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1); + 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1); + 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1); + _ -> Data + end. + +encode_size_packet(Bin, Size, Max) -> + Len = erlang:byte_size(Bin), + case Len > Max of + true -> throw({error, {badarg, {packet_to_large, Len, Max}}}); + false -> <<Len:Size, Bin/binary>> + end. + +time_to_renegotiate(_Data, + #{current_write := #{sequence_number := Num}}, + RenegotiateAt) -> + + %% We could do test: + %% is_time_to_renegotiate((erlang:byte_size(_Data) div ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt), + %% but we chose to have a some what lower renegotiateAt and a much cheaper test + is_time_to_renegotiate(Num, RenegotiateAt). + +is_time_to_renegotiate(N, M) when N < M-> + false; +is_time_to_renegotiate(_,_) -> + true. + + +%% Picks ClientData +get_data(_, _, <<>>) -> + {more, <<>>}; +%% Recv timed out save buffer data until next recv +get_data(#socket_options{active=false}, undefined, Buffer) -> + {passive, Buffer}; +get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) + when Raw =:= raw; Raw =:= 0 -> %% Raw Mode + if + Active =/= false orelse BytesToRead =:= 0 -> + %% Active true or once, or passive mode recv(0) + {ok, Buffer, <<>>}; + byte_size(Buffer) >= BytesToRead -> + %% Passive Mode, recv(Bytes) + <<Data:BytesToRead/binary, Rest/binary>> = Buffer, + {ok, Data, Rest}; + true -> + %% Passive Mode not enough data + {more, Buffer} + end; +get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) -> + PacketOpts = [{packet_size, Size}], + case decode_packet(Type, Buffer, PacketOpts) of + {more, _} -> + {more, Buffer}; + Decoded -> + Decoded + end. + +decode_packet({http, headers}, Buffer, PacketOpts) -> + decode_packet(httph, Buffer, PacketOpts); +decode_packet({http_bin, headers}, Buffer, PacketOpts) -> + decode_packet(httph_bin, Buffer, PacketOpts); +decode_packet(Type, Buffer, PacketOpts) -> + erlang:decode_packet(Type, Buffer, PacketOpts). + +%% Just like with gen_tcp sockets, an ssl socket that has been configured with +%% {packet, http} (or {packet, http_bin}) will automatically switch to expect +%% HTTP headers after it sees a HTTP Request or HTTP Response line. We +%% represent the current state as follows: +%% #socket_options.packet =:= http: Expect a HTTP Request/Response line +%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers +%% Note that if the user has explicitly configured the socket to expect +%% HTTP headers using the {packet, httph} option, we don't do any automatic +%% switching of states. +deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type}, + Data, Pid, From, Tracker, Connection) -> + send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker, Connection)), + SO = case Data of + {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), + ((Type =:= http) or (Type =:= http_bin)) -> + SOpts#socket_options{packet={Type, headers}}; + http_eoh when tuple_size(Type) =:= 2 -> + % End of headers - expect another Request/Response line + {Type1, headers} = Type, + SOpts#socket_options{packet=Type1}; + _ -> + SOpts + end, + case Active of + once -> + SO#socket_options{active=false}; + _ -> + SO + end. + +format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet, + header = Header}, Data, _, _) -> + {ok, do_format_reply(Mode, Packet, Header, Data)}; +format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet, + header = Header}, Data, Tracker, Connection) -> + {ssl, ssl_socket:socket(self(), Transport, Socket, Connection, Tracker), + do_format_reply(Mode, Packet, Header, Data)}. + +deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker, Connection) -> + send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker, Connection)). + +format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _, _) -> + {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}; +format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker, Connection) -> + {ssl_error, ssl_socket:socket(self(), Transport, Socket, Connection, Tracker), + {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}. + +do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode + header(N, Data); +do_format_reply(binary, _, _, Data) -> + Data; +do_format_reply(list, Packet, _, Data) + when Packet == http; Packet == {http, headers}; + Packet == http_bin; Packet == {http_bin, headers}; + Packet == httph; Packet == httph_bin -> + Data; +do_format_reply(list, _,_, Data) -> + binary_to_list(Data). + +header(0, <<>>) -> + <<>>; +header(_, <<>>) -> + []; +header(0, Binary) -> + Binary; +header(N, Binary) -> + <<?BYTE(ByteN), NewBinary/binary>> = Binary, + [ByteN | header(N-1, NewBinary)]. + +send_or_reply(false, _Pid, From, Data) when From =/= undefined -> + gen_statem:reply(From, Data); +%% Can happen when handling own alert or tcp error/close and there is +%% no outstanding gen_fsm sync events +send_or_reply(false, no_pid, _, _) -> + ok; +send_or_reply(_, Pid, _From, Data) -> + send_user(Pid, Data). + +send_user(Pid, Msg) -> + Pid ! Msg. + +alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, Connection) -> + alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, Connection); +alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role, Connection) -> + alert_user(Transport, Tracker, Socket, From, Alert, Role, Connection). + +alert_user(Transport, Tracker, Socket, From, Alert, Role, Connection) -> + alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role, Connection). + +alert_user(_, _, _, false = Active, Pid, From, Alert, Role, _) when From =/= undefined -> + %% If there is an outstanding ssl_accept | recv + %% From will be defined and send_or_reply will + %% send the appropriate error message. + ReasonCode = ssl_alert:reason_code(Alert, Role), + send_or_reply(Active, Pid, From, {error, ReasonCode}); +alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connection) -> + case ssl_alert:reason_code(Alert, Role) of + closed -> + send_or_reply(Active, Pid, From, + {ssl_closed, ssl_socket:socket(self(), + Transport, Socket, Connection, Tracker)}); + ReasonCode -> + send_or_reply(Active, Pid, From, + {ssl_error, ssl_socket:socket(self(), + Transport, Socket, Connection, Tracker), ReasonCode}) + end. + +log_alert(true, Info, Alert) -> + Txt = ssl_alert:alert_txt(Alert), + error_logger:format("SSL: ~p: ~s\n", [Info, Txt]); +log_alert(false, _, _) -> + ok. + +handle_own_alert(Alert, Version, StateName, + #state{transport_cb = Transport, + socket = Socket, + connection_states = ConnectionStates, + ssl_options = SslOpts} = State) -> + try %% Try to tell the other side + {BinMsg, _} = + ssl_alert:encode(Alert, Version, ConnectionStates), + Transport:send(Socket, BinMsg) + catch _:_ -> %% Can crash if we are in a uninitialized state + ignore + end, + try %% Try to tell the local user + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), + handle_normal_shutdown(Alert,StateName, State) + catch _:_ -> + ok + end, + {stop, {shutdown, own_alert}}. + +handle_normal_shutdown(Alert, _, #state{socket = Socket, + transport_cb = Transport, + protocol_cb = Connection, + start_or_recv_from = StartFrom, + tracker = Tracker, + role = Role, renegotiation = {false, first}}) -> + alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role, Connection); + +handle_normal_shutdown(Alert, StateName, #state{socket = Socket, + socket_options = Opts, + transport_cb = Transport, + protocol_cb = Connection, + user_application = {_Mon, Pid}, + tracker = Tracker, + start_or_recv_from = RecvFrom, role = Role}) -> + alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection). + +invalidate_session(client, Host, Port, Session) -> + ssl_manager:invalidate_session(Host, Port, Session); +invalidate_session(server, _, Port, Session) -> + ssl_manager:invalidate_session(Port, Session). + +handle_sni_extension(undefined, State) -> + State; +handle_sni_extension(#sni{hostname = Hostname}, State0) -> + NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname), + case NewOptions of + undefined -> + State0; + _ -> + {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} = + ssl_config:init(NewOptions, State0#state.role), + State0#state{ + session = State0#state.session#session{own_certificate = OwnCert}, + file_ref_db = FileRefHandle, + cert_db_ref = Ref, + cert_db = CertDbHandle, + crl_db = CRLDbHandle, + session_cache = CacheHandle, + private_key = Key, + diffie_hellman_params = DHParams, + ssl_options = NewOptions, + sni_hostname = Hostname + } + end. + +update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) -> + SSLOption = + case OrigSSLOptions#ssl_options.sni_fun of + undefined -> + proplists:get_value(SNIHostname, + OrigSSLOptions#ssl_options.sni_hosts); + SNIFun -> + SNIFun(SNIHostname) + end, + case SSLOption of + undefined -> + undefined; + _ -> + ssl:handle_options(SSLOption, OrigSSLOptions) + end. diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index 4b54943ddf..f1e612a41b 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -46,7 +46,7 @@ socket :: port(), ssl_options :: #ssl_options{}, socket_options :: #socket_options{}, - connection_states :: #connection_states{} | secret_printout(), + connection_states :: ssl_record:connection_states() | secret_printout(), protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout() | 'undefined', diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl index d9f21e04ac..01be1fb9ab 100644 --- a/lib/ssl/src/ssl_crl.erl +++ b/lib/ssl/src/ssl_crl.erl @@ -47,7 +47,7 @@ trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbHandle) -> {ok, unknown_crl_ca, []} end. -find_issuer(CRL, {Db,_}) -> +find_issuer(CRL, {Db,DbRef}) -> Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)), IsIssuerFun = fun({_Key, {_Der,ErlCertCandidate}}, Acc) -> @@ -55,15 +55,27 @@ find_issuer(CRL, {Db,_}) -> (_, Acc) -> Acc end, - - try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of - issuer_not_found -> - {error, issuer_not_found} - catch - {ok, _} = Result -> - Result + if is_reference(DbRef) -> % actual DB exists + try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _} = Result -> + Result + end; + is_tuple(DbRef), element(1,DbRef) =:= extracted -> % cache bypass byproduct + {extracted, CertsData} = DbRef, + Certs = [Entry || {decoded, Entry} <- CertsData], + try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _} = Result -> + Result + end end. + verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) -> TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate, case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 9c3fe9d73b..5b51ac0916 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -51,8 +51,8 @@ %% Handle handshake messages -export([certify/10, client_certificate_verify/6, certificate_verify/6, verify_signature/5, - master_secret/5, server_key_exchange_hash/2, verify_connection/6, - init_handshake_history/0, update_handshake_history/2, verify_server_key/5 + master_secret/4, server_key_exchange_hash/2, verify_connection/6, + init_handshake_history/0, update_handshake_history/3, verify_server_key/5 ]). %% Encode/Decode @@ -74,7 +74,7 @@ ]). %% MISC --export([select_version/3, prf/6, select_hashsign/5, +-export([select_version/3, prf/6, select_hashsign/4, select_hashsign/5, select_hashsign_algs/3, premaster_secret/2, premaster_secret/3, premaster_secret/4]). @@ -94,15 +94,14 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- --spec server_hello(#session{}, ssl_record:ssl_version(), #connection_states{}, +-spec server_hello(#session{}, ssl_record:ssl_version(), ssl_record:connection_states(), #hello_extensions{}) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- server_hello(SessionId, Version, ConnectionStates, Extensions) -> - Pending = ssl_record:pending_connection_state(ConnectionStates, read), - SecParams = Pending#connection_state.security_parameters, - + #{security_parameters := SecParams} = + ssl_record:pending_connection_state(ConnectionStates, read), #server_hello{server_version = Version, cipher_suite = SecParams#security_parameters.cipher_suite, compression_method = @@ -335,9 +334,8 @@ verify_server_key(#server_key_params{params_bin = EncParams, signature = Signature}, HashSign = {HashAlgo, _}, ConnectionStates, Version, PubKeyInfo) -> - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), - SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, Hash = server_key_exchange_hash(HashAlgo, @@ -447,7 +445,7 @@ init_handshake_history() -> {[], []}. %%-------------------------------------------------------------------- --spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term()) -> +-spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term(), boolean()) -> ssl_handshake:ssl_handshake_history(). %% %% Description: Update the handshake history buffer with Data. @@ -457,14 +455,14 @@ update_handshake_history(Handshake, % special-case SSL2 client hello ?UINT16(CSLength), ?UINT16(0), ?UINT16(CDLength), CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>) -> + ChallengeData:CDLength/binary>>, true) -> update_handshake_history(Handshake, <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), ?UINT16(CSLength), ?UINT16(0), ?UINT16(CDLength), CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>); -update_handshake_history({Handshake0, _Prev}, Data) -> + ChallengeData:CDLength/binary>>, true); +update_handshake_history({Handshake0, _Prev}, Data, _) -> {[Data|Handshake0], Handshake0}. %% %%-------------------------------------------------------------------- @@ -581,7 +579,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) -> {atom(), atom()} | undefined | #alert{}. %% -%% Description: Handles signature_algorithms extension +%% Description: Handles signature_algorithms hello extension (server) %%-------------------------------------------------------------------- select_hashsign(_, undefined, _, _, _Version) -> {null, anon}; @@ -593,14 +591,17 @@ select_hashsign(HashSigns, Cert, KeyExAlgo, select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns, {Major, Minor}) when Major >= 3 andalso Minor >= 3 -> #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), - #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, - Sign = cert_sign(Algo), - case lists:filter(fun({sha, dsa = S}) when S == Sign -> - true; - ({_, dsa}) -> - false; - ({_, _} = Algos) -> - is_acceptable_hash_sign(Algos, Sign, KeyExAlgo, SupportedHashSigns); + #'OTPCertificate'{tbsCertificate = TBSCert, + signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp), + #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} = + TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + + Sign = sign_algo(SignAlgo), + SubSing = sign_algo(SubjAlgo), + + case lists:filter(fun({_, S} = Algos) when S == Sign -> + is_acceptable_hash_sign(Algos, Sign, + SubSing, KeyExAlgo, SupportedHashSigns); (_) -> false end, HashSigns) of @@ -613,6 +614,49 @@ select_hashsign(_, Cert, _, _, Version) -> #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, select_hashsign_algs(undefined, Algo, Version). +%%-------------------------------------------------------------------- +-spec select_hashsign(#certificate_request{}, binary(), + [atom()], ssl_record:ssl_version()) -> + {atom(), atom()} | #alert{}. + +%% +%% Description: Handles signature algorithms selection for certificate requests (client) +%%-------------------------------------------------------------------- +select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3-> + %% There client does not have a certificate and will send an empty reply, the server may fail + %% or accept the connection by its own preference. No signature algorihms needed as there is + %% no certificate to verify. + {undefined, undefined}; + +select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns}, + certificate_types = Types}, Cert, SupportedHashSigns, + {Major, Minor}) when Major >= 3 andalso Minor >= 3-> + #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), + #'OTPCertificate'{tbsCertificate = TBSCert, + signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp), + #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} = + TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + + Sign = sign_algo(SignAlgo), + SubSign = sign_algo(SubjAlgo), + + case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of + true -> + case lists:filter(fun({_, S} = Algos) when S == SubSign -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); + (_) -> + false + end, HashSigns) of + [] -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm); + [HashSign | _] -> + HashSign + end; + false -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm) + end; +select_hashsign(#certificate_request{}, Cert, _, Version) -> + select_hashsign(undefined, Cert, undefined, [], Version). %%-------------------------------------------------------------------- -spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) -> @@ -648,34 +692,34 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) -> select_hashsign_algs(undefined, ?'id-dsa', _) -> {sha, dsa}. + %%-------------------------------------------------------------------- --spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{}, - client | server) -> {binary(), #connection_states{}} | #alert{}. +-spec master_secret(ssl_record:ssl_version(), #session{} | binary(), ssl_record:connection_states(), + client | server) -> {binary(), ssl_record:connection_states()} | #alert{}. %% %% Description: Sets or calculates the master secret and calculate keys, %% updating the pending connection states. The Mastersecret and the update %% connection states are returned or an alert if the calculation fails. %%------------------------------------------------------------------- -master_secret(RecordCB, Version, #session{master_secret = Mastersecret}, +master_secret(Version, #session{master_secret = Mastersecret}, ConnectionStates, Role) -> - ConnectionState = + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), - SecParams = ConnectionState#connection_state.security_parameters, - try master_secret(RecordCB, Version, Mastersecret, SecParams, + try master_secret(Version, Mastersecret, SecParams, ConnectionStates, Role) catch exit:_ -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, key_calculation_failure) end; -master_secret(RecordCB, Version, PremasterSecret, ConnectionStates, Role) -> - ConnectionState = +master_secret(Version, PremasterSecret, ConnectionStates, Role) -> + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), - SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{prf_algorithm = PrfAlgo, client_random = ClientRandom, server_random = ServerRandom} = SecParams, - try master_secret(RecordCB, Version, + try master_secret(Version, calc_master_secret(Version,PrfAlgo,PremasterSecret, ClientRandom, ServerRandom), SecParams, ConnectionStates, Role) @@ -1143,11 +1187,13 @@ certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 -> end; certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == rsa; + KeyExchange == dh_rsa; KeyExchange == dhe_rsa; KeyExchange == ecdhe_rsa -> <<?BYTE(?RSA_SIGN)>>; -certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dhe_dss; +certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_dss; + KeyExchange == dhe_dss; KeyExchange == srp_dss -> <<?BYTE(?DSS_SIGN)>>; @@ -1170,13 +1216,18 @@ certificate_authorities(CertDbHandle, CertDbRef) -> end, list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]). -certificate_authorities_from_db(CertDbHandle, CertDbRef) -> +certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) -> ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef -> [Cert | Acc]; (_, Acc) -> Acc end, - ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle). + ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle); +certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) -> + %% Cache disabled, Ref contains data + lists:foldl(fun({decoded, {_Key,Cert}}, Acc) -> [Cert | Acc] end, + [], CertDbData). + %%-------------Extension handling -------------------------------- @@ -1256,35 +1307,67 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, end. select_version(RecordCB, ClientVersion, Versions) -> - ServerVersion = RecordCB:highest_protocol_version(Versions), - RecordCB:lowest_protocol_version(ClientVersion, ServerVersion). + do_select_version(RecordCB, ClientVersion, Versions). + +do_select_version(_, ClientVersion, []) -> + ClientVersion; +do_select_version(RecordCB, ClientVersion, [Version | Versions]) -> + case RecordCB:is_higher(Version, ClientVersion) of + true -> + %% Version too high for client - keep looking + do_select_version(RecordCB, ClientVersion, Versions); + false -> + %% Version ok for client - look for a higher + do_select_version(RecordCB, ClientVersion, Versions, Version) + end. +%% +do_select_version(_, _, [], GoodVersion) -> + GoodVersion; +do_select_version( + RecordCB, ClientVersion, [Version | Versions], GoodVersion) -> + BetterVersion = + case RecordCB:is_higher(Version, ClientVersion) of + true -> + %% Version too high for client + GoodVersion; + false -> + %% Version ok for client + case RecordCB:is_higher(Version, GoodVersion) of + true -> + %% Use higher version + Version; + false -> + GoodVersion + end + end, + do_select_version(RecordCB, ClientVersion, Versions, BetterVersion). renegotiation_info(_, client, _, false) -> #renegotiation_info{renegotiated_connection = undefined}; renegotiation_info(_RecordCB, server, ConnectionStates, false) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), - case CS#connection_state.secure_renegotiation of + ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), + case maps:get(secure_renegotiation, ConnectionState) of true -> #renegotiation_info{renegotiated_connection = ?byte(0)}; false -> #renegotiation_info{renegotiated_connection = undefined} end; renegotiation_info(_RecordCB, client, ConnectionStates, true) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), - case CS#connection_state.secure_renegotiation of + ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), + case maps:get(secure_renegotiation, ConnectionState) of true -> - Data = CS#connection_state.client_verify_data, + Data = maps:get(client_verify_data, ConnectionState), #renegotiation_info{renegotiated_connection = Data}; false -> #renegotiation_info{renegotiated_connection = undefined} end; renegotiation_info(_RecordCB, server, ConnectionStates, true) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), - case CS#connection_state.secure_renegotiation of + ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), + case maps:get(secure_renegotiation, ConnectionState) of true -> - CData = CS#connection_state.client_verify_data, - SData =CS#connection_state.server_verify_data, + CData = maps:get(client_verify_data, ConnectionState), + SData = maps:get(server_verify_data, ConnectionState), #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>}; false -> #renegotiation_info{renegotiated_connection = undefined} @@ -1307,9 +1390,9 @@ handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify}, ConnectionStates, true, _, _) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), - CData = CS#connection_state.client_verify_data, - SData = CS#connection_state.server_verify_data, + ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), + CData = maps:get(client_verify_data, ConnectionState), + SData = maps:get(server_verify_data, ConnectionState), case <<CData/binary, SData/binary>> == ClientServerVerify of true -> {ok, ConnectionStates}; @@ -1323,8 +1406,8 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co true -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); false -> - CS = ssl_record:current_connection_state(ConnectionStates, read), - Data = CS#connection_state.client_verify_data, + ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), + Data = maps:get(client_verify_data, ConnectionState), case Data == ClientVerify of true -> {ok, ConnectionStates}; @@ -1345,8 +1428,8 @@ handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, S end. handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), - case {SecureRenegotation, CS#connection_state.secure_renegotiation} of + ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), + case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of {_, true} -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure); {true, false} -> @@ -1564,7 +1647,7 @@ calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) -> calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) -> tls_v1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)). -master_secret(_RecordCB, Version, MasterSecret, +master_secret(Version, MasterSecret, #security_parameters{ bulk_cipher_algorithm = BCA, client_random = ClientRandom, @@ -1647,18 +1730,16 @@ hello_pending_connection_states(_RecordCB, Role, Version, CipherSuite, Random, C NewWriteSecParams, ConnectionStates). -hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random, +hello_security_parameters(client, Version, #{security_parameters := SecParams}, CipherSuite, Random, Compression) -> - SecParams = ConnectionState#connection_state.security_parameters, NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams), NewSecParams#security_parameters{ server_random = Random, compression_algorithm = Compression }; -hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random, +hello_security_parameters(server, Version, #{security_parameters := SecParams}, CipherSuite, Random, Compression) -> - SecParams = ConnectionState#connection_state.security_parameters, NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams), NewSecParams#security_parameters{ client_random = Random, @@ -2164,27 +2245,73 @@ distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) -> [{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs] end. -cert_sign(?rsaEncryption) -> +sign_algo(?rsaEncryption) -> rsa; -cert_sign(?'id-ecPublicKey') -> +sign_algo(?'id-ecPublicKey') -> ecdsa; -cert_sign(?'id-dsa') -> +sign_algo(?'id-dsa') -> dsa; -cert_sign(Alg) -> +sign_algo(Alg) -> {_, Sign} =public_key:pkix_sign_types(Alg), Sign. -is_acceptable_hash_sign({_, Sign} = Algos, Sign, _, SupportedHashSigns) -> - is_acceptable_hash_sign(Algos, SupportedHashSigns); -is_acceptable_hash_sign(Algos,_, KeyExAlgo, SupportedHashSigns) when KeyExAlgo == dh_ecdsa; - KeyExAlgo == ecdh_rsa; - KeyExAlgo == ecdh_ecdsa -> +is_acceptable_hash_sign(Algos, _, _, KeyExAlgo, SupportedHashSigns) when + KeyExAlgo == dh_dss; + KeyExAlgo == dh_rsa; + KeyExAlgo == dh_ecdsa -> + %% dh_* could be called only dh in TLS-1.2 + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign(Algos, rsa, ecdsa, ecdh_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, dhe_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, ecdhe_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, rsa, SupportedHashSigns) -> is_acceptable_hash_sign(Algos, SupportedHashSigns); -is_acceptable_hash_sign(_,_,_,_) -> - false. +is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, srp_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, rsa_psk, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, dhe_dss, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, srp_dss, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, _, dhe_ecdsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, ecdsa, ecdhe_ecdsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign(_, _, _, KeyExAlgo, _) when + KeyExAlgo == psk; + KeyExAlgo == dhe_psk; + KeyExAlgo == srp_anon; + KeyExAlgo == dh_anon; + KeyExAlgo == ecdhe_anon + -> + true; +is_acceptable_hash_sign(_,_, _,_,_) -> + false. + is_acceptable_hash_sign(Algos, SupportedHashSigns) -> lists:member(Algos, SupportedHashSigns). +is_acceptable_cert_type(Sign, _HashSigns, Types) -> + lists:member(sign_type(Sign), binary_to_list(Types)). + +is_supported_sign(Sign, HashSigns) -> + [] =/= lists:dropwhile(fun({_, S}) when S =/= Sign -> + true; + (_)-> + false + end, HashSigns). +sign_type(rsa) -> + ?RSA_SIGN; +sign_type(dsa) -> + ?DSS_SIGN; +sign_type(ecdsa) -> + ?ECDSA_SIGN. + + bad_key(#'DSAPrivateKey'{}) -> unacceptable_dsa_key; bad_key(#'RSAPrivateKey'{}) -> diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index c7dcbaabe9..5bd9521de7 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -115,13 +115,25 @@ start_link_dist(Opts) -> %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init({der, _} = Trustedcerts, Role, CRLCache) -> - call({connection_init, Trustedcerts, Role, CRLCache}); + case bypass_pem_cache() of + true -> + {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), + call({connection_init, Extracted, Role, CRLCache}); + false -> + call({connection_init, Trustedcerts, Role, CRLCache}) + end; connection_init(<<>> = Trustedcerts, Role, CRLCache) -> call({connection_init, Trustedcerts, Role, CRLCache}); connection_init(Trustedcerts, Role, CRLCache) -> - call({connection_init, Trustedcerts, Role, CRLCache}). + case bypass_pem_cache() of + true -> + {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), + call({connection_init, Extracted, Role, CRLCache}); + false -> + call({connection_init, Trustedcerts, Role, CRLCache}) + end. %%-------------------------------------------------------------------- -spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}. @@ -129,13 +141,18 @@ connection_init(Trustedcerts, Role, CRLCache) -> %% Description: Cache a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of - [{Content,_}] -> - {ok, Content}; - [Content] -> - {ok, Content}; - undefined -> - call({cache_pem, File}) + case bypass_pem_cache() of + true -> + ssl_pkix_db:decode_pem_file(File); + false -> + case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of + [{Content,_}] -> + {ok, Content}; + [Content] -> + {ok, Content}; + undefined -> + call({cache_pem, File}) + end end. %%-------------------------------------------------------------------- @@ -506,6 +523,14 @@ delay_time() -> ?CLEAN_SESSION_DB end. +bypass_pem_cache() -> + case application:get_env(ssl, bypass_pem_cache) of + {ok, Bool} when is_boolean(Bool) -> + Bool; + _ -> + false + end. + max_session_cache_size(CacheType) -> case application:get_env(ssl, CacheType) of {ok, Size} when is_integer(Size) -> diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl index b16903d7c7..0006ce14d9 100644 --- a/lib/ssl/src/ssl_pkix_db.erl +++ b/lib/ssl/src/ssl_pkix_db.erl @@ -29,10 +29,11 @@ -include_lib("kernel/include/file.hrl"). -export([create/0, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3, + extract_trusted_certs/1, remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1, ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2, lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3, - lookup/2]). + decode_pem_file/1, lookup/2]). %%==================================================================== %% Internal application API @@ -82,12 +83,22 @@ remove(Dbs) -> %% <SerialNumber, Issuer>. Ref is used as it is specified %% for each connection which certificates are trusted. %%-------------------------------------------------------------------- -lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> +lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) when is_reference(Ref) -> case lookup({Ref, SerialNumber, Issuer}, DbHandle) of undefined -> undefined; [Certs] -> {ok, Certs} + end; +lookup_trusted_cert(_DbHandle, {extracted,Certs}, SerialNumber, Issuer) -> + try + [throw(Cert) + || {decoded, {{_Ref,CertSerial,CertIssuer}, Cert}} <- Certs, + CertSerial =:= SerialNumber, CertIssuer =:= Issuer], + undefined + catch + Cert -> + {ok, Cert} end. lookup_cached_pem([_, _, PemChache | _], File) -> @@ -103,6 +114,9 @@ lookup_cached_pem(PemChache, File) -> %% runtime database. Returns Ref that should be handed to lookup_trusted_cert %% together with the cert serialnumber and issuer. %%-------------------------------------------------------------------- +add_trusted_certs(_Pid, {extracted, _} = Certs, _) -> + {ok, Certs}; + add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) -> NewRef = make_ref(), add_certs_from_der(DerList, NewRef, CertDb), @@ -122,6 +136,21 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache | _] = Db) -> undefined -> new_trusted_cert_entry(File, Db) end. + +extract_trusted_certs({der, DerList}) -> + {ok, {extracted, certs_from_der(DerList)}}; +extract_trusted_certs(File) -> + case file:read_file(File) of + {ok, PemBin} -> + Content = public_key:pem_decode(PemBin), + DerList = [Cert || {'Certificate', Cert, not_encrypted} <- Content], + {ok, {extracted, certs_from_der(DerList)}}; + Error -> + %% Have to simulate a failure happening in a server for + %% external handlers. + {error, {badmatch, Error}} + end. + %%-------------------------------------------------------------------- %% %% Description: Cache file as binary in DB @@ -141,6 +170,18 @@ cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache| _]) -> insert(File, {Content, Ref}, PemChache), {ok, Content}. +-spec decode_pem_file(binary()) -> {ok, term()}. +decode_pem_file(File) -> + case file:read_file(File) of + {ok, PemBin} -> + Content = public_key:pem_decode(PemBin), + {ok, Content}; + Error -> + %% Have to simulate a failure happening in a server for + %% external handlers. + {error, {badmatch, Error}} + end. + %%-------------------------------------------------------------------- -spec remove_trusted_certs(reference(), db_handle()) -> ok. %% @@ -203,6 +244,8 @@ select_cert_by_issuer(Cache, Issuer) -> %% %% Description: Updates a reference counter in a <Db>. %%-------------------------------------------------------------------- +ref_count({extracted, _}, _Db, _N) -> + not_cached; ref_count(Key, Db, N) -> ets:update_counter(Db,Key,N). @@ -248,23 +291,39 @@ add_certs_from_der(DerList, Ref, CertsDb) -> [Add(Cert) || Cert <- DerList], ok. +certs_from_der(DerList) -> + Ref = make_ref(), + [Decoded || Cert <- DerList, + Decoded <- [decode_certs(Ref, Cert)], + Decoded =/= undefined]. + add_certs_from_pem(PemEntries, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries], ok. add_certs(Cert, Ref, CertsDb) -> + try + {decoded, {Key, Val}} = decode_certs(Ref, Cert), + insert(Key, Val, CertsDb) + catch + error:_ -> + ok + end. + +decode_certs(Ref, Cert) -> try ErlCert = public_key:pkix_decode_cert(Cert, otp), TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber, Issuer = public_key:pkix_normalize_name( TBSCertificate#'OTPTBSCertificate'.issuer), - insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb) + {decoded, {{Ref, SerialNumber, Issuer}, {Cert, ErlCert}}} catch error:_ -> Report = io_lib:format("SSL WARNING: Ignoring a CA cert as " "it could not be correctly decoded.~n", []), - error_logger:info_report(Report) + error_logger:info_report(Report), + undefined end. new_trusted_cert_entry(File, [CertsDb, RefDb, _ | _] = Db) -> diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 5bb1c92c2d..71cd0279f3 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -30,8 +30,7 @@ -include("ssl_alert.hrl"). %% Connection state handling --export([init_connection_states/2, - current_connection_state/2, pending_connection_state/2, +-export([initial_security_params/1, current_connection_state/2, pending_connection_state/2, activate_pending_connection_state/2, set_security_params/3, set_mac_secret/4, @@ -39,7 +38,8 @@ set_pending_cipher_state/4, set_renegotiation_flag/2, set_client_verify_data/3, - set_server_verify_data/3]). + set_server_verify_data/3, + empty_connection_state/2, initial_connection_state/2, record_protocol_role/1]). %% Encoding records -export([encode_handshake/3, encode_alert_record/3, @@ -52,122 +52,92 @@ -export([cipher/4, decipher/4, is_correct_mac/2, cipher_aead/4, decipher_aead/4]). --export_type([ssl_version/0, ssl_atom_version/0]). +-export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]). -type ssl_version() :: {integer(), integer()}. -type ssl_atom_version() :: tls_record:tls_atom_version(). - +-type connection_states() :: term(). %% Map +-type connection_state() :: term(). %% Map %%==================================================================== %% Internal application API %%==================================================================== + %%-------------------------------------------------------------------- --spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled ) -> - #connection_states{}. -%% -%% Description: Creates a connection_states record with appropriate -%% values for the initial SSL connection setup. -%%-------------------------------------------------------------------- -init_connection_states(Role, BeastMitigation) -> - ConnectionEnd = record_protocol_role(Role), - Current = initial_connection_state(ConnectionEnd, BeastMitigation), - Pending = empty_connection_state(ConnectionEnd, BeastMitigation), - #connection_states{dtls_write_msg_seq = 1, % only used by dtls - current_read = Current, - pending_read = Pending, - current_write = Current, - pending_write = Pending - }. - -%%-------------------------------------------------------------------- --spec current_connection_state(#connection_states{}, read | write) -> - #connection_state{}. +-spec current_connection_state(connection_states(), read | write) -> + connection_state(). %% -%% Description: Returns the instance of the connection_state record +%% Description: Returns the instance of the connection_state map %% that is currently defined as the current conection state. %%-------------------------------------------------------------------- -current_connection_state(#connection_states{current_read = Current}, - read) -> - Current; -current_connection_state(#connection_states{current_write = Current}, - write) -> - Current. +current_connection_state(ConnectionStates, read) -> + maps:get(current_read, ConnectionStates); +current_connection_state(ConnectionStates, write) -> + maps:get(current_write, ConnectionStates). %%-------------------------------------------------------------------- --spec pending_connection_state(#connection_states{}, read | write) -> - term(). +-spec pending_connection_state(connection_states(), read | write) -> + connection_state(). %% -%% Description: Returns the instance of the connection_state record -%% that is currently defined as the pending conection state. +%% Description: Returns the instance of the connection_state map +%% that is pendingly defined as the pending conection state. %%-------------------------------------------------------------------- -pending_connection_state(#connection_states{pending_read = Pending}, - read) -> - Pending; -pending_connection_state(#connection_states{pending_write = Pending}, - write) -> - Pending. - +pending_connection_state(ConnectionStates, read) -> + maps:get(pending_read, ConnectionStates); +pending_connection_state(ConnectionStates, write) -> + maps:get(pending_write, ConnectionStates). %%-------------------------------------------------------------------- --spec activate_pending_connection_state(#connection_states{}, read | write) -> - #connection_states{}. +-spec activate_pending_connection_state(connection_states(), read | write) -> + connection_states(). %% %% Description: Creates a new instance of the connection_states record %% where the pending state of <Type> has been activated. %%-------------------------------------------------------------------- -activate_pending_connection_state(States = - #connection_states{current_read = Current, - pending_read = Pending}, +activate_pending_connection_state(#{current_read := Current, + pending_read := Pending} = States, read) -> - NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current), - sequence_number = 0}, - BeastMitigation = Pending#connection_state.beast_mitigation, - SecParams = Pending#connection_state.security_parameters, + #{secure_renegotiation := SecureRenegotation} = Current, + #{beast_mitigation := BeastMitigation, + security_parameters := SecParams} = Pending, + NewCurrent = Pending#{sequence_number => 0}, ConnectionEnd = SecParams#security_parameters.connection_end, EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation), - SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, - NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, - States#connection_states{current_read = NewCurrent, - pending_read = NewPending - }; - -activate_pending_connection_state(States = - #connection_states{current_write = Current, - pending_write = Pending}, + NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation}, + States#{current_read => NewCurrent, + pending_read => NewPending + }; + +activate_pending_connection_state(#{current_write := Current, + pending_write := Pending} = States, write) -> - NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current), - sequence_number = 0}, - BeastMitigation = Pending#connection_state.beast_mitigation, - SecParams = Pending#connection_state.security_parameters, + NewCurrent = Pending#{sequence_number => 0}, + #{secure_renegotiation := SecureRenegotation} = Current, + #{beast_mitigation := BeastMitigation, + security_parameters := SecParams} = Pending, ConnectionEnd = SecParams#security_parameters.connection_end, EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation), - SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, - NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, - States#connection_states{current_write = NewCurrent, - pending_write = NewPending - }. - + NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation}, + States#{current_write => NewCurrent, + pending_write => NewPending + }. %%-------------------------------------------------------------------- -spec set_security_params(#security_parameters{}, #security_parameters{}, - #connection_states{}) -> #connection_states{}. + connection_states()) -> connection_states(). %% %% Description: Creates a new instance of the connection_states record %% where the pending states gets its security parameters updated. %%-------------------------------------------------------------------- -set_security_params(ReadParams, WriteParams, States = - #connection_states{pending_read = Read, - pending_write = Write}) -> - States#connection_states{pending_read = - Read#connection_state{security_parameters = - ReadParams}, - pending_write = - Write#connection_state{security_parameters = - WriteParams} - }. +set_security_params(ReadParams, WriteParams, + #{pending_read := Read, + pending_write := Write} = States) -> + States#{pending_read => Read#{security_parameters => ReadParams}, + pending_write => Write#{security_parameters => WriteParams} + }. %%-------------------------------------------------------------------- -spec set_mac_secret(binary(), binary(), client | server, - #connection_states{}) -> #connection_states{}. + connection_states()) -> connection_states(). %% %% Description: update the mac_secret field in pending connection states %%-------------------------------------------------------------------- @@ -177,152 +147,145 @@ set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, server, States) -> set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, States). set_mac_secret(ReadMacSecret, WriteMacSecret, - States = #connection_states{pending_read = Read, - pending_write = Write}) -> - States#connection_states{ - pending_read = Read#connection_state{mac_secret = ReadMacSecret}, - pending_write = Write#connection_state{mac_secret = WriteMacSecret} + States = #{pending_read := Read, + pending_write := Write}) -> + States#{pending_read => Read#{mac_secret => ReadMacSecret}, + pending_write => Write#{mac_secret => WriteMacSecret} }. %%-------------------------------------------------------------------- --spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}. +-spec set_master_secret(binary(), connection_states()) -> connection_states(). %% %% Description: Set master_secret in pending connection states %%-------------------------------------------------------------------- set_master_secret(MasterSecret, - States = #connection_states{pending_read = Read, - pending_write = Write}) -> - ReadSecPar = Read#connection_state.security_parameters, - Read1 = Read#connection_state{ - security_parameters = ReadSecPar#security_parameters{ - master_secret = MasterSecret}}, - WriteSecPar = Write#connection_state.security_parameters, - Write1 = Write#connection_state{ - security_parameters = WriteSecPar#security_parameters{ - master_secret = MasterSecret}}, - States#connection_states{pending_read = Read1, pending_write = Write1}. - -%%-------------------------------------------------------------------- --spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}. + States = #{pending_read := Read = #{security_parameters := ReadSecPar}, + pending_write := Write = #{security_parameters := WriteSecPar}}) -> + Read1 = Read#{security_parameters => ReadSecPar#security_parameters{ + master_secret = MasterSecret}}, + Write1 = Write#{security_parameters => WriteSecPar#security_parameters{ + master_secret = MasterSecret}}, + States#{pending_read => Read1, pending_write => Write1}. + +%%-------------------------------------------------------------------- +-spec set_renegotiation_flag(boolean(), connection_states()) -> connection_states(). %% %% Description: Set secure_renegotiation in pending connection states %%-------------------------------------------------------------------- -set_renegotiation_flag(Flag, #connection_states{ - current_read = CurrentRead0, - current_write = CurrentWrite0, - pending_read = PendingRead0, - pending_write = PendingWrite0} +set_renegotiation_flag(Flag, #{current_read := CurrentRead0, + current_write := CurrentWrite0, + pending_read := PendingRead0, + pending_write := PendingWrite0} = ConnectionStates) -> - CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag}, - CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag}, - PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag}, - PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag}, - ConnectionStates#connection_states{current_read = CurrentRead, - current_write = CurrentWrite, - pending_read = PendingRead, - pending_write = PendingWrite}. + CurrentRead = CurrentRead0#{secure_renegotiation => Flag}, + CurrentWrite = CurrentWrite0#{secure_renegotiation => Flag}, + PendingRead = PendingRead0#{secure_renegotiation => Flag}, + PendingWrite = PendingWrite0#{secure_renegotiation => Flag}, + ConnectionStates#{current_read => CurrentRead, + current_write => CurrentWrite, + pending_read => PendingRead, + pending_write => PendingWrite}. %%-------------------------------------------------------------------- -spec set_client_verify_data(current_read | current_write | current_both, - binary(), #connection_states{})-> - #connection_states{}. + binary(), connection_states())-> + connection_states(). %% %% Description: Set verify data in connection states. %%-------------------------------------------------------------------- set_client_verify_data(current_read, Data, - #connection_states{current_read = CurrentRead0, - pending_write = PendingWrite0} + #{current_read := CurrentRead0, + pending_write := PendingWrite0} = ConnectionStates) -> - CurrentRead = CurrentRead0#connection_state{client_verify_data = Data}, - PendingWrite = PendingWrite0#connection_state{client_verify_data = Data}, - ConnectionStates#connection_states{current_read = CurrentRead, - pending_write = PendingWrite}; + CurrentRead = CurrentRead0#{client_verify_data => Data}, + PendingWrite = PendingWrite0#{client_verify_data => Data}, + ConnectionStates#{current_read => CurrentRead, + pending_write => PendingWrite}; set_client_verify_data(current_write, Data, - #connection_states{pending_read = PendingRead0, - current_write = CurrentWrite0} + #{pending_read := PendingRead0, + current_write := CurrentWrite0} = ConnectionStates) -> - PendingRead = PendingRead0#connection_state{client_verify_data = Data}, - CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, - ConnectionStates#connection_states{pending_read = PendingRead, - current_write = CurrentWrite}; + PendingRead = PendingRead0#{client_verify_data => Data}, + CurrentWrite = CurrentWrite0#{client_verify_data => Data}, + ConnectionStates#{pending_read => PendingRead, + current_write => CurrentWrite}; set_client_verify_data(current_both, Data, - #connection_states{current_read = CurrentRead0, - current_write = CurrentWrite0} + #{current_read := CurrentRead0, + current_write := CurrentWrite0} = ConnectionStates) -> - CurrentRead = CurrentRead0#connection_state{client_verify_data = Data}, - CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, - ConnectionStates#connection_states{current_read = CurrentRead, - current_write = CurrentWrite}. + CurrentRead = CurrentRead0#{client_verify_data => Data}, + CurrentWrite = CurrentWrite0#{client_verify_data => Data}, + ConnectionStates#{current_read => CurrentRead, + current_write => CurrentWrite}. %%-------------------------------------------------------------------- -spec set_server_verify_data(current_read | current_write | current_both, - binary(), #connection_states{})-> - #connection_states{}. + binary(), connection_states())-> + connection_states(). %% %% Description: Set verify data in pending connection states. %%-------------------------------------------------------------------- set_server_verify_data(current_write, Data, - #connection_states{pending_read = PendingRead0, - current_write = CurrentWrite0} + #{pending_read := PendingRead0, + current_write := CurrentWrite0} = ConnectionStates) -> - PendingRead = PendingRead0#connection_state{server_verify_data = Data}, - CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data}, - ConnectionStates#connection_states{pending_read = PendingRead, - current_write = CurrentWrite}; + PendingRead = PendingRead0#{server_verify_data => Data}, + CurrentWrite = CurrentWrite0#{server_verify_data => Data}, + ConnectionStates#{pending_read => PendingRead, + current_write => CurrentWrite}; set_server_verify_data(current_read, Data, - #connection_states{current_read = CurrentRead0, - pending_write = PendingWrite0} + #{current_read := CurrentRead0, + pending_write := PendingWrite0} = ConnectionStates) -> - CurrentRead = CurrentRead0#connection_state{server_verify_data = Data}, - PendingWrite = PendingWrite0#connection_state{server_verify_data = Data}, - ConnectionStates#connection_states{current_read = CurrentRead, - pending_write = PendingWrite}; + CurrentRead = CurrentRead0#{server_verify_data => Data}, + PendingWrite = PendingWrite0#{server_verify_data => Data}, + ConnectionStates#{current_read => CurrentRead, + pending_write => PendingWrite}; set_server_verify_data(current_both, Data, - #connection_states{current_read = CurrentRead0, - current_write = CurrentWrite0} + #{current_read := CurrentRead0, + current_write := CurrentWrite0} = ConnectionStates) -> - CurrentRead = CurrentRead0#connection_state{server_verify_data = Data}, - CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data}, - ConnectionStates#connection_states{current_read = CurrentRead, - current_write = CurrentWrite}. + CurrentRead = CurrentRead0#{server_verify_data => Data}, + CurrentWrite = CurrentWrite0#{server_verify_data => Data}, + ConnectionStates#{current_read => CurrentRead, + current_write => CurrentWrite}. %%-------------------------------------------------------------------- --spec set_pending_cipher_state(#connection_states{}, #cipher_state{}, +-spec set_pending_cipher_state(connection_states(), #cipher_state{}, #cipher_state{}, client | server) -> - #connection_states{}. + connection_states(). %% %% Description: Set the cipher state in the specified pending connection state. %%-------------------------------------------------------------------- -set_pending_cipher_state(#connection_states{pending_read = Read, - pending_write = Write} = States, +set_pending_cipher_state(#{pending_read := Read, + pending_write := Write} = States, ClientState, ServerState, server) -> - States#connection_states{ - pending_read = Read#connection_state{cipher_state = ClientState}, - pending_write = Write#connection_state{cipher_state = ServerState}}; + States#{ + pending_read => Read#{cipher_state => ClientState}, + pending_write => Write#{cipher_state => ServerState}}; -set_pending_cipher_state(#connection_states{pending_read = Read, - pending_write = Write} = States, +set_pending_cipher_state(#{pending_read := Read, + pending_write := Write} = States, ClientState, ServerState, client) -> - States#connection_states{ - pending_read = Read#connection_state{cipher_state = ServerState}, - pending_write = Write#connection_state{cipher_state = ClientState}}. + States#{ + pending_read => Read#{cipher_state => ServerState}, + pending_write => Write#{cipher_state => ClientState}}. %%-------------------------------------------------------------------- --spec encode_handshake(iolist(), ssl_version(), #connection_states{}) -> - {iolist(), #connection_states{}}. +-spec encode_handshake(iolist(), ssl_version(), connection_states()) -> + {iolist(), connection_states()}. %% %% Description: Encodes a handshake message to send on the ssl-socket. %%-------------------------------------------------------------------- encode_handshake(Frag, Version, - #connection_states{current_write = - #connection_state{ - beast_mitigation = BeastMitigation, - security_parameters = - #security_parameters{bulk_cipher_algorithm = BCA}}} = + #{current_write := + #{beast_mitigation := BeastMitigation, + security_parameters := + #security_parameters{bulk_cipher_algorithm = BCA}}} = ConnectionStates) -when is_list(Frag) -> + when is_list(Frag) -> case iolist_size(Frag) of N when N > ?MAX_PLAIN_TEXT_LENGTH -> Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation), @@ -341,8 +304,8 @@ encode_handshake(Frag, Version, ConnectionStates) -> encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates). %%-------------------------------------------------------------------- --spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) -> - {iolist(), #connection_states{}}. +-spec encode_alert_record(#alert{}, ssl_version(), connection_states()) -> + {iolist(), connection_states()}. %% %% Description: Encodes an alert message to send on the ssl-socket. %%-------------------------------------------------------------------- @@ -352,8 +315,8 @@ encode_alert_record(#alert{level = Level, description = Description}, ConnectionStates). %%-------------------------------------------------------------------- --spec encode_change_cipher_spec(ssl_version(), #connection_states{}) -> - {iolist(), #connection_states{}}. +-spec encode_change_cipher_spec(ssl_version(), connection_states()) -> + {iolist(), connection_states()}. %% %% Description: Encodes a change_cipher_spec-message to send on the ssl socket. %%-------------------------------------------------------------------- @@ -361,15 +324,14 @@ encode_change_cipher_spec(Version, ConnectionStates) -> encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates). %%-------------------------------------------------------------------- --spec encode_data(binary(), ssl_version(), #connection_states{}) -> - {iolist(), #connection_states{}}. +-spec encode_data(binary(), ssl_version(), connection_states()) -> + {iolist(), connection_states()}. %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- encode_data(Frag, Version, - #connection_states{current_write = #connection_state{ - beast_mitigation = BeastMitigation, - security_parameters = + #{current_write := #{beast_mitigation := BeastMitigation, + security_parameters := #security_parameters{bulk_cipher_algorithm = BCA}}} = ConnectionStates) -> Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation), @@ -390,73 +352,74 @@ compressions() -> [?byte(?NULL)]. %%-------------------------------------------------------------------- --spec cipher(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) -> - {CipherFragment::binary(), #connection_state{}}. +-spec cipher(ssl_version(), iodata(), connection_state(), MacHash::binary()) -> + {CipherFragment::binary(), connection_state()}. %% %% Description: Payload encryption %%-------------------------------------------------------------------- cipher(Version, Fragment, - #connection_state{cipher_state = CipherS0, - security_parameters= - #security_parameters{bulk_cipher_algorithm = - BulkCipherAlgo} - } = WriteState0, MacHash) -> - + #{cipher_state := CipherS0, + security_parameters := + #security_parameters{bulk_cipher_algorithm = + BulkCipherAlgo} + } = WriteState0, MacHash) -> + {CipherFragment, CipherS1} = ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version), - {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}. + {CipherFragment, WriteState0#{cipher_state => CipherS1}}. %%-------------------------------------------------------------------- --spec cipher_aead(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) -> - {CipherFragment::binary(), #connection_state{}}. +-spec cipher_aead(ssl_version(), iodata(), connection_state(), MacHash::binary()) -> + {CipherFragment::binary(), connection_state()}. %% %% Description: Payload encryption %%-------------------------------------------------------------------- cipher_aead(Version, Fragment, - #connection_state{cipher_state = CipherS0, - sequence_number = SeqNo, - security_parameters= - #security_parameters{bulk_cipher_algorithm = - BulkCipherAlgo} - } = WriteState0, AAD) -> - + #{cipher_state := CipherS0, + sequence_number := SeqNo, + security_parameters := + #security_parameters{bulk_cipher_algorithm = + BulkCipherAlgo} + } = WriteState0, AAD) -> + {CipherFragment, CipherS1} = ssl_cipher:cipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, Fragment, Version), - {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}. + {CipherFragment, WriteState0#{cipher_state => CipherS1}}. %%-------------------------------------------------------------------- --spec decipher(ssl_version(), binary(), #connection_state{}, boolean()) -> {binary(), binary(), #connection_state{}} | #alert{}. +-spec decipher(ssl_version(), binary(), connection_state(), boolean()) -> {binary(), binary(), connection_state} | #alert{}. %% %% Description: Payload decryption %%-------------------------------------------------------------------- decipher(Version, CipherFragment, - #connection_state{security_parameters = - #security_parameters{bulk_cipher_algorithm = - BulkCipherAlgo, - hash_size = HashSz}, - cipher_state = CipherS0 - } = ReadState, PaddingCheck) -> + #{security_parameters := + #security_parameters{bulk_cipher_algorithm = + BulkCipherAlgo, + hash_size = HashSz}, + cipher_state := CipherS0 + } = ReadState, PaddingCheck) -> case ssl_cipher:decipher(BulkCipherAlgo, HashSz, CipherS0, CipherFragment, Version, PaddingCheck) of {PlainFragment, Mac, CipherS1} -> - CS1 = ReadState#connection_state{cipher_state = CipherS1}, + CS1 = ReadState#{cipher_state => CipherS1}, {PlainFragment, Mac, CS1}; #alert{} = Alert -> Alert end. %%-------------------------------------------------------------------- --spec decipher_aead(ssl_version(), binary(), #connection_state{}, binary()) -> {binary(), binary(), #connection_state{}} | #alert{}. +-spec decipher_aead(ssl_version(), binary(), connection_state(), binary()) -> + {binary(), binary(), connection_state()} | #alert{}. %% %% Description: Payload decryption %%-------------------------------------------------------------------- decipher_aead(Version, CipherFragment, - #connection_state{sequence_number = SeqNo, - security_parameters = - #security_parameters{bulk_cipher_algorithm = - BulkCipherAlgo}, - cipher_state = CipherS0 - } = ReadState, AAD) -> + #{sequence_number := SeqNo, + security_parameters := + #security_parameters{bulk_cipher_algorithm = + BulkCipherAlgo}, + cipher_state := CipherS0 + } = ReadState, AAD) -> case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, CipherFragment, Version) of {PlainFragment, CipherS1} -> - CS1 = ReadState#connection_state{cipher_state = CipherS1}, + CS1 = ReadState#{cipher_state => CipherS1}, {PlainFragment, CS1}; #alert{} = Alert -> Alert @@ -466,8 +429,15 @@ decipher_aead(Version, CipherFragment, %%-------------------------------------------------------------------- empty_connection_state(ConnectionEnd, BeastMitigation) -> SecParams = empty_security_params(ConnectionEnd), - #connection_state{security_parameters = SecParams, - beast_mitigation = BeastMitigation}. + #{security_parameters => SecParams, + beast_mitigation => BeastMitigation, + compression_state => undefined, + cipher_state => undefined, + mac_secret => undefined, + secure_renegotiation => undefined, + client_verify_data => undefined, + server_verify_data => undefined + }. empty_security_params(ConnectionEnd = ?CLIENT) -> #security_parameters{connection_end = ConnectionEnd, @@ -481,10 +451,10 @@ random() -> Random_28_bytes = ssl_cipher:random_bytes(28), <<?UINT32(Secs_since_1970), Random_28_bytes/binary>>. -dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS - undefined; -dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS - Epoch + 1. +%% dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS +%% undefined; +%% dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS +%% Epoch + 1. is_correct_mac(Mac, Mac) -> true; @@ -497,11 +467,17 @@ record_protocol_role(server) -> ?SERVER. initial_connection_state(ConnectionEnd, BeastMitigation) -> - #connection_state{security_parameters = - initial_security_params(ConnectionEnd), - sequence_number = 0, - beast_mitigation = BeastMitigation - }. + #{security_parameters => + initial_security_params(ConnectionEnd), + sequence_number => 0, + beast_mitigation => BeastMitigation, + compression_state => undefined, + cipher_state => undefined, + mac_secret => undefined, + secure_renegotiation => undefined, + client_verify_data => undefined, + server_verify_data => undefined + }. initial_security_params(ConnectionEnd) -> SecParams = #security_parameters{connection_end = ConnectionEnd, diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index a41264ff9b..ed007f58d7 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -30,29 +30,27 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Connection states - RFC 4346 section 6.1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --record(connection_state, { - security_parameters, - compression_state, - cipher_state, - mac_secret, - epoch, %% Only used by DTLS - sequence_number, - %% RFC 5746 - secure_renegotiation, - client_verify_data, - server_verify_data, - %% How to do BEAST mitigation? - beast_mitigation - }). - --record(connection_states, { - dtls_write_msg_seq, %% Only used by DTLS +%% For documentation purposes are now maps in implementation +%% -record(connection_state, { +%% security_parameters, +%% compression_state, +%% cipher_state, +%% mac_secret, +%% sequence_number, +%% %% RFC 5746 +%% secure_renegotiation, +%% client_verify_data, +%% server_verify_data, +%% %% How to do BEAST mitigation? +%% beast_mitigation +%% }). - current_read, - pending_read, - current_write, - pending_write - }). +%% -record(connection_states, { +%% current_read, +%% pending_read, +%% current_write, +%% pending_write, +%% }). -record(security_parameters, { cipher_suite, diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl index 7fa1f7dc9e..ba20f65f44 100644 --- a/lib/ssl/src/ssl_sup.erl +++ b/lib/ssl/src/ssl_sup.erl @@ -47,11 +47,13 @@ init([]) -> SessionCertManager = session_and_cert_manager_child_spec(), TLSConnetionManager = tls_connection_manager_child_spec(), %% Not supported yet - %%DTLSConnetionManager = tls_connection_manager_child_spec(), + %%DTLSConnetionManager = dtls_connection_manager_child_spec(), %% Handles emulated options so that they inherited by the accept socket, even when setopts is performed on %% the listen socket ListenOptionsTracker = listen_options_tracker_child_spec(), - {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager, ListenOptionsTracker]}}. + {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager, + %%DTLSConnetionManager, + ListenOptionsTracker]}}. manager_opts() -> @@ -93,15 +95,14 @@ tls_connection_manager_child_spec() -> {Name, StartFunc, Restart, Shutdown, Type, Modules}. %% dtls_connection_manager_child_spec() -> -%% Name = dtls_connection, +%% Name = dtls_connection, %% StartFunc = {dtls_connection_sup, start_link, []}, -%% Restart = permanent, +%% Restart = permanent, %% Shutdown = 4000, %% Modules = [dtls_connection, ssl_connection], %% Type = supervisor, %% {Name, StartFunc, Restart, Shutdown, Type, Modules}. - listen_options_tracker_child_spec() -> Name = ssl_socket, StartFunc = {ssl_listen_tracker_sup, start_link, []}, diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index a920f54ed2..08947f24dd 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -117,7 +117,7 @@ handle_call({listen, Driver, Name}, _From, State) -> {ok, WorldTcpAddress} = get_tcp_address(World), {_,Port} = WorldTcpAddress#net_address.address, ErlEpmd = net_kernel:epmd_module(), - case ErlEpmd:register_node(Name, Port) of + case ErlEpmd:register_node(Name, Port, Driver) of {ok, Creation} -> {reply, {ok, {Socket, TcpAddress, Creation}}, State#state{listen={Socket, World}}}; diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 9880befa94..9b9031473a 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -51,27 +51,21 @@ %% Handshake handling -export([renegotiate/2, send_handshake/2, queue_handshake/2, queue_change_cipher/2, - reinit_handshake_data/1, handle_sni_extension/2]). + reinit_handshake_data/1, select_sni_extension/1]). %% Alert and close handling --export([send_alert/2, handle_own_alert/4, handle_close_alert/3, - handle_normal_shutdown/3, - close/5, alert_user/6, alert_user/9 - ]). +-export([send_alert/2, close/5]). %% Data handling --export([write_application_data/3, read_application_data/2, - passive_receive/2, next_record_if_active/1, handle_common_event/4]). +-export([passive_receive/2, next_record_if_active/1, handle_common_event/4]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states connection/3]). %% gen_statem callbacks --export([terminate/3, code_change/4, format_status/2]). +-export([callback_mode/0, terminate/3, code_change/4, format_status/2]). --define(GEN_STATEM_CB_MODE, state_functions). - %%==================================================================== %% Internal application API %%==================================================================== @@ -109,9 +103,10 @@ send_handshake(Handshake, State) -> queue_handshake(Handshake, #state{negotiated_version = Version, tls_handshake_history = Hist0, flight_buffer = Flight0, + ssl_options = #ssl_options{v2_hello_compatible = V2HComp}, connection_states = ConnectionStates0} = State0) -> {BinHandshake, ConnectionStates, Hist} = - encode_handshake(Handshake, Version, ConnectionStates0, Hist0), + encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp), State0#state{connection_states = ConnectionStates, tls_handshake_history = Hist, flight_buffer = Flight0 ++ [BinHandshake]}. @@ -149,6 +144,11 @@ reinit_handshake_data(State) -> tls_handshake_history = ssl_handshake:init_handshake_history() }. +select_sni_extension(#client_hello{extensions = HelloExtensions}) -> + HelloExtensions#hello_extensions.sni; +select_sni_extension(_) -> + undefined. + %%==================================================================== %% tls_connection_sup API %%==================================================================== @@ -169,11 +169,14 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), try State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0), - gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State) + gen_statem:enter_loop(?MODULE, [], init, State) catch throw:Error -> - gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error, State0}) + gen_statem:enter_loop(?MODULE, [], error, {Error, State0}) end. +callback_mode() -> + state_functions. + %%-------------------------------------------------------------------- %% State functions %%-------------------------------------------------------------------- @@ -185,7 +188,7 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) -> init({call, From}, {start, Timeout}, #state{host = Host, port = Port, role = client, - ssl_options = SslOpts, + ssl_options = #ssl_options{v2_hello_compatible = V2HComp} = SslOpts, session = #session{own_certificate = Cert} = Session0, transport_cb = Transport, socket = Socket, connection_states = ConnectionStates0, @@ -201,7 +204,7 @@ init({call, From}, {start, Timeout}, HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions), Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = - encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), + encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0, V2HComp), Transport:send(Socket, BinMsg), State1 = State0#state{connection_states = ConnectionStates, negotiated_version = Version, %% Requested version @@ -213,7 +216,7 @@ init({call, From}, {start, Timeout}, {Record, State} = next_record(State1), next_event(hello, Record, State); init(Type, Event, State) -> - ssl_connection:init(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, init, Type, Event, State). %%-------------------------------------------------------------------- -spec error(gen_statem:event_type(), @@ -249,7 +252,7 @@ hello(internal, #client_hello{client_version = ClientVersion, case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State); + ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State); {Version, {Type, Session}, ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> Protocol = case Protocol0 of @@ -257,13 +260,13 @@ hello(internal, #client_hello{client_version = ClientVersion, _ -> Protocol0 end, - ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt}, + gen_handshake(ssl_connection, hello, internal, {common_client_hello, Type, ServerHelloExt}, State#state{connection_states = ConnectionStates, negotiated_version = Version, hashsign_algorithm = HashSign, session = Session, client_ecc = {EllipticCurves, EcPointFormats}, - negotiated_protocol = Protocol}, ?MODULE) + negotiated_protocol = Protocol}) end; hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, @@ -273,42 +276,42 @@ hello(internal, #server_hello{} = Hello, ssl_options = SslOptions} = State) -> case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> - handle_own_alert(Alert, ReqVersion, hello, State); + ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State); {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, Version, NewId, ConnectionStates, ProtoExt, Protocol, State) end; hello(info, Event, State) -> - handle_info(Event, hello, State); + gen_info(Event, hello, State); hello(Type, Event, State) -> - ssl_connection:hello(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, hello, Type, Event, State). %%-------------------------------------------------------------------- -spec abbreviated(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- abbreviated(info, Event, State) -> - handle_info(Event, abbreviated, State); + gen_info(Event, abbreviated, State); abbreviated(Type, Event, State) -> - ssl_connection:abbreviated(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, abbreviated, Type, Event, State). %%-------------------------------------------------------------------- -spec certify(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- certify(info, Event, State) -> - handle_info(Event, certify, State); + gen_info(Event, certify, State); certify(Type, Event, State) -> - ssl_connection:certify(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, certify, Type, Event, State). %%-------------------------------------------------------------------- -spec cipher(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- cipher(info, Event, State) -> - handle_info(Event, cipher, State); + gen_info(Event, cipher, State); cipher(Type, Event, State) -> - ssl_connection:cipher(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, cipher, Type, Event, State). %%-------------------------------------------------------------------- -spec connection(gen_statem:event_type(), @@ -316,7 +319,7 @@ cipher(Type, Event, State) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- connection(info, Event, State) -> - handle_info(Event, connection, State); + gen_info(Event, connection, State); connection(internal, #hello_request{}, #state{role = client, host = Host, port = Port, session = #session{own_certificate = Cert} = Session0, @@ -373,7 +376,7 @@ handle_info({Protocol, _, Data}, StateName, {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> - handle_normal_shutdown(Alert, StateName, State0), + ssl_connection:handle_normal_shutdown(Alert, StateName, State0), {stop, {shutdown, own_alert}} end; handle_info({CloseTag, Socket}, StateName, @@ -393,14 +396,14 @@ handle_info({CloseTag, Socket}, StateName, %%invalidate_session(Role, Host, Port, Session) ok end, - handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), {stop, {shutdown, transport_closed}}; handle_info(Msg, StateName, State) -> ssl_connection:handle_info(Msg, StateName, State). handle_common_event(internal, #alert{} = Alert, StateName, #state{negotiated_version = Version} = State) -> - handle_own_alert(Alert, Version, StateName, State); + ssl_connection:handle_own_alert(Alert, Version, StateName, State); %%% TLS record protocol level handshake messages handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, @@ -421,7 +424,7 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, {next_state, StateName, State, Events} end catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State0) + ssl_connection:handle_own_alert(Alert, Version, StateName, State0) end; %%% TLS record protocol level application data messages handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) -> @@ -432,11 +435,19 @@ handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Da %%% TLS record protocol level Alert messages handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, #state{negotiated_version = Version} = State) -> - case decode_alerts(EncAlerts) of + try decode_alerts(EncAlerts) of Alerts = [_|_] -> handle_alerts(Alerts, {next_state, StateName, State}); + [] -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert), + Version, StateName, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State) + ssl_connection:handle_own_alert(Alert, Version, StateName, State) + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error), + Version, StateName, State) + end; %% Ignore unknown TLS record level protocol messages handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) -> @@ -457,16 +468,16 @@ format_status(Type, Data) -> %%-------------------------------------------------------------------- code_change(_OldVsn, StateName, State0, {Direction, From, To}) -> State = convert_state(State0, Direction, From, To), - {?GEN_STATEM_CB_MODE, StateName, State}; + {ok, StateName, State}; code_change(_OldVsn, StateName, State, _) -> - {?GEN_STATEM_CB_MODE, StateName, State}. + {ok, StateName, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> +encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp) -> Frag = tls_handshake:encode_handshake(Handshake, Version), - Hist = ssl_handshake:update_handshake_history(Hist0, Frag), + Hist = ssl_handshake:update_handshake_history(Hist0, Frag, V2HComp), {Encoded, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), {Encoded, ConnectionStates, Hist}. @@ -480,7 +491,7 @@ decode_alerts(Bin) -> initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, - ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), + ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> @@ -515,23 +526,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us flight_buffer = [] }. - -update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) -> - SSLOption = - case OrigSSLOptions#ssl_options.sni_fun of - undefined -> - proplists:get_value(SNIHostname, - OrigSSLOptions#ssl_options.sni_hosts); - SNIFun -> - SNIFun(SNIHostname) - end, - case SSLOption of - undefined -> - undefined; - _ -> - ssl:handle_options(SSLOption, OrigSSLOptions) - end. - next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buffer = Buf0, tls_cipher_texts = CT0} = Buffers} = State0) -> case tls_record:get_tls_records(Data, Buf0) of @@ -579,7 +573,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> {Record, State} = next_record(State0), next_event(StateName, Record, State); _ -> - {Record, State} = read_application_data(<<>>, State0), + {Record, State} = ssl_connection:read_application_data(<<>>, State0), next_event(StateName, Record, State) end. @@ -591,7 +585,7 @@ next_event(connection = StateName, no_record, State0, Actions) -> {no_record, State} -> ssl_connection:hibernate_after(StateName, State, Actions); {#ssl_tls{} = Record, State} -> - {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]}; + {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; {#alert{} = Alert, State} -> {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} end; @@ -600,169 +594,11 @@ next_event(StateName, Record, State, Actions) -> no_record -> {next_state, StateName, State, Actions}; #ssl_tls{} = Record -> - {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]}; + {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; #alert{} = Alert -> {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} end. -read_application_data(Data, #state{user_application = {_Mon, Pid}, - socket = Socket, - transport_cb = Transport, - socket_options = SOpts, - bytes_to_read = BytesToRead, - start_or_recv_from = RecvFrom, - timer = Timer, - user_data_buffer = Buffer0, - tracker = Tracker} = State0) -> - Buffer1 = if - Buffer0 =:= <<>> -> Data; - Data =:= <<>> -> Buffer0; - true -> <<Buffer0/binary, Data/binary>> - end, - case get_data(SOpts, BytesToRead, Buffer1) of - {ok, ClientData, Buffer} -> % Send data - SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom, Tracker), - cancel_timer(Timer), - State = State0#state{user_data_buffer = Buffer, - start_or_recv_from = undefined, - timer = undefined, - bytes_to_read = undefined, - socket_options = SocketOpt - }, - if - SocketOpt#socket_options.active =:= false; Buffer =:= <<>> -> - %% Passive mode, wait for active once or recv - %% Active and empty, get more data - next_record_if_active(State); - true -> %% We have more data - read_application_data(<<>>, State) - end; - {more, Buffer} -> % no reply, we need more data - next_record(State0#state{user_data_buffer = Buffer}); - {passive, Buffer} -> - next_record_if_active(State0#state{user_data_buffer = Buffer}); - {error,_Reason} -> %% Invalid packet in packet mode - deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker), - {stop, normal, State0} - end. - -%% Picks ClientData -get_data(_, _, <<>>) -> - {more, <<>>}; -%% Recv timed out save buffer data until next recv -get_data(#socket_options{active=false}, undefined, Buffer) -> - {passive, Buffer}; -get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) - when Raw =:= raw; Raw =:= 0 -> %% Raw Mode - if - Active =/= false orelse BytesToRead =:= 0 -> - %% Active true or once, or passive mode recv(0) - {ok, Buffer, <<>>}; - byte_size(Buffer) >= BytesToRead -> - %% Passive Mode, recv(Bytes) - <<Data:BytesToRead/binary, Rest/binary>> = Buffer, - {ok, Data, Rest}; - true -> - %% Passive Mode not enough data - {more, Buffer} - end; -get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) -> - PacketOpts = [{packet_size, Size}], - case decode_packet(Type, Buffer, PacketOpts) of - {more, _} -> - {more, Buffer}; - Decoded -> - Decoded - end. - -decode_packet({http, headers}, Buffer, PacketOpts) -> - decode_packet(httph, Buffer, PacketOpts); -decode_packet({http_bin, headers}, Buffer, PacketOpts) -> - decode_packet(httph_bin, Buffer, PacketOpts); -decode_packet(Type, Buffer, PacketOpts) -> - erlang:decode_packet(Type, Buffer, PacketOpts). - -%% Just like with gen_tcp sockets, an ssl socket that has been configured with -%% {packet, http} (or {packet, http_bin}) will automatically switch to expect -%% HTTP headers after it sees a HTTP Request or HTTP Response line. We -%% represent the current state as follows: -%% #socket_options.packet =:= http: Expect a HTTP Request/Response line -%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers -%% Note that if the user has explicitly configured the socket to expect -%% HTTP headers using the {packet, httph} option, we don't do any automatic -%% switching of states. -deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type}, - Data, Pid, From, Tracker) -> - send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker)), - SO = case Data of - {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), - ((Type =:= http) or (Type =:= http_bin)) -> - SOpts#socket_options{packet={Type, headers}}; - http_eoh when tuple_size(Type) =:= 2 -> - % End of headers - expect another Request/Response line - {Type1, headers} = Type, - SOpts#socket_options{packet=Type1}; - _ -> - SOpts - end, - case Active of - once -> - SO#socket_options{active=false}; - _ -> - SO - end. - -format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet, - header = Header}, Data, _) -> - {ok, do_format_reply(Mode, Packet, Header, Data)}; -format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet, - header = Header}, Data, Tracker) -> - {ssl, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker), - do_format_reply(Mode, Packet, Header, Data)}. - -deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker) -> - send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker)). - -format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _) -> - {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}; -format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker) -> - {ssl_error, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker), - {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}. - -do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode - header(N, Data); -do_format_reply(binary, _, _, Data) -> - Data; -do_format_reply(list, Packet, _, Data) - when Packet == http; Packet == {http, headers}; - Packet == http_bin; Packet == {http_bin, headers}; - Packet == httph; Packet == httph_bin -> - Data; -do_format_reply(list, _,_, Data) -> - binary_to_list(Data). - -header(0, <<>>) -> - <<>>; -header(_, <<>>) -> - []; -header(0, Binary) -> - Binary; -header(N, Binary) -> - <<?BYTE(ByteN), NewBinary/binary>> = Binary, - [ByteN | header(N-1, NewBinary)]. - -send_or_reply(false, _Pid, From, Data) when From =/= undefined -> - gen_statem:reply(From, Data); -%% Can happen when handling own alert or tcp error/close and there is -%% no outstanding gen_fsm sync events -send_or_reply(false, no_pid, _, _) -> - ok; -send_or_reply(_, Pid, _From, Data) -> - send_user(Pid, Data). - -send_user(Pid, Msg) -> - Pid ! Msg. - tls_handshake_events([]) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake)); tls_handshake_events(Packets) -> @@ -770,55 +606,7 @@ tls_handshake_events(Packets) -> {next_event, internal, {handshake, Packet}} end, Packets). -write_application_data(Data0, From, - #state{socket = Socket, - negotiated_version = Version, - transport_cb = Transport, - connection_states = ConnectionStates0, - socket_options = SockOpts, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) -> - Data = encode_packet(Data0, SockOpts), - - case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of - true -> - renegotiate(State#state{renegotiation = {true, internal}}, - [{next_event, {call, From}, {application_data, Data0}}]); - false -> - {Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0), - Result = Transport:send(Socket, Msgs), - ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates}, - [{reply, From, Result}]) - end. - -encode_packet(Data, #socket_options{packet=Packet}) -> - case Packet of - 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1); - 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1); - 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1); - _ -> Data - end. - -encode_size_packet(Bin, Size, Max) -> - Len = erlang:byte_size(Bin), - case Len > Max of - true -> throw({error, {badarg, {packet_to_large, Len, Max}}}); - false -> <<Len:Size, Bin/binary>> - end. -time_to_renegotiate(_Data, - #connection_states{current_write = - #connection_state{sequence_number = Num}}, - RenegotiateAt) -> - - %% We could do test: - %% is_time_to_renegotiate((erlang:byte_size(_Data) div ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt), - %% but we chose to have a some what lower renegotiateAt and a much cheaper test - is_time_to_renegotiate(Num, RenegotiateAt). - -is_time_to_renegotiate(N, M) when N < M-> - false; -is_time_to_renegotiate(_,_) -> - true. renegotiate(#state{role = client} = State, Actions) -> %% Handle same way as if server requested %% the renegotiation @@ -848,131 +636,10 @@ handle_alerts([], Result) -> handle_alerts(_, {stop,_} = Stop) -> Stop; handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> - handle_alerts(Alerts, handle_alert(Alert, StateName, State)); + handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)); handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> - handle_alerts(Alerts, handle_alert(Alert, StateName, State)). -handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{socket = Socket, transport_cb = Transport, - ssl_options = SslOpts, start_or_recv_from = From, host = Host, - port = Port, session = Session, user_application = {_Mon, Pid}, - role = Role, socket_options = Opts, tracker = Tracker}) -> - invalidate_session(Role, Host, Port, Session), - log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), - alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role), - {stop, normal}; - -handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, - StateName, State) -> - handle_normal_shutdown(Alert, StateName, State), - {stop, {shutdown, peer_close}}; - -handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) -> - log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), - handle_normal_shutdown(Alert, StateName, State), - {stop, {shutdown, peer_close}}; - -handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{ssl_options = SslOpts, renegotiation = {true, From}} = State0) -> - log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), - gen_statem:reply(From, {error, renegotiation_rejected}), - {Record, State} = next_record(State0), - %% Go back to connection! - next_event(connection, Record, State); - -%% Gracefully log and ignore all other warning alerts -handle_alert(#alert{level = ?WARNING} = Alert, StateName, - #state{ssl_options = SslOpts} = State0) -> - log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), - {Record, State} = next_record(State0), - next_event(StateName, Record, State). - -alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role) -> - alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role); -alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role) -> - alert_user(Transport, Tracker, Socket, From, Alert, Role). - -alert_user(Transport, Tracker, Socket, From, Alert, Role) -> - alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role). - -alert_user(_, _, _, false = Active, Pid, From, Alert, Role) when From =/= undefined -> - %% If there is an outstanding ssl_accept | recv - %% From will be defined and send_or_reply will - %% send the appropriate error message. - ReasonCode = ssl_alert:reason_code(Alert, Role), - send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role) -> - case ssl_alert:reason_code(Alert, Role) of - closed -> - send_or_reply(Active, Pid, From, - {ssl_closed, ssl_socket:socket(self(), - Transport, Socket, ?MODULE, Tracker)}); - ReasonCode -> - send_or_reply(Active, Pid, From, - {ssl_error, ssl_socket:socket(self(), - Transport, Socket, ?MODULE, Tracker), ReasonCode}) - end. - -log_alert(true, Info, Alert) -> - Txt = ssl_alert:alert_txt(Alert), - error_logger:format("SSL: ~p: ~s\n", [Info, Txt]); -log_alert(false, _, _) -> - ok. - -handle_own_alert(Alert, Version, StateName, - #state{transport_cb = Transport, - socket = Socket, - connection_states = ConnectionStates, - ssl_options = SslOpts} = State) -> - try %% Try to tell the other side - {BinMsg, _} = - ssl_alert:encode(Alert, Version, ConnectionStates), - Transport:send(Socket, BinMsg) - catch _:_ -> %% Can crash if we are in a uninitialized state - ignore - end, - try %% Try to tell the local user - log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), - handle_normal_shutdown(Alert,StateName, State) - catch _:_ -> - ok - end, - {stop, {shutdown, own_alert}}. - -handle_normal_shutdown(Alert, _, #state{socket = Socket, - transport_cb = Transport, - start_or_recv_from = StartFrom, - tracker = Tracker, - role = Role, renegotiation = {false, first}}) -> - alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role); + handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)). -handle_normal_shutdown(Alert, StateName, #state{socket = Socket, - socket_options = Opts, - transport_cb = Transport, - user_application = {_Mon, Pid}, - tracker = Tracker, - start_or_recv_from = RecvFrom, role = Role}) -> - alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role). - -handle_close_alert(Data, StateName, State0) -> - case next_tls_record(Data, State0) of - {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} -> - [Alert|_] = decode_alerts(EncAlerts), - handle_normal_shutdown(Alert, StateName, State); - _ -> - ok - end. - -cancel_timer(undefined) -> - ok; -cancel_timer(Timer) -> - erlang:cancel_timer(Timer), - ok. - -invalidate_session(client, Host, Port, Session) -> - ssl_manager:invalidate_session(Host, Port, Session); -invalidate_session(server, _, Port, Session) -> - ssl_manager:invalidate_session(Port, Session). %% User closes or recursive call! close({close, Timeout}, Socket, Transport = gen_tcp, _,_) -> @@ -1011,31 +678,37 @@ convert_options_partial_chain(Options, up) -> convert_options_partial_chain(Options, down) -> list_to_tuple(proplists:delete(partial_chain, tuple_to_list(Options))). -handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) -> - case HelloExtensions#hello_extensions.sni of - undefined -> - State0; - #sni{hostname = Hostname} -> - NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname), - case NewOptions of - undefined -> - State0; - _ -> - {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} = - ssl_config:init(NewOptions, State0#state.role), - State0#state{ - session = State0#state.session#session{own_certificate = OwnCert}, - file_ref_db = FileRefHandle, - cert_db_ref = Ref, - cert_db = CertDbHandle, - crl_db = CRLDbHandle, - session_cache = CacheHandle, - private_key = Key, - diffie_hellman_params = DHParams, - ssl_options = NewOptions, - sni_hostname = Hostname - } - end +gen_handshake(GenConnection, StateName, Type, Event, + #state{negotiated_version = Version} = State) -> + try GenConnection:StateName(Type, Event, State, ?MODULE) of + Result -> + Result + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + malformed_handshake_data), + Version, StateName, State) + end. + +gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) -> + try handle_info(Event, StateName, State) of + Result -> + Result + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, + malformed_data), + Version, StateName, State) end; -handle_sni_extension(_, State) -> - State. + +gen_info(Event, StateName, #state{negotiated_version = Version} = State) -> + try handle_info(Event, StateName, State) of + Result -> + Result + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + malformed_handshake_data), + Version, StateName, State) + end. + diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 566b7db332..a2486bf752 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -41,7 +41,7 @@ %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- --spec client_hello(host(), inet:port_number(), #connection_states{}, +-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(), #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% @@ -54,8 +54,7 @@ client_hello(Host, Port, ConnectionStates, } = SslOpts, Cache, CacheCb, Renegotiation, OwnCert) -> Version = tls_record:highest_protocol_version(Versions), - Pending = ssl_record:pending_connection_state(ConnectionStates, read), - SecParams = Pending#connection_state.security_parameters, + #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version), Extensions = ssl_handshake:client_hello_extensions(Host, Version, AvailableCipherSuites, @@ -78,14 +77,14 @@ client_hello(Host, Port, ConnectionStates, %%-------------------------------------------------------------------- -spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, - #connection_states{} | {inet:port_number(), #session{}, db_handle(), - atom(), #connection_states{}, + ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(), + atom(), ssl_record:connection_states(), binary() | undefined, ssl_cipher:key_algo()}, boolean()) -> {tls_record:tls_version(), session_id(), - #connection_states{}, alpn | npn, binary() | undefined}| + ssl_record:connection_states(), alpn | npn, binary() | undefined}| {tls_record:tls_version(), {resumed | new, #session{}}, - #connection_states{}, binary() | undefined, + ssl_record:connection_states(), binary() | undefined, #hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} | undefined} | #alert{}. %% @@ -109,19 +108,25 @@ hello(#client_hello{client_version = ClientVersion, cipher_suites = CipherSuites} = Hello, #ssl_options{versions = Versions} = SslOpts, Info, Renegotiation) -> - Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), - case ssl_cipher:is_fallback(CipherSuites) of + try + Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), + case ssl_cipher:is_fallback(CipherSuites) of true -> - Highest = tls_record:highest_protocol_version(Versions), - case tls_record:is_higher(Highest, Version) of - true -> - ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); - false -> - handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) - end; - false -> - handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) - end. + Highest = tls_record:highest_protocol_version(Versions), + case tls_record:is_higher(Highest, Version) of + true -> + ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); + false -> + handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) + end; + false -> + handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) + end + catch + _:_ -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) + end. + %%-------------------------------------------------------------------- -spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist(). %% @@ -187,8 +192,13 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId, get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - Handshake = decode_handshake(Version, Type, Body, V2Hello), - get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]); + try decode_handshake(Version, Type, Body, V2Hello) of + Handshake -> + get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) + catch + _:_ -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error)) + end; get_tls_handshake_aux(_Version, Data, _, Acc) -> {lists:reverse(Acc), Data}. diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 9348c8bbdd..5331dd1303 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -32,7 +32,7 @@ -include("ssl_cipher.hrl"). %% Handling of incoming data --export([get_tls_records/2]). +-export([get_tls_records/2, init_connection_states/2]). %% Decoding -export([decode_cipher_text/3]). @@ -56,12 +56,28 @@ %%==================================================================== %% Internal application API %%==================================================================== +%%-------------------------------------------------------------------- +-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) -> + ssl_record:connection_states(). +%% % + % +%% Description: Creates a connection_states record with appropriate +%% values for the initial SSL connection setup. +%%-------------------------------------------------------------------- +init_connection_states(Role, BeastMitigation) -> + ConnectionEnd = ssl_record:record_protocol_role(Role), + Current = initial_connection_state(ConnectionEnd, BeastMitigation), + Pending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation), + #{current_read => Current, + pending_read => Pending, + current_write => Current, + pending_write => Pending}. %%-------------------------------------------------------------------- -spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}. %% -%% Description: Given old buffer and new data from TCP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover +%% Description: Given old buffer and new data from TCP, packs up a records %% data %%-------------------------------------------------------------------- get_tls_records(Data, <<>>) -> @@ -129,63 +145,61 @@ get_tls_records_aux(Data, Acc) -> end. encode_plain_text(Type, Version, Data, - #connection_states{current_write = - #connection_state{ - sequence_number = Seq, - compression_state=CompS0, - security_parameters= - #security_parameters{ - cipher_type = ?AEAD, - compression_algorithm=CompAlg} - }= WriteState0} = ConnectionStates) -> + #{current_write := + #{sequence_number := Seq, + compression_state := CompS0, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + compression_algorithm = CompAlg} + }= WriteState0} = ConnectionStates) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - WriteState1 = WriteState0#connection_state{compression_state = CompS1}, + WriteState1 = WriteState0#{compression_state => CompS1}, AAD = calc_aad(Type, Version, WriteState1), {CipherFragment, WriteState} = ssl_record:cipher_aead(Version, Comp, WriteState1, AAD), CipherText = encode_tls_cipher_text(Type, Version, CipherFragment), - {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}}; + {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}}; encode_plain_text(Type, Version, Data, - #connection_states{current_write = - #connection_state{ - sequence_number = Seq, - compression_state=CompS0, - security_parameters= - #security_parameters{compression_algorithm=CompAlg} - }= WriteState0} = ConnectionStates) -> + #{current_write := + #{sequence_number := Seq, + compression_state := CompS0, + security_parameters := + #security_parameters{compression_algorithm = CompAlg} + }= WriteState0} = ConnectionStates) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - WriteState1 = WriteState0#connection_state{compression_state = CompS1}, + WriteState1 = WriteState0#{compression_state => CompS1}, MacHash = calc_mac_hash(Type, Version, Comp, WriteState1), {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash), CipherText = encode_tls_cipher_text(Type, Version, CipherFragment), - {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}}. + {CipherText, ConnectionStates#{current_write => WriteState#{sequence_number => Seq +1}}}; +encode_plain_text(_,_,_, CS) -> + exit({cs, CS}). %%-------------------------------------------------------------------- --spec decode_cipher_text(#ssl_tls{}, #connection_states{}, boolean()) -> - {#ssl_tls{}, #connection_states{}}| #alert{}. +-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) -> + {#ssl_tls{}, ssl_record:connection_states()}| #alert{}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- decode_cipher_text(#ssl_tls{type = Type, version = Version, fragment = CipherFragment} = CipherText, - #connection_states{current_read = - #connection_state{ - compression_state = CompressionS0, - sequence_number = Seq, - security_parameters= - #security_parameters{ - cipher_type = ?AEAD, - compression_algorithm=CompAlg} - } = ReadState0} = ConnnectionStates0, _) -> + #{current_read := + #{compression_state := CompressionS0, + sequence_number := Seq, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + compression_algorithm = CompAlg} + } = ReadState0} = ConnnectionStates0, _) -> AAD = calc_aad(Type, Version, ReadState0), case ssl_record:decipher_aead(Version, CipherFragment, ReadState0, AAD) of {PlainFragment, ReadState1} -> {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - sequence_number = Seq + 1, - compression_state = CompressionS1}}, + ConnnectionStates = ConnnectionStates0#{ + current_read => ReadState1#{sequence_number => Seq + 1, + compression_state => CompressionS1}}, {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; #alert{} = Alert -> Alert @@ -193,13 +207,12 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, decode_cipher_text(#ssl_tls{type = Type, version = Version, fragment = CipherFragment} = CipherText, - #connection_states{current_read = - #connection_state{ - compression_state = CompressionS0, - sequence_number = Seq, - security_parameters= - #security_parameters{compression_algorithm=CompAlg} - } = ReadState0} = ConnnectionStates0, PaddingCheck) -> + #{current_read := + #{compression_state := CompressionS0, + sequence_number := Seq, + security_parameters := + #security_parameters{compression_algorithm = CompAlg} + } = ReadState0} = ConnnectionStates0, PaddingCheck) -> case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of {PlainFragment, Mac, ReadState1} -> MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1), @@ -207,10 +220,10 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, true -> {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - sequence_number = Seq + 1, - compression_state = CompressionS1}}, + ConnnectionStates = ConnnectionStates0#{ + current_read => ReadState1#{ + sequence_number => Seq + 1, + compression_state => CompressionS1}}, {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) @@ -375,6 +388,18 @@ is_acceptable_version(_,_) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +initial_connection_state(ConnectionEnd, BeastMitigation) -> + #{security_parameters => + ssl_record:initial_security_params(ConnectionEnd), + sequence_number => 0, + beast_mitigation => BeastMitigation, + compression_state => undefined, + cipher_state => undefined, + mac_secret => undefined, + secure_renegotiation => undefined, + client_verify_data => undefined, + server_verify_data => undefined + }. lowest_list_protocol_version(Ver, []) -> Ver; @@ -413,15 +438,15 @@ sufficient_tlsv1_2_crypto_support() -> proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)). calc_mac_hash(Type, Version, - PlainFragment, #connection_state{sequence_number = SeqNo, - mac_secret = MacSecret, - security_parameters = - SecPars}) -> + PlainFragment, #{sequence_number := SeqNo, + mac_secret := MacSecret, + security_parameters:= + SecPars}) -> Length = erlang:iolist_size(PlainFragment), mac_hash(Version, SecPars#security_parameters.mac_algorithm, MacSecret, SeqNo, Type, Length, PlainFragment). calc_aad(Type, {MajVer, MinVer}, - #connection_state{sequence_number = SeqNo}) -> + #{sequence_number := SeqNo}) -> <<SeqNo:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>. diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec index 86e14c033e..0ad94e22bc 100644 --- a/lib/ssl/test/ssl.spec +++ b/lib/ssl/test/ssl.spec @@ -1,4 +1,5 @@ {suites,"../ssl_test",all}. {skip_cases, "../ssl_test", - ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple], + ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple, + use_pem_cache, bypass_pem_cache], "Benchmarks run separately"}. diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl index b8a03f578d..258922d128 100644 --- a/lib/ssl/test/ssl_ECC_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_SUITE.erl @@ -145,7 +145,7 @@ init_per_testcase(TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]), end_per_testcase(TestCase, Config), - ssl:start(), + ssl_test_lib:clean_start(), ct:timetrap({seconds, 15}), Config. @@ -159,42 +159,42 @@ end_per_testcase(_TestCase, Config) -> client_ecdh_server_ecdh(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdh_rsa_opts, Config), - SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config), + SOpts = proplists:get_value(server_ecdh_rsa_opts, Config), basic_test(COpts, SOpts, Config). client_ecdh_server_rsa(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdh_rsa_opts, Config), - SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config), + SOpts = proplists:get_value(server_opts, Config), basic_test(COpts, SOpts, Config). client_rsa_server_ecdh(Config) when is_list(Config) -> - COpts = proplists:get_value(client_ecdh_rsa_opts, Config), - SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config), + COpts = proplists:get_value(client_opts, Config), + SOpts = proplists:get_value(server_ecdh_rsa_opts, Config), basic_test(COpts, SOpts, Config). client_rsa_server_rsa(Config) when is_list(Config) -> - COpts = proplists:get_value(client_verification_opts, Config), - SOpts = proplists:get_value(server_verification_opts, Config), + COpts = proplists:get_value(client_opts, Config), + SOpts = proplists:get_value(server_opts, Config), basic_test(COpts, SOpts, Config). client_ecdsa_server_ecdsa(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), basic_test(COpts, SOpts, Config). client_ecdsa_server_rsa(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + SOpts = proplists:get_value(server_opts, Config), basic_test(COpts, SOpts, Config). client_rsa_server_ecdsa(Config) when is_list(Config) -> - COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + COpts = proplists:get_value(client_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), basic_test(COpts, SOpts, Config). client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), ServerCert = proplists:get_value(certfile, SOpts), ServerKeyFile = proplists:get_value(keyfile, SOpts), {ok, PemBin} = file:read_file(ServerKeyFile), @@ -244,20 +244,20 @@ basic_test(ClientCert, ClientKey, ClientCA, ServerCert, ServerKey, ServerCA, Con check_result(Server, SType, Client, CType), close(Server, Client). -start_client(openssl, Port, CA, OwnCa, Cert, Key, Config) -> - PrivDir = proplists:get_value(priv_dir, Config), - NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa), +start_client(openssl, Port, PeerCA, OwnCa, Cert, Key, _Config) -> + CA = new_openssl_ca("openssl_client_ca", PeerCA, OwnCa), Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Exe = "openssl", Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", Cert, "-CAfile", NewCA, + "-cert", Cert, "-CAfile", CA, "-key", Key, "-host","localhost", "-msg", "-debug"], OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), OpenSslPort; -start_client(erlang, Port, CA, _, Cert, Key, Config) -> +start_client(erlang, Port, PeerCA, OwnCa, Cert, Key, Config) -> + CA = new_ca("erlang_client_ca", PeerCA, OwnCa), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -267,20 +267,19 @@ start_client(erlang, Port, CA, _, Cert, Key, Config) -> {cacertfile, CA}, {certfile, Cert}, {keyfile, Key}]}]). -start_server(openssl, CA, OwnCa, Cert, Key, Config) -> - PrivDir = proplists:get_value(priv_dir, Config), - NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa), - +start_server(openssl, PeerCA, OwnCa, Cert, Key, _Config) -> + CA = new_openssl_ca("openssl_server_ca", PeerCA, OwnCa), Port = ssl_test_lib:inet_port(node()), Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Exe = "openssl", Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-verify", "2", "-cert", Cert, "-CAfile", NewCA, + "-verify", "2", "-cert", Cert, "-CAfile", CA, "-key", Key, "-msg", "-debug"], OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), {OpenSslPort, Port}; -start_server(erlang, CA, _, Cert, Key, Config) -> +start_server(erlang, PeerCA, OwnCa, Cert, Key, Config) -> + CA = new_ca("erlang_server_ca", PeerCA, OwnCa), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, @@ -291,7 +290,8 @@ start_server(erlang, CA, _, Cert, Key, Config) -> [{verify, verify_peer}, {cacertfile, CA}, {certfile, Cert}, {keyfile, Key}]}]), {Server, ssl_test_lib:inet_port(Server)}. -start_server_with_raw_key(erlang, CA, _, Cert, Key, Config) -> +start_server_with_raw_key(erlang, PeerCA, OwnCa, Cert, Key, Config) -> + CA = new_ca("erlang_server_ca", PeerCA, OwnCa), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, @@ -336,17 +336,27 @@ close(Client, Server) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -%% Work around OpenSSL bug, apparently the same bug as we had fixed in -%% 11629690ba61f8e0c93ef9b2b6102fd279825977 new_ca(FileName, CA, OwnCa) -> {ok, P1} = file:read_file(CA), E1 = public_key:pem_decode(P1), {ok, P2} = file:read_file(OwnCa), E2 = public_key:pem_decode(P2), + Pem = public_key:pem_encode(E1 ++E2), + file:write_file(FileName, Pem), + FileName. + +new_openssl_ca(FileName, CA, OwnCa) -> + {ok, P1} = file:read_file(CA), + E1 = public_key:pem_decode(P1), + {ok, P2} = file:read_file(OwnCa), + E2 = public_key:pem_decode(P2), case os:cmd("openssl version") of "OpenSSL 1.0.1p-freebsd" ++ _ -> Pem = public_key:pem_encode(E1 ++E2), file:write_file(FileName, Pem); + "LibreSSL" ++ _ -> + Pem = public_key:pem_encode(E1 ++E2), + file:write_file(FileName, Pem); _ -> Pem = public_key:pem_encode(E2 ++E1), file:write_file(FileName, Pem) diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl index da181faf64..9d57e89b9b 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl @@ -71,7 +71,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index efa5faa218..57963fd44b 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -40,6 +40,7 @@ -define(SLEEP, 500). -define(RENEGOTIATION_DISABLE_TIME, 12000). -define(CLEAN_SESSION_DB, 60000). +-define(SEC_RENEGOTIATION_TIMEOUT, 30). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -249,7 +250,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), @@ -306,6 +307,7 @@ init_per_testcase(protocol_versions, Config) -> init_per_testcase(reuse_session_expired, Config) -> ssl:stop(), application:load(ssl), + ssl_test_lib:clean_env(), application:set_env(ssl, session_lifetime, ?EXPIRE), application:set_env(ssl, session_delay_cleanup_time, 500), ssl:start(), @@ -315,6 +317,7 @@ init_per_testcase(reuse_session_expired, Config) -> init_per_testcase(empty_protocol_versions, Config) -> ssl:stop(), application:load(ssl), + ssl_test_lib:clean_env(), application:set_env(ssl, protocol_version, []), ssl:start(), ct:timetrap({seconds, 5}), @@ -340,7 +343,7 @@ init_per_testcase(TestCase, Config) when TestCase == client_renegotiate; TestCase == renegotiate_dos_mitigate_passive; TestCase == renegotiate_dos_mitigate_absolute -> ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 30}), + ct:timetrap({seconds, ?SEC_RENEGOTIATION_TIMEOUT + 5}), Config; init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites; @@ -350,6 +353,11 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites; TestCase == ciphers_dsa_signed_certs; TestCase == ciphers_dsa_signed_certs_openssl_names; TestCase == anonymous_cipher_suites; + TestCase == ciphers_ecdsa_signed_certs; + TestCase == ciphers_ecdsa_signed_certs_openssl_names; + TestCase == anonymous_cipher_suites; + TestCase == psk_anon_cipher_suites; + TestCase == psk_anon_with_hint_cipher_suites; TestCase == versions_option, TestCase == tls_tcp_connect_big -> ssl_test_lib:ct_log_supported_protocol_versions(Config), @@ -408,8 +416,13 @@ init_per_testcase(TestCase, Config) when TestCase == tls_ssl_accept_timeout; ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 15}), Config; -init_per_testcase(clear_pem_cache, Config) -> +init_per_testcase(TestCase, Config) when TestCase == clear_pem_cache; + TestCase == der_input; + TestCase == defaults -> ssl_test_lib:ct_log_supported_protocol_versions(Config), + %% White box test need clean start + ssl:stop(), + ssl:start(), ct:timetrap({seconds, 20}), Config; init_per_testcase(raw_ssl_option, Config) -> @@ -430,7 +443,9 @@ init_per_testcase(accept_pool, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), Config end; - +init_per_testcase(controller_dies, Config) -> + ct:timetrap({seconds, 10}), + Config; init_per_testcase(_TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 5}), @@ -441,6 +456,11 @@ end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_delay_cleanup_time), end_per_testcase(default_action, Config); +end_per_testcase(Case, Config) when Case == protocol_versions; + Case == empty_protocol_versions-> + application:unset_env(ssl, protocol_versions), + end_per_testcase(default_action, Config); + end_per_testcase(_TestCase, Config) -> Config. @@ -567,8 +587,8 @@ prf(Config) when is_list(Config) -> connection_info() -> [{doc,"Test the API function ssl:connection_information/1"}]. connection_info(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1144,8 +1164,8 @@ cipher_suites_mix() -> cipher_suites_mix(Config) when is_list(Config) -> CipherSuites = [{ecdh_rsa,aes_128_cbc,sha256,sha256}, {rsa,aes_128_cbc,sha}], - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -4288,7 +4308,7 @@ erlang_ssl_receive(Socket, Data) -> erlang_ssl_receive(Socket, tl(Data)); Other -> ct:fail({unexpected_message, Other}) - after ?SLEEP * 3 * test_server:timetrap_scale_factor() -> + after timer:seconds(?SEC_RENEGOTIATION_TIMEOUT) * test_server:timetrap_scale_factor() -> ct:fail({did_not_get, Data}) end. @@ -4409,14 +4429,14 @@ run_suites(Ciphers, Version, Config, Type) -> {ClientOpts, ServerOpts} = case Type of rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), - ssl_test_lib:ssl_options(server_opts, Config)}; + {ssl_test_lib:ssl_options(client_verification_opts, Config), + ssl_test_lib:ssl_options(server_verification_opts, Config)}; dsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_dsa_opts, Config)}; anonymous -> %% No certs in opts! - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_anon, Config)}; psk -> {ssl_test_lib:ssl_options(client_psk, Config), @@ -4440,31 +4460,31 @@ run_suites(Ciphers, Version, Config, Type) -> {ssl_test_lib:ssl_options(client_srp_dsa, Config), ssl_test_lib:ssl_options(server_srp_dsa, Config)}; ecdsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_ecdsa_opts, Config)}; ecdh_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}; rc4_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_opts, Config)]}; + ssl_test_lib:ssl_options(server_verification_opts, Config)]}; rc4_ecdh_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; rc4_ecdsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; des_dhe_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_opts, Config)]}; + ssl_test_lib:ssl_options(server_verification_opts, Config)]}; des_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_opts, Config)]} + ssl_test_lib:ssl_options(server_verification_opts, Config)]} end, Result = lists:map(fun(Cipher) -> diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index ed439a425f..21989f8d99 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -25,11 +25,12 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. -all() -> [{group, setup}, {group, payload}]. +all() -> [{group, setup}, {group, payload}, {group, pem_cache}]. groups() -> [{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]}, - {payload, [{repeat, 3}], [payload_simple]} + {payload, [{repeat, 3}], [payload_simple]}, + {pem_cache, [{repeat, 3}], [use_pem_cache, bypass_pem_cache]} ]. init_per_group(_GroupName, Config) -> @@ -49,9 +50,33 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_testcase(use_pem_cache, Conf) -> + case bypass_pem_cache_supported() of + false -> {skipped, "PEM cache bypass support required"}; + true -> + application:set_env(ssl, bypass_pem_cache, false), + Conf + end; +init_per_testcase(bypass_pem_cache, Conf) -> + case bypass_pem_cache_supported() of + false -> {skipped, "PEM cache bypass support required"}; + true -> + application:set_env(ssl, bypass_pem_cache, true), + Conf + end; init_per_testcase(_Func, Conf) -> Conf. +end_per_testcase(use_pem_cache, _Config) -> + case bypass_pem_cache_supported() of + false -> ok; + true -> application:set_env(ssl, bypass_pem_cache, false) + end; +end_per_testcase(bypass_pem_cache, _Config) -> + case bypass_pem_cache_supported() of + false -> ok; + true -> application:set_env(ssl, bypass_pem_cache, false) + end; end_per_testcase(_Func, _Conf) -> ok. @@ -94,6 +119,18 @@ payload_simple(Config) -> {suite, "ssl"}, {name, "Payload simple"}]}), ok. +use_pem_cache(_Config) -> + {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()), + ct_event:notify(#event{name = benchmark_data, + data=[{value, Result}, + {suite, "ssl"}, {name, "Use PEM cache"}]}). + +bypass_pem_cache(_Config) -> + {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()), + ct_event:notify(#event{name = benchmark_data, + data=[{value, Result}, + {suite, "ssl"}, {name, "Bypass PEM cache"}]}). + ssl() -> test(ssl, ?COUNT, node()). @@ -172,6 +209,18 @@ server_init(ssl, payload, Loop, _, Server) -> ssl:close(TSocket) end, setup_server_connection(Socket, Test); +server_init(ssl, pem_cache, Loop, _, Server) -> + {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)), + {ok, {_Host, Port}} = ssl:sockname(Socket), + {ok, Host} = inet:gethostname(), + Server ! {self(), {init, Host, Port}}, + Test = fun(TSocket) -> + ok = ssl:ssl_accept(TSocket), + Size = byte_size(msg()), + server_echo(TSocket, Size, Loop), + ssl:close(TSocket) + end, + setup_server_connection(Socket, Test); server_init(Type, Tc, _, _, Server) -> io:format("No server init code for ~p ~p~n",[Type, Tc]), @@ -185,6 +234,11 @@ client_init(Master, ssl, payload, Host, Port) -> Master ! {self(), init}, Size = byte_size(msg()), {Sock, Size}; +client_init(Master, ssl, pem_cache, Host, Port) -> + {ok, Sock} = ssl:connect(Host, Port, ssl_opts(connect_der)), + Master ! {self(), init}, + Size = byte_size(msg()), + {Sock, Size}; client_init(_Me, Type, Tc, Host, Port) -> io:format("No client init code for ~p ~p~n",[Type, Tc]), {Host, Port}. @@ -228,6 +282,13 @@ payload(Loop, ssl, D = {Socket, Size}) when Loop > 0 -> payload(_, _, {Socket, _}) -> ssl:close(Socket). +pem_cache(N, ssl, Data = {Socket, Size}) when N > 0 -> + ok = ssl:send(Socket, msg()), + {ok, _} = ssl:recv(Socket, Size), + pem_cache(N-1, ssl, Data); +pem_cache(_, _, {Socket, _}) -> + ssl:close(Socket). + msg() -> <<"Hello", 0:(512*8), @@ -352,16 +413,43 @@ stop_profile(fprof, File) -> ssl_opts(listen) -> [{backlog, 500} | ssl_opts("server")]; ssl_opts(connect) -> - [{verify, verify_peer} - | ssl_opts("client")]; + [{verify, verify_peer} | ssl_opts("client")]; +ssl_opts(listen_der) -> + [{backlog, 500} | ssl_opts("server_der")]; +ssl_opts(connect_der) -> + [{verify, verify_peer} | ssl_opts("client_der")]; ssl_opts(Role) -> - Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + CertData = cert_data(Role), [{active, false}, {depth, 2}, {reuseaddr, true}, {mode,binary}, {nodelay, true}, - {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}, - {cacertfile, filename:join([Dir, Role, "cacerts.pem"])}, + {ciphers, [{dhe_rsa,aes_256_cbc,sha}]} + |CertData]. + +cert_data(Der) when Der =:= "server_der"; Der =:= "client_der" -> + [Role,_] = string:tokens(Der, "_"), + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + {ok, CaCert0} = file:read_file(filename:join([Dir, Role, "cacerts.pem"])), + {ok, Cert0} = file:read_file(filename:join([Dir, Role, "cert.pem"])), + {ok, Key0} = file:read_file(filename:join([Dir, Role, "key.pem"])), + [{_, Cert, _}] = public_key:pem_decode(Cert0), + CaCert1 = public_key:pem_decode(CaCert0), + CaCert = [CCert || {_, CCert, _} <- CaCert1], + [{KeyType, Key, _}] = public_key:pem_decode(Key0), + [{cert, Cert}, + {cacerts, CaCert}, + {key, {KeyType, Key}}]; +cert_data(Role) -> + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + [{cacertfile, filename:join([Dir, Role, "cacerts.pem"])}, {certfile, filename:join([Dir, Role, "cert.pem"])}, {keyfile, filename:join([Dir, Role, "key.pem"])}]. + +bypass_pem_cache_supported() -> + %% This function is currently critical to support cache bypass + %% and did not exist in prior versions. + catch ssl_pkix_db:module_info(), % ensure module is loaded + erlang:function_exported(ssl_pkix_db, extract_trusted_certs, 1). + diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 20165c70f0..4c6f1d7c01 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -65,9 +65,10 @@ tests() -> cert_expired, invalid_signature_client, invalid_signature_server, - extended_key_usage_verify_peer, - extended_key_usage_verify_none, - critical_extension_verify_peer, + extended_key_usage_verify_client, + extended_key_usage_verify_server, + critical_extension_verify_client, + critical_extension_verify_server, critical_extension_verify_none]. error_handling_tests()-> @@ -84,7 +85,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), @@ -122,6 +123,8 @@ init_per_testcase(TestCase, Config) when TestCase == cert_expired; ssl:clear_pem_cache(), init_per_testcase(common, Config); init_per_testcase(_TestCase, Config) -> + ssl:stop(), + ssl:start(), ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 5}), Config. @@ -136,7 +139,7 @@ end_per_testcase(_TestCase, Config) -> verify_peer() -> [{doc,"Test option verify_peer"}]. verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -190,7 +193,7 @@ server_verify_client_once() -> [{doc,"Test server option verify_client_once"}]. server_verify_client_once(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -230,7 +233,7 @@ server_require_peer_cert_ok() -> server_require_peer_cert_ok(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -259,7 +262,7 @@ server_require_peer_cert_fail() -> server_require_peer_cert_fail(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - BadClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + BadClientOpts = ssl_test_lib:ssl_options(client_opts, []), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, @@ -290,7 +293,7 @@ server_require_peer_cert_partial_chain() -> server_require_peer_cert_partial_chain(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), @@ -325,13 +328,13 @@ server_require_peer_cert_allow_partial_chain() -> server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), - {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), - [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs), + {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), + [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ClientCAs), PartialChain = fun(CertChain) -> case lists:member(IntermidiateCA, CertChain) of @@ -367,7 +370,7 @@ server_require_peer_cert_do_not_allow_partial_chain() -> server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), @@ -408,7 +411,7 @@ server_require_peer_cert_partial_chain_fun_fail() -> server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = proplists:get_value(client_verification_opts, Config), + ClientOpts = proplists:get_value(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), @@ -448,7 +451,7 @@ verify_fun_always_run_client() -> verify_fun_always_run_client(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -492,7 +495,7 @@ verify_fun_always_run_client(Config) when is_list(Config) -> verify_fun_always_run_server() -> [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. verify_fun_always_run_server(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -524,9 +527,7 @@ verify_fun_always_run_server(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, - {options, - [{verify, verify_peer} - | ClientOpts]}]), + {options, ClientOpts}]), %% Client error may be {tls_alert, "handshake failure" } or closed depending on timing %% this is not a bug it is a circumstance of how tcp works! @@ -544,7 +545,7 @@ cert_expired() -> cert_expired(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), @@ -607,11 +608,11 @@ two_digits_str(N) -> lists:flatten(io_lib:format("~p", [N])). %%-------------------------------------------------------------------- -extended_key_usage_verify_peer() -> - [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}]. +extended_key_usage_verify_server() -> + [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode for server"}]. -extended_key_usage_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), +extended_key_usage_verify_server(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), @@ -660,7 +661,7 @@ extended_key_usage_verify_peer(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | + {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -669,12 +670,12 @@ extended_key_usage_verify_peer(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -extended_key_usage_verify_none() -> - [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}]. +extended_key_usage_verify_client() -> + [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}]. -extended_key_usage_verify_none(Config) when is_list(Config) -> +extended_key_usage_verify_client(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -730,11 +731,11 @@ extended_key_usage_verify_none(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -critical_extension_verify_peer() -> +critical_extension_verify_server() -> [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. -critical_extension_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), +critical_extension_verify_server(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), @@ -766,7 +767,7 @@ critical_extension_verify_peer(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]), + {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]), %% This certificate has a critical extension that we don't %% understand. Therefore, verification should fail. @@ -775,14 +776,60 @@ critical_extension_verify_peer(Config) when is_list(Config) -> ssl_test_lib:close(Server), ok. +%%-------------------------------------------------------------------- + +critical_extension_verify_client() -> + [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. + +critical_extension_verify_client(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + PrivDir = proplists:get_value(priv_dir, Config), + Active = proplists:get_value(active, Config), + ReceiveFunction = proplists:get_value(receive_function, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem", + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join([PrivDir, "server", NewCertName]), + add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join([PrivDir, "client", NewCertName]), + add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]), + + %% This certificate has a critical extension that we don't + %% understand. Therefore, verification should fail. + tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}}, + Client, {error, {tls_alert, "unsupported certificate"}}), + ssl_test_lib:close(Server), + ok. %%-------------------------------------------------------------------- critical_extension_verify_none() -> [{doc,"Test cert that has a critical unknown extension in verify_none mode"}]. critical_extension_verify_none(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -1070,7 +1117,7 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> server_verify_no_cacerts() -> [{doc,"Test server must have cacerts if it wants to verify client"}]. server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_opts, Config)), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -1084,7 +1131,7 @@ server_verify_no_cacerts(Config) when is_list(Config) -> unknown_server_ca_fail() -> [{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}]. unknown_server_ca_fail(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, @@ -1128,7 +1175,7 @@ unknown_server_ca_fail(Config) when is_list(Config) -> unknown_server_ca_accept_verify_none() -> [{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}]. unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1153,7 +1200,7 @@ unknown_server_ca_accept_verify_peer() -> [{doc, "Test that the client succeds if the ca is unknown in verify_peer mode" " with a verify_fun that accepts the unknown ca error"}]. unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts =ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1192,7 +1239,7 @@ unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> unknown_server_ca_accept_backwardscompatibility() -> [{doc,"Test that old style verify_funs will work"}]. unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl index 00636e5660..bc2822f0c4 100644 --- a/lib/ssl/test/ssl_crl_SUITE.erl +++ b/lib/ssl/test/ssl_crl_SUITE.erl @@ -72,7 +72,7 @@ init_per_suite(Config) -> false -> {skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])}; _ -> - catch crypto:stop(), + end_per_suite(Config), try crypto:start() of ok -> {ok, Hostname0} = inet:gethostname(), @@ -136,7 +136,7 @@ init_per_testcase(Case, Config0) -> true -> end_per_testcase(Case, Config0), inets:start(), - ssl:start(), + ssl_test_lib:clean_start(), ServerRoot = make_dir_path([proplists:get_value(priv_dir, Config0), idp_crl, tmp]), %% start a HTTP server to serve the CRLs {ok, Httpd} = inets:start(httpd, [{ipfamily, proplists:get_value(ipfamily, Config0)}, @@ -155,7 +155,7 @@ init_per_testcase(Case, Config0) -> [{cert_dir, CertDir} | Config]; false -> end_per_testcase(Case, Config0), - ssl:start(), + ssl_test_lib:clean_start(), Config0 end. diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 5ebf9bb2de..8740e8c8f0 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -109,11 +109,11 @@ common_end(_, _Config) -> basic() -> [{doc,"Test that two nodes can connect via ssl distribution"}]. basic(Config) when is_list(Config) -> - NH1 = start_ssl_node(Config), + gen_dist_test(basic_test, Config). + +basic_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(Config), Node2 = NH2#node_handle.nodename, - pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), @@ -161,18 +161,16 @@ basic(Config) when is_list(Config) -> ok end end) - end, - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + end. %%-------------------------------------------------------------------- payload() -> [{doc,"Test that send a lot of data between the ssl distributed noes"}]. payload(Config) when is_list(Config) -> - NH1 = start_ssl_node(Config), + gen_dist_test(payload_test, Config). + +payload_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(Config), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), @@ -204,10 +202,8 @@ payload(Config) when is_list(Config) -> ok end end) - end, - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + end. + %%-------------------------------------------------------------------- plain_options() -> [{doc,"Test specifying additional options"}]. @@ -218,20 +214,17 @@ plain_options(Config) when is_list(Config) -> "client_verify verify_none server_verify verify_none " "server_depth 1 client_depth 1 " "server_hibernate_after 500 client_hibernate_after 500", + gen_dist_test(plain_options_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +plain_options_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- plain_verify_options() -> [{doc,"Test specifying additional options"}]. @@ -240,20 +233,18 @@ plain_verify_options(Config) when is_list(Config) -> "client_secure_renegotiate true " "server_reuse_sessions true client_reuse_sessions true " "server_hibernate_after 500 client_hibernate_after 500", + gen_dist_test(plain_verify_options_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +plain_verify_options_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), Node2 = NH2#node_handle.nodename, - + pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), - + [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). + - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- nodelay_option() -> [{doc,"Test specifying dist_nodelay option"}]. @@ -265,6 +256,7 @@ nodelay_option(Config) -> after application:unset_env(kernel, dist_nodelay) end. +%%-------------------------------------------------------------------- listen_port_options() -> [{doc, "Test specifying listening ports"}]. @@ -285,32 +277,39 @@ listen_port_options(Config) when is_list(Config) -> #node_handle{} -> %% If the node was able to start, it didn't take the port %% option into account. + stop_ssl_node(NH1), exit(unexpected_success) catch exit:{accept_failed, timeout} -> %% The node failed to start, as expected. ok end, - + %% Try again, now specifying a high max port. PortOpt2 = "-kernel inet_dist_listen_min " ++ integer_to_list(Port1) ++ - " inet_dist_listen_max 65535", + " inet_dist_listen_max 65535", NH2 = start_ssl_node([{additional_dist_opts, PortOpt2} | Config]), - Node2 = NH2#node_handle.nodename, - Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)), - {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0), - {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2), - - %% The new port should be higher: - if Port2 > Port1 -> - ok; - true -> - error({port, Port2, not_higher_than, Port1}) + + try + Node2 = NH2#node_handle.nodename, + Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)), + {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0), + {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2), + + %% The new port should be higher: + if Port2 > Port1 -> + ok; + true -> + error({port, Port2, not_higher_than, Port1}) + end + catch + _:Reason -> + stop_ssl_node(NH2), + ct:fail(Reason) end, - - stop_ssl_node(NH1), stop_ssl_node(NH2), success(Config). + %%-------------------------------------------------------------------- listen_options() -> [{doc, "Test inet_dist_listen_options"}]. @@ -329,28 +328,25 @@ do_listen_options(Prio, Config) -> end, Options = "-kernel inet_dist_listen_options " ++ PriorityString, - - NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]), - NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]), - Node2 = NH2#node_handle.nodename, - + gen_dist_test(listen_options_test, [{prio, Prio}, {additional_dist_opts, Options} | Config]). + +listen_options_test(NH1, NH2, Config) -> + Prio = proplists:get_value(prio, Config), + Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), PrioritiesNode1 = apply_on_ssl_node(NH1, fun get_socket_priorities/0), PrioritiesNode2 = apply_on_ssl_node(NH2, fun get_socket_priorities/0), - + Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio], - ?t:format("Elevated1: ~p~n", [Elevated1]), + ct:pal("Elevated1: ~p~n", [Elevated1]), Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio], - ?t:format("Elevated2: ~p~n", [Elevated2]), + ct:pal("Elevated2: ~p~n", [Elevated2]), [_|_] = Elevated1, - [_|_] = Elevated2, + [_|_] = Elevated2. - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- connect_options() -> [{doc, "Test inet_dist_connect_options"}]. @@ -369,9 +365,11 @@ do_connect_options(Prio, Config) -> end, Options = "-kernel inet_dist_connect_options " ++ PriorityString, + gen_dist_test(connect_options_test, + [{prio, Prio}, {additional_dist_opts, Options} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]), - NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]), +connect_options_test(NH1, NH2, Config) -> + Prio = proplists:get_value(prio, Config), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), @@ -382,17 +380,14 @@ do_connect_options(Prio, Config) -> apply_on_ssl_node(NH2, fun get_socket_priorities/0), Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio], - ?t:format("Elevated1: ~p~n", [Elevated1]), + ct:pal("Elevated1: ~p~n", [Elevated1]), Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio], - ?t:format("Elevated2: ~p~n", [Elevated2]), + ct:pal("Elevated2: ~p~n", [Elevated2]), %% Node 1 will have a socket with elevated priority. [_|_] = Elevated1, %% Node 2 will not, since it only applies to outbound connections. - [] = Elevated2, + [] = Elevated2. - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- use_interface() -> [{doc, "Test inet_dist_use_interface"}]. @@ -403,22 +398,28 @@ use_interface(Config) when is_list(Config) -> %% Start a node, and get the port number it's listening on. NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]), - Node1 = NH1#node_handle.nodename, - Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)), - {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0), - {Name, Port} = lists:keyfind(Name, 1, NodesPorts), - - %% Now find the socket listening on that port, and check its sockname. - Sockets = apply_on_ssl_node( - NH1, - fun() -> - [inet:sockname(P) || - P <- inet_ports(), - {ok, Port} =:= (catch inet:port(P))] - end), - %% And check that it's actually listening on localhost. - [{ok,{{127,0,0,1},Port}}] = Sockets, - + + try + Node1 = NH1#node_handle.nodename, + Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)), + {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0), + {Name, Port} = lists:keyfind(Name, 1, NodesPorts), + + %% Now find the socket listening on that port, and check its sockname. + Sockets = apply_on_ssl_node( + NH1, + fun() -> + [inet:sockname(P) || + P <- inet_ports(), + {ok, Port} =:= (catch inet:port(P))] + end), + %% And check that it's actually listening on localhost. + [{ok,{{127,0,0,1},Port}}] = Sockets + catch + _:Reason -> + stop_ssl_node(NH1), + ct:fail(Reason) + end, stop_ssl_node(NH1), success(Config). %%-------------------------------------------------------------------- @@ -430,11 +431,11 @@ verify_fun_fail(Config) when is_list(Config) -> "\"{ssl_dist_SUITE,verify_fail_always,{}}\" " "client_verify verify_peer client_verify_fun " "\"{ssl_dist_SUITE,verify_fail_always,{}}\" ", + gen_dist_test(verify_fun_fail_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +verify_fun_fail_test(NH1, NH2, _) -> Node2 = NH2#node_handle.nodename, - + pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [] = apply_on_ssl_node(NH1, fun () -> nodes() end), @@ -446,25 +447,9 @@ verify_fun_fail(Config) when is_list(Config) -> %% On the server node, it wouldn't run, because the server didn't %% request a certificate from the client. undefined = - apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end), + apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end). - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). -verify_fail_always(_Certificate, _Event, _State) -> - %% Create an ETS table, to record the fact that the verify function ran. - %% Spawn a new process, to avoid the ETS table disappearing. - Parent = self(), - spawn( - fun() -> - ets:new(verify_fun_ran, [public, named_table]), - ets:insert(verify_fun_ran, {verify_fail_always_ran, true}), - Parent ! go_ahead, - timer:sleep(infinity) - end), - receive go_ahead -> ok end, - {fail, bad_certificate}. %%-------------------------------------------------------------------- verify_fun_pass() -> @@ -476,10 +461,10 @@ verify_fun_pass(Config) when is_list(Config) -> "server_fail_if_no_peer_cert true " "client_verify verify_peer client_verify_fun " "\"{ssl_dist_SUITE,verify_pass_always,{}}\" ", + gen_dist_test(verify_fun_pass_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +verify_fun_pass_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), @@ -494,25 +479,8 @@ verify_fun_pass(Config) when is_list(Config) -> %% requested and verified the client's certificate because we %% passed fail_if_no_peer_cert. [{verify_pass_always_ran, true}] = - apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end), + apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end). - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). - -verify_pass_always(_Certificate, _Event, State) -> - %% Create an ETS table, to record the fact that the verify function ran. - %% Spawn a new process, to avoid the ETS table disappearing. - Parent = self(), - spawn( - fun() -> - ets:new(verify_fun_ran, [public, named_table]), - ets:insert(verify_fun_ran, {verify_pass_always_ran, true}), - Parent ! go_ahead, - timer:sleep(infinity) - end), - receive go_ahead -> ok end, - {valid, State}. %%-------------------------------------------------------------------- crl_check_pass() -> [{doc,"Test crl_check with non-revoked certificate"}]. @@ -520,10 +488,10 @@ crl_check_pass(Config) when is_list(Config) -> DistOpts = "-ssl_dist_opt client_crl_check true", NewConfig = [{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_check_pass_test, NewConfig). - NH1 = start_ssl_node(NewConfig), +crl_check_pass_test(NH1, NH2, Config) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), Node2 = NH2#node_handle.nodename, PrivDir = ?config(priv_dir, Config), @@ -533,11 +501,7 @@ crl_check_pass(Config) when is_list(Config) -> pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_check_fail() -> @@ -549,10 +513,9 @@ crl_check_fail(Config) when is_list(Config) -> %% The server uses a revoked certificate. {server_cert_dir, "revoked"}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_check_fail_test, NewConfig). - NH1 = start_ssl_node(NewConfig), - %%Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), +crl_check_fail_test(NH1, NH2, Config) -> Node2 = NH2#node_handle.nodename, PrivDir = ?config(priv_dir, Config), @@ -562,11 +525,7 @@ crl_check_fail(Config) when is_list(Config) -> pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_check_best_effort() -> @@ -576,22 +535,18 @@ crl_check_best_effort(Config) when is_list(Config) -> "server_verify verify_peer server_crl_check best_effort", NewConfig = [{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_check_best_effort_test, NewConfig). +crl_check_best_effort_test(NH1, NH2, _Config) -> %% We don't have the correct CRL at hand, but since crl_check is %% best_effort, we accept it anyway. - NH1 = start_ssl_node(NewConfig), Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_cache_check_pass() -> @@ -605,20 +560,16 @@ crl_cache_check_pass(Config) when is_list(Config) -> "\"{ssl_dist_SUITE,{\\\"" ++ NodeDir ++ "\\\",[]}}\"", NewConfig = [{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_cache_check_pass_test, NewConfig). - NH1 = start_ssl_node(NewConfig), +crl_cache_check_pass_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_cache_check_fail() -> @@ -636,44 +587,31 @@ crl_cache_check_fail(Config) when is_list(Config) -> {server_cert_dir, "revoked"}, {additional_dist_opts, DistOpts}] ++ Config, - NH1 = start_ssl_node(NewConfig), - NH2 = start_ssl_node(NewConfig), - Node2 = NH2#node_handle.nodename, + gen_dist_test(crl_cache_check_fail_test, NewConfig). +crl_cache_check_fail_test(NH1, NH2, _) -> + Node2 = NH2#node_handle.nodename, pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). - -%% ssl_crl_cache_api callbacks -lookup(_DistributionPoint, _DbHandle) -> - not_available. - -select({rdnSequence, NameParts}, {NodeDir, _}) -> - %% Extract the CN from the issuer name... - [CN] = [CN || - [#'AttributeTypeAndValue'{ - type = ?'id-at-commonName', - value = <<_, _, CN/binary>>}] <- NameParts], - %% ...and use that as the directory name to find the CRL. - error_logger:info_report([{found_cn, CN}]), - CRLFile = filename:join([NodeDir, CN, "crl.pem"]), - {ok, PemBin} = file:read_file(CRLFile), - PemEntries = public_key:pem_decode(PemBin), - CRLs = [ CRL || {'CertificateList', CRL, not_encrypted} - <- PemEntries], - CRLs. - -fresh_crl(_DistributionPoint, CRL) -> - CRL. - + [] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- %%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- +gen_dist_test(Test, Config) -> + NH1 = start_ssl_node(Config), + NH2 = start_ssl_node(Config), + try + ?MODULE:Test(NH1, NH2, Config) + catch + _:Reason -> + stop_ssl_node(NH1), + stop_ssl_node(NH2), + ct:fail(Reason) + end, + stop_ssl_node(NH1), + stop_ssl_node(NH2), + success(Config). %% ssl_node side api %% @@ -742,13 +680,15 @@ stop_ssl_node(#node_handle{connection_handler = Handler, receive {'DOWN', Mon, process, Handler, Reason} -> case Reason of - normal -> ok; - _ -> exit(Reason) + normal -> + ok; + _ -> + ct:pal("Down ~p ~n", [Reason]) end end; Error -> erlang:demonitor(Mon, [flush]), - exit(Error) + ct:pal("Warning ~p ~n", [Error]) end. start_ssl_node(Config) -> @@ -1226,3 +1166,53 @@ vsn(App) -> after application:stop(ssl) end. + +verify_fail_always(_Certificate, _Event, _State) -> + %% Create an ETS table, to record the fact that the verify function ran. + %% Spawn a new process, to avoid the ETS table disappearing. + Parent = self(), + spawn( + fun() -> + ets:new(verify_fun_ran, [public, named_table]), + ets:insert(verify_fun_ran, {verify_fail_always_ran, true}), + Parent ! go_ahead, + timer:sleep(infinity) + end), + receive go_ahead -> ok end, + {fail, bad_certificate}. + +verify_pass_always(_Certificate, _Event, State) -> + %% Create an ETS table, to record the fact that the verify function ran. + %% Spawn a new process, to avoid the ETS table disappearing. + Parent = self(), + spawn( + fun() -> + ets:new(verify_fun_ran, [public, named_table]), + ets:insert(verify_fun_ran, {verify_pass_always_ran, true}), + Parent ! go_ahead, + timer:sleep(infinity) + end), + receive go_ahead -> ok end, + {valid, State}. + +%% ssl_crl_cache_api callbacks +lookup(_DistributionPoint, _DbHandle) -> + not_available. + +select({rdnSequence, NameParts}, {NodeDir, _}) -> + %% Extract the CN from the issuer name... + [CN] = [CN || + [#'AttributeTypeAndValue'{ + type = ?'id-at-commonName', + value = <<_, _, CN/binary>>}] <- NameParts], + %% ...and use that as the directory name to find the CRL. + error_logger:info_report([{found_cn, CN}]), + CRLFile = filename:join([NodeDir, CN, "crl.pem"]), + {ok, PemBin} = file:read_file(CRLFile), + PemEntries = public_key:pem_decode(PemBin), + CRLs = [ CRL || {'CertificateList', CRL, not_encrypted} + <- PemEntries], + CRLs. + +fresh_crl(_DistributionPoint, CRL) -> + CRL. diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index a671e3e307..51f0651568 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -60,7 +60,7 @@ init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) -> ok -> case is_supported(sha512) of true -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index c55fa73cfb..a02881f1ae 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -68,7 +68,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 00eb9fee4f..69aeea10c5 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -41,6 +41,15 @@ all() -> create_server_hello_with_advertised_protocols_test, create_server_hello_with_no_advertised_protocols_test]. +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + init_per_testcase(_TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 5}), @@ -126,15 +135,12 @@ create_server_handshake(Npn) -> }, Vsn). create_connection_states() -> - #connection_states{ - pending_read = #connection_state{ - security_parameters = #security_parameters{ + #{pending_read => #{security_parameters => #security_parameters{ server_random = <<1:256>>, compression_algorithm = 1, cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA } - }, - current_read = #connection_state { - secure_renegotiation = false - } - }. + }, + current_read => #{secure_renegotiation => false + } + }. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index e49d432c21..900b6de9ca 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -41,7 +41,7 @@ -define(MANY, 1000). -define(SOME, 50). --define(BASE_TIMEOUT_SECONDS, 15). +-define(BASE_TIMEOUT_SECONDS, 30). -define(SOME_SCALE, 20). -define(MANY_SCALE, 20). @@ -140,7 +140,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) @@ -162,6 +162,7 @@ init_per_group(GroupName, Config) -> {skip, "Missing crypto support"} end; _ -> + ssl:stop(), ssl:start(), Config end. diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index cb0571d0a7..cb1957327a 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -70,7 +70,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) catch _:_ -> @@ -104,8 +104,13 @@ init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge; TestCase == client_echos_passive_huge; TestCase == client_echos_active_once_huge; TestCase == client_echos_active_huge -> - ct:timetrap({seconds, 90}), - Config; + case erlang:system_info(system_architecture) of + "sparc-sun-solaris2.10" -> + {skip,"Will take to long time on an old Sparc"}; + _ -> + ct:timetrap({seconds, 90}), + Config + end; init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_big; TestCase == server_echos_active_once_big; diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index 13b0ce8ed9..02c98fc40f 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -43,7 +43,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), @@ -63,14 +63,15 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(pem_cleanup = Case, Config) -> - end_per_testcase(Case, Config) , application:load(ssl), + end_per_testcase(Case, Config) , application:set_env(ssl, ssl_pem_cache_clean, ?CLEANUP_INTERVAL), ssl:start(), ct:timetrap({minutes, 1}), Config. end_per_testcase(_TestCase, Config) -> + ssl_test_lib:clean_env(), ssl:stop(), Config. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index b352844ba0..28637fc32d 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -58,7 +58,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index 34ef2e6af9..4e916a7f03 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -41,7 +41,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), ssl_test_lib:cert_options(Config0) diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 27c670cdc2..81f16030f7 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -385,7 +385,9 @@ cert_options(Config) -> SNIServerAKeyFile = filename:join([proplists:get_value(priv_dir, Config), "a.server", "key.pem"]), SNIServerBCertFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "cert.pem"]), SNIServerBKeyFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "key.pem"]), - [{client_opts, []}, + [{client_opts, [{cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, + {keyfile, ClientKeyFile}]}, {client_verification_opts, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, @@ -394,7 +396,7 @@ cert_options(Config) -> {certfile, ClientCertFileDigitalSignatureOnly}, {keyfile, ClientKeyFile}, {ssl_imp, new}]}, - {server_opts, [{ssl_imp, new},{reuseaddr, true}, + {server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, {client_psk, [{ssl_imp, new},{reuseaddr, true}, @@ -494,7 +496,7 @@ make_ecdsa_cert(Config) -> {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, - {cacertfile, ServerCaCertFile}, + {cacertfile, ClientCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, {verify, verify_peer}]}, {client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true}, @@ -519,7 +521,7 @@ make_ecdh_rsa_cert(Config) -> {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, - {cacertfile, ServerCaCertFile}, + {cacertfile, ClientCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, {verify, verify_peer}]}, {client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true}, @@ -805,16 +807,24 @@ send_selected_port(_,_,_) -> rsa_suites(CounterPart) -> ECC = is_sane_ecc(CounterPart), FIPS = is_fips(CounterPart), + CryptoSupport = crypto:supports(), + Ciphers = proplists:get_value(ciphers, CryptoSupport), lists:filter(fun({rsa, des_cbc, sha}) when FIPS == true -> false; ({dhe_rsa, des_cbc, sha}) when FIPS == true -> false; - ({rsa, _, _}) -> - true; - ({dhe_rsa, _, _}) -> - true; - ({ecdhe_rsa, _, _}) when ECC == true -> - true; + ({rsa, Cipher, _}) -> + lists:member(Cipher, Ciphers); + ({dhe_rsa, Cipher, _}) -> + lists:member(Cipher, Ciphers); + ({ecdhe_rsa, Cipher, _}) when ECC == true -> + lists:member(Cipher, Ciphers); + ({rsa, Cipher, _, _}) -> + lists:member(Cipher, Ciphers); + ({dhe_rsa, Cipher, _,_}) -> + lists:member(Cipher, Ciphers); + ({ecdhe_rsa, Cipher, _,_}) when ECC == true -> + lists:member(Cipher, Ciphers); (_) -> false end, @@ -1345,3 +1355,19 @@ ct_log_supported_protocol_versions(Config) -> _ -> ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]) end. + +clean_env() -> + application:unset_env(ssl, protocol_version), + application:unset_env(ssl, session_lifetime), + application:unset_env(ssl, session_cb), + application:unset_env(ssl, session_cb_init_args), + application:unset_env(ssl, session_cache_client_max), + application:unset_env(ssl, session_cache_server_max), + application:unset_env(ssl, ssl_pem_cache_clean), + application:unset_env(ssl, alert_timeout). + +clean_start() -> + ssl:stop(), + application:load(ssl), + clean_env(), + ssl:start(). diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index b3109b5de9..9ecfe5b0ea 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -55,7 +55,9 @@ groups() -> basic_tests() -> [basic_erlang_client_openssl_server, basic_erlang_server_openssl_client, - expired_session]. + expired_session, + ssl2_erlang_server_openssl_client_comp + ]. all_versions_tests() -> [ @@ -74,7 +76,8 @@ all_versions_tests() -> ciphers_dsa_signed_certs, erlang_client_bad_openssl_server, expired_session, - ssl2_erlang_server_openssl_client]. + ssl2_erlang_server_openssl_client + ]. alpn_tests() -> [erlang_client_alpn_openssl_server_alpn, @@ -116,7 +119,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), Config1 = ssl_test_lib:make_dsa_cert(Config0), @@ -180,7 +183,8 @@ special_init(TestCase, Config) {ok, Version} = application:get_env(ssl, protocol_version), check_sane_openssl_renegotaite(Config, Version); -special_init(ssl2_erlang_server_openssl_client, Config) -> +special_init(Case, Config) when Case == ssl2_erlang_server_openssl_client; + Case == ssl2_erlang_server_openssl_client_comp -> case ssl_test_lib:supports_ssl_tls_version(sslv2) of true -> Config; @@ -954,8 +958,52 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> Data = "From openssl to erlang", Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Exe = "openssl", + Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port), + "-ssl2", "-msg"], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + true = port_command(OpenSslPort, Data), + + ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]), + receive + {'EXIT', OpenSslPort, _} = Exit -> + ct:log("Received: ~p ~n", [Exit]), + ok + end, + receive + {'EXIT', _, _} = UnkownExit -> + Msg = lists:flatten(io_lib:format("Received: ~p ~n", [UnkownExit])), + ct:log(Msg), + ct:comment(Msg), + ok + after 0 -> + ok + end, + ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}), + process_flag(trap_exit, false). +%%-------------------------------------------------------------------- +ssl2_erlang_server_openssl_client_comp() -> + [{doc,"Test that ssl v2 clients are rejected"}]. + +ssl2_erlang_server_openssl_client_comp(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + V2Compat = proplists:get_value(v2_hello_compatible, Config), + + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {options, ServerOpts}]), + {options, [{v2_hello_compatible, V2Compat} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Exe = "openssl", @@ -1264,7 +1312,7 @@ client_check_result(Port, DataExpected, DataReceived) -> _ -> client_check_result(Port, DataExpected, NewData) end - after 3000 -> + after 20000 -> ct:fail({"Time out on openSSL Client", {expected, DataExpected}, {got, DataReceived}}) end. diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl index 113b3b4158..f6af1e6182 100644 --- a/lib/ssl/test/ssl_upgrade_SUITE.erl +++ b/lib/ssl/test/ssl_upgrade_SUITE.erl @@ -29,7 +29,8 @@ server, client, soft, - result_proxy + result_proxy, + skip }). all() -> @@ -73,8 +74,15 @@ major_upgrade(Config) when is_list(Config) -> minor_upgrade(Config) when is_list(Config) -> ct_release_test:upgrade(ssl, minor,{?MODULE, #state{config = Config}}, Config). -upgrade_init(CTData, #state{config = Config} = State) -> - {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssl), +upgrade_init(CtData, State) -> + {ok,{FromVsn,ToVsn}} = ct_release_test:get_app_vsns(CtData, ssl), + upgrade_init(FromVsn, ToVsn, CtData, State). + +upgrade_init(_, "8.0.2", _, State) -> + %% Requires stdlib upgrade so it will be a node upgrade! + State#state{skip = true}; +upgrade_init(_, _, CtData, #state{config = Config} = State) -> + {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CtData, ssl), ct:pal("Up: ~p", [Up]), Soft = is_soft(Up), %% It is symmetrical, if upgrade is soft so is downgrade Pid = spawn(?MODULE, result_proxy_init, [[]]), @@ -88,6 +96,8 @@ upgrade_init(CTData, #state{config = Config} = State) -> State#state{soft = Soft, result_proxy = Pid} end. +upgrade_upgraded(_, #state{skip = true} = State) -> + State; upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) -> ct:pal("Restart upgrade ~n", []), {Server, Client} = restart_start_connection(Config, Pid), @@ -96,7 +106,6 @@ upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = ssl_test_lib:close(Client), ok = Result, State; - upgrade_upgraded(_, #state{server = Server0, client = Client0, config = Config, soft = true, result_proxy = Pid} = State) -> @@ -110,6 +119,8 @@ upgrade_upgraded(_, #state{server = Server0, client = Client0, {Server, Client} = soft_start_connection(Config, Pid), State#state{server = Server, client = Client}. +upgrade_downgraded(_, #state{skip = true} = State) -> + State; upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) -> ct:pal("Restart downgrade: ~n", []), {Server, Client} = restart_start_connection(Config, Pid), @@ -119,7 +130,6 @@ upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} Pid ! stop, ok = Result, State; - upgrade_downgraded(_, #state{server = Server, client = Client, soft = true, result_proxy = Pid} = State) -> ct:pal("Soft downgrade: ~n", []), Server ! changed_version, diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 3b51fa8c6b..914eb43505 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 8.0 +SSL_VSN = 8.0.2 diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml index 3b134d00b7..2e4261d72e 100644 --- a/lib/stdlib/doc/src/dets.xml +++ b/lib/stdlib/doc/src/dets.xml @@ -391,7 +391,7 @@ <item> <p><c>{hash, Hash}</c> - Describes which BIF is used to calculate the hash values of the objects stored in the - <c>dets</c> table. Possible values of <c>Hash</c>:</p> + Dets table. Possible values of <c>Hash</c>:</p> <list> <item> <p><c>hash</c> - Implies that the <c>erlang:hash/2</c> BIF diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index b8e262208d..5f5d2b7f36 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -369,7 +369,7 @@ variable that in turn is passed to the function.</p> <p>The parse transform is provided in the <c>ms_transform</c> module and the source <em>must</em> include - file <c>ms_transform.hrl</c> in <c>STDLIB</c> for this + file <c>ms_transform.hrl</c> in STDLIB for this pseudo function to work. Failing to include the hrl file in the source results in a runtime error, not a compile time error. The include file is easiest included by adding line @@ -1458,7 +1458,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> specification returned <c>true</c>.</fsummary> <desc> <p>Matches the objects in table <c><anno>Tab</anno></c> using a - <seealso marker="#match_spec">match specificationc</seealso>. If the + <seealso marker="#match_spec">match specification</seealso>. If the match specification returns <c>true</c> for an object, that object considered a match and is counted. For any other result from the match specification the object is not considered a match and is @@ -1644,7 +1644,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </taglist> <p>Whenever option <c>extended_info</c> is used, it results in a file not readable by versions of ETS before - that in <c>STDLIB</c> 1.15.1</p> + that in STDLIB 1.15.1</p> <p>If option <c>sync</c> is set to <c>true</c>, it ensures that the content of the file is written to the disk before <c>tab2file</c> returns. Defaults to <c>{sync, false}</c>.</p> @@ -1725,7 +1725,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> <p>A tuple <c>{<anno>Major</anno>,<anno>Minor</anno>}</c> containing the major and minor version of the file format for ETS table dumps. This - version field was added beginning with <c>STDLIB</c> 1.5.1. + version field was added beginning with STDLIB 1.5.1. Files dumped with older versions return <c>{0,0}</c> in this field.</p> </item> diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index c57a31fa21..3322571b2c 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -97,6 +97,9 @@ gen_statem module Callback module gen_statem:start gen_statem:start_link -----> Module:init/1 +Server start or code change + -----> Module:callback_mode/0 + gen_statem:stop -----> Module:terminate/3 gen_statem:call @@ -116,9 +119,11 @@ erlang:'!' -----> Module:StateName/3 </p> <p> If a callback function fails or returns a bad value, - the <c>gen_statem</c> terminates. However, an exception of class + the <c>gen_statem</c> terminates, unless otherwise stated. + However, an exception of class <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso> - is not regarded as an error but as a valid return. + is not regarded as an error but as a valid return + from all callback functions. </p> <marker id="state_function"/> <p> @@ -127,7 +132,8 @@ erlang:'!' -----> Module:StateName/3 in a <c>gen_statem</c> is the callback function that is called for all events in this state. It is selected depending on which <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> - that the implementation specifies when the server starts. + that the callback module defines with the callback function + <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>. </p> <p> When the @@ -138,9 +144,9 @@ erlang:'!' -----> Module:StateName/3 This gathers all code for a specific state in one function as the <c>gen_statem</c> engine branches depending on state name. - Notice that in this mode the mandatory callback function + Notice the fact that there is a mandatory callback function <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso> - makes the state name <c>terminate</c> unusable. + makes the state name <c>terminate</c> unusable in this mode. </p> <p> When the @@ -249,11 +255,10 @@ erlang:'!' -----> Module:StateName/3 -behaviour(gen_statem). -export([start/0,push/0,get_count/0,stop/0]). --export([terminate/3,code_change/4,init/1]). +-export([terminate/3,code_change/4,init/1,callback_mode/0]). -export([on/3,off/3]). name() -> pushbutton_statem. % The registered server name -callback_mode() -> state_functions. %% API. This example uses a registered name name() %% and does not link to the caller. @@ -270,15 +275,14 @@ stop() -> terminate(_Reason, _State, _Data) -> void. code_change(_Vsn, State, Data, _Extra) -> - {callback_mode(),State,Data}. + {ok,State,Data}. init([]) -> - %% Set the callback mode and initial state + data. - %% Data is used only as a counter. + %% Set the initial state + data. Data is used only as a counter. State = off, Data = 0, - {callback_mode(),State,Data}. - + {ok,State,Data}. +callback_mode() -> state_functions. -%%% State functions +%%% State function(s) off({call,From}, push, Data) -> %% Go to 'on', increment count and reply @@ -326,18 +330,13 @@ ok To compare styles, here follows the same example using <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> <c>state_functions</c>, or rather the code to replace - from function <c>init/1</c> of the <c>pushbutton.erl</c> + after function <c>init/1</c> of the <c>pushbutton.erl</c> example file above: </p> <code type="erl"> -init([]) -> - %% Set the callback mode and initial state + data. - %% Data is used only as a counter. - State = off, Data = 0, - {handle_event_function,State,Data}. - +callback_mode() -> handle_event_function. -%%% Event handling +%%% State function(s) handle_event({call,From}, push, off, Data) -> %% Go to 'on', increment count and reply @@ -426,8 +425,8 @@ handle_event(_, _, State, Data) -> <desc> <p> Debug option that can be used when starting - a <c>gen_statem</c> server through, for example, - <seealso marker="#enter_loop/5"><c>enter_loop/5</c></seealso>. + a <c>gen_statem</c> server through, + <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>. </p> <p> For every entry in <c><anno>Dbgs</anno></c>, @@ -525,12 +524,9 @@ handle_event(_, _, State, Data) -> <desc> <p> The <em>callback mode</em> is selected when starting the - <c>gen_statem</c> using the return value from - <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> - or when calling - <seealso marker="#enter_loop/5"><c>enter_loop/5,6,7</c></seealso>, - and with the return value from - <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>. + <c>gen_statem</c> and after code change + using the return value from + <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>. </p> <taglist> <tag><c>state_functions</c></tag> @@ -691,7 +687,7 @@ handle_event(_, _, State, Data) -> <seealso marker="#state_function">state function</seealso>, from <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> or by giving them to - <seealso marker="#enter_loop/6"><c>enter_loop/6,7</c></seealso>. + <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>. </p> <p> Actions are executed in the containing list order. @@ -923,7 +919,8 @@ handle_event(_, _, State, Data) -> </p> <note> <p> - To avoid getting a late reply in the caller's + For <c><anno>Timeout</anno> =/= infinity</c>, + to avoid getting a late reply in the caller's inbox, this function spawns a proxy process that does the call. A late reply gets delivered to the dead proxy process, hence gets discarded. This is @@ -958,35 +955,36 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="enter_loop" arity="5"/> + <name name="enter_loop" arity="4"/> <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary> <desc> <p> The same as - <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso> - except that no + <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso> + with <c>Actions = []</c> except that no <seealso marker="#type-server_name"><c>server_name()</c></seealso> - must have been registered. + must have been registered. This creates an anonymous server. </p> </desc> </func> <func> - <name name="enter_loop" arity="6"/> + <name name="enter_loop" arity="5"/> <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary> <desc> <p> If <c><anno>Server_or_Actions</anno></c> is a <c>list()</c>, the same as - <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso> + <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso> except that no <seealso marker="#type-server_name"><c>server_name()</c></seealso> must have been registered and <c>Actions = <anno>Server_or_Actions</anno></c>. + This creates an anonymous server. </p> <p> Otherwise the same as - <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso> + <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso> with <c>Server = <anno>Server_or_Actions</anno></c> and <c>Actions = []</c>. @@ -995,7 +993,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="enter_loop" arity="7"/> + <name name="enter_loop" arity="6"/> <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary> <desc> <p> @@ -1015,21 +1013,31 @@ handle_event(_, _, State, Data) -> the <c>gen_statem</c> behavior provides. </p> <p> - <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>, and - <c><anno>Server</anno></c> have the same meanings - as when calling + <c><anno>Module</anno></c>, <c><anno>Opts</anno></c> + have the same meaning as when calling <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>. + </p> + <p> + If <c><anno>Server</anno></c> is <c>self()</c> an anonymous + server is created just as when using + <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>. + If <c><anno>Server</anno></c> is a + <seealso marker="#type-server_name"><c>server_name()</c></seealso> + a named server is created just as when using + <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>. However, the <seealso marker="#type-server_name"><c>server_name()</c></seealso> name must have been registered accordingly - <em>before</em> this function is called.</p> + <em>before</em> this function is called. + </p> <p> - <c><anno>CallbackMode</anno></c>, <c><anno>State</anno></c>, - <c><anno>Data</anno></c>, and <c><anno>Actions</anno></c> + <c><anno>State</anno></c>, <c><anno>Data</anno></c>, + and <c><anno>Actions</anno></c> have the same meanings as in the return value of <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>. - Also, the callback module <c><anno>Module</anno></c> - does not need to export an <c>init/1</c> function. + Also, the callback module does not need to export a + <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> + function. </p> <p> The function fails if the calling process was not started by a @@ -1253,6 +1261,48 @@ handle_event(_, _, State, Data) -> <funcs> <func> + <name>Module:callback_mode() -> CallbackMode</name> + <fsummary>Update the internal state during upgrade/downgrade.</fsummary> + <type> + <v> + CallbackMode = + <seealso marker="#type-callback_mode">callback_mode()</seealso> + </v> + </type> + <desc> + <p> + This function is called by a <c>gen_statem</c> + when it needs to find out the + <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> + of the callback module. The value is cached by <c>gen_statem</c> + for efficiency reasons, so this function is only called + once after server start and after code change, + but before the first + <seealso marker="#state_function">state function</seealso> + is called. More occasions may be added in future versions + of <c>gen_statem</c>. + </p> + <p> + Server start happens either when + <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> + returns or when + <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso> + is called. Code change happens when + <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso> + returns. + </p> + <note> + <p> + If this function's body does not consist of solely one of two + possible + <seealso marker="#type-callback_mode">atoms</seealso> + the callback module is doing something strange. + </p> + </note> + </desc> + </func> + + <func> <name>Module:code_change(OldVsn, OldState, OldData, Extra) -> Result </name> @@ -1262,11 +1312,7 @@ handle_event(_, _, State, Data) -> <v> Vsn = term()</v> <v>OldState = NewState = term()</v> <v>Extra = term()</v> - <v>Result = {NewCallbackMode,NewState,NewData} | Reason</v> - <v> - NewCallbackMode = - <seealso marker="#type-callback_mode">callback_mode()</seealso> - </v> + <v>Result = {ok,NewState,NewData} | Reason</v> <v> OldState = NewState = <seealso marker="#type-state">state()</seealso> @@ -1295,21 +1341,6 @@ handle_event(_, _, State, Data) -> <c>Module</c>. If no such attribute is defined, the version is the checksum of the Beam file. </p> - <note> - <p> - If you would dare to change - <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> - during release upgrade/downgrade, the upgrade is no problem, - as the new code surely knows what <em>callback mode</em> - it needs. However, for a downgrade this function must - know from argument <c>Extra</c> that comes from the - <seealso marker="sasl:appup"><c>sasl:appup</c></seealso> - file what <em>callback mode</em> the old code did use. - It can also be possible to figure this out - from argument <c>{down,Vsn}</c>, as <c>Vsn</c> - in effect defines the old callback module version. - </p> - </note> <p> <c>OldState</c> and <c>OldData</c> is the internal state of the <c>gen_statem</c>. @@ -1321,31 +1352,32 @@ handle_event(_, _, State, Data) -> <p> If successful, the function must return the updated internal state in an - <c>{NewCallbackMode,NewState,NewData}</c> tuple. + <c>{ok,NewState,NewData}</c> tuple. </p> <p> - If the function returns <c>Reason</c>, the ongoing - upgrade fails and rolls back to the old release.</p> - <p> - This function can use - <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> - to return <c>Result</c> or <c>Reason</c>. + If the function returns a failure <c>Reason</c>, the ongoing + upgrade fails and rolls back to the old release. + Note that <c>Reason</c> can not be an <c>{ok,_,_}</c> tuple + since that will be regarded as a + <c>{ok,NewState,NewData}</c> tuple, + and that a tuple matching <c>{ok,_}</c> + is an also invalid failure <c>Reason</c>. + It is recommended to use an atom as <c>Reason</c> since + it will be wrapped in an <c>{error,Reason}</c> tuple. </p> </desc> </func> <func> <name>Module:init(Args) -> Result</name> - <fsummary>Initialize process and internal state.</fsummary> + <fsummary> + Optional function for initializing process and internal state. + </fsummary> <type> <v>Args = term()</v> - <v>Result = {CallbackMode,State,Data}</v> - <v> | {CallbackMode,State,Data,Actions}</v> + <v>Result = {ok,State,Data}</v> + <v> | {ok,State,Data,Actions}</v> <v> | {stop,Reason} | ignore</v> - <v> - CallbackMode = - <seealso marker="#type-callback_mode">callback_mode()</seealso> - </v> <v>State = <seealso marker="#type-state">state()</seealso></v> <v> Data = <seealso marker="#type-data">data()</seealso> @@ -1364,7 +1396,7 @@ handle_event(_, _, State, Data) -> <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso> or <seealso marker="#start/3"><c>start/3,4</c></seealso>, - this function is called by the new process to initialize + this optional function is called by the new process to initialize the implementation state and server data. </p> <p> @@ -1373,11 +1405,8 @@ handle_event(_, _, State, Data) -> </p> <p> If the initialization is successful, the function is to - return <c>{CallbackMode,State,Data}</c> or - <c>{CallbackMode,State,Data,Actions}</c>. - <c>CallbackMode</c> selects the - <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> - of the <c>gen_statem</c>. + return <c>{ok,State,Data}</c> or + <c>{ok,State,Data,Actions}</c>. <c>State</c> is the initial <seealso marker="#type-state"><c>state()</c></seealso> and <c>Data</c> the initial server @@ -1395,11 +1424,16 @@ handle_event(_, _, State, Data) -> or <c>ignore</c>; see <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>. </p> - <p> - This function can use - <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> - to return <c>Result</c>. - </p> + <note> + <p> + This callback is optional, so a callback module does not need + to export it, but most do. If this function is not exported, + the <c>gen_statem</c> should be started through + <seealso marker="proc_lib"><c>proc_lib</c></seealso> + and + <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>. + </p> + </note> </desc> </func> @@ -1430,10 +1464,14 @@ handle_event(_, _, State, Data) -> This callback is optional, so a callback module does not need to export it. The <c>gen_statem</c> module provides a default implementation of this function that returns - <c>{State,Data}</c>. If this callback fails, the default - function returns <c>{State,Info}</c>, - where <c>Info</c> informs of the crash but no details, - to hide possibly sensitive data. + <c>{State,Data}</c>. + </p> + <p> + If this callback is exported but fails, + to hide possibly sensitive data, + the default function will instead return <c>{State,Info}</c>, + where <c>Info</c> says nothing but the fact that + <c>format_status/2</c> has crashed. </p> </note> <p>This function is called by a <c>gen_statem</c> process when @@ -1494,11 +1532,6 @@ handle_event(_, _, State, Data) -> printed in log files. Another use is to hide sensitive data from being written to the error log. </p> - <p> - This function can use - <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> - to return <c>Status</c>. - </p> </desc> </func> @@ -1573,9 +1606,12 @@ handle_event(_, _, State, Data) -> see <seealso marker="#type-action"><c>action()</c></seealso>. </p> <p> - These functions can use - <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>, - to return the result. + Note the fact that you can use + <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso> + to return the result, which can be useful. + For example to bail out with <c>throw(keep_state_and_data)</c> + from deep within complex code that is in no position to + return <c>{next_state,State,Data}</c>. </p> </desc> </func> @@ -1648,11 +1684,6 @@ handle_event(_, _, State, Data) -> and an error report is issued using <seealso marker="kernel:error_logger#format/2"><c>error_logger:format/2</c></seealso>. </p> - <p> - This function can use - <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> - to return <c>Ignored</c>, which is ignored anyway. - </p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index ad2599c5a0..f0347703e7 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 3.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Correct a bug regarding typed records in the Erlang + shell. The bug was introduced in OTP-19.0. </p> + <p> + Own Id: OTP-13719 Aux Id: ERL-182 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.0</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -511,7 +526,7 @@ </item> <item> <p> - The <c>stdlib</c> reference manual is updated to show + The STDLIB reference manual is updated to show correct information about the return value of <c>gen_fsm:reply/2</c>.</p> <p> @@ -6221,7 +6236,7 @@ documentation for <c>compile</c> on how to provide the key for encrypting, and the documentation for <c>beam_lib</c> on how to provide the key for decryption so that tools such - as the Debugger, <c>xref</c>, or <c>cover</c> can be used.</p> + as the Debugger, Xref, or Cover can be used.</p> <p>The <c>beam_lib:chunks/2</c> functions now accepts an additional chunk type <c>compile_info</c> to retrieve the compilation information directly as a term. (Thanks diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index 58ca5644cf..da03c39a26 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -59,9 +59,9 @@ <p>When a process that is started using <c>proc_lib</c> terminates abnormally (that is, with another exit reason than <c>normal</c>, <c>shutdown</c>, or <c>{shutdown,Term}</c>), a <em>crash report</em> - is generated, which is written to terminal by the default <c>SASL</c> + is generated, which is written to terminal by the default SASL event handler. That is, the crash report is normally only visible - if the <c>SASL</c> application is started; see + if the SASL application is started; see <seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso> and section <seealso marker="sasl:error_logging">SASL Error Logging</seealso> in the SASL User's Guide.</p> diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index a46ca47033..9f3aff03a3 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -401,7 +401,7 @@ </func> <func> - <name name="liat" arity="1"/>v + <name name="liat" arity="1"/> <fsummary>Remove the tail item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing diff --git a/lib/stdlib/doc/src/stdlib_app.xml b/lib/stdlib/doc/src/stdlib_app.xml index cde73269a8..f857cc394b 100644 --- a/lib/stdlib/doc/src/stdlib_app.xml +++ b/lib/stdlib/doc/src/stdlib_app.xml @@ -31,14 +31,14 @@ <app>STDLIB</app> <appsummary>The STDLIB application.</appsummary> <description> - <p>The <c>STDLIB</c> application is mandatory in the sense that the minimal - system based on Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>. - The <c>STDLIB</c> application contains no services.</p> + <p>The STDLIB application is mandatory in the sense that the minimal + system based on Erlang/OTP consists of Kernel and STDLIB. + The STDLIB application contains no services.</p> </description> <section> <title>Configuration</title> - <p>The following configuration parameters are defined for the <c>STDLIB</c> + <p>The following configuration parameters are defined for the STDLIB application. For more information about configuration parameters, see the <seealso marker="kernel:app"><c>app(4)</c></seealso> module in Kernel.</p> diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index 8f2ce36b06..fcaccdb2cb 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -253,7 +253,7 @@ is needed. This is useful during development, but in a target system the server is to be started explicitly. Use configuration parameters for - <seealso marker="kernel:index"><c>Kernel</c></seealso> for this.</p> + <seealso marker="kernel:index">Kernel</seealso> for this.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index 7f79ac88a1..efc8b75075 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -274,8 +274,8 @@ marker="stdlib:io"><c>io</c></seealso> module, the file handling, the <seealso marker="stdlib:unicode"><c>unicode</c></seealso> module, and - the bit syntax). Today most modules in <c>Kernel</c> and - <c>STDLIB</c>, as well as the VM are Unicode-aware.</p> + the bit syntax). Today most modules in Kernel and + STDLIB, as well as the VM are Unicode-aware.</p> </item> <tag>File I/O</tag> <item> @@ -765,7 +765,7 @@ Eshell V5.10.1 (abort with ^G) file system). The Unicode character list is used to denote filenames or directory names. If the file system content is listed, you also get Unicode lists as return value. The support - lies in the <c>Kernel</c> and <c>STDLIB</c> modules, which is why + lies in the Kernel and STDLIB modules, which is why most applications (that does not explicitly require the filenames to be in the ISO Latin-1 range) benefit from the Unicode support without change.</p> @@ -843,7 +843,7 @@ Eshell V5.10.1 (abort with ^G) <title>Notes About Raw Filenames</title> <marker id="notes-about-raw-filenames"/> <p>Raw filenames were introduced together with Unicode filename support - in <c>ERTS</c> 5.8.2 (Erlang/OTP R14B01). The reason "raw + in ERTS 5.8.2 (Erlang/OTP R14B01). The reason "raw filenames" were introduced in the system was to be able to represent filenames, specified in different encodings on the same system, diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index de23608046..0b5eac1e16 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -138,7 +138,7 @@ <p>File information as in <seealso marker="kernel:file#read_file_info/1"> <c>file:read_file_info/1</c></seealso> - in <c>Kernel</c></p> + in Kernel</p> </item> <tag><c>comment</c></tag> <item> @@ -345,7 +345,7 @@ prepended to filenames when extracting them from the zip archive. (Acting like <seealso marker="kernel:file#set_cwd/1"> - <c>file:set_cwd/1</c></seealso> in <c>Kernel</c>, + <c>file:set_cwd/1</c></seealso> in Kernel, but without changing the global <c>cwd</c> property.)</p> </item> </taglist> @@ -420,7 +420,7 @@ (<c>cwd</c>). This is prepended to filenames when adding them, although not in the zip archive (acting like <seealso marker="kernel:file#set_cwd/1"> - <c>file:set_cwd/1</c></seealso> in <c>Kernel</c>, but without + <c>file:set_cwd/1</c></seealso> in Kernel, but without changing the global <c>cwd</c> property.).</p> </item> <tag><c>{compress, <anno>What</anno>}</c></tag> diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index fe9df601eb..d7ee5c1f5d 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -55,7 +55,7 @@ -type beam() :: module() | file:filename() | binary(). --type forms() :: [erl_parse:abstract_form()]. +-type forms() :: [erl_parse:abstract_form() | erl_parse:form_info()]. -type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'. -type dataB() :: binary(). diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 3f74e01692..20de06fd0b 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -232,20 +232,20 @@ match(_) -> match_object(_, _) -> erlang:nif_error(undef). --spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} | +-spec match_object(Tab, Pattern, Limit) -> {[Object], Continuation} | '$end_of_table' when Tab :: tab(), Pattern :: match_pattern(), Limit :: pos_integer(), - Match :: [term()], + Object :: tuple(), Continuation :: continuation(). match_object(_, _, _) -> erlang:nif_error(undef). --spec match_object(Continuation) -> {[Match], Continuation} | +-spec match_object(Continuation) -> {[Object], Continuation} | '$end_of_table' when - Match :: [term()], + Object :: tuple(), Continuation :: continuation(). match_object(_) -> diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index 23bddafeed..3b3477b282 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -24,7 +24,7 @@ [start/3,start/4,start_link/3,start_link/4, stop/1,stop/3, cast/2,call/2,call/3, - enter_loop/5,enter_loop/6,enter_loop/7, + enter_loop/4,enter_loop/5,enter_loop/6, reply/1,reply/2]). %% gen callbacks @@ -63,8 +63,8 @@ {To :: pid(), Tag :: term()}. % Reply-to specifier for call -type state() :: - state_name() | % For state callback function StateName/5 - term(). % For state callback function handle_event/5 + state_name() | % For StateName/3 callback functios + term(). % For handle_event/4 callback function -type state_name() :: atom(). @@ -174,28 +174,33 @@ %% an {ok, ...} tuple. Thereafter the state callbacks are called %% for all events to this server. -callback init(Args :: term()) -> - {callback_mode(), state(), data()} | - {callback_mode(), state(), data(), [action()] | action()} | + {ok, state(), data()} | + {ok, state(), data(), [action()] | action()} | 'ignore' | {'stop', Reason :: term()}. -%% Example state callback for callback_mode() =:= state_functions -%% state name 'state_name'. +%% This callback shall return the callback mode of the callback module. %% -%% In this mode all states has to be type state_name() i.e atom(). +%% It is called once after init/0 and code_change/4 but before +%% the first state callback StateName/3 or handle_event/4. +-callback callback_mode() -> callback_mode(). + +%% Example state callback for StateName = 'state_name' +%% when callback_mode() =:= state_functions. +%% +%% In this mode all states has to be of type state_name() i.e atom(). %% -%% Note that state callbacks and only state callbacks have arity 5 -%% and that is intended. +%% Note that the only callbacks that have arity 3 are these +%% StateName/3 callbacks and terminate/3, so the state name +%% 'terminate' is unusable in this mode. -callback state_name( event_type(), EventContent :: term(), Data :: data()) -> state_function_result(). %% -%% State callback for callback_mode() =:= handle_event_function. -%% -%% Note that state callbacks and only state callbacks have arity 5 -%% and that is intended. +%% State callback for all states +%% when callback_mode() =:= handle_event_function. -callback handle_event( event_type(), EventContent :: term(), @@ -219,9 +224,8 @@ OldState :: state(), OldData :: data(), Extra :: term()) -> - {NewCallbackMode :: callback_mode(), - NewState :: state(), - NewData :: data()}. + {ok, NewState :: state(), NewData :: data()} | + (Reason :: term()). %% Format the callback module state in some sensible that is %% often condensed way. For StatusOption =:= 'normal' the perferred @@ -239,10 +243,13 @@ [init/1, % One may use enter_loop/5,6,7 instead format_status/2, % Has got a default implementation %% - state_name/3, % Example for callback_mode =:= state_functions: - %% there has to be a StateName/5 callback function for every StateName. + state_name/3, % Example for callback_mode() =:= state_functions: + %% there has to be a StateName/3 callback function + %% for every StateName in your state machine but the state name + %% 'state_name' does of course not have to be used. %% - handle_event/4]). % For callback_mode =:= handle_event_function + handle_event/4 % For callback_mode() =:= handle_event_function + ]). %% Type validation functions callback_mode(CallbackMode) -> @@ -450,43 +457,35 @@ reply({To,Tag}, Reply) when is_pid(To) -> %% the same arguments as you would have returned from init/1 -spec enter_loop( Module :: module(), Opts :: [debug_opt()], - CallbackMode :: callback_mode(), State :: state(), Data :: data()) -> no_return(). -enter_loop(Module, Opts, CallbackMode, State, Data) -> - enter_loop(Module, Opts, CallbackMode, State, Data, self()). +enter_loop(Module, Opts, State, Data) -> + enter_loop(Module, Opts, State, Data, self()). %% -spec enter_loop( Module :: module(), Opts :: [debug_opt()], - CallbackMode :: callback_mode(), State :: state(), Data :: data(), Server_or_Actions :: server_name() | pid() | [action()]) -> no_return(). -enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Actions) -> +enter_loop(Module, Opts, State, Data, Server_or_Actions) -> if is_list(Server_or_Actions) -> - enter_loop( - Module, Opts, CallbackMode, State, Data, - self(), Server_or_Actions); + enter_loop(Module, Opts, State, Data, self(), Server_or_Actions); true -> - enter_loop( - Module, Opts, CallbackMode, State, Data, - Server_or_Actions, []) + enter_loop(Module, Opts, State, Data, Server_or_Actions, []) end. %% -spec enter_loop( Module :: module(), Opts :: [debug_opt()], - CallbackMode :: callback_mode(), State :: state(), Data :: data(), Server :: server_name() | pid(), Actions :: [action()] | action()) -> no_return(). -enter_loop(Module, Opts, CallbackMode, State, Data, Server, Actions) -> +enter_loop(Module, Opts, State, Data, Server, Actions) -> is_atom(Module) orelse error({atom,Module}), - callback_mode(CallbackMode) orelse error({callback_mode,CallbackMode}), Parent = gen:get_parent(), - enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent). + enter(Module, Opts, State, Data, Server, Actions, Parent). %%--------------------------------------------------------------------------- %% API helpers @@ -514,7 +513,7 @@ send(Proc, Msg) -> end. %% Here the init_it/6 and enter_loop/5,6,7 functions converge -enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) -> +enter(Module, Opts, State, Data, Server, Actions, Parent) -> %% The values should already have been type checked Name = gen:get_proc_name(Server), Debug = gen:debug_options(Name, Opts), @@ -530,7 +529,7 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) -> [Actions,{postpone,false}] end, S = #{ - callback_mode => CallbackMode, + callback_mode => undefined, module => Module, name => Name, %% All fields below will be replaced according to the arguments to @@ -558,9 +557,15 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) -> Result -> init_result(Starter, Parent, ServerRef, Module, Result, Opts); Class:Reason -> + Stacktrace = erlang:get_stacktrace(), + Name = gen:get_proc_name(ServerRef), gen:unregister_name(ServerRef), proc_lib:init_ack(Starter, {error,Reason}), - erlang:raise(Class, Reason, erlang:get_stacktrace()) + error_info( + Class, Reason, Stacktrace, + #{name => Name, callback_mode => undefined}, + [], [], undefined), + erlang:raise(Class, Reason, Stacktrace) end. %%--------------------------------------------------------------------------- @@ -568,30 +573,12 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) -> init_result(Starter, Parent, ServerRef, Module, Result, Opts) -> case Result of - {CallbackMode,State,Data} -> - case callback_mode(CallbackMode) of - true -> - proc_lib:init_ack(Starter, {ok,self()}), - enter( - Module, Opts, CallbackMode, State, Data, - ServerRef, [], Parent); - false -> - Error = {callback_mode,CallbackMode}, - proc_lib:init_ack(Starter, {error,Error}), - exit(Error) - end; - {CallbackMode,State,Data,Actions} -> - case callback_mode(CallbackMode) of - true -> - proc_lib:init_ack(Starter, {ok,self()}), - enter( - Module, Opts, CallbackMode, State, Data, - ServerRef, Actions, Parent); - false -> - Error = {callback_mode,CallbackMode}, - proc_lib:init_ack(Starter, {error,Error}), - exit(Error) - end; + {ok,State,Data} -> + proc_lib:init_ack(Starter, {ok,self()}), + enter(Module, Opts, State, Data, ServerRef, [], Parent); + {ok,State,Data,Actions} -> + proc_lib:init_ack(Starter, {ok,self()}), + enter(Module, Opts, State, Data, ServerRef, Actions, Parent); {stop,Reason} -> gen:unregister_name(ServerRef), proc_lib:init_ack(Starter, {error,Reason}), @@ -601,8 +588,14 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) -> proc_lib:init_ack(Starter, ignore), exit(normal); _ -> - Error = {bad_return_value,Result}, + Name = gen:get_proc_name(ServerRef), + gen:unregister_name(ServerRef), + Error = {bad_return_from_init,Result}, proc_lib:init_ack(Starter, {error,Error}), + error_info( + error, Error, ?STACKTRACE(), + #{name => Name, callback_mode => undefined}, + [], [], undefined), exit(Error) end. @@ -630,11 +623,9 @@ system_code_change( Result -> Result end of - {NewCallbackMode,NewState,NewData} -> - callback_mode(NewCallbackMode) orelse - error({callback_mode,NewCallbackMode}), + {ok,NewState,NewData} -> {ok, - S#{callback_mode := NewCallbackMode, + S#{callback_mode := undefined, state := NewState, data := NewData}}; {ok,_} = Error -> @@ -675,14 +666,14 @@ format_status( %% them, not as the real erlang messages. Use trace for that. %%--------------------------------------------------------------------------- -print_event(Dev, {in,Event}, {Name,_}) -> +print_event(Dev, {in,Event}, {Name,State}) -> io:format( - Dev, "*DBG* ~p received ~s~n", - [Name,event_string(Event)]); -print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) -> + Dev, "*DBG* ~p receive ~s in state ~p~n", + [Name,event_string(Event),State]); +print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) -> io:format( - Dev, "*DBG* ~p sent ~p to ~p~n", - [Name,Reply,To]); + Dev, "*DBG* ~p send ~p to ~p from state ~p~n", + [Name,Reply,To,State]); print_event(Dev, {Tag,Event,NextState}, {Name,State}) -> StateString = case NextState of @@ -874,22 +865,36 @@ loop_event( %% try case CallbackMode of + undefined -> + Module:callback_mode(); state_functions -> - Module:State(Type, Content, Data); + erlang:apply(Module, State, [Type,Content,Data]); handle_event_function -> Module:handle_event(Type, Content, State, Data) - end of + end + of + Result when CallbackMode =:= undefined -> + loop_event_callback_mode( + Parent, Debug, S, Events, State, Data, P, Event, Result); Result -> loop_event_result( Parent, Debug, S, Events, State, Data, P, Event, Result) catch + Result when CallbackMode =:= undefined -> + loop_event_callback_mode( + Parent, Debug, S, Events, State, Data, P, Event, Result); Result -> loop_event_result( Parent, Debug, S, Events, State, Data, P, Event, Result); - error:badarg when CallbackMode =:= state_functions -> + error:badarg -> case erlang:get_stacktrace() of - [{erlang,apply,[Module,State,_],_}|Stacktrace] -> - Args = [Type,Content,Data], + [{erlang,apply, + [Module,State,[Type,Content,Data]=Args], + _} + |Stacktrace] + when CallbackMode =:= state_functions -> + %% We get here e.g if apply fails + %% due to State not being an atom terminate( error, {undef_state_function,{Module,State,Args}}, @@ -901,24 +906,29 @@ loop_event( Debug, S, [Event|Events], State, Data, P) end; error:undef -> - %% Process an undef to check for the simple mistake + %% Process undef to check for the simple mistake %% of calling a nonexistent state function + %% to make the undef more precise case erlang:get_stacktrace() of - [{Module,State, - [Type,Content,Data]=Args, - _} + [{Module,callback_mode,[]=Args,_} + |Stacktrace] + when CallbackMode =:= undefined -> + terminate( + error, + {undef_callback,{Module,callback_mode,Args}}, + Stacktrace, + Debug, S, [Event|Events], State, Data, P); + [{Module,State,[Type,Content,Data]=Args,_} |Stacktrace] - when CallbackMode =:= state_functions -> + when CallbackMode =:= state_functions -> terminate( error, {undef_state_function,{Module,State,Args}}, Stacktrace, Debug, S, [Event|Events], State, Data, P); - [{Module,handle_event, - [Type,Content,State,Data]=Args, - _} + [{Module,handle_event,[Type,Content,State,Data]=Args,_} |Stacktrace] - when CallbackMode =:= handle_event_function -> + when CallbackMode =:= handle_event_function -> terminate( error, {undef_state_function,{Module,handle_event,Args}}, @@ -936,6 +946,25 @@ loop_event( Debug, S, [Event|Events], State, Data, P) end. +%% Interpret callback_mode() result +loop_event_callback_mode( + Parent, Debug, S, Events, State, Data, P, Event, CallbackMode) -> + case callback_mode(CallbackMode) of + true -> + Hibernate = false, % We have already GC:ed recently + loop_event( + Parent, Debug, + S#{callback_mode := CallbackMode}, + Events, + State, Data, P, Event, Hibernate); + false -> + terminate( + error, + {bad_return_from_callback_mode,CallbackMode}, + ?STACKTRACE(), + Debug, S, [Event|Events], State, Data, P) + end. + %% Interpret all callback return variants loop_event_result( Parent, Debug, S, Events, State, Data, P, Event, Result) -> @@ -988,7 +1017,9 @@ loop_event_result( State, Data, P, Event, State, Actions); _ -> terminate( - error, {bad_return_value,Result}, ?STACKTRACE(), + error, + {bad_return_from_state_function,Result}, + ?STACKTRACE(), Debug, S, [Event|Events], State, Data, P) end. @@ -1025,20 +1056,26 @@ loop_event_actions( Postpone, Hibernate, Timeout, NextEvents); false -> terminate( - error, {bad_action,Action}, ?STACKTRACE(), + error, + {bad_action_from_state_function,Action}, + ?STACKTRACE(), Debug, S, [Event|Events], State, NewData, P) end; {next_event,Type,Content} -> case event_type(Type) of true -> + NewDebug = + sys_debug(Debug, S, State, {in,{Type,Content}}), loop_event_actions( - Parent, Debug, S, Events, + Parent, NewDebug, S, Events, State, NewData, P, Event, NextState, Actions, Postpone, Hibernate, Timeout, [{Type,Content}|NextEvents]); false -> terminate( - error, {bad_action,Action}, ?STACKTRACE(), + error, + {bad_action_from_state_function,Action}, + ?STACKTRACE(), Debug, S, [Event|Events], State, NewData, P) end; %% Actions that set options @@ -1049,7 +1086,9 @@ loop_event_actions( NewPostpone, Hibernate, Timeout, NextEvents); {postpone,_} -> terminate( - error, {bad_action,Action}, ?STACKTRACE(), + error, + {bad_action_from_state_function,Action}, + ?STACKTRACE(), Debug, S, [Event|Events], State, NewData, P); postpone -> loop_event_actions( @@ -1063,7 +1102,9 @@ loop_event_actions( Postpone, NewHibernate, Timeout, NextEvents); {hibernate,_} -> terminate( - error, {bad_action,Action}, ?STACKTRACE(), + error, + {bad_action_from_state_function,Action}, + ?STACKTRACE(), Debug, S, [Event|Events], State, NewData, P); hibernate -> loop_event_actions( @@ -1082,7 +1123,9 @@ loop_event_actions( Postpone, Hibernate, NewTimeout, NextEvents); {timeout,_,_} -> terminate( - error, {bad_action,Action}, ?STACKTRACE(), + error, + {bad_action_from_state_function,Action}, + ?STACKTRACE(), Debug, S, [Event|Events], State, NewData, P); infinity -> % Clear timer - it will never trigger loop_event_actions( @@ -1097,7 +1140,9 @@ loop_event_actions( Postpone, Hibernate, NewTimeout, NextEvents); _ -> terminate( - error, {bad_action,Action}, ?STACKTRACE(), + error, + {bad_action_from_state_function,Action}, + ?STACKTRACE(), Debug, S, [Event|Events], State, NewData, P) end; %% @@ -1169,7 +1214,9 @@ do_reply_then_terminate( NewDebug, S, Q, State, Data, P, Rs); _ -> terminate( - error, {bad_action,R}, ?STACKTRACE(), + error, + {bad_reply_action_from_state_function,R}, + ?STACKTRACE(), Debug, S, Q, State, Data, P) end. @@ -1188,8 +1235,9 @@ terminate( C:R -> ST = erlang:get_stacktrace(), error_info( - C, R, ST, Debug, S, Q, P, + C, R, ST, S, Q, P, format_status(terminate, get(), S, State, Data)), + sys:print_log(Debug), erlang:raise(C, R, ST) end, case Reason of @@ -1198,8 +1246,9 @@ terminate( {shutdown,_} -> ok; _ -> error_info( - Class, Reason, Stacktrace, Debug, S, Q, P, - format_status(terminate, get(), S, State, Data)) + Class, Reason, Stacktrace, S, Q, P, + format_status(terminate, get(), S, State, Data)), + sys:print_log(Debug) end, case Stacktrace of [] -> @@ -1209,7 +1258,7 @@ terminate( end. error_info( - Class, Reason, Stacktrace, Debug, + Class, Reason, Stacktrace, #{name := Name, callback_mode := CallbackMode}, Q, P, FmtData) -> {FixedReason,FixedStacktrace} = @@ -1276,9 +1325,7 @@ error_info( case FixedStacktrace of [] -> []; _ -> [FixedStacktrace] - end), - sys:print_log(Debug), - ok. + end). %% Call Module:format_status/2 or return a default value @@ -1291,7 +1338,7 @@ format_status(Opt, PDict, #{module := Module}, State, Data) -> _:_ -> format_status_default( Opt, State, - "Module:format_status/2 crashed") + atom_to_list(Module) ++ ":format_status/2 crashed") end; false -> format_status_default(Opt, State, Data) @@ -1299,10 +1346,10 @@ format_status(Opt, PDict, #{module := Module}, State, Data) -> %% The default Module:format_status/2 format_status_default(Opt, State, Data) -> - SSD = {State,Data}, + StateData = {State,Data}, case Opt of terminate -> - SSD; + StateData; _ -> - [{data,[{"State",SSD}]}] + [{data,[{"State",StateData}]}] end. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index 82a3a2be4f..28f37ef8bf 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2015. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -768,6 +768,8 @@ used_records({call,_,{atom,_,record_info},[A,{atom,_,Name}]}) -> {name, Name, A}; used_records({call,Line,{tuple,_,[M,F]},As}) -> used_records({call,Line,{remote,Line,M,F},As}); +used_records({type,_,record,[{atom,_,Name}|Fs]}) -> + {name, Name, Fs}; used_records(T) when is_tuple(T) -> {expr, tuple_to_list(T)}; used_records(E) -> diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index f8ba6f18e9..340cc21390 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -279,7 +279,8 @@ do_openzip_get(F, #openzip{files = Files, in = In0, input = Input, case file_name_search(F, Files) of {#zip_file{offset = Offset},_}=ZFile -> In1 = Input({seek, bof, Offset}, In0), - case get_z_file(In1, Z, Input, Output, [], fun silent/1, CWD, ZFile) of + case get_z_file(In1, Z, Input, Output, [], fun silent/1, + CWD, ZFile, fun all/1) of {file, R, _In2} -> {ok, R}; _ -> throw(file_not_found) end; @@ -1403,9 +1404,10 @@ get_z_files([{#zip_file{offset = Offset},_} = ZFile | Rest], Z, In0, true -> In1 = Input({seek, bof, Offset}, In0), {In2, Acc1} = - case get_z_file(In1, Z, Input, Output, OpO, FB, CWD, ZFile) of + case get_z_file(In1, Z, Input, Output, OpO, FB, + CWD, ZFile, Filter) of {file, GZD, Inx} -> {Inx, [GZD | Acc0]}; - {dir, Inx} -> {Inx, Acc0} + {_, Inx} -> {Inx, Acc0} end, get_z_files(Rest, Z, In2, Opts, Acc1); _ -> @@ -1413,7 +1415,8 @@ get_z_files([{#zip_file{offset = Offset},_} = ZFile | Rest], Z, In0, end. %% get a file from the archive, reading chunks -get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) -> +get_z_file(In0, Z, Input, Output, OpO, FB, + CWD, {ZipFile,Extra}, Filter) -> case Input({read, ?LOCAL_FILE_HEADER_SZ}, In0) of {eof, In1} -> {eof, In1}; @@ -1433,29 +1436,64 @@ get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) -> end, {BFileN, In3} = Input({read, FileNameLen + ExtraLen}, In1), {FileName, _} = get_file_name_extra(FileNameLen, ExtraLen, BFileN), - FileName1 = add_cwd(CWD, FileName), - case lists:last(FileName) of - $/ -> - %% perhaps this should always be done? - Output({ensure_dir,FileName1},[]), - {dir, In3}; - _ -> - %% FileInfo = local_file_header_to_file_info(LH) - %%{Out, In4, CRC, UncompSize} = - {Out, In4, CRC, _UncompSize} = - get_z_data(CompMethod, In3, FileName1, - CompSize, Input, Output, OpO, Z), - In5 = skip_z_data_descriptor(GPFlag, Input, In4), - %% TODO This should be fixed some day: - %% In5 = Input({set_file_info, FileName, FileInfo#file_info{size=UncompSize}}, In4), - FB(FileName), - CRC =:= CRC32 orelse throw({bad_crc, FileName}), - {file, Out, In5} + ReadAndWrite = + case check_valid_location(CWD, FileName) of + {true,FileName1} -> + true; + {false,FileName1} -> + Filter({ZipFile#zip_file{name = FileName1},Extra}) + end, + case ReadAndWrite of + true -> + case lists:last(FileName) of + $/ -> + %% perhaps this should always be done? + Output({ensure_dir,FileName1},[]), + {dir, In3}; + _ -> + %% FileInfo = local_file_header_to_file_info(LH) + %%{Out, In4, CRC, UncompSize} = + {Out, In4, CRC, _UncompSize} = + get_z_data(CompMethod, In3, FileName1, + CompSize, Input, Output, OpO, Z), + In5 = skip_z_data_descriptor(GPFlag, Input, In4), + %% TODO This should be fixed some day: + %% In5 = Input({set_file_info, FileName, + %% FileInfo#file_info{size=UncompSize}}, In4), + FB(FileName), + CRC =:= CRC32 orelse throw({bad_crc, FileName}), + {file, Out, In5} + end; + false -> + {ignore, In3} end; _ -> throw(bad_local_file_header) end. +%% make sure FileName doesn't have relative path that points over CWD +check_valid_location(CWD, FileName) -> + %% check for directory traversal exploit + case check_dir_level(filename:split(FileName), 0) of + {FileOrDir,Level} when Level < 0 -> + CWD1 = if CWD == "" -> "./"; + true -> CWD + end, + error_logger:format("Illegal path: ~ts, extracting in ~ts~n", + [add_cwd(CWD,FileName),CWD1]), + {false,add_cwd(CWD, FileOrDir)}; + _ -> + {true,add_cwd(CWD, FileName)} + end. + +check_dir_level([FileOrDir], Level) -> + {FileOrDir,Level}; +check_dir_level(["." | Parts], Level) -> + check_dir_level(Parts, Level); +check_dir_level([".." | Parts], Level) -> + check_dir_level(Parts, Level-1); +check_dir_level([_Dir | Parts], Level) -> + check_dir_level(Parts, Level+1). get_file_name_extra(FileNameLen, ExtraLen, B) -> case B of diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 8c1c625676..b02d17bdb6 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -47,6 +47,7 @@ fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1, update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]). -export([update_counter_with_default/1]). +-export([update_counter_table_growth/1]). -export([member/1]). -export([memory/1]). -export([select_fail/1]). @@ -100,6 +101,7 @@ heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1, do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2, types_do/1, sleeper/0, memory_do/1, update_counter_with_default_do/1, + update_counter_table_growth_do/1, ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4 ]). @@ -137,6 +139,7 @@ all() -> rename, rename_unnamed, evil_rename, update_element, update_counter, evil_update_counter, update_counter_with_default, partly_bound, + update_counter_table_growth, match_heavy, {group, fold}, member, t_delete_object, t_init_table, t_whitebox, t_delete_all_objects, t_insert_list, t_test_ms, t_select_delete, t_ets_dets, @@ -1955,6 +1958,16 @@ update_counter_with_default_do(Opts) -> ok. +update_counter_table_growth(_Config) -> + repeat_for_opts(update_counter_table_growth_do). + +update_counter_table_growth_do(Opts) -> + Set = ets_new(b, [set | Opts]), + [ets:update_counter(Set, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)], + OrderedSet = ets_new(b, [ordered_set | Opts]), + [ets:update_counter(OrderedSet, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)], + ok. + %% Check that a first-next sequence always works on a fixed table. fixtable_next(Config) when is_list(Config) -> repeat_for_opts(fixtable_next_do, [write_concurrency,all_types]). diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 364314f91b..1d1417c2e6 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -37,33 +37,32 @@ all() -> {group, stop_handle_event}, {group, abnormal}, {group, abnormal_handle_event}, - shutdown, stop_and_reply, event_order, + shutdown, stop_and_reply, event_order, code_change, {group, sys}, hibernate, enter_loop]. groups() -> - [{start, [], - [start1, start2, start3, start4, start5, start6, start7, - start8, start9, start10, start11, start12, next_events]}, - {start_handle_event, [], - [start1, start2, start3, start4, start5, start6, start7, - start8, start9, start10, start11, start12, next_events]}, - {stop, [], - [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]}, - {stop_handle_event, [], - [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]}, - {abnormal, [], [abnormal1, abnormal2]}, - {abnormal_handle_event, [], [abnormal1, abnormal2]}, - {sys, [], - [sys1, code_change, - call_format_status, - error_format_status, terminate_crash_format, - get_state, replace_state]}, - {sys_handle_event, [], - [sys1, - call_format_status, - error_format_status, terminate_crash_format, - get_state, replace_state]}]. + [{start, [], tcs(start)}, + {start_handle_event, [], tcs(start)}, + {stop, [], tcs(stop)}, + {stop_handle_event, [], tcs(stop)}, + {abnormal, [], tcs(abnormal)}, + {abnormal_handle_event, [], tcs(abnormal)}, + {sys, [], tcs(sys)}, + {sys_handle_event, [], tcs(sys)}]. + +tcs(start) -> + [start1, start2, start3, start4, start5, start6, start7, + start8, start9, start10, start11, start12, next_events]; +tcs(stop) -> + [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]; +tcs(abnormal) -> + [abnormal1, abnormal2]; +tcs(sys) -> + [sys1, call_format_status, + error_format_status, terminate_crash_format, + get_state, replace_state]. + init_per_suite(Config) -> Config. @@ -461,10 +460,10 @@ abnormal2(Config) -> {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []), %% bad return value in the gen_statem loop - {{bad_return_value,badreturn},_} = + {{bad_return_from_state_function,badreturn},_} = ?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason), receive - {'EXIT',Pid,{bad_return_value,badreturn}} -> ok + {'EXIT',Pid,{bad_return_from_state_function,badreturn}} -> ok after 5000 -> ct:fail(gen_statem_did_not_die) end, @@ -633,11 +632,13 @@ sys1(Config) -> sys:resume(Pid), stop_it(Pid). -code_change(Config) -> - Mode = handle_event_function, - {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []), +code_change(_Config) -> + {ok,Pid} = + gen_statem:start( + ?MODULE, {callback_mode,state_functions,[]}, []), {idle,data} = sys:get_state(Pid), sys:suspend(Pid), + Mode = handle_event_function, sys:change_code(Pid, ?MODULE, old_vsn, Mode), sys:resume(Pid), {idle,{old_vsn,data,Mode}} = sys:get_state(Pid), @@ -708,7 +709,7 @@ error_format_status(Config) -> gen_statem:start( ?MODULE, start_arg(Config, {data,Data}), []), %% bad return value in the gen_statem loop - {{bad_return_value,badreturn},_} = + {{bad_return_from_state_function,badreturn},_} = ?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason), receive {error,_, @@ -716,7 +717,7 @@ error_format_status(Config) -> "** State machine"++_, [Pid,{{call,_},badreturn}, {formatted,idle,Data}, - error,{bad_return_value,badreturn}|_]}} -> + error,{bad_return_from_state_function,badreturn}|_]}} -> ok; Other when is_tuple(Other), element(1, Other) =:= error -> error_logger_forwarder:unregister(), @@ -1029,11 +1030,7 @@ enter_loop(_Config) -> end, %% Process not started using proc_lib - CallbackMode = state_functions, - Pid4 = - spawn_link( - gen_statem, enter_loop, - [?MODULE,[],CallbackMode,state0,[]]), + Pid4 = spawn_link(gen_statem, enter_loop, [?MODULE,[],state0,[]]), receive {'EXIT',Pid4,process_was_not_started_by_proc_lib} -> ok @@ -1107,21 +1104,18 @@ enter_loop(Reg1, Reg2) -> anon -> ignore end, proc_lib:init_ack({ok, self()}), - CallbackMode = state_functions, case Reg2 of local -> gen_statem:enter_loop( - ?MODULE, [], CallbackMode, state0, [], {local,armitage}); + ?MODULE, [], state0, [], {local,armitage}); global -> gen_statem:enter_loop( - ?MODULE, [], CallbackMode, state0, [], {global,armitage}); + ?MODULE, [], state0, [], {global,armitage}); via -> gen_statem:enter_loop( - ?MODULE, [], CallbackMode, state0, [], - {via, dummy_via, armitage}); + ?MODULE, [], state0, [], {via, dummy_via, armitage}); anon -> - gen_statem:enter_loop( - ?MODULE, [], CallbackMode, state0, []) + gen_statem:enter_loop(?MODULE, [], state0, []) end. @@ -1266,33 +1260,39 @@ init(stop_shutdown) -> {stop,shutdown}; init(sleep) -> ?t:sleep(1000), - {state_functions,idle,data}; + {ok,idle,data}; init(hiber) -> - {state_functions,hiber_idle,[]}; + {ok,hiber_idle,[]}; init(hiber_now) -> - {state_functions,hiber_idle,[],[hibernate]}; + {ok,hiber_idle,[],[hibernate]}; init({data, Data}) -> - {state_functions,idle,Data}; + {ok,idle,Data}; init({callback_mode,CallbackMode,Arg}) -> - case init(Arg) of - {_,State,Data,Ops} -> - {CallbackMode,State,Data,Ops}; - {_,State,Data} -> - {CallbackMode,State,Data}; - Other -> - Other - end; + ets:new(?MODULE, [named_table,private]), + ets:insert(?MODULE, {callback_mode,CallbackMode}), + init(Arg); init({map_statem,#{init := Init}=Machine}) -> + ets:new(?MODULE, [named_table,private]), + ets:insert(?MODULE, {callback_mode,handle_event_function}), case Init() of {ok,State,Data,Ops} -> - {handle_event_function,State,[Data|Machine],Ops}; + {ok,State,[Data|Machine],Ops}; {ok,State,Data} -> - {handle_event_function,State,[Data|Machine]}; + {ok,State,[Data|Machine]}; Other -> Other end; init([]) -> - {state_functions,idle,data}. + {ok,idle,data}. + +callback_mode() -> + try ets:lookup(?MODULE, callback_mode) of + [{callback_mode,CallbackMode}] -> + CallbackMode + catch + error:badarg -> + state_functions + end. terminate(_, _State, crash_terminate) -> exit({crash,terminate}); @@ -1568,7 +1568,12 @@ wrap_result(Result) -> code_change(OldVsn, State, Data, CallbackMode) -> - {CallbackMode,State,{OldVsn,Data,CallbackMode}}. + io:format( + "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]), + ets:insert(?MODULE, {callback_mode,CallbackMode}), + io:format( + "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]), + {ok,State,{OldVsn,Data,CallbackMode}}. format_status(terminate, [_Pdict,State,Data]) -> {formatted,State,Data}; diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index dc82e92876..c409a6949b 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -30,7 +30,7 @@ progex_bit_syntax/1, progex_records/1, progex_lc/1, progex_funs/1, otp_5990/1, otp_6166/1, otp_6554/1, - otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]). + otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1]). -export([ start_restricted_from_shell/1, start_restricted_on_command_line/1,restricted_local/1]). @@ -91,7 +91,7 @@ groups() -> progex_funs]}, {tickets, [], [otp_5990, otp_6166, otp_6554, otp_7184, - otp_7232, otp_8393, otp_10302]}]. + otp_7232, otp_8393, otp_10302, otp_13719]}]. init_per_suite(Config) -> Config. @@ -2810,6 +2810,19 @@ otp_10302(Config) when is_list(Config) -> test_server:stop_node(Node), ok. +otp_13719(Config) when is_list(Config) -> + Test = <<"-module(otp_13719). + -record(bar, {}). + -record(foo, {bar :: #bar{}}).">>, + File = filename("otp_13719.erl", Config), + Beam = filename("otp_13719.beam", Config), + ok = compile_file(Config, File, Test, []), + RR = "rr(\"" ++ Beam ++ "\"). #foo{}.", + "[bar,foo]\n#foo{bar = undefined}.\n" = t(RR), + file:delete(filename("test.beam", Config)), + file:delete(File), + ok. + scan(B) -> F = fun(Ts) -> case erl_parse:parse_term(Ts) of diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl index 2add5a39a2..7d90795c9e 100644 --- a/lib/stdlib/test/zip_SUITE.erl +++ b/lib/stdlib/test/zip_SUITE.erl @@ -25,6 +25,7 @@ zip_to_binary/1, unzip_options/1, zip_options/1, list_dir_options/1, aliases/1, openzip_api/1, zip_api/1, open_leak/1, unzip_jar/1, + unzip_traversal_exploit/1, compress_control/1, foldl/1]). @@ -38,7 +39,8 @@ all() -> [borderline, atomic, bad_zip, unzip_from_binary, unzip_to_binary, zip_to_binary, unzip_options, zip_options, list_dir_options, aliases, openzip_api, - zip_api, open_leak, unzip_jar, compress_control, foldl]. + zip_api, open_leak, unzip_jar, compress_control, foldl, + unzip_traversal_exploit]. groups() -> []. @@ -377,6 +379,52 @@ unzip_options(Config) when is_list(Config) -> 0 = delete_files([Subdir]), ok. +%% Test that unzip handles directory traversal exploit (OTP-13633) +unzip_traversal_exploit(Config) -> + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + ZipName = filename:join(DataDir, "exploit.zip"), + + %% $ zipinfo -1 test/zip_SUITE_data/exploit.zip + %% clash.txt + %% ../clash.txt + %% ../above.txt + %% subdir/../in_root_dir.txt + + %% create a temp directory + SubDir = filename:join(PrivDir, "exploit_test"), + ok = file:make_dir(SubDir), + + ClashFile = filename:join(SubDir,"clash.txt"), + AboveFile = filename:join(SubDir,"above.txt"), + RelativePathFile = filename:join(SubDir,"subdir/../in_root_dir.txt"), + + %% unzip in SubDir + {ok, [ClashFile, ClashFile, AboveFile, RelativePathFile]} = + zip:unzip(ZipName, [{cwd,SubDir}]), + + {ok,<<"This file will overwrite other file.\n">>} = + file:read_file(ClashFile), + {ok,_} = file:read_file(AboveFile), + {ok,_} = file:read_file(RelativePathFile), + + %% clean up + delete_files([SubDir]), + + %% create the temp directory again + ok = file:make_dir(SubDir), + + %% unzip in SubDir + {ok, [ClashFile, AboveFile, RelativePathFile]} = + zip:unzip(ZipName, [{cwd,SubDir},keep_old_files]), + + {ok,<<"This is the original file.\n">>} = + file:read_file(ClashFile), + + %% clean up + delete_files([SubDir]), + ok. + %% Test unzip a jar file (OTP-7382). unzip_jar(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), diff --git a/lib/stdlib/test/zip_SUITE_data/exploit.zip b/lib/stdlib/test/zip_SUITE_data/exploit.zip Binary files differnew file mode 100644 index 0000000000..afb8dbd192 --- /dev/null +++ b/lib/stdlib/test/zip_SUITE_data/exploit.zip diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 46e3ceac03..c74343d9ca 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 3.0 +STDLIB_VSN = 3.1 diff --git a/lib/syntax_tools/doc/specs/.gitignore b/lib/syntax_tools/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/syntax_tools/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile index ff4f3f78ff..e55222e59c 100644 --- a/lib/syntax_tools/doc/src/Makefile +++ b/lib/syntax_tools/doc/src/Makefile @@ -81,10 +81,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include DVIPS_FLAGS += # ---------------------------------------------------- @@ -93,7 +98,7 @@ DVIPS_FLAGS += $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -120,6 +125,7 @@ clean clean_docs: rm -f $(MAN3DIR)/* rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ # ---------------------------------------------------- diff --git a/lib/syntax_tools/doc/src/specs.xml b/lib/syntax_tools/doc/src/specs.xml new file mode 100644 index 0000000000..04c3a494e7 --- /dev/null +++ b/lib/syntax_tools/doc/src/specs.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_epp_dodger.xml"/> + <xi:include href="../specs/specs_erl_comment_scan.xml"/> + <xi:include href="../specs/specs_erl_prettypr.xml"/> + <xi:include href="../specs/specs_erl_recomment.xml"/> + <xi:include href="../specs/specs_erl_syntax.xml"/> + <xi:include href="../specs/specs_erl_syntax_lib.xml"/> + <xi:include href="../specs/specs_erl_tidy.xml"/> + <xi:include href="../specs/specs_igor.xml"/> + <xi:include href="../specs/specs_merl.xml"/> + <xi:include href="../specs/specs_merl_transform.xml"/> + <xi:include href="../specs/specs_prettypr.xml"/> +</specs> diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl index 03429d4d42..b5ac564146 100644 --- a/lib/syntax_tools/src/erl_comment_scan.erl +++ b/lib/syntax_tools/src/erl_comment_scan.erl @@ -30,8 +30,14 @@ %% ===================================================================== --type comment() :: {integer(), integer(), integer(), [string()]}. --type commentLine() :: {integer(), integer(), integer(), string()}. +-type comment() :: {Line:: integer(), + Column:: integer(), + Indentation :: integer(), + Text :: [string()]}. +-type commentLine() :: {Line :: integer(), + Column :: integer(), + Indent :: integer(), + Text :: string()}. %% ===================================================================== %% @spec file(FileName::file:filename()) -> [Comment] diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index f1615b2610..df0d78031c 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -195,10 +195,16 @@ format(Node) -> %% ===================================================================== %% @spec format(Tree::syntaxTree(), Options::[term()]) -> string() -%% syntaxTree() = erl_syntax:syntaxTree() %% -%% @type hook() = (syntaxTree(), context(), Continuation) -> document() -%% Continuation = (syntaxTree(), context()) -> document(). +%% @type syntaxTree() = erl_syntax:syntaxTree(). +%% +%% An abstract syntax tree. See the {@link erl_syntax} module for +%% details. +%% +%% @type hook() = (syntaxTree(), context(), Continuation) -> +%% prettypr:document() +%% Continuation = (syntaxTree(), context()) -> +%% prettypr:document(). %% %% A call-back function for user-controlled formatting. See {@link %% format/2}. @@ -277,7 +283,7 @@ format(Node, Options) -> %% ===================================================================== -%% @spec best(Tree::syntaxTree()) -> empty | document() +%% @spec best(Tree::syntaxTree()) -> empty | prettypr:document() %% @equiv best(Tree, []) -spec best(erl_syntax:syntaxTree()) -> 'empty' | prettypr:document(). @@ -288,7 +294,7 @@ best(Node) -> %% ===================================================================== %% @spec best(Tree::syntaxTree(), Options::[term()]) -> -%% empty | document() +%% empty | prettypr:document() %% %% @doc Creates a fixed "best" abstract layout for a syntax tree. This %% is similar to the `layout/2' function, except that here, the final @@ -310,7 +316,7 @@ best(Node, Options) -> %% ===================================================================== -%% @spec layout(Tree::syntaxTree()) -> document() +%% @spec layout(Tree::syntaxTree()) -> prettypr:document() %% @equiv layout(Tree, []) -spec layout(erl_syntax:syntaxTree()) -> prettypr:document(). @@ -320,8 +326,7 @@ layout(Node) -> %% ===================================================================== -%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> document() -%% document() = prettypr:document() +%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> prettypr:document() %% %% @doc Creates an abstract document layout for a syntax tree. The %% result represents a set of possible layouts (cf. module `prettypr'). diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl index c1141b2bc6..1d23034991 100644 --- a/lib/syntax_tools/src/erl_recomment.erl +++ b/lib/syntax_tools/src/erl_recomment.erl @@ -30,6 +30,9 @@ -export([recomment_forms/2, quick_recomment_forms/2, recomment_tree/2]). +%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax +%% tree. See the {@link erl_syntax} module for details. + %% ===================================================================== %% @spec quick_recomment_forms(Forms, Comments::[Comment]) -> %% syntaxTree() @@ -55,7 +58,6 @@ quick_recomment_forms(Tree, Cs) -> %% ===================================================================== %% @spec recomment_forms(Forms, Comments::[Comment]) -> syntaxTree() %% -%% syntaxTree() = erl_syntax:syntaxTree() %% Forms = syntaxTree() | [syntaxTree()] %% Comment = {Line, Column, Indentation, Text} %% Line = integer() diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 9815559779..5aecf5d774 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -280,7 +280,7 @@ mapfoldl(_, S, []) -> %% ===================================================================== %% @spec variables(syntaxTree()) -> set(atom()) %% -%% set(T) = //stdlib/sets:set(T) +%% @type set(T) = //stdlib/sets:set(T) %% %% @doc Returns the names of variables occurring in a syntax tree, The %% result is a set of variable names represented by atoms. Macro names @@ -1955,7 +1955,7 @@ analyze_application(Node) -> %% ===================================================================== -%% @spec analyze_type_application(Node::syntaxTree()) -> typeName() +%% @spec analyze_type_application(Node::syntaxTree()) -> TypeName %% %% TypeName = {atom(), integer()} %% | {ModuleName, {atom(), integer()}} diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index f2de12b410..9e273dfb84 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -36,6 +36,11 @@ %% been reasonably well tested, but the possibility of errors remains. %% Keep backups of your original code safely stored, until you feel %% confident that the new, modified code can be trusted. +%% +%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax +%% tree. See the {@link erl_syntax} module for details. +%% +%% @type filename() = file:filename(). -module(erl_tidy). @@ -79,7 +84,6 @@ dir(Dir) -> %% ===================================================================== %% @spec dir(Directory::filename(), Options::[term()]) -> ok -%% filename() = file:filename() %% %% @doc Tidies Erlang source files in a directory and its %% subdirectories. @@ -206,7 +210,7 @@ file__defaults() -> {verbose, false}]. default_printer() -> - fun (Tree, Options) -> erl_prettypr:format(Tree, Options) end. + fun erl_prettypr:format/2. %% ===================================================================== %% @spec file(Name) -> ok @@ -253,7 +257,7 @@ file(Name) -> %% %% <dt>{printer, Function}</dt> %% <dd><ul> -%% <li>`Function = (syntaxTree()) -> string()'</li> +%% <li>`Function = (syntaxTree(), [term()]) -> string()'</li> %% </ul> %% %% Specifies a function for prettyprinting Erlang syntax trees. @@ -414,7 +418,7 @@ write_module(Tree, Name, Opts) -> print_module(Tree, Opts) -> Printer = proplists:get_value(printer, Opts), - io:format(Printer(Tree, Opts)). + io:put_chars(Printer(Tree, Opts)). output(FD, Printer, Tree, Opts) -> io:put_chars(FD, Printer(Tree, Opts)), @@ -513,7 +517,6 @@ module(Forms) -> %% @spec module(Forms, Options::[term()]) -> syntaxTree() %% %% Forms = syntaxTree() | [syntaxTree()] -%% syntaxTree() = erl_syntax:syntaxTree() %% %% @doc Tidies a syntax tree representation of a module %% definition. The given `Forms' may be either a single diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index 1d14bd7c3a..943250e5cd 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -151,7 +151,8 @@ default_printer(Tree, Options) -> %% @spec parse_transform(Forms::[syntaxTree()], Options::[term()]) -> %% [syntaxTree()] %% -%% syntaxTree() = erl_syntax:syntaxTree() +%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax +%% tree. See the {@link erl_syntax} module for details. %% %% @doc Allows Igor to work as a component of the Erlang compiler. %% Including the term `{parse_transform, igor}' in the @@ -212,7 +213,7 @@ merge(Name, Files) -> %% @spec merge(Name::atom(), Files::[filename()], Options::[term()]) -> %% [filename()] %% -%% filename() = file:filename() +%% @type filename() = file:filename() %% %% @doc Merges source code files to a single file. `Name' %% specifies the name of the resulting module - not the name of the @@ -367,6 +368,7 @@ merge_files(Name, Files, Options) -> %% @spec merge_files(Name::atom(), Sources::[Forms], %% Files::[filename()], Options::[term()]) -> %% {syntaxTree(), [stubDescriptor()]} +%% %% Forms = syntaxTree() | [syntaxTree()] %% %% @doc Merges source code files and syntax trees to a single syntax diff --git a/lib/syntax_tools/src/merl_tests.erl b/lib/syntax_tools/src/merl_tests.erl index c1aae3100e..27db594050 100644 --- a/lib/syntax_tools/src/merl_tests.erl +++ b/lib/syntax_tools/src/merl_tests.erl @@ -48,6 +48,21 @@ parse_error_test_() -> f(merl:quote("{"))) ]. +transform_parse_error_test_() -> + [?_assertEqual("merl:quote(\"{\")", + f(merl_transform:parse_transform( + [?Q("merl:quote(\"{\")")], []))), + ?_assertEqual("merl:quote(2, \"{\")", + f(merl_transform:parse_transform( + [?Q("merl:quote(2, \"{\")")], []))), + ?_assertEqual("merl:qquote(\"{\", [{var, V}])", + f(merl_transform:parse_transform( + [?Q("merl:qquote(\"{\", [{var, V}])")], []))), + ?_assertEqual("merl:qquote(2, \"{\", [{var, V}])", + f(merl_transform:parse_transform( + [?Q("merl:qquote(2, \"{\", [{var, V}])")], []))) + ]. + term_test_() -> [?_assertEqual(tuple, erl_syntax:type(merl:term({}))), ?_assertEqual("{foo, 42}", f(merl:term({foo, 42}))) diff --git a/lib/syntax_tools/src/merl_transform.erl b/lib/syntax_tools/src/merl_transform.erl index fe58b6a122..497baddd0a 100644 --- a/lib/syntax_tools/src/merl_transform.erl +++ b/lib/syntax_tools/src/merl_transform.erl @@ -104,10 +104,15 @@ expand_qquote([Text, Env], T, Line) -> case erl_syntax:is_literal(Text) of true -> As = [Line, erl_syntax:concrete(Text)], - %% expand further if possible - expand(merl:qquote(Line, "merl:subst(_@tree, _@env)", - [{tree, eval_call(Line, quote, As, T)}, - {env, Env}])); + case eval_call(Line, quote, As, failed) of + failed -> + T; + T1 -> + %% expand further if possible + expand(merl:qquote(Line, "merl:subst(_@tree, _@env)", + [{tree, T1}, + {env, Env}])) + end; false -> T end; diff --git a/lib/syntax_tools/test/merl_SUITE.erl b/lib/syntax_tools/test/merl_SUITE.erl index 945972d405..52bbd9b3b8 100644 --- a/lib/syntax_tools/test/merl_SUITE.erl +++ b/lib/syntax_tools/test/merl_SUITE.erl @@ -29,12 +29,14 @@ init_per_group/2,end_per_group/2]). %% Test cases --export([merl_smoke_test/1]). +-export([merl_smoke_test/1, + transform_parse_error_test/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [merl_smoke_test]. + [merl_smoke_test, + transform_parse_error_test]. groups() -> []. @@ -84,6 +86,21 @@ merl_smoke_test(Config) when is_list(Config) -> end)), ok. +transform_parse_error_test(_Config) -> + ?assertEqual("merl:quote(\"{\")", + f(merl_transform:parse_transform( + [?Q("merl:quote(\"{\")")], []))), + ?assertEqual("merl:quote(2, \"{\")", + f(merl_transform:parse_transform( + [?Q("merl:quote(2, \"{\")")], []))), + ?assertEqual("merl:qquote(\"{\", [{var, V}])", + f(merl_transform:parse_transform( + [?Q("merl:qquote(\"{\", [{var, V}])")], []))), + ?assertEqual("merl:qquote(2, \"{\", [{var, V}])", + f(merl_transform:parse_transform( + [?Q("merl:qquote(2, \"{\", [{var, V}])")], []))), + ok. + %% utilities f(Ts) when is_list(Ts) -> diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index b935d42bb7..43c17e9f1f 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -27,14 +27,14 @@ %% Test cases -export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1, t_abstract_type/1,t_erl_parse_type/1,t_epp_dodger/1, - t_comment_scan/1,t_igor/1]). + t_comment_scan/1,t_igor/1,t_erl_tidy/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [app_test,appup_test,smoke_test,revert,revert_map, t_abstract_type,t_erl_parse_type,t_epp_dodger, - t_comment_scan,t_igor]. + t_comment_scan,t_igor,t_erl_tidy]. groups() -> []. @@ -237,6 +237,12 @@ t_igor(Config) when is_list(Config) -> ok. +t_erl_tidy(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + File = filename:join(DataDir,"erl_tidy_tilde.erl"), + ok = erl_tidy:file(File, [{stdout, true}]), + ok. + test_comment_scan([],_) -> ok; test_comment_scan([File|Files],DataDir) -> Filename = filename:join(DataDir,File), diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl new file mode 100644 index 0000000000..888264bad6 --- /dev/null +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl @@ -0,0 +1,13 @@ +%% +%% File: erl_tidy_tilde.erl +%% Author: Mark Bucciarelli +%% Created: 2016-06-05 +%% + +-module(erl_tidy_tilde). + +-export([start/0]). + +start() -> + io:put_chars("tilde characters ('~')in source were " + "breaking erl_tidy\n"). diff --git a/lib/tools/doc/src/erlang_mode.xml b/lib/tools/doc/src/erlang_mode.xml index 00cf5196b4..7fef74813b 100644 --- a/lib/tools/doc/src/erlang_mode.xml +++ b/lib/tools/doc/src/erlang_mode.xml @@ -252,7 +252,14 @@ behavior</item> <item>gen_event - skeleton for the OTP gen_event behavior</item> <item>gen_fsm - skeleton for the OTP gen_fsm behavior</item> - <item>gen_statem - skeleton for the OTP gen_statem behavior</item> + <item> + gen_statem (StateName/3) - skeleton for the OTP gen_statem behavior + using state name functions + </item> + <item> + gen_statem (handle_event/4) - skeleton for the OTP gen_statem behavior + using one state function + </item> <item>Library module - skeleton for a module that does not implement a process.</item> <item>Corba callback - skeleton for a Corba callback module.</item> diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index 0b8a2be715..a0a817c0f2 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 2.8.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Correct a bug when adding multiple modules to an Xref + server. The bug was introduced in OTP-19.0. </p> + <p> + Own Id: OTP-13708 Aux Id: ERL-173 </p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.8.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml index 8b14e03064..872793bdcb 100644 --- a/lib/tools/doc/src/xref_chapter.xml +++ b/lib/tools/doc/src/xref_chapter.xml @@ -234,7 +234,7 @@ operand of the intersection operator <c>*</c> is implicitly converted to the more special type of the second operand.</item> <tag><c>xref:q(s, "(Mod) tools").</c></tag> - <item>All modules of the <c>tools</c> application.</item> + <item>All modules of the Tools application.</item> <tag><c>xref:q(s, '"xref_.*" : Mod').</c></tag> <item>All modules with a name beginning with <c>xref_</c>.</item> <tag><c>xref:q(s, "# E | X ").</c></tag> @@ -252,9 +252,9 @@ <tag><c>xref:q(s, "XC * (ME - strict ME)").</c></tag> <item>External calls within some module.</item> <tag><c>xref:q(s, "E ||| kernel").</c></tag> - <item>All calls within the <c>kernel</c> application. </item> + <item>All calls within the Kernel application. </item> <tag><c>xref:q(s, "closure E | kernel || kernel").</c></tag> - <item>All direct and indirect calls within the <c>kernel</c> + <item>All direct and indirect calls within the Kernel application. Both the calling and the used functions of indirect calls are defined in modules of the kernel application, but it is possible that some functions outside diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el index ce26c83295..0284c9d686 100644 --- a/lib/tools/emacs/erlang-skels.el +++ b/lib/tools/emacs/erlang-skels.el @@ -56,8 +56,10 @@ erlang-skel-gen-event erlang-skel-header) ("gen_fsm" "gen-fsm" erlang-skel-gen-fsm erlang-skel-header) - ("gen_statem" "gen-statem" - erlang-skel-gen-statem erlang-skel-header) + ("gen_statem (StateName/3)" "gen-statem-StateName" + erlang-skel-gen-statem-StateName erlang-skel-header) + ("gen_statem (handle_event/4)" "gen-statem-handle-event" + erlang-skel-gen-statem-handle-event erlang-skel-header) ("wx_object" "wx-object" erlang-skel-wx-object erlang-skel-header) ("Library module" "gen-lib" @@ -497,6 +499,7 @@ Please see the function `tempo-define-template'.") "%% {stop, Reason}" n (erlang-skel-separator-end 2) "init([]) ->" n> + "process_flag(trap_exit, true)," n> "{ok, #state{}}." n n (erlang-skel-separator-start 2) @@ -740,6 +743,7 @@ Please see the function `tempo-define-template'.") "%% {stop, StopReason}" n (erlang-skel-separator-end 2) "init([]) ->" n> + "process_flag(trap_exit, true)," n> "{ok, state_name, #state{}}." n n (erlang-skel-separator-start 2) @@ -860,7 +864,7 @@ Please see the function `tempo-define-template'.") "*The template of a gen_fsm. Please see the function `tempo-define-template'.") -(defvar erlang-skel-gen-statem +(defvar erlang-skel-gen-statem-StateName '((erlang-skel-include erlang-skel-large-header) "-behaviour(gen_statem)." n n @@ -868,9 +872,8 @@ Please see the function `tempo-define-template'.") "-export([start_link/0])." n n "%% gen_statem callbacks" n - "-export([init/1, terminate/3, code_change/4])." n + "-export([callback_mode/0, init/1, terminate/3, code_change/4])." n "-export([state_name/3])." n - "-export([handle_event/4])." n n "-define(SERVER, ?MODULE)." n n @@ -899,48 +902,151 @@ Please see the function `tempo-define-template'.") (erlang-skel-separator-start 2) "%% @private" n "%% @doc" n + "%% Define the callback_mode() for this callback module." n + (erlang-skel-separator-end 2) + "-spec callback_mode() -> gen_statem:callback_mode()." n + "callback_mode() -> state_functions." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n "%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n "%% gen_statem:start_link/[3,4], this function is called by the new" n "%% process to initialize." n (erlang-skel-separator-end 2) - "-spec init(Args :: term()) -> " n> - "{gen_statem:callback_mode()," n> - "State :: term(), Data :: term()} |" n> - "{gen_statem:callback_mode()," n> - "State :: term(), Data :: term()," n> + "-spec init(Args :: term()) ->" n> + "{ok, State :: term(), Data :: term()} |" n> + "{ok, State :: term(), Data :: term()," n> "[gen_statem:action()] | gen_statem:action()} |" n> "ignore |" n> "{stop, Reason :: term()}." n "init([]) ->" n> - "{state_functions, state_name, #data{}}." n + "process_flag(trap_exit, true)," n> + "{ok, state_name, #data{}}." n n (erlang-skel-separator-start 2) "%% @private" n "%% @doc" n - "%% If the gen_statem runs with CallbackMode =:= state_functions" n - "%% there should be one instance of this function for each possible" n - "%% state name. Whenever a gen_statem receives an event," n - "%% the instance of this function with the same name" n - "%% as the current state name StateName is called to" n - "%% handle the event." n + "%% There should be one function like this for each state name." n + "%% Whenever a gen_statem receives an event, the function " n + "%% with the name of the current state (StateName) " n + "%% is called to handle the event." n + "%%" n + "%% NOTE: If there is an exported function handle_event/4, it is called" n + "%% instead of StateName/3 functions like this!" n (erlang-skel-separator-end 2) "-spec state_name(" n> "gen_statem:event_type(), Msg :: term()," n> "Data :: term()) ->" n> - "gen_statem:state_function_result(). " n + "gen_statem:state_function_result()." n "state_name({call,Caller}, _Msg, Data) ->" n> "{next_state, state_name, Data, [{reply,Caller,ok}]}." n n (erlang-skel-separator-start 2) "%% @private" n "%% @doc" n - "%% If the gen_statem runs with CallbackMode =:= handle_event_function" n - "%% this function is called for every event a gen_statem receives." n + "%% This function is called by a gen_statem when it is about to" n + "%% terminate. It should be the opposite of Module:init/1 and do any" n + "%% necessary cleaning up. When it returns, the gen_statem terminates with" n + "%% Reason. The return value is ignored." n + (erlang-skel-separator-end 2) + "-spec terminate(Reason :: term(), State :: term(), Data :: term()) ->" n> + "any()." n + "terminate(_Reason, _State, _Data) ->" n> + "void." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Convert process state when code is changed" n + (erlang-skel-separator-end 2) + "-spec code_change(" n> + "OldVsn :: term() | {down,term()}," n> + "State :: term(), Data :: term(), Extra :: term()) ->" n> + "{ok, NewState :: term(), NewData :: term()} |" n> + "(Reason :: term())." n + "code_change(_OldVsn, State, Data, _Extra) ->" n> + "{ok, State, Data}." n + n + (erlang-skel-double-separator-start 3) + "%%% Internal functions" n + (erlang-skel-double-separator-end 3) + ) + "*The template of a gen_statem (StateName/3). +Please see the function `tempo-define-template'.") + +(defvar erlang-skel-gen-statem-handle-event + '((erlang-skel-include erlang-skel-large-header) + "-behaviour(gen_statem)." n n + + "%% API" n + "-export([start_link/0])." n + n + "%% gen_statem callbacks" n + "-export([callback_mode/0, init/1, terminate/3, code_change/4])." n + "-export([handle_event/4])." n + n + "-define(SERVER, ?MODULE)." n + n + "-record(data, {})." n + n + (erlang-skel-double-separator-start 3) + "%%% API" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @doc" n + "%% Creates a gen_statem process which calls Module:init/1 to" n + "%% initialize. To ensure a synchronized start-up procedure, this" n + "%% function does not return until Module:init/1 has returned." n + "%%" n + (erlang-skel-separator-end 2) + "-spec start_link() ->" n> + "{ok, Pid :: pid()} |" n> + "ignore |" n> + "{error, Error :: term()}." n + "start_link() ->" n> + "gen_statem:start_link({local, ?SERVER}, ?MODULE, [], [])." n + n + (erlang-skel-double-separator-start 3) + "%%% gen_statem callbacks" n + (erlang-skel-double-separator-end 3) n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Define the callback_mode() for this callback module." n + (erlang-skel-separator-end 2) + "-spec callback_mode() -> gen_statem:callback_mode()." n + "callback_mode() -> handle_event_function." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n + "%% gen_statem:start_link/[3,4], this function is called by the new" n + "%% process to initialize." n + (erlang-skel-separator-end 2) + "-spec init(Args :: term()) ->" n> + "{ok, State :: term(), Data :: term()} |" n> + "{ok, State :: term(), Data :: term()," n> + "[gen_statem:action()] | gen_statem:action()} |" n> + "ignore |" n> + "{stop, Reason :: term()}." n + "init([]) ->" n> + "process_flag(trap_exit, true)," n> + "{ok, state_name, #data{}}." n + n + (erlang-skel-separator-start 2) + "%% @private" n + "%% @doc" n + "%% This function is called for every event a gen_statem receives." n + "%%" n + "%% NOTE: If there is no exported function handle_event/4," n + "%% StateName/3 functions are called instead!" n (erlang-skel-separator-end 2) "-spec handle_event(" n> "gen_statem:event_type(), Msg :: term()," n> "State :: term(), Data :: term()) ->" n> - "gen_statem:handle_event_result(). " n + "gen_statem:handle_event_result()." n "handle_event({call,From}, _Msg, State, Data) ->" n> "{next_state, State, Data, [{reply,From,ok}]}." n n @@ -965,7 +1071,8 @@ Please see the function `tempo-define-template'.") "-spec code_change(" n> "OldVsn :: term() | {down,term()}," n> "State :: term(), Data :: term(), Extra :: term()) ->" n> - "{ok, NewState :: term(), NewData :: term()}." n + "{ok, NewState :: term(), NewData :: term()} |" n> + "(Reason :: term())." n "code_change(_OldVsn, State, Data, _Extra) ->" n> "{ok, State, Data}." n n @@ -973,7 +1080,7 @@ Please see the function `tempo-define-template'.") "%%% Internal functions" n (erlang-skel-double-separator-end 3) ) - "*The template of a gen_statem. + "*The template of a gen_statem (handle_event/4). Please see the function `tempo-define-template'.") (defvar erlang-skel-wx-object diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el index 9a146632c5..ba6190d194 100644 --- a/lib/tools/emacs/erlang-test.el +++ b/lib/tools/emacs/erlang-test.el @@ -52,28 +52,42 @@ concatenated to form an erlang file to test on.") (ert-deftest erlang-test-tags () (let* ((dir (make-temp-file "erlang-test" t)) - (erlang-file (expand-file-name "erlang_test.erl" dir)) - (tags-file (expand-file-name "TAGS" dir)) - tags-file-name tags-table-list erlang-buffer) - (unwind-protect - (progn - (erlang-test-create-erlang-file erlang-file) - (erlang-test-compile-tags erlang-file tags-file) - (setq erlang-buffer (find-file-noselect erlang-file)) - (with-current-buffer erlang-buffer - (setq-local tags-file-name tags-file)) - ;; Setting global tags-file-name is a workaround for - ;; GNU Emacs bug#23164. - (setq tags-file-name tags-file) - (erlang-test-completion-table) - (erlang-test-xref-find-definitions erlang-file erlang-buffer)) - (when (buffer-live-p erlang-buffer) - (kill-buffer erlang-buffer)) - (let ((tags-buffer (find-buffer-visiting tags-file))) - (when (buffer-live-p tags-buffer) - (kill-buffer tags-buffer))) - (when (file-exists-p dir) - (delete-directory dir t))))) + (erlang-file (expand-file-name "erlang_test.erl" dir)) + (tags-file (expand-file-name "TAGS" dir)) + (old-tags-file-name (default-value 'tags-file-name)) + (old-tags-table-list (default-value 'tags-table-list)) + tags-file-name + tags-table-list + tags-table-set-list + erlang-buffer + erlang-mode-hook + prog-mode-hook + erlang-shell-mode-hook + tags-add-tables) + (unwind-protect + (progn + (setq-default tags-file-name nil) + (setq-default tags-table-list nil) + (erlang-test-create-erlang-file erlang-file) + (erlang-test-compile-tags erlang-file tags-file) + (setq erlang-buffer (find-file-noselect erlang-file)) + (with-current-buffer erlang-buffer + (setq-local tags-file-name tags-file)) + ;; Setting global tags-file-name is a workaround for + ;; GNU Emacs bug#23164. + (setq tags-file-name tags-file) + (erlang-test-complete-at-point tags-file) + (erlang-test-completion-table) + (erlang-test-xref-find-definitions erlang-file erlang-buffer)) + (when (buffer-live-p erlang-buffer) + (kill-buffer erlang-buffer)) + (let ((tags-buffer (find-buffer-visiting tags-file))) + (when (buffer-live-p tags-buffer) + (kill-buffer tags-buffer))) + (when (file-exists-p dir) + (delete-directory dir t)) + (setq-default tags-file-name old-tags-file-name) + (setq-default tags-table-list old-tags-table-list)))) (defun erlang-test-create-erlang-file (erlang-file) (with-temp-file erlang-file @@ -116,6 +130,54 @@ concatenated to form an erlang file to test on.") (should (eq expected-line (line-number-at-pos))) (should (= (point-at-bol) (point)))) +(defun erlang-test-complete-at-point (tags-file) + (with-temp-buffer + (erlang-mode) + (setq-local tags-file-name tags-file) + (insert "\nerlang_test:fun") + (erlang-complete-tag) + (should (looking-back "erlang_test:function")) + (insert "\nfun") + (erlang-complete-tag) + (should (looking-back "function")) + (insert "\nerlang_") + (erlang-complete-tag) + (should (looking-back "erlang_test:")))) + + +(ert-deftest erlang-test-compile-options () + (erlang-test-format-opt t + "t") + (erlang-test-format-opt nil + "nil") + (erlang-test-format-opt (cons 1 2) + "{1, 2}") + (erlang-test-format-opt (list 1) + "[1]") + (erlang-test-format-opt (list 1 2) + "[1, 2]") + (erlang-test-format-opt (list 1 2 3) + "[1, 2, 3]") + (erlang-test-format-opt 'symbol + "symbol") + (erlang-test-format-opt "string" + "\"string\"") + (erlang-test-format-opt [] + "{}") + (erlang-test-format-opt [1] + "{1}") + (erlang-test-format-opt [1 2] + "{1, 2}") + (erlang-test-format-opt [1 2 (3 [4 5 6] 7)] + "{1, 2, [3, {4, 5, 6}, 7]}")) + +(defun erlang-test-format-opt (elisp &optional expected-erlang) + (let ((erlang (inferior-erlang-format-opt elisp))) + (message "%s -> %s" elisp erlang) + (when expected-erlang + (should (equal erlang expected-erlang))) + erlang)) + (provide 'erlang-test) diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index a2062180f3..73c6b8d768 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -1436,8 +1436,6 @@ Other commands: (erlang-skel-init) (when (fboundp 'tempo-use-tag-list) (tempo-use-tag-list 'erlang-tempo-tags)) - (when (boundp 'xref-backend-functions) - (add-hook 'xref-backend-functions #'erlang-etags--xref-backend nil t)) (run-hooks 'erlang-mode-hook) (if (zerop (buffer-size)) (run-hooks 'erlang-new-file-hook))) @@ -1548,9 +1546,7 @@ Other commands: (set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$") (set (make-local-variable 'outline-level) (lambda () 1)) (set (make-local-variable 'add-log-current-defun-function) - 'erlang-current-defun) - (set (make-local-variable 'find-tag-default-function) - 'erlang-find-tag-for-completion)) + 'erlang-current-defun)) (defun erlang-font-lock-init () "Initialize Font Lock for Erlang mode." @@ -3233,18 +3229,16 @@ With argument, do this that many times." (interactive "p") (or arg (setq arg 1)) (while (and (looking-at "[ \t]*[%\n]") - (zerop (forward-line 1)))) + (zerop (forward-line 1)))) ;; Move to the next clause. (erlang-beginning-of-clause (- arg)) (beginning-of-line);; Just to be sure... (let ((continue t)) (while (and (not (bobp)) continue) (forward-line -1) - (skip-chars-forward " \t") - (if (looking-at "[%\n]") - nil - (end-of-line) - (setq continue nil))))) + (unless (looking-at "[ \t]*[%\n]") + (end-of-line) + (setq continue nil))))) (defun erlang-mark-clause () "Put mark at end of clause, point at beginning." @@ -4352,11 +4346,6 @@ as on the old form `tag'. In the completion list, `module:tag' and `module:' shows up. -Call this function from an appropriate init file, or add it to -Erlang mode hook with the commands: - (add-hook 'erlang-mode-hook 'erlang-tags-init) - (add-hook 'erlang-shell-mode-hook 'erlang-tags-init) - This function only works under Emacs 18 and Emacs 19. Currently, It is not implemented under XEmacs. (Hint: The Emacs 19 etags module works under XEmacs.)" @@ -4367,11 +4356,16 @@ works under XEmacs.)" (setq erlang-tags-installed t)) (t (require 'etags) - ;; Test on a function available in the Emacs 19 version - ;; of tags but not in the XEmacs version. - (when (fboundp 'find-tag-noselect) - (erlang-tags-define-keys (current-local-map)) - (setq erlang-tags-installed t))))) + (set (make-local-variable 'find-tag-default-function) + 'erlang-find-tag-for-completion) + (if (>= emacs-major-version 25) + (add-hook 'xref-backend-functions + #'erlang-etags--xref-backend nil t) + ;; Test on a function available in the Emacs 19 version + ;; of tags but not in the XEmacs version. + (when (fboundp 'find-tag-noselect) + (erlang-tags-define-keys (current-local-map)) + (setq erlang-tags-installed t)))))) @@ -4775,8 +4769,6 @@ for a tag on the form `module:tag'." ;;; completion-table' containing all normal tags plus tags on the form ;;; `module:tag' and `module:'. -;; PENDING - Should probably make use of the -;; `completion-at-point-functions' hook instead of this advice. (when (and (locate-library "etags") (require 'etags) (fboundp 'etags-tags-completion-table) @@ -4784,11 +4776,7 @@ for a tag on the form `module:tag'." (if (fboundp 'advice-add) ;; Emacs 24.4+ (advice-add 'etags-tags-completion-table :around - (lambda (oldfun) - (if erlang-replace-etags-tags-completion-table - (erlang-etags-tags-completion-table) - (funcall oldfun))) - (list :name 'erlang-replace-tags-table)) + #'erlang-etags-tags-completion-table-advice) ;; Emacs 23.1-24.3 (defadvice etags-tags-completion-table (around erlang-replace-tags-table @@ -4797,6 +4785,11 @@ for a tag on the form `module:tag'." (setq ad-return-value (erlang-etags-tags-completion-table)) ad-do-it)))) +(defun erlang-etags-tags-completion-table-advice (oldfun) + (if erlang-replace-etags-tags-completion-table + (erlang-etags-tags-completion-table) + (funcall oldfun))) + (defun erlang-complete-tag () "Perform tags completion on the text around point. Completes to the set of names listed in the current tags table. @@ -4807,8 +4800,7 @@ about Erlang modules." (condition-case nil (require 'etags) (error nil)) - (cond ((and erlang-tags-installed - (fboundp 'etags-tags-completion-table) + (cond ((and (fboundp 'etags-tags-completion-table) (fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+ (let ((erlang-replace-etags-tags-completion-table t)) (complete-tag))) @@ -5213,19 +5205,10 @@ The following special commands are available: (setq comint-input-ignoredups t) (setq comint-scroll-show-maximum-output t) (setq comint-scroll-to-bottom-on-output t) - ;; In Emacs 19.30, `add-hook' has got a `local' flag, use it. If - ;; the call fails, just call the normal `add-hook'. - (condition-case nil - (progn - (add-hook 'comint-output-filter-functions - 'inferior-erlang-strip-delete nil t) - (add-hook 'comint-output-filter-functions - 'inferior-erlang-strip-ctrl-m nil t)) - (error - (funcall (symbol-function 'make-local-hook) - 'comint-output-filter-functions) ; obsolete as of Emacs 21.1 - (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-delete) - (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-ctrl-m))) + (add-hook 'comint-output-filter-functions + 'inferior-erlang-strip-delete nil t) + (add-hook 'comint-output-filter-functions + 'inferior-erlang-strip-ctrl-m nil t) ;; Some older versions of comint don't have an input ring. (if (fboundp 'comint-read-input-ring) (progn @@ -5251,6 +5234,7 @@ The following special commands are available: (define-key map [menu-bar compilation] (cons "Errors" compilation-menu-map))) map))))) + (erlang-tags-init) (run-hooks 'erlang-shell-mode-hook)) @@ -5730,31 +5714,29 @@ unless the optional NO-DISPLAY is non-nil." (defun inferior-erlang-format-comma-opts (opts) (if (null opts) "" - (concat ", " (inferior-erlang-format-opts opts)))) - -(defun inferior-erlang-format-opts (opts) - (concat "[" (inferior-erlang-string-join (mapcar 'inferior-erlang-format-opt - opts) - ", ") - "]")) + (concat ", " (inferior-erlang-format-opt opts)))) (defun inferior-erlang-format-opt (opt) (cond ((stringp opt) (concat "\"" opt "\"")) - ((atom opt) (format "%s" opt)) - ((consp opt) (concat "{" (inferior-erlang-string-join - (mapcar 'inferior-erlang-format-opt - (list (car opt) (cdr opt))) - ", ") - "}")) - (t (error (format "Unexpected opt %s" opt))))) - -(defun inferior-erlang-string-join (strs sep) - (let ((result (or (car strs) ""))) - (setq strs (cdr strs)) - (while strs - (setq result (concat result sep (car strs))) - (setq strs (cdr strs))) - result)) + ((vectorp opt) (inferior-erlang-tuple (append opt nil))) + ((atom opt) (format "%s" opt)) + ((consp opt) (if (listp (cdr opt)) + (inferior-erlang-list opt) + (inferior-erlang-tuple (list (car opt) (cdr opt))))) + (t (error "Unexpected erlang compile option %s" opt)))) + +(defun inferior-erlang-tuple (opts) + (concat "{" (mapconcat 'inferior-erlang-format-opt + opts + ", ") + "}")) + +(defun inferior-erlang-list (opts) + (concat "[" (mapconcat 'inferior-erlang-format-opt + opts + ", ") + "]")) + (defun erlang-local-buffer-file-name () ;; When editing a file remotely via tramp, diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index 8db23dd151..1291a3e5ec 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -1636,6 +1636,11 @@ trace_handler({trace_ts, Pid, gc_major_start, _Func, TS} = Trace, Table, _, Dump dump_stack(Dump, get(Pid), Trace), trace_gc_start(Table, Pid, TS), TS; + +trace_handler({trace_ts, Pid, gc_start, _Func, TS} = Trace, Table, _, Dump) -> + dump_stack(Dump, get(Pid), Trace), + trace_gc_start(Table, Pid, TS), + TS; %% %% gc_end @@ -1648,6 +1653,12 @@ trace_handler({trace_ts, Pid, gc_major_end, _Func, TS} = Trace, Table, _, Dump) dump_stack(Dump, get(Pid), Trace), trace_gc_end(Table, Pid, TS), TS; + +trace_handler({trace_ts, Pid, gc_end, _Func, TS} = Trace, Table, _, Dump) -> + dump_stack(Dump, get(Pid), Trace), + trace_gc_end(Table, Pid, TS), + TS; + %% %% link trace_handler({trace_ts, Pid, link, _OtherPid, TS} = Trace, diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl index 26378f28a0..37e67cbe34 100644 --- a/lib/tools/src/make.erl +++ b/lib/tools/src/make.erl @@ -317,5 +317,7 @@ check_includes2(Epp, File, ObjMTime) -> epp:close(Epp), false; {error, _Error} -> + check_includes2(Epp, File, ObjMTime); + {warning, _Warning} -> check_includes2(Epp, File, ObjMTime) end. diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src index a00969eabe..18166cceb0 100644 --- a/lib/tools/src/tools.app.src +++ b/lib/tools/src/tools.app.src @@ -40,7 +40,7 @@ {env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]} ] }, - {runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14", + {runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14", "kernel-3.0","inets-5.10","erts-7.0", "compiler-5.0"]} ] diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl index bb9815f9b0..f298a1ce81 100644 --- a/lib/tools/src/xref_base.erl +++ b/lib/tools/src/xref_base.erl @@ -746,7 +746,7 @@ read_a_module({Dir, BaseName}, AppName, Builtins, Verbose, Warnings, Mode) -> message(Warnings, no_debug_info, [File]), no; {ok, M, Data, UnresCalls0} -> - message(Verbose, done, [File]), + message(Verbose, done_file, [File]), %% Remove duplicates. Identical unresolved calls on the %% same line are counted as _one_ unresolved call. UnresCalls = usort(UnresCalls0), @@ -1842,6 +1842,8 @@ message(true, What, Arg) -> set_up -> io:format("Setting up...", Arg); done -> + io:format("done~n", Arg); + done_file -> io:format("done reading ~ts~n", Arg); error -> io:format("error~n", Arg); diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index b4c9264b30..90e113c178 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -1001,41 +1001,40 @@ otp_6115(Config) when is_list(Config) -> %% Cover compile f1, but not f2 {ok, f1} = cover:compile(f1), + %% This test used to ensure that a process containing a + %% fun refering to cover compiled code was killed. + %% check_process_code may however ignore funs as of ERTS + %% version 8.1. The test has therefore been rewritten to + %% check that a process with a direct reference (in this + %% case a return address) to the code is killed. + %% %% If f1 is cover compiled, a process P is started with a - %% reference to the fun created in start_fail/0, and cover:stop() is - %% called, then P should be killed. - %% This is because (the fun held by P) references the cover + %% direct reference to the f1, and cover:stop() is called, + %% then P should be killed. + %% This is because of the return address to the cover %% compiled code which should be *unloaded* when cover:stop() is %% called -- running cover compiled code when there is no cover %% server and thus no ets tables to bump counters in, makes no %% sense. - Pid1 = f1:start_a(), - Pid2 = f1:start_b(), + Pid = spawn(fun () -> f1:non_tail_call_f2_wait() end), %% Now stop cover cover:stop(), %% Ensure that f1 is loaded (and not cover compiled), and that - %% both Pid1 and Pid2 are dead. + %% both Pid is dead. case code:which(f1) of Beam when is_list(Beam) -> ok; Other -> ct:fail({"f1 is not reloaded", Other}) end, - case process_info(Pid1) of + case process_info(Pid) of undefined -> ok; - _PI1 -> - RefToOldP1 = erlang:check_process_code(Pid1, f1), - ct:fail({"Pid1 still alive", RefToOldP1}) - end, - case process_info(Pid2) of - undefined -> - ok; - _PI2 -> - RefToOldP2 = erlang:check_process_code(Pid1, f2), - ct:fail({"Pid2 still alive", RefToOldP2}) + _PI -> + RefToOldP = erlang:check_process_code(Pid, f1), + ct:fail({"Pid still alive", RefToOldP}) end, file:set_cwd(CWD), diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl index 5399b33f19..fc4a62e70e 100644 --- a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl +++ b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl @@ -1,13 +1,6 @@ -module(f1). --export([start_a/0, start_b/0]). +-export([non_tail_call_f2_wait/0]). -start_a() -> - f2:start(fun() -> - ok - end). - -start_b() -> - f2:start(fun fun1/0). - -fun1() -> - ok. +non_tail_call_f2_wait() -> + f2:wait(), + im_back. diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl index 72a6a64c4d..4bc88035c7 100644 --- a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl +++ b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl @@ -1,13 +1,5 @@ -module(f2). --export([start/1]). +-export([wait/0]). -start(Fun) -> - spawn(fun() -> - wait(Fun) - end). - -wait(Fun) -> - receive - go -> - Fun() - end. +wait() -> + receive after infinity -> ok end. diff --git a/lib/tools/test/make_SUITE_data/test1.erl b/lib/tools/test/make_SUITE_data/test1.erl index f4a133008e..9e21bdc767 100644 --- a/lib/tools/test/make_SUITE_data/test1.erl +++ b/lib/tools/test/make_SUITE_data/test1.erl @@ -3,6 +3,8 @@ -vsn('$Revision: /main/release/2 $'). -compile(export_all). +-warning("a warning"). + f1() -> true. diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index ce30fb711a..01dbac6ecb 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -50,7 +50,7 @@ -export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]). --export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]). +-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1]). -import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]). @@ -82,7 +82,7 @@ groups() -> fun_mfa_r14, fun_mfa_vars, qlc]}, {analyses, [], [analyze, basic, md, q, variables, unused_locals]}, - {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}]. + {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708]}]. init_per_suite(Conf) when is_list(Conf) -> @@ -2393,6 +2393,19 @@ otp_10192(Conf) when is_list(Conf) -> xref:stop(s), ok. +%% OTP-10192. Allow filenames with character codes greater than 126. +otp_13708(Conf) when is_list(Conf) -> + {ok, _} = start(s), + ok = xref:set_default(s, [{verbose, true}]), + {ok, []} = xref:q(s,"E"), + xref:stop(s), + + CopyDir = ?copydir, + Dir = fname(CopyDir,"lib_test"), + {ok, _} = start(s), + ok = xref:set_library_path(s, [Dir], [{verbose, true}]), + xref:stop(s). + %%% %%% Utilities %%% diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 8c889cbe4e..e066dbf5e9 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.8.4 +TOOLS_VSN = 2.8.6 diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.erl b/lib/wx/api_gen/wx_extra/wxListCtrl.erl index acdb69fdeb..355a4cdfd1 100644 --- a/lib/wx/api_gen/wx_extra/wxListCtrl.erl +++ b/lib/wx/api_gen/wx_extra/wxListCtrl.erl @@ -43,33 +43,36 @@ SortItems>> <<EXPORT:wxListCtrl new/0, new/1, new/2 wxListCtrl:EXPORT>> <<wxListCtrl_new_0 -%% @spec () -> wxListCtrl() %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>. +-spec new() -> wxListCtrl(). new() -> wxe_util:construct(~s, <<>>). wxListCtrl_new_0>> <<wxListCtrl_new_2 -%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl() -%% @equiv new(Parent, []) +-spec new(Parent) -> wxListCtrl() when + Parent::wxWindow:wxWindow(). new(Parent) when is_record(Parent, wx_ref) -> new(Parent, []). -%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl() -%% Option = {winid, integer()} | -%% {pos, {X::integer(),Y::integer()}} | -%% {size, {W::integer(),H::integer()}} | -%% {style, integer()} | -%% {validator, wx:wx()} | -%% {onGetItemText, OnGetItemText} | -%% {onGetItemAttr, OnGetItemAttr} | -%% {onGetItemColumnImage, OnGetItemColumnImage} +%% @doc Creates a listctrl with optional callback functions: %% -%% OnGetItemText = (This, Item, Column) -> wxString() -%% OnGetItemAttr = (This, Item) -> wxListItemAttr() +%% OnGetItemText = (This, Item, Column) -> unicode:charlist() +%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr() %% OnGetItemColumnImage = (This, Item, Column) -> integer() -%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>. +%% +%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>. +-spec new(Parent, [Option]) -> wxListCtrl() when + Parent::wxWindow:wxWindow(), + Option::{winid, integer()} | + {pos, {X::integer(),Y::integer()}} | + {size, {W::integer(),H::integer()}} | + {style, integer()} | + {validator, wx:wx_object()} | + {onGetItemText, function()} | + {onGetItemAttr, function()} | + {onGetItemColumnImage, function()}. new(#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options)-> @@ -101,26 +104,27 @@ wxListCtrl_new_2>> <<EXPORT:Create create/2, create/3 Create:EXPORT>> <<Create -%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool() %% @equiv create(This,Parent, []) +-spec create(This, Parent) -> wxListCtrl() when + This::wxWindow:wxWindow(), + Parent::wxWindow:wxWindow(). create(This,Parent) when is_record(This, wx_ref),is_record(Parent, wx_ref) -> create(This,Parent, []). -%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool() -%% Option = {winid, integer()} | -%% {pos, {X::integer(),Y::integer()}} | -%% {size, {W::integer(),H::integer()}} | -%% {style, integer()} | -%% {validator, wx:wx()} | -%% {onGetItemText, OnGetItemText} | -%% {onGetItemAttr, OnGetItemAttr} | -%% {onGetItemColumnImage, OnGetItemColumnImage} -%% -%% OnGetItemText = (This, Item, Column) -> wxString() -%% OnGetItemAttr = (This, Item) -> wxListItemAttr() -%% OnGetItemColumnImage = (This, Item, Column) -> integer() %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>. +-spec create(This, Parent, [Option]) -> wxListCtrl() when + This::wxWindow:wxWindow(), + Parent::wxWindow:wxWindow(), + Option::{winid, integer()} | + {pos, {X::integer(),Y::integer()}} | + {size, {W::integer(),H::integer()}} | + {style, integer()} | + {validator, wx:wx_object()} | + {onGetItemText, function()} | + {onGetItemAttr, function()} | + {onGetItemColumnImage, function()}. + create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options) -> ?CLASS(ThisT,wxListCtrl), diff --git a/lib/wx/api_gen/wx_extra/wxXmlResource.erl b/lib/wx/api_gen/wx_extra/wxXmlResource.erl index 7700e2333e..b29ffba7c6 100644 --- a/lib/wx/api_gen/wx_extra/wxXmlResource.erl +++ b/lib/wx/api_gen/wx_extra/wxXmlResource.erl @@ -21,8 +21,6 @@ <<EXPORT:xrcctrl xrcctrl/3 xrcctrl:EXPORT>> <<xrcctrl -%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject() - %% @doc Looks up a control with Name in a window created with XML %% resources. You can use it to set/get values from controls. %% The object is type casted to <b>Type</b>. @@ -32,6 +30,10 @@ %% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br /> %% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br /> %% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br /> +-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when + Window::wxWindow:wxWindow(), + Name::string(), + Type::atom(). xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) -> %% Func Id ~s diff --git a/lib/wx/c_src/wxe_main.cpp b/lib/wx/c_src/wxe_main.cpp index 6fcde42eb5..c7565e33bd 100644 --- a/lib/wx/c_src/wxe_main.cpp +++ b/lib/wx/c_src/wxe_main.cpp @@ -67,6 +67,7 @@ int load_native_gui() int start_native_gui(wxe_data *sd) { int res; + ErlDrvThreadOpts *opts = NULL; wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m"); wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c"); @@ -78,8 +79,11 @@ int start_native_gui(wxe_data *sd) res = erl_drv_steal_main_thread((char *)"wxwidgets", &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL); #else + opts = erl_drv_thread_opts_create((char *)"wx thread"); + opts->suggested_stack_size = 8192; res = erl_drv_thread_create((char *)"wxwidgets", - &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL); + &wxe_thread,wxe_main_loop,(void *) sd->pdl,opts); + erl_drv_thread_opts_destroy(opts); #endif if(res == 0) { erl_drv_mutex_lock(wxe_status_m); diff --git a/lib/wx/doc/specs/.gitignore b/lib/wx/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/wx/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile index cae2f9fe4e..23890f47f0 100644 --- a/lib/wx/doc/src/Makefile +++ b/lib/wx/doc/src/Makefile @@ -63,10 +63,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include -I../../src DVIPS_FLAGS += # ---------------------------------------------------- @@ -111,6 +116,7 @@ clean clean_docs: rm -rf $(HTMLDIR)/* rm -f $(MAN3DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ ../html/edoc-info rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html diff --git a/lib/wx/doc/src/specs.xml b/lib/wx/doc/src/specs.xml new file mode 100644 index 0000000000..2e19baafc4 --- /dev/null +++ b/lib/wx/doc/src/specs.xml @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="utf-8" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_wx.xml"/> + <xi:include href="../specs/specs_wx_object.xml"/> + <xi:include href="../specs/specs_wxAcceleratorEntry.xml"/> + <xi:include href="../specs/specs_wxAcceleratorTable.xml"/> + <xi:include href="../specs/specs_wxActivateEvent.xml"/> + <xi:include href="../specs/specs_wxArtProvider.xml"/> + <xi:include href="../specs/specs_wxAuiDockArt.xml"/> + <xi:include href="../specs/specs_wxAuiManager.xml"/> + <xi:include href="../specs/specs_wxAuiManagerEvent.xml"/> + <xi:include href="../specs/specs_wxAuiNotebook.xml"/> + <xi:include href="../specs/specs_wxAuiNotebookEvent.xml"/> + <xi:include href="../specs/specs_wxAuiPaneInfo.xml"/> + <xi:include href="../specs/specs_wxAuiSimpleTabArt.xml"/> + <xi:include href="../specs/specs_wxAuiTabArt.xml"/> + <xi:include href="../specs/specs_wxBitmapButton.xml"/> + <xi:include href="../specs/specs_wxBitmapDataObject.xml"/> + <xi:include href="../specs/specs_wxBitmap.xml"/> + <xi:include href="../specs/specs_wxBoxSizer.xml"/> + <xi:include href="../specs/specs_wxBrush.xml"/> + <xi:include href="../specs/specs_wxBufferedDC.xml"/> + <xi:include href="../specs/specs_wxBufferedPaintDC.xml"/> + <xi:include href="../specs/specs_wxButton.xml"/> + <xi:include href="../specs/specs_wxCalendarCtrl.xml"/> + <xi:include href="../specs/specs_wxCalendarDateAttr.xml"/> + <xi:include href="../specs/specs_wxCalendarEvent.xml"/> + <xi:include href="../specs/specs_wxCaret.xml"/> + <xi:include href="../specs/specs_wxCheckBox.xml"/> + <xi:include href="../specs/specs_wxCheckListBox.xml"/> + <xi:include href="../specs/specs_wxChildFocusEvent.xml"/> + <xi:include href="../specs/specs_wxChoicebook.xml"/> + <xi:include href="../specs/specs_wxChoice.xml"/> + <xi:include href="../specs/specs_wxClientDC.xml"/> + <xi:include href="../specs/specs_wxClipboard.xml"/> + <xi:include href="../specs/specs_wxClipboardTextEvent.xml"/> + <xi:include href="../specs/specs_wxCloseEvent.xml"/> + <xi:include href="../specs/specs_wxColourData.xml"/> + <xi:include href="../specs/specs_wxColourDialog.xml"/> + <xi:include href="../specs/specs_wxColourPickerCtrl.xml"/> + <xi:include href="../specs/specs_wxColourPickerEvent.xml"/> + <xi:include href="../specs/specs_wxComboBox.xml"/> + <xi:include href="../specs/specs_wxCommandEvent.xml"/> + <xi:include href="../specs/specs_wxContextMenuEvent.xml"/> + <xi:include href="../specs/specs_wxControl.xml"/> + <xi:include href="../specs/specs_wxControlWithItems.xml"/> + <xi:include href="../specs/specs_wxCursor.xml"/> + <xi:include href="../specs/specs_wxDataObject.xml"/> + <xi:include href="../specs/specs_wxDateEvent.xml"/> + <xi:include href="../specs/specs_wxDatePickerCtrl.xml"/> + <xi:include href="../specs/specs_wxDC.xml"/> + <xi:include href="../specs/specs_wxDCOverlay.xml"/> + <xi:include href="../specs/specs_wxDialog.xml"/> + <xi:include href="../specs/specs_wxDirDialog.xml"/> + <xi:include href="../specs/specs_wxDirPickerCtrl.xml"/> + <xi:include href="../specs/specs_wxDisplayChangedEvent.xml"/> + <xi:include href="../specs/specs_wxEraseEvent.xml"/> + <xi:include href="../specs/specs_wxEvent.xml"/> + <xi:include href="../specs/specs_wxEvtHandler.xml"/> + <xi:include href="../specs/specs_wxFileDataObject.xml"/> + <xi:include href="../specs/specs_wxFileDialog.xml"/> + <xi:include href="../specs/specs_wxFileDirPickerEvent.xml"/> + <xi:include href="../specs/specs_wxFilePickerCtrl.xml"/> + <xi:include href="../specs/specs_wxFindReplaceData.xml"/> + <xi:include href="../specs/specs_wxFindReplaceDialog.xml"/> + <xi:include href="../specs/specs_wxFlexGridSizer.xml"/> + <xi:include href="../specs/specs_wxFocusEvent.xml"/> + <xi:include href="../specs/specs_wxFontData.xml"/> + <xi:include href="../specs/specs_wxFontDialog.xml"/> + <xi:include href="../specs/specs_wxFont.xml"/> + <xi:include href="../specs/specs_wxFontPickerCtrl.xml"/> + <xi:include href="../specs/specs_wxFontPickerEvent.xml"/> + <xi:include href="../specs/specs_wxFrame.xml"/> + <xi:include href="../specs/specs_wxGauge.xml"/> + <xi:include href="../specs/specs_wxGBSizerItem.xml"/> + <xi:include href="../specs/specs_wxGenericDirCtrl.xml"/> + <xi:include href="../specs/specs_wxGLCanvas.xml"/> + <xi:include href="../specs/specs_wxGraphicsBrush.xml"/> + <xi:include href="../specs/specs_wxGraphicsContext.xml"/> + <xi:include href="../specs/specs_wxGraphicsFont.xml"/> + <xi:include href="../specs/specs_wxGraphicsMatrix.xml"/> + <xi:include href="../specs/specs_wxGraphicsObject.xml"/> + <xi:include href="../specs/specs_wxGraphicsPath.xml"/> + <xi:include href="../specs/specs_wxGraphicsPen.xml"/> + <xi:include href="../specs/specs_wxGraphicsRenderer.xml"/> + <xi:include href="../specs/specs_wxGridBagSizer.xml"/> + <xi:include href="../specs/specs_wxGridCellAttr.xml"/> + <xi:include href="../specs/specs_wxGridCellBoolEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellBoolRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellChoiceEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellFloatEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellFloatRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellNumberEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellNumberRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellStringRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellTextEditor.xml"/> + <xi:include href="../specs/specs_wxGrid.xml"/> + <xi:include href="../specs/specs_wxGridEvent.xml"/> + <xi:include href="../specs/specs_wxGridSizer.xml"/> + <xi:include href="../specs/specs_wxHelpEvent.xml"/> + <xi:include href="../specs/specs_wxHtmlEasyPrinting.xml"/> + <xi:include href="../specs/specs_wxHtmlLinkEvent.xml"/> + <xi:include href="../specs/specs_wxHtmlWindow.xml"/> + <xi:include href="../specs/specs_wxIconBundle.xml"/> + <xi:include href="../specs/specs_wxIcon.xml"/> + <xi:include href="../specs/specs_wxIconizeEvent.xml"/> + <xi:include href="../specs/specs_wxIdleEvent.xml"/> + <xi:include href="../specs/specs_wxImage.xml"/> + <xi:include href="../specs/specs_wxImageList.xml"/> + <xi:include href="../specs/specs_wxInitDialogEvent.xml"/> + <xi:include href="../specs/specs_wxJoystickEvent.xml"/> + <xi:include href="../specs/specs_wxKeyEvent.xml"/> + <xi:include href="../specs/specs_wxLayoutAlgorithm.xml"/> + <xi:include href="../specs/specs_wxListbook.xml"/> + <xi:include href="../specs/specs_wxListBox.xml"/> + <xi:include href="../specs/specs_wxListCtrl.xml"/> + <xi:include href="../specs/specs_wxListEvent.xml"/> + <xi:include href="../specs/specs_wxListItemAttr.xml"/> + <xi:include href="../specs/specs_wxListItem.xml"/> + <xi:include href="../specs/specs_wxListView.xml"/> + <xi:include href="../specs/specs_wxLocale.xml"/> + <xi:include href="../specs/specs_wxLogNull.xml"/> + <xi:include href="../specs/specs_wxMask.xml"/> + <xi:include href="../specs/specs_wxMaximizeEvent.xml"/> + <xi:include href="../specs/specs_wxMDIChildFrame.xml"/> + <xi:include href="../specs/specs_wxMDIClientWindow.xml"/> + <xi:include href="../specs/specs_wxMDIParentFrame.xml"/> + <xi:include href="../specs/specs_wxMemoryDC.xml"/> + <xi:include href="../specs/specs_wxMenuBar.xml"/> + <xi:include href="../specs/specs_wxMenu.xml"/> + <xi:include href="../specs/specs_wxMenuEvent.xml"/> + <xi:include href="../specs/specs_wxMenuItem.xml"/> + <xi:include href="../specs/specs_wxMessageDialog.xml"/> + <xi:include href="../specs/specs_wxMiniFrame.xml"/> + <xi:include href="../specs/specs_wxMirrorDC.xml"/> + <xi:include href="../specs/specs_wxMouseCaptureChangedEvent.xml"/> + <xi:include href="../specs/specs_wxMouseCaptureLostEvent.xml"/> + <xi:include href="../specs/specs_wxMouseEvent.xml"/> + <xi:include href="../specs/specs_wxMoveEvent.xml"/> + <xi:include href="../specs/specs_wxMultiChoiceDialog.xml"/> + <xi:include href="../specs/specs_wxNavigationKeyEvent.xml"/> + <xi:include href="../specs/specs_wxNotebook.xml"/> + <xi:include href="../specs/specs_wxNotebookEvent.xml"/> + <xi:include href="../specs/specs_wxNotifyEvent.xml"/> + <xi:include href="../specs/specs_wxOverlay.xml"/> + <xi:include href="../specs/specs_wxPageSetupDialogData.xml"/> + <xi:include href="../specs/specs_wxPageSetupDialog.xml"/> + <xi:include href="../specs/specs_wxPaintDC.xml"/> + <xi:include href="../specs/specs_wxPaintEvent.xml"/> + <xi:include href="../specs/specs_wxPaletteChangedEvent.xml"/> + <xi:include href="../specs/specs_wxPalette.xml"/> + <xi:include href="../specs/specs_wxPanel.xml"/> + <xi:include href="../specs/specs_wxPasswordEntryDialog.xml"/> + <xi:include href="../specs/specs_wxPen.xml"/> + <xi:include href="../specs/specs_wxPickerBase.xml"/> + <xi:include href="../specs/specs_wxPopupTransientWindow.xml"/> + <xi:include href="../specs/specs_wxPopupWindow.xml"/> + <xi:include href="../specs/specs_wxPostScriptDC.xml"/> + <xi:include href="../specs/specs_wxPreviewCanvas.xml"/> + <xi:include href="../specs/specs_wxPreviewControlBar.xml"/> + <xi:include href="../specs/specs_wxPreviewFrame.xml"/> + <xi:include href="../specs/specs_wxPrintData.xml"/> + <xi:include href="../specs/specs_wxPrintDialogData.xml"/> + <xi:include href="../specs/specs_wxPrintDialog.xml"/> + <xi:include href="../specs/specs_wxPrinter.xml"/> + <xi:include href="../specs/specs_wxPrintout.xml"/> + <xi:include href="../specs/specs_wxPrintPreview.xml"/> + <xi:include href="../specs/specs_wxProgressDialog.xml"/> + <xi:include href="../specs/specs_wxQueryNewPaletteEvent.xml"/> + <xi:include href="../specs/specs_wxRadioBox.xml"/> + <xi:include href="../specs/specs_wxRadioButton.xml"/> + <xi:include href="../specs/specs_wxRegion.xml"/> + <xi:include href="../specs/specs_wxSashEvent.xml"/> + <xi:include href="../specs/specs_wxSashLayoutWindow.xml"/> + <xi:include href="../specs/specs_wxSashWindow.xml"/> + <xi:include href="../specs/specs_wxScreenDC.xml"/> + <xi:include href="../specs/specs_wxScrollBar.xml"/> + <xi:include href="../specs/specs_wxScrolledWindow.xml"/> + <xi:include href="../specs/specs_wxScrollEvent.xml"/> + <xi:include href="../specs/specs_wxScrollWinEvent.xml"/> + <xi:include href="../specs/specs_wxSetCursorEvent.xml"/> + <xi:include href="../specs/specs_wxShowEvent.xml"/> + <xi:include href="../specs/specs_wxSingleChoiceDialog.xml"/> + <xi:include href="../specs/specs_wxSizeEvent.xml"/> + <xi:include href="../specs/specs_wxSizer.xml"/> + <xi:include href="../specs/specs_wxSizerFlags.xml"/> + <xi:include href="../specs/specs_wxSizerItem.xml"/> + <xi:include href="../specs/specs_wxSlider.xml"/> + <xi:include href="../specs/specs_wxSpinButton.xml"/> + <xi:include href="../specs/specs_wxSpinCtrl.xml"/> + <xi:include href="../specs/specs_wxSpinEvent.xml"/> + <xi:include href="../specs/specs_wxSplashScreen.xml"/> + <xi:include href="../specs/specs_wxSplitterEvent.xml"/> + <xi:include href="../specs/specs_wxSplitterWindow.xml"/> + <xi:include href="../specs/specs_wxStaticBitmap.xml"/> + <xi:include href="../specs/specs_wxStaticBox.xml"/> + <xi:include href="../specs/specs_wxStaticBoxSizer.xml"/> + <xi:include href="../specs/specs_wxStaticLine.xml"/> + <xi:include href="../specs/specs_wxStaticText.xml"/> + <xi:include href="../specs/specs_wxStatusBar.xml"/> + <xi:include href="../specs/specs_wxStdDialogButtonSizer.xml"/> + <xi:include href="../specs/specs_wxStyledTextCtrl.xml"/> + <xi:include href="../specs/specs_wxStyledTextEvent.xml"/> + <xi:include href="../specs/specs_wxSysColourChangedEvent.xml"/> + <xi:include href="../specs/specs_wxSystemOptions.xml"/> + <xi:include href="../specs/specs_wxSystemSettings.xml"/> + <xi:include href="../specs/specs_wxTaskBarIcon.xml"/> + <xi:include href="../specs/specs_wxTaskBarIconEvent.xml"/> + <xi:include href="../specs/specs_wxTextAttr.xml"/> + <xi:include href="../specs/specs_wxTextCtrl.xml"/> + <xi:include href="../specs/specs_wxTextDataObject.xml"/> + <xi:include href="../specs/specs_wxTextEntryDialog.xml"/> + <xi:include href="../specs/specs_wxToggleButton.xml"/> + <xi:include href="../specs/specs_wxToolBar.xml"/> + <xi:include href="../specs/specs_wxToolbook.xml"/> + <xi:include href="../specs/specs_wxToolTip.xml"/> + <xi:include href="../specs/specs_wxTopLevelWindow.xml"/> + <xi:include href="../specs/specs_wxTreebook.xml"/> + <xi:include href="../specs/specs_wxTreeCtrl.xml"/> + <xi:include href="../specs/specs_wxTreeEvent.xml"/> + <xi:include href="../specs/specs_wxUpdateUIEvent.xml"/> + <xi:include href="../specs/specs_wxWindowCreateEvent.xml"/> + <xi:include href="../specs/specs_wxWindowDC.xml"/> + <xi:include href="../specs/specs_wxWindowDestroyEvent.xml"/> + <xi:include href="../specs/specs_wxWindow.xml"/> + <xi:include href="../specs/specs_wxXmlResource.xml"/> + <xi:include href="../specs/specs_wx_misc.xml"/> + <xi:include href="../specs/specs_glu.xml"/> + <xi:include href="../specs/specs_gl.xml"/> +</specs> diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl index af7cca7ed2..30baeebb18 100644 --- a/lib/wx/include/wx.hrl +++ b/lib/wx/include/wx.hrl @@ -33,94 +33,51 @@ %% Here comes the definitions of all event records. %% they contain the event type and possible some extra information. --record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent} --type wxInitDialogEventType() :: 'init_dialog'. --type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent} - --record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent} --type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'. --type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent} - --record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent} - position :: integer(), - key :: integer(), - modifiers :: integer(), - modificationType :: integer(), - text :: unicode:chardata(), - length :: integer(), - linesAdded :: integer(), - line :: integer(), - foldLevelNow :: integer(), - foldLevelPrev :: integer(), - margin :: integer(), - message :: integer(), - wParam :: integer(), - lParam :: integer(), - listType :: integer(), - x :: integer(), - y :: integer(), - dragText :: unicode:chardata(), - dragAllowMove :: boolean(), - dragResult :: wx:wx_enum()}). --type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'. --type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent} - --record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent} - path :: unicode:chardata()}). --type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'. --type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent} - --record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent} - nSel :: integer(), - nOldSel :: integer()}). --type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'. --type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent} +-record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent} + win :: wxWindow:wxWindow()}). +-type wxFocusEventType() :: 'set_focus' | 'kill_focus'. +-type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent} --record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent} --type wxIdleEventType() :: 'idle'. --type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent} +-record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent} + cmdString :: unicode:chardata(), + commandInt :: integer(), + extraLong :: integer()}). +-type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'. +-type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent} -record(wxColourPicker,{type :: wxColourPickerEventType(), %% Callback event: {@link wxColourPickerEvent} colour :: wx:wx_colour()}). -type wxColourPickerEventType() :: 'command_colourpicker_changed'. -type wxColourPicker() :: #wxColourPicker{}. %% Callback event: {@link wxColourPickerEvent} --record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent} --type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'. --type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent} - --record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent} - edge :: wx:wx_enum(), - dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()}, - dragStatus :: wx:wx_enum()}). --type wxSashEventType() :: 'sash_dragged'. --type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent} - -record(wxHelp, {type :: wxHelpEventType()}). %% Callback event: {@link wxHelpEvent} -type wxHelpEventType() :: 'help' | 'detailed_help'. -type wxHelp() :: #wxHelp{}. %% Callback event: {@link wxHelpEvent} --record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent} --type wxDisplayChangedEventType() :: 'display_changed'. --type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent} - --record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent} --type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'. --type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent} +-record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent} + commandInt :: integer()}). +-type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'. +-type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent} --record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent} - font :: wxFont:wxFont()}). --type wxFontPickerEventType() :: 'command_fontpicker_changed'. --type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent} +-record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent} + manager :: wxAuiManager:wxAuiManager(), + pane :: wxAuiPaneInfo:wxAuiPaneInfo(), + button :: integer(), + veto_flag :: boolean(), + canveto_flag :: boolean(), + dc :: wxDC:wxDC()}). +-type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'. +-type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent} --record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent} - win :: wxWindow:wxWindow()}). --type wxFocusEventType() :: 'set_focus' | 'kill_focus'. --type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent} +-record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent} + date :: wx:wx_datetime()}). +-type wxDateEventType() :: 'date_changed'. +-type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent} --record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent} --type wxPaletteChangedEventType() :: 'palette_changed'. --type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent} +-record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent} + iconized :: boolean()}). +-type wxIconizeEventType() :: 'iconize'. +-type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent} -record(wxScroll,{type :: wxScrollEventType(), %% Callback event: {@link wxScrollEvent} commandInt :: integer(), @@ -128,38 +85,41 @@ -type wxScrollEventType() :: 'scroll_top' | 'scroll_bottom' | 'scroll_lineup' | 'scroll_linedown' | 'scroll_pageup' | 'scroll_pagedown' | 'scroll_thumbtrack' | 'scroll_thumbrelease' | 'scroll_changed'. -type wxScroll() :: #wxScroll{}. %% Callback event: {@link wxScrollEvent} --record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent} --type wxChildFocusEventType() :: 'child_focus'. --type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent} +-record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent} +-type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'. +-type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent} --record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent} - old_selection :: integer(), - selection :: integer(), - drag_source :: wxAuiNotebook:wxAuiNotebook()}). --type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'. --type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent} +-record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent} +-type wxPaletteChangedEventType() :: 'palette_changed'. +-type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent} --record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent} - size :: {W::integer(), H::integer()}, - rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}). --type wxSizeEventType() :: 'size'. --type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent} +-record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent} + nSel :: integer(), + nOldSel :: integer()}). +-type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'. +-type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent} --record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent} - cmdString :: unicode:chardata(), - commandInt :: integer(), - extraLong :: integer()}). --type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'. --type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent} +-record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent} + pos :: {X::integer(), Y::integer()}}). +-type wxContextMenuEventType() :: 'context_menu'. +-type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent} --record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent} --type wxMaximizeEventType() :: 'maximize'. --type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent} +-record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent} + font :: wxFont:wxFont()}). +-type wxFontPickerEventType() :: 'command_fontpicker_changed'. +-type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent} --record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent} - commandInt :: integer()}). --type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'. --type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent} +-record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent} +-type wxChildFocusEventType() :: 'child_focus'. +-type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent} + +-record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent} +-type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'. +-type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent} + +-record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent} +-type wxWindowDestroyEventType() :: 'destroy'. +-type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent} -record(wxMenu,{type :: wxMenuEventType(), %% Callback event: {@link wxMenuEvent} menuId :: integer(), @@ -167,30 +127,63 @@ -type wxMenuEventType() :: 'menu_open' | 'menu_close' | 'menu_highlight'. -type wxMenu() :: #wxMenu{}. %% Callback event: {@link wxMenuEvent} +-record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent} + active :: boolean()}). +-type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'. +-type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent} + +-record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent} + row :: integer(), + col :: integer(), + x :: integer(), + y :: integer(), + selecting :: boolean(), + control :: boolean(), + meta :: boolean(), + shift :: boolean(), + alt :: boolean()}). +-type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'. +-type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent} + +-record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent} +-type wxPaintEventType() :: 'paint'. +-type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent} + -record(wxShow,{type :: wxShowEventType(), %% Callback event: {@link wxShowEvent} show :: boolean()}). -type wxShowEventType() :: 'show'. -type wxShow() :: #wxShow{}. %% Callback event: {@link wxShowEvent} --record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent} --type wxWindowDestroyEventType() :: 'destroy'. --type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent} - --record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent} - pos :: {X::integer(), Y::integer()}}). --type wxContextMenuEventType() :: 'context_menu'. --type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent} - --record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent} - active :: boolean()}). --type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'. --type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent} +-record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent} + position :: integer(), + key :: integer(), + modifiers :: integer(), + modificationType :: integer(), + text :: unicode:chardata(), + length :: integer(), + linesAdded :: integer(), + line :: integer(), + foldLevelNow :: integer(), + foldLevelPrev :: integer(), + margin :: integer(), + message :: integer(), + wParam :: integer(), + lParam :: integer(), + listType :: integer(), + x :: integer(), + y :: integer(), + dragText :: unicode:chardata(), + dragAllowMove :: boolean(), + dragResult :: wx:wx_enum()}). +-type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'. +-type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent} --record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent} - pos :: {X::integer(), Y::integer()}, - rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}). --type wxMoveEventType() :: 'move'. --type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent} +-record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent} + old_selection :: integer(), + selection :: integer(), + drag_source :: wxAuiNotebook:wxAuiNotebook()}). +-type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'. +-type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent} -record(wxList,{type :: wxListEventType(), %% Callback event: {@link wxListEvent} code :: integer(), @@ -201,9 +194,9 @@ -type wxListEventType() :: 'command_list_begin_drag' | 'command_list_begin_rdrag' | 'command_list_begin_label_edit' | 'command_list_end_label_edit' | 'command_list_delete_item' | 'command_list_delete_all_items' | 'command_list_key_down' | 'command_list_insert_item' | 'command_list_col_click' | 'command_list_col_right_click' | 'command_list_col_begin_drag' | 'command_list_col_dragging' | 'command_list_col_end_drag' | 'command_list_item_selected' | 'command_list_item_deselected' | 'command_list_item_right_click' | 'command_list_item_middle_click' | 'command_list_item_activated' | 'command_list_item_focused' | 'command_list_cache_hint'. -type wxList() :: #wxList{}. %% Callback event: {@link wxListEvent} --record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent} --type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'. --type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent} +-record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent} +-type wxUpdateUIEventType() :: 'update_ui'. +-type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent} -record(wxScrollWin,{type :: wxScrollWinEventType(), %% Callback event: {@link wxScrollWinEvent} commandInt :: integer(), @@ -211,92 +204,81 @@ -type wxScrollWinEventType() :: 'scrollwin_top' | 'scrollwin_bottom' | 'scrollwin_lineup' | 'scrollwin_linedown' | 'scrollwin_pageup' | 'scrollwin_pagedown' | 'scrollwin_thumbtrack' | 'scrollwin_thumbrelease'. -type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent} --record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent} - iconized :: boolean()}). --type wxIconizeEventType() :: 'iconize'. --type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent} +-record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent} + pos :: {X::integer(), Y::integer()}, + zPosition :: integer(), + buttonChange :: integer(), + buttonState :: integer(), + joyStick :: integer()}). +-type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'. +-type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent} --record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent} --type wxUpdateUIEventType() :: 'update_ui'. --type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent} +-record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent} +-type wxWindowCreateEventType() :: 'create'. +-type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent} --record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent} +-record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent} +-type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'. +-type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent} + +-record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent} x :: integer(), y :: integer(), - leftDown :: boolean(), - middleDown :: boolean(), - rightDown :: boolean(), + keyCode :: integer(), controlDown :: boolean(), shiftDown :: boolean(), altDown :: boolean(), metaDown :: boolean(), - wheelRotation :: integer(), - wheelDelta :: integer(), - linesPerAction :: integer()}). --type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'. --type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent} - --record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent} - item :: integer(), - itemOld :: integer(), - pointDrag :: {X::integer(), Y::integer()}}). --type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'. --type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent} - --record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent} --type wxSysColourChangedEventType() :: 'sys_colour_changed'. --type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent} + scanCode :: boolean(), + uniChar :: integer(), + rawCode :: integer(), + rawFlags :: integer()}). +-type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'. +-type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent} --record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent} - flags :: integer(), - focus :: wxWindow:wxWindow()}). --type wxNavigationKeyEventType() :: 'navigation_key'. --type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent} +-record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent} +-type wxIdleEventType() :: 'idle'. +-type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent} -record(wxQueryNewPalette, {type :: wxQueryNewPaletteEventType()}). %% Callback event: {@link wxQueryNewPaletteEvent} -type wxQueryNewPaletteEventType() :: 'query_new_palette'. -type wxQueryNewPalette() :: #wxQueryNewPalette{}. %% Callback event: {@link wxQueryNewPaletteEvent} --record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent} --type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'. --type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent} - -record(wxHtmlLink,{type :: wxHtmlLinkEventType(), %% Callback event: {@link wxHtmlLinkEvent} linkInfo :: wx:wx_wxHtmlLinkInfo()}). -type wxHtmlLinkEventType() :: 'command_html_link_clicked'. -type wxHtmlLink() :: #wxHtmlLink{}. %% Callback event: {@link wxHtmlLinkEvent} --record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent} - x :: integer(), - y :: integer(), - keyCode :: integer(), - controlDown :: boolean(), - shiftDown :: boolean(), - altDown :: boolean(), - metaDown :: boolean(), - scanCode :: boolean(), - uniChar :: integer(), - rawCode :: integer(), - rawFlags :: integer()}). --type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'. --type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent} +-record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent} +-type wxInitDialogEventType() :: 'init_dialog'. +-type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent} --record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent} --type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'. --type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent} +-record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent} +-type wxMaximizeEventType() :: 'maximize'. +-type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent} --record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent} - row :: integer(), - col :: integer(), - x :: integer(), - y :: integer(), - selecting :: boolean(), - control :: boolean(), - meta :: boolean(), - shift :: boolean(), - alt :: boolean()}). --type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'. --type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent} +-record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent} +-type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'. +-type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent} + +-record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent} + item :: integer(), + itemOld :: integer(), + pointDrag :: {X::integer(), Y::integer()}}). +-type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'. +-type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent} + +-record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent} + dc :: wxDC:wxDC()}). +-type wxEraseEventType() :: 'erase_background'. +-type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent} + +-record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent} + edge :: wx:wx_enum(), + dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()}, + dragStatus :: wx:wx_enum()}). +-type wxSashEventType() :: 'sash_dragged'. +-type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent} -record(wxCalendar,{type :: wxCalendarEventType(), %% Callback event: {@link wxCalendarEvent} wday :: wx:wx_enum(), @@ -304,42 +286,52 @@ -type wxCalendarEventType() :: 'calendar_sel_changed' | 'calendar_day_changed' | 'calendar_month_changed' | 'calendar_year_changed' | 'calendar_doubleclicked' | 'calendar_weekday_clicked'. -type wxCalendar() :: #wxCalendar{}. %% Callback event: {@link wxCalendarEvent} --record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent} --type wxWindowCreateEventType() :: 'create'. --type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent} +-record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent} + x :: integer(), + y :: integer(), + leftDown :: boolean(), + middleDown :: boolean(), + rightDown :: boolean(), + controlDown :: boolean(), + shiftDown :: boolean(), + altDown :: boolean(), + metaDown :: boolean(), + wheelRotation :: integer(), + wheelDelta :: integer(), + linesPerAction :: integer()}). +-type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'. +-type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent} --record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent} - date :: wx:wx_datetime()}). --type wxDateEventType() :: 'date_changed'. --type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent} +-record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent} + size :: {W::integer(), H::integer()}, + rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}). +-type wxSizeEventType() :: 'size'. +-type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent} --record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent} - manager :: wxAuiManager:wxAuiManager(), - pane :: wxAuiPaneInfo:wxAuiPaneInfo(), - button :: integer(), - veto_flag :: boolean(), - canveto_flag :: boolean(), - dc :: wxDC:wxDC()}). --type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'. --type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent} +-record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent} +-type wxSysColourChangedEventType() :: 'sys_colour_changed'. +-type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent} --record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent} +-record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent} +-type wxDisplayChangedEventType() :: 'display_changed'. +-type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent} + +-record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent} pos :: {X::integer(), Y::integer()}, - zPosition :: integer(), - buttonChange :: integer(), - buttonState :: integer(), - joyStick :: integer()}). --type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'. --type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent} + rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}). +-type wxMoveEventType() :: 'move'. +-type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent} --record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent} --type wxPaintEventType() :: 'paint'. --type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent} +-record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent} + flags :: integer(), + focus :: wxWindow:wxWindow()}). +-type wxNavigationKeyEventType() :: 'navigation_key'. +-type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent} --record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent} - dc :: wxDC:wxDC()}). --type wxEraseEventType() :: 'erase_background'. --type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent} +-record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent} + path :: unicode:chardata()}). +-type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'. +-type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent} -record(wxSetCursor,{type :: wxSetCursorEventType(), %% Callback event: {@link wxSetCursorEvent} x :: integer(), @@ -348,6 +340,14 @@ -type wxSetCursorEventType() :: 'set_cursor'. -type wxSetCursor() :: #wxSetCursor{}. %% Callback event: {@link wxSetCursorEvent} +-record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent} +-type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'. +-type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent} + +-record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent} +-type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'. +-type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent} + -type event() :: wxActivate() | wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClipboardText() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxInitDialog() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMouseCaptureLost() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy(). -type wxEventType() :: wxActivateEventType() | wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxClipboardTextEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxInitDialogEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseCaptureLostEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType(). diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl index d1a063d900..851686062a 100644 --- a/lib/wx/src/gen/wxListCtrl.erl +++ b/lib/wx/src/gen/wxListCtrl.erl @@ -94,31 +94,34 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}). -type wxListCtrl() :: wx:wx_object(). -%% @spec () -> wxListCtrl() %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>. +-spec new() -> wxListCtrl(). new() -> wxe_util:construct(?wxListCtrl_new_0, <<>>). -%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl() -%% @equiv new(Parent, []) +-spec new(Parent) -> wxListCtrl() when + Parent::wxWindow:wxWindow(). new(Parent) when is_record(Parent, wx_ref) -> new(Parent, []). -%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl() -%% Option = {winid, integer()} | -%% {pos, {X::integer(),Y::integer()}} | -%% {size, {W::integer(),H::integer()}} | -%% {style, integer()} | -%% {validator, wx:wx()} | -%% {onGetItemText, OnGetItemText} | -%% {onGetItemAttr, OnGetItemAttr} | -%% {onGetItemColumnImage, OnGetItemColumnImage} +%% @doc Creates a listctrl with optional callback functions: %% -%% OnGetItemText = (This, Item, Column) -> wxString() -%% OnGetItemAttr = (This, Item) -> wxListItemAttr() +%% OnGetItemText = (This, Item, Column) -> unicode:charlist() +%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr() %% OnGetItemColumnImage = (This, Item, Column) -> integer() -%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>. +%% +%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>. +-spec new(Parent, [Option]) -> wxListCtrl() when + Parent::wxWindow:wxWindow(), + Option::{winid, integer()} | + {pos, {X::integer(),Y::integer()}} | + {size, {W::integer(),H::integer()}} | + {style, integer()} | + {validator, wx:wx_object()} | + {onGetItemText, function()} | + {onGetItemAttr, function()} | + {onGetItemColumnImage, function()}. new(#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options)-> @@ -185,26 +188,27 @@ clearAll(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). -%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool() %% @equiv create(This,Parent, []) +-spec create(This, Parent) -> wxListCtrl() when + This::wxWindow:wxWindow(), + Parent::wxWindow:wxWindow(). create(This,Parent) when is_record(This, wx_ref),is_record(Parent, wx_ref) -> create(This,Parent, []). -%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool() -%% Option = {winid, integer()} | -%% {pos, {X::integer(),Y::integer()}} | -%% {size, {W::integer(),H::integer()}} | -%% {style, integer()} | -%% {validator, wx:wx()} | -%% {onGetItemText, OnGetItemText} | -%% {onGetItemAttr, OnGetItemAttr} | -%% {onGetItemColumnImage, OnGetItemColumnImage} -%% -%% OnGetItemText = (This, Item, Column) -> wxString() -%% OnGetItemAttr = (This, Item) -> wxListItemAttr() -%% OnGetItemColumnImage = (This, Item, Column) -> integer() %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>. +-spec create(This, Parent, [Option]) -> wxListCtrl() when + This::wxWindow:wxWindow(), + Parent::wxWindow:wxWindow(), + Option::{winid, integer()} | + {pos, {X::integer(),Y::integer()}} | + {size, {W::integer(),H::integer()}} | + {style, integer()} | + {validator, wx:wx_object()} | + {onGetItemText, function()} | + {onGetItemAttr, function()} | + {onGetItemColumnImage, function()}. + create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options) -> ?CLASS(ThisT,wxListCtrl), diff --git a/lib/wx/src/gen/wxXmlResource.erl b/lib/wx/src/gen/wxXmlResource.erl index ae02c74751..aa65b8b04e 100644 --- a/lib/wx/src/gen/wxXmlResource.erl +++ b/lib/wx/src/gen/wxXmlResource.erl @@ -334,8 +334,6 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename) <<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>). -%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject() - %% @doc Looks up a control with Name in a window created with XML %% resources. You can use it to set/get values from controls. %% The object is type casted to <b>Type</b>. @@ -345,6 +343,10 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename) %% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br /> %% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br /> %% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br /> +-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when + Window::wxWindow:wxWindow(), + Name::string(), + Type::atom(). xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) -> %% Func Id ?wxXmlResource_xrcctrl diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl index 40ee308358..40170b6eb1 100644 --- a/lib/wx/src/wx_object.erl +++ b/lib/wx/src/wx_object.erl @@ -55,7 +55,7 @@ %% When stop is returned in one of the functions above with Reason = %% normal | shutdown | Term, terminate(State) is called. It lets the %% user module clean up, it is always called when server terminates or -%% when wxObject() in the driver is deleted. If the Parent process +%% when wx_object() in the driver is deleted. If the Parent process %% terminates the Module:terminate/2 function is called. <br/> %% terminate(Reason, State) %% @@ -171,58 +171,59 @@ %% ----------------------------------------------------------------- -%% @spec (Mod, Args, Options) -> wxWindow:wxWindow() -%% Mod = atom() -%% Args = term() -%% Options = [{timeout, Timeout} | {debug, [Flag]}] -%% Flag = trace | log | {logfile, File} | statistics | debug %% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the %% new process. +-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when + Mod::atom(), + Args::term(), + Flag::trace | log | {logfile, string()} | statistics | debug, + Options::[{timeout, timeout()} | {debug, [Flag]}]. start(Mod, Args, Options) -> gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])). - -%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow() -%% Name = {local, atom()} -%% Mod = atom() -%% Args = term() -%% Options = [{timeout, Timeout} | {debug, [Flag]}] -%% Flag = trace | log | {logfile, File} | statistics | debug + %% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the %% new process. +-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when + Name::{local, atom()}, + Mod::atom(), + Args::term(), + Flag::trace | log | {logfile, string()} | statistics | debug, + Options::[{timeout, timeout()} | {debug, [Flag]}]. start(Name, Mod, Args, Options) -> gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])). -%% @spec (Mod, Args, Options) -> wxWindow:wxWindow() -%% Mod = atom() -%% Args = term() -%% Options = [{timeout, Timeout} | {debug, [Flag]}] -%% Flag = trace | log | {logfile, File} | statistics | debug %% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the %% new process. +-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when + Mod::atom(), + Args::term(), + Flag::trace | log | {logfile, string()} | statistics | debug, + Options::[{timeout, timeout()} | {debug, [Flag]}]. start_link(Mod, Args, Options) -> gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])). -%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow() -%% Name = {local, atom()} -%% Mod = atom() -%% Args = term() -%% Options = [{timeout, Timeout} | {debug, [Flag]}] -%% Flag = trace | log | {logfile, File} | statistics | debug %% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the %% new process. +-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when + Name::{local, atom()}, + Mod::atom(), + Args::term(), + Flag::trace | log | {logfile, string()} | statistics | debug, + Options::[{timeout, timeout()} | {debug, [Flag]}]. start_link(Name, Mod, Args, Options) -> gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])). - + gen_response({ok, Pid}) -> receive {ack, Pid, Ref = #wx_ref{}} -> Ref end; gen_response(Reply) -> Reply. -%% @spec (Ref::wxObject()|atom()|pid()) -> ok %% @doc Stops a generic wx_object server with reason 'normal'. %% Invokes terminate(Reason,State) in the server. The call waits until %% the process is terminated. If the process does not exist, an %% exception is raised. +-spec stop(Obj) -> ok when + Obj::wx:wx_object()|atom()|pid(). stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) -> try gen:stop(Pid) @@ -236,11 +237,14 @@ stop(Name) when is_atom(Name) orelse is_pid(Name) -> erlang:error({ExitReason, {?MODULE, stop, [Name]}}) end. -%% @spec (Ref::wxObject()|atom()|pid(), Reason::term(), Timeout::timeout()) -> ok %% @doc Stops a generic wx_object server with the given Reason. %% Invokes terminate(Reason,State) in the server. The call waits until %% the process is terminated. If the call times out, or if the process %% does not exist, an exception is raised. +-spec stop(Obj, Reason, Timeout) -> ok when + Obj::wx:wx_object()|atom()|pid(), + Reason::term(), + Timeout::timeout(). stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) -> try gen:stop(Pid, Reason, Timeout) @@ -254,12 +258,14 @@ stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) -> erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}}) end. -%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> term() %% @doc Make a call to a wx_object server. %% The call waits until it gets a result. %% Invokes handle_call(Request, From, State) in the server +-spec call(Obj, Request) -> term() when + Obj::wx:wx_object()|atom()|pid(), + Request::term(). call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) -> - try + try {ok,Res} = gen:call(Pid, '$gen_call', Request, infinity), Res catch _:Reason -> @@ -272,10 +278,13 @@ call(Name, Request) when is_atom(Name) orelse is_pid(Name) -> catch _:Reason -> erlang:error({Reason, {?MODULE, call, [Name, Request]}}) end. - -%% @spec (Ref::wxObject()|atom()|pid(), Request::term(), Timeout::integer()) -> term() + %% @doc Make a call to a wx_object server with a timeout. %% Invokes handle_call(Request, From, State) in server +-spec call(Obj, Request, Timeout) -> term() when + Obj::wx:wx_object()|atom()|pid(), + Request::term(), + Timeout::integer(). call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) -> try {ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout), @@ -291,10 +300,11 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) -> erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}}) end. -%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> ok %% @doc Make a cast to a wx_object server. %% Invokes handle_cast(Request, State) in the server - +-spec cast(Obj, Request) -> ok when + Obj::wx:wx_object()|atom()|pid(), + Request::term(). cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) -> Pid ! {'$gen_cast',Request}, ok; @@ -302,21 +312,23 @@ cast(Name, Request) when is_atom(Name) orelse is_pid(Name) -> Name ! {'$gen_cast',Request}, ok. -%% @spec (Ref::wxObject()) -> pid() %% @doc Get the pid of the object handle. +-spec get_pid(Obj) -> pid() when + Obj::wx:wx_object()|atom()|pid(). get_pid(#wx_ref{state=Pid}) when is_pid(Pid) -> Pid. -%% @spec (Ref::wxObject(), pid()) -> wxObject() %% @doc Sets the controlling process of the object handle. +-spec set_pid(Obj, pid()) -> wx:wx_object() when + Obj::wx:wx_object()|atom()|pid(). set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) -> R#wx_ref{state=Pid}. %% ----------------------------------------------------------------- %% Send a reply to the client. %% ----------------------------------------------------------------- -%% @spec (From::tuple(), Reply::term()) -> pid() %% @doc Get the pid of the object handle. +-spec reply({pid(), Tag::term()}, Reply::term()) -> pid(). reply({To, Tag}, Reply) -> catch To ! {Tag, Reply}. diff --git a/lib/xmerl/src/xmerl_eventp.erl b/lib/xmerl/src/xmerl_eventp.erl index 2cb76abc6e..8d7ea25e24 100644 --- a/lib/xmerl/src/xmerl_eventp.erl +++ b/lib/xmerl/src/xmerl_eventp.erl @@ -25,6 +25,90 @@ %% Each contain more elaborate settings of xmerl_scan that makes usage of %% the customization functions. %% +%% @type xmlElement() = #xmlElement{}. +%% +%% @type option_list(). <p>Options allow to customize the behaviour of the +%% scanner. +%% See also <a href="xmerl_examples.html">tutorial</a> on customization +%% functions. +%% </p> +%% <p> +%% Possible options are: +%% </p> +%% <dl> +%% <dt><code>{acc_fun, Fun}</code></dt> +%% <dd>Call back function to accumulate contents of entity.</dd> +%% <dt><code>{continuation_fun, Fun} | +%% {continuation_fun, Fun, ContinuationState}</code></dt> +%% <dd>Call back function to decide what to do if the scanner runs into EOF +%% before the document is complete.</dd> +%% <dt><code>{event_fun, Fun} | +%% {event_fun, Fun, EventState}</code></dt> +%% <dd>Call back function to handle scanner events.</dd> +%% <dt><code>{fetch_fun, Fun} | +%% {fetch_fun, Fun, FetchState}</code></dt> +%% <dd>Call back function to fetch an external resource.</dd> +%% <dt><code>{hook_fun, Fun} | +%% {hook_fun, Fun, HookState}</code></dt> +%% <dd>Call back function to process the document entities once +%% identified.</dd> +%% <dt><code>{close_fun, Fun}</code></dt> +%% <dd>Called when document has been completely parsed.</dd> +%% <dt><code>{rules, ReadFun, WriteFun, RulesState} | +%% {rules, Rules}</code></dt> +%% <dd>Handles storing of scanner information when parsing.</dd> +%% <dt><code>{user_state, UserState}</code></dt> +%% <dd>Global state variable accessible from all customization functions</dd> +%% +%% <dt><code>{fetch_path, PathList}</code></dt> +%% <dd>PathList is a list of +%% directories to search when fetching files. If the file in question +%% is not in the fetch_path, the URI will be used as a file +%% name.</dd> +%% <dt><code>{space, Flag}</code></dt> +%% <dd>'preserve' (default) to preserve spaces, 'normalize' to +%% accumulate consecutive whitespace and replace it with one space.</dd> +%% <dt><code>{line, Line}</code></dt> +%% <dd>To specify starting line for scanning in document which contains +%% fragments of XML.</dd> +%% <dt><code>{namespace_conformant, Flag}</code></dt> +%% <dd>Controls whether to behave as a namespace conformant XML parser, +%% 'false' (default) to not otherwise 'true'.</dd> +%% <dt><code>{validation, Flag}</code></dt> +%% <dd>Controls whether to process as a validating XML parser: +%% 'off' (default) no validation, or validation 'dtd' by DTD or 'schema' +%% by XML Schema. 'false' and 'true' options are obsolete +%% (i.e. they may be removed in a future release), if used 'false' +%% equals 'off' and 'true' equals 'dtd'.</dd> +%% <dt><code>{schemaLocation, [{Namespace,Link}|...]}</code></dt> +%% <dd>Tells explicitly which XML Schema documents to use to validate +%% the XML document. Used together with the +%% <code>{validation,schema}</code> option.</dd> +%% <dt><code>{quiet, Flag}</code></dt> +%% <dd>Set to 'true' if xmerl should behave quietly and not output any +%% information to standard output (default 'false').</dd> +%% <dt><code>{doctype_DTD, DTD}</code></dt> +%% <dd>Allows to specify DTD name when it isn't available in the XML +%% document. This option has effect only together with +%% <code>{validation,'dtd'</code> option.</dd> +%% <dt><code>{xmlbase, Dir}</code></dt> +%% <dd>XML Base directory. If using string/1 default is current directory. +%% If using file/1 default is directory of given file.</dd> +%% <dt><code>{encoding, Enc}</code></dt> +%% <dd>Set default character set used (default UTF-8). +%% This character set is used only if not explicitly given by the XML +%% declaration. </dd> +%% <dt><code>{document, Flag}</code></dt> +%% <dd>Set to 'true' if xmerl should return a complete XML document +%% as an xmlDocument record (default 'false').</dd> +%% <dt><code>{comments, Flag}</code></dt> +%% <dd>Set to 'false' if xmerl should skip comments otherwise they will +%% be returned as xmlComment records (default 'true').</dd> +%% <dt><code>{default_attrs, Flag}</code></dt> +%% <dd>Set to 'true' if xmerl should add to elements missing attributes +%% with a defined default value (default 'false').</dd> +%% </dl> +%% -module(xmerl_eventp). -vsn('0.19'). -date('03-09-17'). diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl index 2147a46a13..5e0459ec21 100644 --- a/lib/xmerl/src/xmerl_scan.erl +++ b/lib/xmerl/src/xmerl_scan.erl @@ -111,13 +111,16 @@ %% <dd>Set to 'true' if xmerl should add to elements missing attributes %% with a defined default value (default 'false').</dd> %% </dl> +%% @type xmlElement() = #xmlElement{}. +%% The record definition is found in xmerl.hrl. +%% @type xmlDocument() = #xmlDocument{}. +%% The record definition is found in xmerl.hrl. %% @type document() = xmlElement() | xmlDocument(). <p> %% The document returned by <tt>xmerl_scan:string/[1,2]</tt> and %% <tt>xmerl_scan:file/[1,2]</tt>. The type of the returned record depends on %% the value of the document option passed to the function. %% </p> - -module(xmerl_scan). -vsn('0.20'). -date('03-09-16'). diff --git a/lib/xmerl/src/xmerl_xpath.erl b/lib/xmerl/src/xmerl_xpath.erl index bbebda1030..6146feba49 100644 --- a/lib/xmerl/src/xmerl_xpath.erl +++ b/lib/xmerl/src/xmerl_xpath.erl @@ -43,13 +43,27 @@ %% </pre> %% %% @type nodeEntity() = -%% xmlElement() -%% | xmlAttribute() -%% | xmlText() -%% | xmlPI() -%% | xmlComment() -%% | xmlNsNode() -%% | xmlDocument() +%% #xmlElement{} +%% | #xmlAttribute{} +%% | #xmlText{} +%% | #xmlPI{} +%% | #xmlComment{} +%% | #xmlNsNode{} +%% | #xmlDocument{} +%% +%% @type docNodes() = #xmlElement{} +%% | #xmlAttribute{} +%% | #xmlText{} +%% | #xmlPI{} +%% | #xmlComment{} +%% | #xmlNsNode{} +%% +%% @type docEntity() = #xmlDocument{} | [docNodes()] +%% +%% @type xPathString() = string() +%% +%% @type parentList() = [{atom(), integer()}] +%% %% @type option_list(). <p>Options allows to customize the behaviour of the %% XPath scanner. %% </p> @@ -115,7 +129,7 @@ string(Str, Doc, Options) -> %% Parents = parentList() %% Doc = nodeEntity() %% Options = option_list() -%% Scalar = xmlObj +%% Scalar = #xmlObj{} %% @doc Extracts the nodes from the parsed XML tree according to XPath. %% xmlObj is a record with fields type and value, %% where type is boolean | number | string diff --git a/lib/xmerl/src/xmerl_xs.erl b/lib/xmerl/src/xmerl_xs.erl index 3e9f6622b8..1ce76cfa41 100644 --- a/lib/xmerl/src/xmerl_xs.erl +++ b/lib/xmerl/src/xmerl_xs.erl @@ -45,7 +45,6 @@ % XSLT package which is written i C++. % See also the <a href="xmerl_xs_examples.html">Tutorial</a>. % </p> - -module(xmerl_xs). -export([xslapply/2, value_of/1, select/2, built_in_rules/2 ]). @@ -71,15 +70,13 @@ %% xslapply(fun template/1, E), %% "</h1>"]; %% </pre> - xslapply(Fun, EList) when is_list(EList) -> - lists:map( Fun, EList); + lists:map(Fun, EList); xslapply(Fun, E = #xmlElement{})-> lists:map( Fun, E#xmlElement.content). - %% @spec value_of(E) -> List -%% E = unknown() +%% E = term() %% %% @doc Concatenates all text nodes within the tree. %% diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl index 4b5efae8dd..a89b3159ec 100644 --- a/lib/xmerl/src/xmerl_xsd.erl +++ b/lib/xmerl/src/xmerl_xsd.erl @@ -49,6 +49,7 @@ %% <dd>It is possible by this option to provide a state with process %% information from an earlier validation.</dd> %% </dl> +%% @type filename() = string() %% @end %%%------------------------------------------------------------------- -module(xmerl_xsd). @@ -138,7 +139,7 @@ state2file(S=#xsd_state{schema_name=SN}) -> %% @spec state2file(State,FileName) -> ok | {error,Reason} %% State = global_state() -%% FileName = filename() +%% FileName = string() %% @doc Saves the schema state with all information of the processed %% schema in a file. You can provide the file name for the saved %% state. FileName is saved with the <code>.xss</code> extension @@ -153,7 +154,7 @@ state2file(S,FileName) when is_record(S,xsd_state) -> %% @spec file2state(FileName) -> {ok,State} | {error,Reason} %% State = global_state() -%% FileName = filename() +%% FileName = string() %% @doc Reads the schema state with all information of the processed %% schema from a file created with <code>state2file/[1,2]</code>. The %% format of this file is internal. The state can then be used @@ -202,7 +203,7 @@ xmerl_xsd_vsn_check(S=#xsd_state{vsn=MD5_VSN}) -> process_validate(Schema,Xml) -> process_validate(Schema,Xml,[]). %% @spec process_validate(Schema,Element,Options) -> Result -%% Schema = filename() +%% Schema = string() %% Element = XmlElement %% Options = option_list() %% Result = {ValidXmlElement,State} | {error,Reason} @@ -282,7 +283,7 @@ validate3(_,_,S) -> process_schema(Schema) -> process_schema(Schema,[]). %% @spec process_schema(Schema,Options) -> Result -%% Schema = filename() +%% Schema = string() %% Result = {ok,State} | {error,Reason} %% State = global_state() %% Reason = [ErrorReason] | ErrorReason @@ -324,7 +325,7 @@ process_schema2({SE,_},State,_Schema) -> process_schemas(Schemas) -> process_schemas(Schemas,[]). %% @spec process_schemas(Schemas,Options) -> Result -%% Schemas = [{NameSpace,filename()}|Schemas] | [] +%% Schemas = [{NameSpace,string()}|Schemas] | [] %% Result = {ok,State} | {error,Reason} %% Reason = [ErrorReason] | ErrorReason %% Options = option_list() @@ -5426,7 +5427,7 @@ add_key_once(Key,N,El,L) -> %% {filename:join([[io_lib:format("/~w(~w)",[X,Y])||{X,Y}<-Parents],Type]),Pos}. %% @spec format_error(Errors) -> Result -%% Errors = error_tuple() | [error_tuple()] +%% Errors = tuple() | [tuple()] %% Result = string() | [string()] %% @doc Formats error descriptions to human readable strings. format_error(L) when is_list(L) -> diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index a78a035a1f..95adaa5bb0 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3.11 +XMERL_VSN = 1.3.12 diff --git a/make/emd2exml.in b/make/emd2exml.in index 903d707716..b4e052fef5 100644..100755 --- a/make/emd2exml.in +++ b/make/emd2exml.in @@ -1,4 +1,4 @@ -#!/usr/bin/env escript +#!@ENV@ escript %% -*- erlang -*- %%! -smp disable diff --git a/make/make_emakefile b/make/make_emakefile.in index 56440d9bf0..fbca77887a 100755 --- a/make/make_emakefile +++ b/make/make_emakefile.in @@ -1,4 +1,4 @@ -#!/usr/bin/env perl +#!@PERL@ # -*- cperl -*- use strict; diff --git a/make/otp.mk.in b/make/otp.mk.in index c05c499d66..7e2945b561 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -4,7 +4,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -260,6 +260,7 @@ DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif XSLTPROC = @XSLTPROC@ FOP = @FOP@ XMLLINT = @XMLLINT@ +CP = @CP@ DOCGEN=$(ERL_TOP)/lib/erl_docgen FOP_CONFIG = $(DOCGEN)/priv/fop.xconf @@ -271,6 +272,8 @@ SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript # Extract specifications and types from Erlang source files (-spec, -type) $(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $< +$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/gen/%.erl + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $< $(MAN1DIR)/%.1: %.xml diff --git a/otp_versions.table b/otp_versions.table index bfaa17a394..9d7d0a78bb 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,4 +1,13 @@ +OTP-19.0.5 : kernel-5.0.2 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0.3 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 : +OTP-19.0.4 : erts-8.0.3 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.1 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 : +OTP-19.0.3 : inets-6.3.2 kernel-5.0.1 ssl-8.0.1 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0.2 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 : +OTP-19.0.2 : compiler-7.0.1 erts-8.0.2 stdlib-3.0.1 # asn1-4.0.3 common_test-1.12.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 : +OTP-19.0.1 : dialyzer-3.0.1 erts-8.0.1 inets-6.3.1 observer-2.2.1 ssh-4.3.1 tools-2.8.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 typer-0.9.11 wx-1.7 xmerl-1.3.11 : OTP-19.0 : asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 tools-2.8.4 typer-0.9.11 wx-1.7 xmerl-1.3.11 # : +OTP-18.3.4.4 : erts-7.3.1.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : +OTP-18.3.4.3 : ssh-4.2.2.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : +OTP-18.3.4.2 : common_test-1.12.1.1 erts-7.3.1.1 ssl-7.3.3.1 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : +OTP-18.3.4.1 : ssh-4.2.2.1 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : OTP-18.3.4 : inets-6.2.4 ssl-7.3.3 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : OTP-18.3.3 : common_test-1.12.1 inets-6.2.3 ssl-7.3.2 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : OTP-18.3.2 : inets-6.2.2 ssl-7.3.1 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : diff --git a/system/README b/system/README deleted file mode 100644 index 97ec9177c4..0000000000 --- a/system/README +++ /dev/null @@ -1,78 +0,0 @@ -Erlang/OTP December 6, 2013 - - -Release of Erlang 5.10.4/OTP R16B03 - - -1. GENERAL - ------- - -1.1 Installation Guide - - The installation guide can be found in - - <inst-root>doc/installation_guide/users_guide.html - -1.2 Java - - The Java sources were compiled using Java version 1.5. - -1.3 Disk space - - An installation of Erlang/OTP needs approximately 300 MB of - disk space. - -1.4 The package contains HTML documentation. You can also get this - documentation preformatted for printing in PDF format from - - http://www.erlang.se/doc/ - - This site also contains HTML documentation for older releases - as a reference. - -1.5 The Erlang system can run old BEAM files compiled with R11B-2 - or later (and almost all BEAM files compiled with R11B-0 or - R11B-1). BEAM files from R10B or earlier are not supported. - - To get the best performance, you should recompile your - application code with the R15B compiler. - - -2. NOTES ABOUT THE SOLARIS VERSION - ------------------------------- - -2.1 For the Sparc Solaris environment, Solaris10 (2.10) and above is - supported. The emulator runs on older Solaris 8 (2.8) versions - and above. Older Solaris versions are neither tested nor supported. - Also an Ultrasparc (sun4u architecture) is required. - - -3. NOTES ABOUT THE VXWORKS VERSION - ------------------------------- - -3.1 The platform VxWorks is discontinued in the sense that only the - libraries (erl_interface and ic's libraries) are supported. The VxWorks - release is still packaged as a full release, but no support will be - available for anything but the communication libraries. - -4 NOTES ABOUT THE LINUX VERSIONS - ----------------------------- - -4.1 The following linux distributions/version combinations are supported - and tested: - - SuSE 10.1 x86, SuSE 10.1 x86_64, SuSE 11.0 x86, SuSE 11.0 x86_64 - -5. APPLICATIONS NOTES - ------------------ - - -6. MORE INFORMATION - ---------------- - - Commercial customers please visit http://www.erlang.se to find - releases and more information. Commercial support is given through - - Open source users please visit http://www.erlang.org and direct - problems to the open source community through the mailing list. diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index b63327291d..57e47431b8 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -33,7 +33,7 @@ <p> This section is to be read with the <seealso marker="stdlib:gen_statem"><c>gen_statem(3)</c></seealso> - manual page in <c>STDLIB</c>, where all interface functions and callback + manual page in STDLIB, where all interface functions and callback functions are described in detail. </p> <note> @@ -52,7 +52,7 @@ <section> <title>Event-Driven State Machines</title> <p> - Established Automata theory does not deal much with + Established Automata Theory does not deal much with how a state transition is triggered, but assumes that the output is a function of the input (and the state) and that they are @@ -226,11 +226,10 @@ handle_event(EventType, EventContent, State, Data) -> -module(code_lock). -behaviour(gen_statem). -define(NAME, code_lock). --define(CALLBACK_MODE, state_functions). -export([start_link/1]). -export([button/1]). --export([init/1,terminate/3,code_change/4]). +-export([init/1,callback_mode/0,terminate/3,code_change/4]). -export([locked/3,open/3]). start_link(Code) -> @@ -242,7 +241,10 @@ button(Digit) -> init(Code) -> do_lock(), Data = #{code => Code, remaining => Code}, - {?CALLBACK_MODE,locked,Data}. + {ok,locked,Data}. + +callback_mode() -> + state_functions. locked( cast, {button,Digit}, @@ -273,7 +275,7 @@ terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. code_change(_Vsn, State, Data, _Extra) -> - {?CALLBACK_MODE,State,Data}. + {ok,State,Data}. ]]></code> <p>The code is explained in the next sections.</p> </section> @@ -308,7 +310,7 @@ start_link(Code) -> as <c>{global,Name}</c>, then the <c>gen_statem</c> is registered using <seealso marker="kernel:global#register_name/2"><c>global:register_name/2</c></seealso> - in <c>Kernel</c>. + in Kernel. </p> </item> <item> @@ -343,14 +345,8 @@ start_link(Code) -> <p> If name registration succeeds, the new <c>gen_statem</c> process calls callback function <c>code_lock:init(Code)</c>. - This function is expected to return <c>{CallbackMode,State,Data}</c>, - where - <seealso marker="#callback_modes"><c>CallbackMode</c></seealso> - selects callback module state function mode, in this case - <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso> - through macro <c>?CALLBACK_MODE</c>. That is, each state - has got its own handler function. - <c>State</c> is the initial state of the <c>gen_statem</c>, + This function is expected to return <c>{ok,State,Data}</c>, + where <c>State</c> is the initial state of the <c>gen_statem</c>, in this case <c>locked</c>; assuming that the door is locked to begin with. <c>Data</c> is the internal server data of the <c>gen_statem</c>. Here the server data is a <seealso marker="stdlib:maps">map</seealso> @@ -359,11 +355,12 @@ start_link(Code) -> that stores the remaining correct button sequence (the same as the <c>code</c> to begin with). </p> + <code type="erl"><![CDATA[ init(Code) -> do_lock(), Data = #{code => Code, remaining => Code}, - {?CALLBACK_MODE,locked,Data}. + {ok,locked,Data}. ]]></code> <p>Function <seealso marker="stdlib:gen_statem#start_link/3"><c>gen_statem:start_link</c></seealso> @@ -380,6 +377,21 @@ init(Code) -> can be used to start a standalone <c>gen_statem</c>, that is, a <c>gen_statem</c> that is not part of a supervision tree. </p> + + <code type="erl"><![CDATA[ +callback_mode() -> + state_functions. + ]]></code> + <p> + Function + <seealso marker="stdlib:gen_statem#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso> + selects the + <seealso marker="#callback_modes"><c>CallbackMode</c></seealso> + for the callback module, in this case + <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>. + That is, each state has got its own handler function. + </p> + </section> <!-- =================================================================== --> @@ -533,12 +545,11 @@ handle_event({call,From}, code_length, #{code := Code} = Data) -> </p> <code type="erl"><![CDATA[ ... --define(CALLBACK_MODE, handle_event_function). - -... -export([handle_event/4]). ... +callback_mode() -> + handle_event_function. handle_event(cast, {button,Digit}, State, #{code := Code} = Data) -> case State of @@ -597,7 +608,7 @@ init(Args) -> callback function <c>terminate(shutdown, State, Data)</c>. </p> <p> - In the following example, function <c>terminate/3</c> + In this example, function <c>terminate/3</c> locks the door if it is open, so we do not accidentally leave the door open when the supervision tree terminates: </p> @@ -755,7 +766,7 @@ stop() -> Suppose that we do not want a button to lock the door, instead we want to ignore button events in the <c>open</c> state. Then we start a timer when entering the <c>open</c> state - and waits for it to expire while ignoring button events: + and wait for it to expire while ignoring button events: </p> <code type="erl"><![CDATA[ ... @@ -962,16 +973,13 @@ do_unlock() -> </p> <code type="erl"><![CDATA[ ... --define(CALLBACK_MODE, state_functions). - -... - init(Code) -> process_flag(trap_exit, true), Data = #{code => Code}, - enter(?CALLBACK_MODE, locked, Data). + enter(ok, locked, Data). -... +callback_mode() -> + state_functions. locked(internal, enter, _Data) -> do_lock(), @@ -1027,11 +1035,10 @@ enter(Tag, State, Data) -> -module(code_lock). -behaviour(gen_statem). -define(NAME, code_lock_2). --define(CALLBACK_MODE, state_functions). -export([start_link/1,stop/0]). -export([button/1,code_length/0]). --export([init/1,terminate/3,code_change/4]). +-export([init/1,callback_mode/0,terminate/3,code_change/4]). -export([locked/3,open/3]). start_link(Code) -> @@ -1047,7 +1054,10 @@ code_length() -> init(Code) -> process_flag(trap_exit, true), Data = #{code => Code}, - enter(?CALLBACK_MODE, locked, Data). + enter(ok, locked, Data). + +callback_mode() -> + state_functions. locked(internal, enter, #{code := Code} = Data) -> do_lock(), @@ -1091,7 +1101,7 @@ terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. code_change(_Vsn, State, Data, _Extra) -> - {?CALLBACK_MODE,State,Data}. + {ok,State,Data}. ]]></code> </section> @@ -1106,12 +1116,11 @@ code_change(_Vsn, State, Data, _Extra) -> </p> <code type="erl"><![CDATA[ ... --define(CALLBACK_MODE, handle_event_function). - -... -export([handle_event/4]). ... +callback_mode() -> + handle_event_function. %% State: locked handle_event(internal, enter, locked, #{code := Code} = Data) -> @@ -1208,7 +1217,8 @@ format_status(Opt, [_PDict,State,Data]) -> <seealso marker="stdlib:gen_statem#Module:format_status/2"><c>Module:format_status/2</c></seealso> function. If you do not, a default implementation is used that does the same as this example function without filtering - the <c>Data</c> term, that is, <c>StateData = {State,Data}</c>. + the <c>Data</c> term, that is, <c>StateData = {State,Data}</c>, + in this example containing sensitive information. </p> </section> @@ -1273,11 +1283,10 @@ format_status(Opt, [_PDict,State,Data]) -> -module(code_lock). -behaviour(gen_statem). -define(NAME, code_lock_3). --define(CALLBACK_MODE, handle_event_function). -export([start_link/2,stop/0]). -export([button/1,code_length/0,set_lock_button/1]). --export([init/1,terminate/3,code_change/4,format_status/2]). +-export([init/1,callback_mode/0,terminate/3,code_change/4,format_status/2]). -export([handle_event/4]). start_link(Code, LockButton) -> @@ -1296,7 +1305,10 @@ set_lock_button(LockButton) -> init({Code,LockButton}) -> process_flag(trap_exit, true), Data = #{code => Code, remaining => undefined, timer => undefined}, - enter(?CALLBACK_MODE, {locked,LockButton}, Data, []). + enter(ok, {locked,LockButton}, Data, []). + +callback_mode() -> + handle_event_function. handle_event( {call,From}, {set_lock_button,NewLockButton}, @@ -1366,7 +1378,7 @@ terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. code_change(_Vsn, State, Data, _Extra) -> - {?CALLBACK_MODE,State,Data}. + {ok,State,Data}. format_status(Opt, [_PDict,State,Data]) -> StateData = {State, diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml index 016302fe50..eee2648f34 100644 --- a/system/doc/efficiency_guide/advanced.xml +++ b/system/doc/efficiency_guide/advanced.xml @@ -156,7 +156,7 @@ <seealso marker="erts:erl#max_processes"><c>+P</c></seealso> command-line flag in the <seealso marker="erts:erl"><c>erl(1)</c></seealso> - manual page in <c>erts</c>.</cell> + manual page in ERTS.</cell> </row> <row> <cell>Known nodes</cell> @@ -236,7 +236,7 @@ <seealso marker="erts:erl#max_ports"><c>+Q</c></seealso> command-line flag in the <seealso marker="erts:erl"><c>erl(1)</c></seealso> manual page - in <c>erts</c>.</cell> + in ERTS.</cell> </row> <row> <cell><marker id="files_sockets"></marker>Open files and diff --git a/system/doc/efficiency_guide/profiling.xml b/system/doc/efficiency_guide/profiling.xml index 1f3d503170..bf50a03fa6 100644 --- a/system/doc/efficiency_guide/profiling.xml +++ b/system/doc/efficiency_guide/profiling.xml @@ -128,7 +128,7 @@ performance impact. Using <c>fprof</c> is just a matter of calling a few library functions, see the <seealso marker="tools:fprof">fprof</seealso> manual page in - <c>tools</c> .<c>fprof</c> was introduced in R8.</p> + Tools .<c>fprof</c> was introduced in R8.</p> </section> <section> @@ -138,7 +138,7 @@ and in which function calls this time has been spent. Time is shown as percentage of total time and absolute time. For more information, see the <seealso marker="tools:eprof">eprof</seealso> - manual page in <c>tools</c>.</p> + manual page in Tools.</p> </section> <section> @@ -152,7 +152,7 @@ optimization. Using <c>cover</c> is just a matter of calling a few library functions, see the <seealso marker="tools:cover">cover</seealso> manual page in - <c>tools</c>.</p> + Tools.</p> </section> <section> @@ -165,7 +165,7 @@ any modules to profile (compared with <c>cover</c>). For more information, see the <seealso marker="tools:cprof">cprof</seealso> manual page in - <c>tools</c>.</p> + Tools.</p> </section> <section> diff --git a/system/doc/embedded/embedded_nt.xml b/system/doc/embedded/embedded_nt.xml index a1a4b90f3c..8e05100585 100644 --- a/system/doc/embedded/embedded_nt.xml +++ b/system/doc/embedded/embedded_nt.xml @@ -62,7 +62,7 @@ <p>For Windows NT running on standard PCs with ISA and/or PCI bus, an extension card with a hardware watchdog can be installed.</p> <p>For more information, see the <c>heart(3)</c> manual page in - <c>kernel</c>.</p> + Kernel.</p> </section> </section> @@ -72,7 +72,7 @@ to install the Erlang process as a Windows system service. This service can start after Windows NT has booted.</p> <p>For more information, see the <c>erlsrv</c> manual page - in <c>erts</c>.</p> + in ERTS.</p> </section> </chapter> diff --git a/system/doc/embedded/embedded_solaris.xml b/system/doc/embedded/embedded_solaris.xml index f8febcc546..eaa334fb39 100644 --- a/system/doc/embedded/embedded_solaris.xml +++ b/system/doc/embedded/embedded_solaris.xml @@ -190,7 +190,7 @@ esac</pre> the onboard hardware watchdog can be activated, provided a VME bus driver is added to the operating system (see also Installation Problems).</p> - <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p> + <p>See also the <c>heart(3)</c> manual page in Kernel.</p> </section> <section> @@ -206,7 +206,7 @@ esac</pre> <pre> chown 0 /usr/sbin/reboot chmod 4755 /usr/sbin/reboot</pre> - <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p> + <p>See also the <c>heart(3)</c> manual page in Kernel.</p> </section> <section> @@ -413,8 +413,8 @@ chown root mod_syslog]]></code> <section> <title>Related Documents</title> <p>See the <c>os_mon(3)</c> application, - the <c>application(3)</c> manual page in <c>kernel</c>, - and the <c>erl(1)</c> manual page in <c>erts</c>.</p> + the <c>application(3)</c> manual page in Kernel, + and the <c>erl(1)</c> manual page in ERTS.</p> </section> </section> @@ -474,7 +474,7 @@ chown root mod_syslog]]></code> default, it must be called <c>start</c> and reside in <c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. Another start program can be used, by using configuration parameter - <c>start_prg</c> in application <c>sasl</c>.</p> + <c>start_prg</c> in application SASL.</p> <p>The start program must call <c>run_erl</c> as shown below. It must also take an optional parameter, which defaults to <c><![CDATA[<ERL_INSTALL_DIR>/releases/start_erl.data]]></c>.</p> @@ -484,7 +484,7 @@ chown root mod_syslog]]></code> <p>The <c><![CDATA[<RELDIR>]]></c> directory is where new release packets are installed, and where the release handler keeps information about releases. For more information, see the - <c>release_handler(3)</c> manual page in <c>sasl</c>.</p> + <c>release_handler(3)</c> manual page in SASL.</p> <p>The following script illustrates the default behaviour of the program:</p> <code type="none"><![CDATA[ @@ -624,7 +624,7 @@ export RELDIR exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $*</code> <p>If a diskless and/or read-only client node with the - <c>sasl</c> configuration parameter <c>static_emulator</c> set + SASL configuration parameter <c>static_emulator</c> set to <c>true</c> is about to start, the <c>-boot</c> and <c>-config</c> flags must be changed.</p> <p>As such a client cannot diff --git a/system/doc/embedded/starting.xml b/system/doc/embedded/starting.xml index 720383e8ac..11bf9b412a 100644 --- a/system/doc/embedded/starting.xml +++ b/system/doc/embedded/starting.xml @@ -69,7 +69,7 @@ default, it must be called <c>start</c> and reside in <c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. Another start program can be used, by using the configuration parameter <c>start_prg</c> in - the application <c>sasl</c>.</p> + application SASL.</p> <p>The start program must call <c>run_erl</c> as shown below. It must also take an optional parameter which defaults to <c><![CDATA[<ERL_INSTALL_DIR>/bin/start_erl.data]]></c>. @@ -80,8 +80,8 @@ </p> <p>The <c><![CDATA[<RELDIR>]]></c> directory is where new release packets are installed, and where the release handler keeps information - about releases. See <c>release_handler(3)</c> in the - application <c>sasl</c> for further information. + about releases. See <c>release_handler(3)</c> in + application SASL for further information. </p> <p>The following script illustrates the default behaviour of the program. @@ -228,7 +228,7 @@ export PROGNAME export RELDIR exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $* </code> - <p>If a diskless and/or read-only client node with the <c>sasl</c> + <p>If a diskless and/or read-only client node with the SASL configuration parameter <c>static_emulator</c> set to <c>true</c> is about to start the <c>-boot</c> and <c>-config</c> flags must be changed. As such a client can not read a new <c>start_erl.data</c> diff --git a/system/doc/oam/oam_intro.xml b/system/doc/oam/oam_intro.xml index cdcb6e3111..8b8d69e638 100644 --- a/system/doc/oam/oam_intro.xml +++ b/system/doc/oam/oam_intro.xml @@ -178,7 +178,7 @@ <section> <title>MIB Structure</title> <p>The top-level OTP MIB is called <c>OTP-REG</c> and it is - included in the <c>sasl</c> application. All other OTP MIBs + included in the SASL application. All other OTP MIBs import some objects from this MIB.</p> <p>Each MIB is contained in one application. The MIB text @@ -188,7 +188,7 @@ <c><![CDATA[include/<MIB>.hrl]]></c>, and the compiled MIBs are stored under <c><![CDATA[priv/mibs/<MIB>.bin]]></c>. For example, the <c>OTP-MIB</c> is included in the - <c>sasl</c> application:</p> + SASL application:</p> <code type="none"> sasl-1.3/mibs/OTP-MIB.mib @@ -211,11 +211,11 @@ snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code> <p>The following MIBs are defined in the OTP system:</p> <list type="bulleted"> - <item><p><c>OTP-REG)</c> (in <c>sasl</c>) contains the top-level + <item><p><c>OTP-REG)</c> (in SASL) contains the top-level OTP registration objects, used by all other MIBs.</p></item> - <item><p><c>OTP-TC</c> (in <c>sasl</c>) contains the general + <item><p><c>OTP-TC</c> (in SASL) contains the general Textual Conventions, which can be used by any other MIB.</p></item> - <item><p><c>OTP-MIB</c> (in <c>sasl</c>) contains objects for + <item><p><c>OTP-MIB</c> (in SASL) contains objects for instrumentation of the Erlang nodes, the Erlang machines, and the applications in the system.</p></item> <item><p><c>OTP-OS-MON-MIB</c> (in <c>oc_mon</c>) contains diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml index da346dd0b3..074aa636b4 100644 --- a/system/doc/programming_examples/records.xml +++ b/system/doc/programming_examples/records.xml @@ -93,7 +93,7 @@ person</pre> at compile time, not at runtime. For details on records in the shell, see the <seealso marker="stdlib:shell">shell(3)</seealso> - manual page in <c>stdlib</c>.</p> + manual page in STDLIB.</p> </section> <section> diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml index 355fd3cfef..1a3d19aed1 100644 --- a/system/doc/reference_manual/expressions.xml +++ b/system/doc/reference_manual/expressions.xml @@ -1541,7 +1541,16 @@ end</pre> <pre> 1> <input>[X*2 || X <- [1,2,3]].</input> [2,4,6]</pre> - <p>More examples are provoded in + <p>When there are no generators or bit string generators, a list comprehension + returns either a list with one element (the result of evaluating <c>Expr</c>) + if all filters are true or an empty list otherwise.</p> + <p><em>Example:</em></p> + <pre> +1> <input>[2 || is_integer(2)].</input> +[2] +2> <input>[x || is_integer(x)].</input> +[]</pre> + <p>More examples are provided in <seealso marker="doc/programming_examples:list_comprehensions"> Programming Examples.</seealso></p> diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index f17e5df277..1899efd5f3 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -47,7 +47,7 @@ <list type="bulleted"> <item>To document function interfaces</item> <item>To provide more information for bug detection tools, - such as <c>Dialyzer</c></item> + such as Dialyzer</item> <item>To be exploited by documentation tools, such as EDoc, for generating program documentation of various forms</item> </list> diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile index 7f9cec540b..116ec688fa 100644 --- a/system/doc/top/Makefile +++ b/system/doc/top/Makefile @@ -35,7 +35,7 @@ RELSYSDIR = "$(RELEASE_PATH)/doc" GIF_FILES = -INFO_FILES = ../../README ../../COPYRIGHT PR.template +INFO_FILES = ../../../README.md ../../COPYRIGHT PR.template TOPDOCDIR=. |