diff options
240 files changed, 8619 insertions, 6428 deletions
diff --git a/.gitignore b/.gitignore index 18a54c21ca..07b66c3e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -381,6 +381,7 @@ JAVADOC-GENERATED /system/doc/installation_guide/INSTALL.xml /system/doc/installation_guide/INSTALL-CROSS.xml /system/doc/installation_guide/INSTALL-WIN32.xml +/system/doc/installation_guide/OTP-PATCH-APPLY.xml /system/doc/installation_guide/MARKDOWN.xml # test_server diff --git a/HOWTO/OTP-PATCH-APPLY.md b/HOWTO/OTP-PATCH-APPLY.md new file mode 100644 index 0000000000..2aa31629ef --- /dev/null +++ b/HOWTO/OTP-PATCH-APPLY.md @@ -0,0 +1,144 @@ +Patching OTP Applications +========================= + +Introduction +------------ + +This document describes the process of patching an existing OTP +installation with one or more Erlang/OTP applications of newer versions +than already installed. The tool `otp_patch_apply` is available for this +specific purpose. It resides in the top directory of the Erlang/OTP +source tree. + +The `otp_patch_apply` tool utilizes the [runtime_dependencies][] tag in +the [application resource file][]. This information is used to determine +if the patch can be installed in the given Erlang/OTP installation +directory. + +Read more about the [version handling][] introduced in Erlang/OTP release +17, which also describes how to determine if an installation includes one +or more patched applications. + +If you want to apply patches of multiple OTP applications that resides +in different OTP versions, you have to apply these patches in multiple +steps. It is only possible to apply multiple OTP applications from the +same OTP version at once. + +Prerequisites +------------- + +It's assumed that the reader is familiar with +[building and installing Erlang/OTP][]. To be able to patch an +application, the following must exist: + +* An Erlang/OTP installation. + +* An Erlang/OTP source tree containing the updated applications that + you want to patch into the existing Erlang/OTP installation. + +Using otp\_patch\_apply +----------------------- + +> *WARNING*: Patching applications is a one-way process. +> Create a backup of your OTP installation directory before +> proceeding. + +First of all, build the OTP source tree at `$ERL_TOP` containing +the updated applications. + +> *NOTE*: Before applying a patch you need to do a *full* build +> of OTP in the source directory. + +If you are building in `git` you first need to generate the +`configure` scripts: + + $ ./otp_build autoconf + +Configure and build all applications in OTP: + + $ configure + $ make + +or + + $ ./otp_build configure + $ ./otp_build boot -a + +If you have installed documentation in the OTP installation, also +build the documentation: + + $ make docs + +After the successful build it's time to patch. The source tree directory, +the directory of the installation and the applications to patch are given +as arguments to `otp_patch_apply`. The dependencies of each application +are validated against the applications in the installation and the other +applications given as arguments. If a dependency error is detected, the +script will be aborted. + +The `otp_patch_apply` syntax: + + $ otp_patch_apply -s <Dir> -i <Dir> [-l <Dir>] [-c] [-f] [-h] \ + [-n] [-v] <App1> [... <AppN>] + + -s <Dir> -- OTP source directory that contains build results. + -i <Dir> -- OTP installation directory to patch. + -l <Dir> -- Alternative OTP source library directory path(s) + containing build results of OTP applications. + Multiple paths should be colon separated. + -c -- Cleanup (remove) old versions of applications + patched in the installation. + -f -- Force patch of application(s) even though + dependencies are not fulfilled (should only be + considered in a test environment). + -h -- Print help then exit. + -n -- Do not install documentation. + -v -- Print version then exit. + <AppX> -- Application to patch. + + Environment Variable: + ERL_LIBS -- Alternative OTP source library directory path(s) + containing build results of OTP applications. + Multiple paths should be colon separated. + +> *NOTE*: The complete build environment is required while running +> `otp_patch_apply`. + +> *NOTE*: All source directories identified by `-s` and `-l` should +> contain build results of OTP applications. + +For example, if the user wants to install patched versions of `mnesia` +and `ssl` built in `/home/me/git/otp` into the OTP installation +located in `/opt/erlang/my_otp` type + + $ otp_patch_apply -s /home/me/git/otp -i /opt/erlang/my_otp \ + mnesia ssl + +> *NOTE*: If the list of applications contains core applications, +> i.e `erts`, `kernel`, `stdlib` or `sasl`, the `Install` script in +> the patched Erlang/OTP installation must be rerun. + +The patched applications are appended to the list of installed +applications. Take a look at +`<InstallDir>/releases/OTP-REL/installed_application_versions`. + +Sanity check +------------ + +The application dependencies can be checked using the Erlang shell. +Application dependencies are verified among installed applications by +`otp_patch_apply`, but these are not necessarily those actually loaded. +By calling `system_information:sanity_check()` one can validate +dependencies among applications actually loaded. + + 1> system_information:sanity_check(). + ok + +Please take a look at the reference of [sanity_check()][] for more +information. + +[application resource file]: kernel:app +[runtime_dependencies]: kernel:app#runtime_dependencies +[building and installing Erlang/OTP]: INSTALL.md +[version handling]: ../system_principles/versions +[sanity_check()]: runtime_tools:system_information#sanity_check-0 diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam Binary files differindex bcbf1f6c2a..eea00c7267 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 8790340543..74969880e0 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 6454719448..0fa7612114 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_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam Binary files differindex 93188416d6..dfeec25a9c 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_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam Binary files differindex 214fa56911..7a7856e247 100644 --- a/bootstrap/lib/compiler/ebin/beam_peep.beam +++ b/bootstrap/lib/compiler/ebin/beam_peep.beam diff --git a/bootstrap/lib/compiler/ebin/beam_receive.beam b/bootstrap/lib/compiler/ebin/beam_receive.beam Binary files differindex aec35c43eb..78c9eb1c39 100644 --- a/bootstrap/lib/compiler/ebin/beam_receive.beam +++ b/bootstrap/lib/compiler/ebin/beam_receive.beam diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam Binary files differindex 24a36e139d..a089535e59 100644 --- a/bootstrap/lib/compiler/ebin/cerl_inline.beam +++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam Binary files differindex f36cc31458..45f76f5c89 100644 --- a/bootstrap/lib/compiler/ebin/cerl_trees.beam +++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam Binary files differindex 5df7d4aa86..2dc2c16b8e 100644 --- a/bootstrap/lib/compiler/ebin/compile.beam +++ b/bootstrap/lib/compiler/ebin/compile.beam diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam Binary files differindex 6ea05bf28f..f0e746cbd0 100644 --- a/bootstrap/lib/compiler/ebin/core_parse.beam +++ b/bootstrap/lib/compiler/ebin/core_parse.beam diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam Binary files differindex 5b485a6a94..25930bc5f1 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/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam Binary files differindex 0ad8bc7ae4..f727e5c1b9 100644 --- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam +++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam Binary files differindex 2f39db14c7..e01c356f39 100644 --- a/bootstrap/lib/compiler/ebin/v3_codegen.beam +++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam Binary files differindex dff562a748..492b73e543 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 20612f3407..5f42d5acb6 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 0eb0cb6caf..3c977ceb0e 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.beam b/bootstrap/lib/kernel/ebin/application.beam Binary files differindex b8f7f156fe..b0b9e3d12c 100644 --- a/bootstrap/lib/kernel/ebin/application.beam +++ b/bootstrap/lib/kernel/ebin/application.beam diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam Binary files differindex 4ff90f7eff..d8018f0a40 100644 --- a/bootstrap/lib/kernel/ebin/application_master.beam +++ b/bootstrap/lib/kernel/ebin/application_master.beam diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam Binary files differindex 5428f2cf0c..93bf7ad032 100644 --- a/bootstrap/lib/kernel/ebin/code.beam +++ b/bootstrap/lib/kernel/ebin/code.beam diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam Binary files differindex aa75ae9bd1..7b0517c500 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 bc73a8e71c..3ccdfaea05 100644 --- a/bootstrap/lib/kernel/ebin/disk_log.beam +++ b/bootstrap/lib/kernel/ebin/disk_log.beam diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam Binary files differindex 25b155418c..7027118b3f 100644 --- a/bootstrap/lib/kernel/ebin/dist_ac.beam +++ b/bootstrap/lib/kernel/ebin/dist_ac.beam diff --git a/bootstrap/lib/kernel/ebin/erl_boot_server.beam b/bootstrap/lib/kernel/ebin/erl_boot_server.beam Binary files differindex 6e56a1061d..91899fe231 100644 --- a/bootstrap/lib/kernel/ebin/erl_boot_server.beam +++ b/bootstrap/lib/kernel/ebin/erl_boot_server.beam diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam Binary files differindex 18ba4449b8..32a3a27993 100644 --- a/bootstrap/lib/kernel/ebin/file.beam +++ b/bootstrap/lib/kernel/ebin/file.beam diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam Binary files differindex f85f130258..be820676ed 100644 --- a/bootstrap/lib/kernel/ebin/file_io_server.beam +++ b/bootstrap/lib/kernel/ebin/file_io_server.beam diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam Binary files differindex d11f60015a..85306ef123 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 5ec6322df2..0f72880f49 100644 --- a/bootstrap/lib/kernel/ebin/group.beam +++ b/bootstrap/lib/kernel/ebin/group.beam diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam Binary files differindex dac744c265..3e83eb0805 100644 --- a/bootstrap/lib/kernel/ebin/inet_db.beam +++ b/bootstrap/lib/kernel/ebin/inet_db.beam diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam Binary files differindex 01d3b08362..0c5b6c73e1 100644 --- a/bootstrap/lib/kernel/ebin/inet_dns.beam +++ b/bootstrap/lib/kernel/ebin/inet_dns.beam diff --git a/bootstrap/lib/kernel/ebin/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam Binary files differindex 6abf358157..dd43b18864 100644 --- a/bootstrap/lib/kernel/ebin/pg2.beam +++ b/bootstrap/lib/kernel/ebin/pg2.beam diff --git a/bootstrap/lib/stdlib/ebin/beam_lib.beam b/bootstrap/lib/stdlib/ebin/beam_lib.beam Binary files differindex b0f2c784da..463e571164 100644 --- a/bootstrap/lib/stdlib/ebin/beam_lib.beam +++ b/bootstrap/lib/stdlib/ebin/beam_lib.beam diff --git a/bootstrap/lib/stdlib/ebin/c.beam b/bootstrap/lib/stdlib/ebin/c.beam Binary files differindex 3fed93c41a..a08df44eb7 100644 --- a/bootstrap/lib/stdlib/ebin/c.beam +++ b/bootstrap/lib/stdlib/ebin/c.beam diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam Binary files differindex f5fac6e8a0..925b20069a 100644 --- a/bootstrap/lib/stdlib/ebin/dets.beam +++ b/bootstrap/lib/stdlib/ebin/dets.beam diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam Binary files differindex b4b243cd39..0c85b9a912 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_v9.beam b/bootstrap/lib/stdlib/ebin/dets_v9.beam Binary files differindex 360c95ee8f..9d543abc94 100644 --- a/bootstrap/lib/stdlib/ebin/dets_v9.beam +++ b/bootstrap/lib/stdlib/ebin/dets_v9.beam diff --git a/bootstrap/lib/stdlib/ebin/digraph_utils.beam b/bootstrap/lib/stdlib/ebin/digraph_utils.beam Binary files differindex fdc6741439..458c6d2895 100644 --- a/bootstrap/lib/stdlib/ebin/digraph_utils.beam +++ b/bootstrap/lib/stdlib/ebin/digraph_utils.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex 34af4c99f4..606666808d 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam Binary files differindex 6325142290..1ac419a727 100644 --- a/bootstrap/lib/stdlib/ebin/ets.beam +++ b/bootstrap/lib/stdlib/ebin/ets.beam diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam Binary files differindex 62062c6a56..4c3cc98c86 100644 --- a/bootstrap/lib/stdlib/ebin/file_sorter.beam +++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam Binary files differindex 31cc4a7309..59d18f9a86 100644 --- a/bootstrap/lib/stdlib/ebin/filename.beam +++ b/bootstrap/lib/stdlib/ebin/filename.beam diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam Binary files differindex 61259fd267..ccaabd8087 100644 --- a/bootstrap/lib/stdlib/ebin/gen.beam +++ b/bootstrap/lib/stdlib/ebin/gen.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam Binary files differindex d46e225182..d7d25774f4 100644 --- a/bootstrap/lib/stdlib/ebin/gen_event.beam +++ b/bootstrap/lib/stdlib/ebin/gen_event.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam Binary files differindex af31d1b283..aa08f18a97 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam Binary files differindex b284a99f70..f4483c908e 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam Binary files differindex 7265b0e865..eed02e5fa5 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam Binary files differindex abc4d0d33d..b679703b97 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 da6056d3c5..e9c359400b 100644 --- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam +++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam Binary files differindex f30fc894d1..9e140def2c 100644 --- a/bootstrap/lib/stdlib/ebin/re.beam +++ b/bootstrap/lib/stdlib/ebin/re.beam diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam Binary files differindex 37cb1626c2..c0450dab5b 100644 --- a/bootstrap/lib/stdlib/ebin/shell.beam +++ b/bootstrap/lib/stdlib/ebin/shell.beam diff --git a/bootstrap/lib/stdlib/ebin/timer.beam b/bootstrap/lib/stdlib/ebin/timer.beam Binary files differindex c12b0986df..38349d9a71 100644 --- a/bootstrap/lib/stdlib/ebin/timer.beam +++ b/bootstrap/lib/stdlib/ebin/timer.beam diff --git a/bootstrap/lib/stdlib/ebin/unicode.beam b/bootstrap/lib/stdlib/ebin/unicode.beam Binary files differindex 138a1ce28d..16557d5631 100644 --- a/bootstrap/lib/stdlib/ebin/unicode.beam +++ b/bootstrap/lib/stdlib/ebin/unicode.beam diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam Binary files differindex 33402a7c58..784a490ac9 100644 --- a/bootstrap/lib/stdlib/ebin/zip.beam +++ b/bootstrap/lib/stdlib/ebin/zip.beam diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 264a1726db..670907a41d 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -246,31 +246,31 @@ lbl1: return 1; lbl2: return 2; -],ac_cv_prog_emu_cc=$CC,ac_cv_prog_emu_cc=no) +],ac_cv_prog_emu_cc="$CC",ac_cv_prog_emu_cc=no) -if test $ac_cv_prog_emu_cc = no; then +if test "$ac_cv_prog_emu_cc" = no; then for ac_progname in emu_cc.sh gcc-4.2 gcc; do IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_progname; then - ac_cv_prog_emu_cc=$ac_dir/$ac_progname + if test -f "$ac_dir/$ac_progname"; then + ac_cv_prog_emu_cc="$ac_dir/$ac_progname" break fi done IFS="$ac_save_ifs" - if test $ac_cv_prog_emu_cc != no; then + if test "$ac_cv_prog_emu_cc" != no; then break fi done fi -if test $ac_cv_prog_emu_cc != no; then - save_CC=$CC +if test "$ac_cv_prog_emu_cc" != no; then + save_CC="$CC" save_CFLAGS=$CFLAGS save_CPPFLAGS=$CPPFLAGS - CC=$ac_cv_prog_emu_cc + CC="$ac_cv_prog_emu_cc" CFLAGS="" CPPFLAGS="" AC_TRY_COMPILE([],[ @@ -291,17 +291,17 @@ if test $ac_cv_prog_emu_cc != no; then return 1; lbl2: return 2; - ],ac_cv_prog_emu_cc=$CC,ac_cv_prog_emu_cc=no) + ],ac_cv_prog_emu_cc="$CC",ac_cv_prog_emu_cc=no) CC=$save_CC CFLAGS=$save_CFLAGS CPPFLAGS=$save_CPPFLAGS fi ]) -if test $ac_cv_prog_emu_cc = no; then +if test "$ac_cv_prog_emu_cc" = no; then AC_DEFINE(NO_JUMP_TABLE,[],[Defined if no found C compiler can handle jump tables]) - EMU_CC=$CC + EMU_CC="$CC" else - EMU_CC=$ac_cv_prog_emu_cc + EMU_CC="$ac_cv_prog_emu_cc" fi AC_SUBST(EMU_CC) ]) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 07b5fdd039..6daa4b68a8 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1357,7 +1357,7 @@ true <name name="get" arity="1"/> <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 + <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> <pre> diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 673b743e2e..1f10ddef6d 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -99,7 +99,7 @@ list_to_binary([Compressed|Last])</pre> <datatype> <name name="zwindowbits"/> <desc> - <p>Normally in the range <c>-15..-9 | 9..15</c>.</p> + <p>Normally in the range <c>-15..-8 | 8..15</c>.</p> </desc> </datatype> </datatypes> @@ -149,7 +149,7 @@ list_to_binary([Compressed|Last])</pre> 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 9 through 15. Larger values + 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> @@ -288,7 +288,7 @@ list_to_binary([B1,B2])</pre> <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 9 through 15. + 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> diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b89c8b3900..e847403882 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3582,7 +3582,7 @@ get_map_elements_fail: vbf = (BifFunction) Arg(0); PROCESS_MAIN_CHK_LOCKS(c_p); bif_nif_arity = I[-1]; - ASSERT(bif_nif_arity <= 3); + ASSERT(bif_nif_arity <= 4); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); reg[0] = r(0); { diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 7b69b39511..837cb017ac 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -30,10 +30,12 @@ extern Export* erts_format_cpu_topology_trap; #define BIF_ALIST_1 Process* A__p, Eterm* BIF__ARGS #define BIF_ALIST_2 Process* A__p, Eterm* BIF__ARGS #define BIF_ALIST_3 Process* A__p, Eterm* BIF__ARGS +#define BIF_ALIST_4 Process* A__p, Eterm* BIF__ARGS #define BIF_ARG_1 (BIF__ARGS[0]) #define BIF_ARG_2 (BIF__ARGS[1]) #define BIF_ARG_3 (BIF__ARGS[2]) +#define BIF_ARG_4 (BIF__ARGS[3]) #define ERTS_IS_PROC_OUT_OF_REDS(p) \ ((p)->fcalls > 0 \ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 1d0d214e77..788c866e63 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -613,6 +613,7 @@ bif erlang:fun_info_mfa/1 # bif erlang:get_keys/0 +bif ets:update_counter/4 # # Obsolete diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 4806befd99..fff892ae54 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -805,7 +805,7 @@ BIF_RETTYPE ets_update_element_3(BIF_ALIST_3) list = BIF_ARG_3; } - if (!tb->common.meth->db_lookup_dbterm(tb, BIF_ARG_2, &handle)) { + if (!tb->common.meth->db_lookup_dbterm(BIF_P, tb, BIF_ARG_2, THE_NON_VALUE, &handle)) { cret = DB_ERROR_BADKEY; goto bail_out; } @@ -844,7 +844,7 @@ BIF_RETTYPE ets_update_element_3(BIF_ALIST_3) } finalize: - tb->common.meth->db_finalize_dbterm(&handle); + tb->common.meth->db_finalize_dbterm(cret, &handle); bail_out: UnUseTmpHeap(2,BIF_P); @@ -863,14 +863,8 @@ bail_out: } } -/* -** update_counter(Tab, Key, Incr) -** update_counter(Tab, Key, {Upop}) -** update_counter(Tab, Key, [{Upop}]) -** Upop = {Pos,Incr} | {Pos,Incr,Threshold,WarpTo} -** Returns new value(s) (integer or [integer]) -*/ -BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3) +static BIF_RETTYPE +do_update_counter(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, Eterm arg4) { DbTable* tb; int cret = DB_ERROR_BADITEM; @@ -880,7 +874,7 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3) Eterm* ret_list_currp = NULL; Eterm* ret_list_prevp = NULL; Eterm iter; - DeclareTmpHeap(cell,5,BIF_P); + DeclareTmpHeap(cell, 5, p); Eterm *tuple = cell+2; DbUpdateHandle handle; Uint halloc_size = 0; /* overestimated heap usage */ @@ -888,28 +882,29 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3) Eterm* hstart; Eterm* hend; - if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, LCK_WRITE_REC)) == NULL) { - BIF_ERROR(BIF_P, BADARG); + if ((tb = db_get_table(p, arg1, DB_WRITE, LCK_WRITE_REC)) == NULL) { + BIF_ERROR(p, BADARG); } - UseTmpHeap(5,BIF_P); + UseTmpHeap(5, p); if (!(tb->common.status & (DB_SET | DB_ORDERED_SET))) { goto bail_out; } - if (is_integer(BIF_ARG_3)) { /* Incr */ - upop_list = CONS(cell, TUPLE2(tuple, make_small(tb->common.keypos+1), - BIF_ARG_3), NIL); + if (is_integer(arg3)) { /* Incr */ + upop_list = CONS(cell, + TUPLE2(tuple, make_small(tb->common.keypos+1), arg3), + NIL); } - else if (is_tuple(BIF_ARG_3)) { /* {Upop} */ - upop_list = CONS(cell, BIF_ARG_3, NIL); + else if (is_tuple(arg3)) { /* {Upop} */ + upop_list = CONS(cell, arg3, NIL); } else { /* [{Upop}] (probably) */ - upop_list = BIF_ARG_3; + upop_list = arg3; ret_list_prevp = &ret; } - if (!tb->common.meth->db_lookup_dbterm(tb, BIF_ARG_2, &handle)) { + if (!tb->common.meth->db_lookup_dbterm(p, tb, arg2, arg4, &handle)) { goto bail_out; /* key not found */ } @@ -982,13 +977,13 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3) if (ret_list_prevp) { /* Prepare to return a list */ ret = NIL; halloc_size += list_size; - hstart = HAlloc(BIF_P, halloc_size); + hstart = HAlloc(p, halloc_size); ret_list_currp = hstart; htop = hstart + list_size; hend = hstart + halloc_size; } else { - hstart = htop = HAlloc(BIF_P, halloc_size); + hstart = htop = HAlloc(p, halloc_size); } hend = hstart + halloc_size; @@ -1035,26 +1030,54 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3) (is_list(ret) && (list_val(ret)+list_size)==ret_list_currp)); ASSERT(htop <= hend); - HRelease(BIF_P,hend,htop); + HRelease(p, hend, htop); finalize: - tb->common.meth->db_finalize_dbterm(&handle); + tb->common.meth->db_finalize_dbterm(cret, &handle); bail_out: - UnUseTmpHeap(5,BIF_P); + UnUseTmpHeap(5, p); db_unlock(tb, LCK_WRITE_REC); switch (cret) { case DB_ERROR_NONE: BIF_RET(ret); case DB_ERROR_SYSRES: - BIF_ERROR(BIF_P, SYSTEM_LIMIT); + BIF_ERROR(p, SYSTEM_LIMIT); default: - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(p, BADARG); break; } } +/* +** update_counter(Tab, Key, Incr) +** update_counter(Tab, Key, Upop) +** update_counter(Tab, Key, [{Upop}]) +** Upop = {Pos,Incr} | {Pos,Incr,Threshold,WarpTo} +** Returns new value(s) (integer or [integer]) +*/ +BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3) +{ + return do_update_counter(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, THE_NON_VALUE); +} + +/* +** update_counter(Tab, Key, Incr, Default) +** update_counter(Tab, Key, Upop, Default) +** update_counter(Tab, Key, [{Upop}], Default) +** Upop = {Pos,Incr} | {Pos,Incr,Threshold,WarpTo} +** Returns new value(s) (integer or [integer]) +*/ +BIF_RETTYPE ets_update_counter_4(BIF_ALIST_4) +{ + if (is_not_tuple(BIF_ARG_4)) { + BIF_ERROR(BIF_P, BADARG); + } + return do_update_counter(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, BIF_ARG_4); +} + + /* ** The put BIF */ diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index c2157457a0..8668a87ba1 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -444,8 +444,11 @@ static int db_delete_all_objects_hash(Process* p, DbTable* tbl); #ifdef HARDDEBUG static void db_check_table_hash(DbTableHash *tb); #endif -static int db_lookup_dbterm_hash(DbTable *tbl, Eterm key, DbUpdateHandle* handle); -static void db_finalize_dbterm_hash(DbUpdateHandle* handle); +static int +db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, + DbUpdateHandle* handle); +static void +db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle); static ERTS_INLINE void try_shrink(DbTableHash* tb) { @@ -2796,59 +2799,129 @@ static HashDbTerm* next(DbTableHash *tb, Uint *iptr, erts_smp_rwmtx_t** lck_ptr, return NULL; } -static int db_lookup_dbterm_hash(DbTable *tbl, Eterm key, DbUpdateHandle* handle) +static int +db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, + DbUpdateHandle* handle) { DbTableHash *tb = &tbl->hash; - HashDbTerm* b; - HashDbTerm** prevp; - int ix; HashValue hval; + HashDbTerm **bp, *b; erts_smp_rwmtx_t* lck; + int flags = 0; + + ASSERT(tb->common.status & DB_SET); hval = MAKE_HASH(key); - lck = WLOCK_HASH(tb,hval); - ix = hash_to_ix(tb, hval); - prevp = &BUCKET(tb, ix); - b = *prevp; + lck = WLOCK_HASH(tb, hval); + bp = &BUCKET(tb, hash_to_ix(tb, hval)); + b = *bp; - while (b != 0) { - if (has_live_key(tb,b,key,hval)) { - handle->tb = tbl; - handle->bp = (void**) prevp; - handle->dbterm = &b->dbterm; - handle->mustResize = 0; - handle->new_size = b->dbterm.size; - #if HALFWORD_HEAP - handle->abs_vec = NULL; - #endif - handle->lck = lck; - /* KEEP hval WLOCKED, db_finalize_dbterm_hash will WUNLOCK */ - return 1; - } - prevp = &b->next; - b = *prevp; + for (;;) { + if (b == NULL) { + break; + } + if (has_key(tb, b, key, hval)) { + if (b->hvalue != INVALID_HASH) { + goto Ldone; + } + break; + } + bp = &b->next; + b = *bp; } - WUNLOCK_HASH(lck); - return 0; + + if (obj == THE_NON_VALUE) { + WUNLOCK_HASH(lck); + return 0; + } + + { + Eterm *objp = tuple_val(obj); + int arity = arityval(*objp); + Eterm *htop, *hend; + + ASSERT(arity >= tb->common.keypos); + htop = HAlloc(p, arity + 1); + hend = htop + arity + 1; + sys_memcpy(htop, objp, sizeof(Eterm) * (arity + 1)); + htop[tb->common.keypos] = key; + obj = make_tuple(htop); + + if (b == NULL) { + HashDbTerm *q = new_dbterm(tb, 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); + } + } + } else { + HashDbTerm *q, *next = b->next; + + ASSERT(b->hvalue == INVALID_HASH); + q = replace_dbterm(tb, b, obj); + q->next = next; + q->hvalue = hval; + *bp = b = q; + erts_smp_atomic_inc_nob(&tb->common.nitems); + } + + HRelease(p, hend, htop); + flags |= DB_NEW_OBJECT; + } + +Ldone: + handle->tb = tbl; + handle->bp = (void **)bp; + handle->dbterm = &b->dbterm; + handle->flags = flags; + handle->new_size = b->dbterm.size; +#if HALFWORD_HEAP + handle->abs_vec = NULL; +#endif + handle->lck = lck; + return 1; } /* Must be called after call to db_lookup_dbterm */ -static void db_finalize_dbterm_hash(DbUpdateHandle* handle) +static void +db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) { DbTable* tbl = handle->tb; - HashDbTerm* oldp = (HashDbTerm*) *(handle->bp); + DbTableHash *tb = &tbl->hash; + HashDbTerm **bp = (HashDbTerm **) handle->bp; + HashDbTerm *b = *bp; erts_smp_rwmtx_t* lck = (erts_smp_rwmtx_t*) handle->lck; - ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(&tbl->hash,lck)); /* locked by db_lookup_dbterm_hash */ + ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */ + + ASSERT((&b->dbterm == handle->dbterm) == !(tb->common.compress && handle->flags & DB_MUST_RESIZE)); - ASSERT((&oldp->dbterm == handle->dbterm) == !(tbl->common.compress && handle->mustResize)); + if (handle->flags & DB_NEW_OBJECT && cret != DB_ERROR_NONE) { + if (IS_FIXED(tb)) { + add_fixed_deletion(tb, hash_to_ix(tb, b->hvalue)); + b->hvalue = INVALID_HASH; + } else { + *bp = b->next; + free_term(tb, b); + } - if (handle->mustResize) { + 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(&tbl->hash, oldp); + free_term(tb, b); } else { WUNLOCK_HASH(lck); diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 720c0659c3..577da35b75 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -399,8 +399,11 @@ static int db_delete_all_objects_tree(Process* p, DbTable* tbl); #ifdef HARDDEBUG static void db_check_table_tree(DbTable *tbl); #endif -static int db_lookup_dbterm_tree(DbTable *, Eterm key, DbUpdateHandle*); -static void db_finalize_dbterm_tree(DbUpdateHandle*); +static int +db_lookup_dbterm_tree(Process *, DbTable *, Eterm key, Eterm obj, + DbUpdateHandle*); +static void +db_finalize_dbterm_tree(int cret, DbUpdateHandle *); /* ** Static variables @@ -2546,16 +2549,43 @@ static TreeDbTerm **find_node2(DbTableTree *tb, Eterm key) return this; } -static int db_lookup_dbterm_tree(DbTable *tbl, Eterm key, DbUpdateHandle* handle) +static int +db_lookup_dbterm_tree(Process *p, DbTable *tbl, Eterm key, Eterm obj, + DbUpdateHandle* handle) { DbTableTree *tb = &tbl->tree; TreeDbTerm **pp = find_node2(tb, key); - - if (pp == NULL) return 0; + int flags = 0; + + if (pp == NULL) { + if (obj == THE_NON_VALUE) { + return 0; + } else { + Eterm *objp = tuple_val(obj); + int arity = arityval(*objp); + Eterm *htop, *hend; + + ASSERT(arity >= tb->common.keypos); + htop = HAlloc(p, arity + 1); + hend = htop + arity + 1; + sys_memcpy(htop, objp, sizeof(Eterm) * (arity + 1)); + htop[tb->common.keypos] = key; + obj = make_tuple(htop); + + if (db_put_tree(tbl, obj, 1) != DB_ERROR_NONE) { + return 0; + } + + pp = find_node2(tb, key); + ASSERT(pp != NULL); + HRelease(p, hend, htop); + flags |= DB_NEW_OBJECT; + } + } handle->tb = tbl; handle->dbterm = &(*pp)->dbterm; - handle->mustResize = 0; + handle->flags = flags; handle->bp = (void**) pp; handle->new_size = (*pp)->dbterm.size; #if HALFWORD_HEAP @@ -2564,15 +2594,21 @@ static int db_lookup_dbterm_tree(DbTable *tbl, Eterm key, DbUpdateHandle* handle return 1; } -static void db_finalize_dbterm_tree(DbUpdateHandle* handle) +static void +db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle) { - if (handle->mustResize) { - TreeDbTerm* oldp = (TreeDbTerm*) *handle->bp; + DbTable *tbl = handle->tb; + DbTableTree *tb = &tbl->tree; + TreeDbTerm *bp = (TreeDbTerm *) *handle->bp; + if (handle->flags & DB_NEW_OBJECT && cret != DB_ERROR_NONE) { + Eterm ret; + db_erase_tree(tbl, GETKEY(tb, bp->dbterm.tpl), &ret); + } else if (handle->flags & DB_MUST_RESIZE) { db_finalize_resize(handle, offsetof(TreeDbTerm,dbterm)); - reset_static_stack(&handle->tb->tree); + reset_static_stack(tb); - free_term(&handle->tb->tree, oldp); + free_term(tb, bp); } #ifdef DEBUG handle->dbterm = 0; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 748be93fe3..60da35da56 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2710,10 +2710,10 @@ Wterm db_do_read_element(DbUpdateHandle* handle, Sint position) } ASSERT(((DbTableCommon*)handle->tb)->compress); - ASSERT(!handle->mustResize); + ASSERT(!(handle->flags & DB_MUST_RESIZE)); handle->dbterm = db_alloc_tmp_uncompressed(&handle->tb->common, handle->dbterm); - handle->mustResize = 1; + handle->flags |= DB_MUST_RESIZE; return handle->dbterm->tpl[position]; } @@ -2746,11 +2746,11 @@ void db_do_update_element(DbUpdateHandle* handle, #endif return; } - if (!handle->mustResize) { + if (!(handle->flags & DB_MUST_RESIZE)) { if (handle->tb->common.compress) { handle->dbterm = db_alloc_tmp_uncompressed(&handle->tb->common, handle->dbterm); - handle->mustResize = 1; + handle->flags |= DB_MUST_RESIZE; oldval = handle->dbterm->tpl[position]; #if HALFWORD_HEAP old_base = NULL; @@ -2810,7 +2810,7 @@ both_size_set: /* write new value in old dbterm, finalize will make a flat copy */ handle->dbterm->tpl[position] = newval; - handle->mustResize = 1; + handle->flags |= DB_MUST_RESIZE; #if HALFWORD_HEAP if (old_base && newval_sz > 0) { diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 5ace93c8ed..ca206c7f58 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -76,6 +76,9 @@ typedef struct db_term { union db_table; typedef union db_table DbTable; +#define DB_MUST_RESIZE 1 +#define DB_NEW_OBJECT 2 + /* Info about a database entry while it's being updated * (by update_counter or update_element) */ @@ -84,7 +87,7 @@ typedef struct { DbTerm* dbterm; void** bp; /* {Hash|Tree}DbTerm** */ Uint new_size; - int mustResize; + int flags; void* lck; #if HALFWORD_HEAP unsigned char* abs_vec; /* [i] true if dbterm->tpl[i] is absolute Eterm */ @@ -183,15 +186,14 @@ typedef struct db_table_method void *arg); void (*db_check_table)(DbTable* tb); - /* Lookup a dbterm for updating. Return false if not found. - */ - int (*db_lookup_dbterm)(DbTable*, Eterm key, - DbUpdateHandle* handle); /* [out] */ + /* Lookup a dbterm for updating. Return false if not found. */ + int (*db_lookup_dbterm)(Process *, DbTable *, Eterm key, Eterm obj, + DbUpdateHandle* handle); - /* Must be called for each db_lookup_dbterm that returned true, - ** even if dbterm was not updated. - */ - void (*db_finalize_dbterm)(DbUpdateHandle* handle); + /* Must be called for each db_lookup_dbterm that returned true, even if + ** dbterm was not updated. If the handle was of a new object and cret is + ** not DB_ERROR_NONE, the object is removed from the table. */ + void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle); } DbTableMethod; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index dc4c6fc350..9377237475 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4085,6 +4085,9 @@ erts_port_control(Process* c_p, size, &resp_bufp, &resp_size); + + control_flags = prt->control_flags; + finalize_imm_drv_call(&try_call_state); if (tmp_alloced) erts_free(ERTS_ALC_T_TMP, bufp); @@ -4092,8 +4095,6 @@ erts_port_control(Process* c_p, return ERTS_PORT_OP_BADARG; } - control_flags = prt->control_flags; - hsz = port_control_result_size(control_flags, resp_bufp, &resp_size, diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index b341c4d949..b8f58754b3 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -710,7 +710,7 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, ** If N < 0, Y = FUNNY_NUMBER4 else Y = FUNNY_NUMBER3. ** The hash value is Y*h(J) mod 2^32 where h(J) is calculated like ** h(0) = <initial hash> -** h(i) = h(i-i)*X + B(i-1) +** h(i) = h(i-1)*X + B(i-1) ** The above should hold regardless of internal representation. ** Pids are hashed like small numbers but with differrent constants, as are ** ports. diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index b62e9a0306..3b8e7acb6e 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -264,23 +264,6 @@ dt_private *get_dt_private(int); -#define GET_TIME(i, b) \ - (i).year = get_int32((b) + 0 * 4); \ - (i).month = get_int32((b) + 1 * 4); \ - (i).day = get_int32((b) + 2 * 4); \ - (i).hour = get_int32((b) + 3 * 4); \ - (i).minute = get_int32((b) + 4 * 4); \ - (i).second = get_int32((b) + 5 * 4) - -#define PUT_TIME(i, b) \ - put_int32((i).year, (b) + 0 * 4); \ - put_int32((i).month, (b) + 1 * 4); \ - put_int32((i).day, (b) + 2 * 4); \ - put_int32((i).hour, (b) + 3 * 4); \ - put_int32((i).minute,(b) + 4 * 4); \ - put_int32((i).second,(b) + 5 * 4) - - #if ALWAYS_READ_LINE_AHEAD #define DEFAULT_LINEBUF_SIZE 2048 #else diff --git a/erts/emulator/hipe/hipe_amd64_abi.txt b/erts/emulator/hipe/hipe_amd64_abi.txt index 8a34bfa67f..72aed13995 100644 --- a/erts/emulator/hipe/hipe_amd64_abi.txt +++ b/erts/emulator/hipe/hipe_amd64_abi.txt @@ -45,7 +45,7 @@ The first return value from a function is placed in %rax, the second (if any) is placed in %rdx. Notes: -- Currently, NR_ARG_REGS==0. +- Currently, NR_ARG_REGS == 4. - C BIFs expect P in C parameter register 1: %rdi. By making Erlang parameter registers 1-5 coincide with C parameter registers 2-6, our BIF wrappers can simply move P to %rdi without having to shift diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index b4b3c073ab..ca55d5bf3b 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -253,6 +253,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(%rsi,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(%rdx,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(%rcx,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(%rsi,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(%rdx,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(%rcx,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(%r8,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(%rsi,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(%rdx,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(%rcx,5,2)` */' @@ -277,6 +281,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' `#endif /* ASM */' diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 7a4bb30447..7d94aa05b3 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -51,9 +51,10 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -154,6 +155,43 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + TEXT + .align 4 + GLOBAL(ASYM($1)) +ASYM($1): + /* set up the parameters */ + movq P, %rdi + NBIF_ARG(%rsi,4,0) + NBIF_ARG(%rdx,4,1) + NBIF_ARG(%rcx,4,2) + NBIF_ARG(%r8,4,3) + + /* make the call on the C stack */ + SWITCH_ERLANG_TO_C + pushq %r8 + pushq %rcx + pushq %rdx + pushq %rsi + movq %rsp, %rsi /* Eterm* BIF__ARGS */ + sub $(8), %rsp /* stack frame 16-byte alignment */ + CALL_BIF($2) + add $(4*8 + 8), %rsp + TEST_GOT_MBUF + SWITCH_C_TO_ERLANG + + /* throw exception if failure, otherwise return */ + TEST_GOT_EXN + jz nbif_4_simple_exception + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 955f7362b4..3cb0a2875b 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -321,6 +321,7 @@ ASYM(nbif_fail): GLOBAL(nbif_1_gc_after_bif) GLOBAL(nbif_2_gc_after_bif) GLOBAL(nbif_3_gc_after_bif) + GLOBAL(nbif_4_gc_after_bif) .align 4 nbif_0_gc_after_bif: xorl %edx, %edx @@ -336,6 +337,10 @@ nbif_2_gc_after_bif: .align 4 nbif_3_gc_after_bif: movl $3, %edx + jmp .gc_after_bif + .align 4 +nbif_4_gc_after_bif: + movl $4, %edx /*FALLTHROUGH*/ .align 4 .gc_after_bif: @@ -359,6 +364,7 @@ nbif_3_gc_after_bif: GLOBAL(nbif_1_simple_exception) GLOBAL(nbif_2_simple_exception) GLOBAL(nbif_3_simple_exception) + GLOBAL(nbif_4_simple_exception) .align 4 nbif_0_simple_exception: xorl %eax, %eax @@ -374,6 +380,10 @@ nbif_2_simple_exception: .align 4 nbif_3_simple_exception: movl $3, %eax + jmp .nbif_simple_exception + .align 4 +nbif_4_simple_exception: + movl $4, %eax /*FALLTHROUGH*/ .align 4 .nbif_simple_exception: diff --git a/erts/emulator/hipe/hipe_ppc_asm.m4 b/erts/emulator/hipe/hipe_ppc_asm.m4 index 4a1caa1543..e5a56de687 100644 --- a/erts/emulator/hipe/hipe_ppc_asm.m4 +++ b/erts/emulator/hipe/hipe_ppc_asm.m4 @@ -280,6 +280,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(r3,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(r3,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(r3,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(r3,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(r3,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(r3,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(r3,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(r3,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(r3,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(r3,5,2)` */' @@ -301,6 +305,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index f53b79b52e..b173b896b8 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -46,9 +46,10 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -144,6 +145,41 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + GLOBAL(ASYM($1)) +ASYM($1): + /* Set up C argument registers. */ + mr r3, P + NBIF_ARG(r4,4,0) + NBIF_ARG(r5,4,1) + NBIF_ARG(r6,4,2) + NBIF_ARG(r7,4,3) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_BIF + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ + STORE r5, P_ARG1(r3) + STORE r6, P_ARG2(r3) + STORE r7, P_ARG3(r3) + addi r4, r3, P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + CMPI r3, THE_NON_VALUE + RESTORE_CONTEXT_BIF + beq- 1f + NBIF_RET(4) +1: /* workaround for bc:s small offset operand */ + b CSYM(nbif_4_simple_exception) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index c48fb150af..b07f4bc9c8 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -462,10 +462,12 @@ ASYM(nbif_fail): OPD(nbif_1_gc_after_bif) OPD(nbif_2_gc_after_bif) OPD(nbif_3_gc_after_bif) + OPD(nbif_4_gc_after_bif) GLOBAL(CSYM(nbif_0_gc_after_bif)) GLOBAL(CSYM(nbif_1_gc_after_bif)) GLOBAL(CSYM(nbif_2_gc_after_bif)) GLOBAL(CSYM(nbif_3_gc_after_bif)) + GLOBAL(CSYM(nbif_4_gc_after_bif)) CSYM(nbif_0_gc_after_bif): li r4, 0 b .gc_after_bif @@ -477,6 +479,9 @@ CSYM(nbif_2_gc_after_bif): b .gc_after_bif CSYM(nbif_3_gc_after_bif): li r4, 3 + b .gc_after_bif +CSYM(nbif_4_gc_after_bif): + li r4, 4 /*FALLTHROUGH*/ .gc_after_bif: stw r4, P_NARITY(P) /* Note: narity is a 32-bit field */ @@ -519,6 +524,11 @@ CSYM(nbif_2_simple_exception): GLOBAL(CSYM(nbif_3_simple_exception)) CSYM(nbif_3_simple_exception): li r4, 3 + b .nbif_simple_exception + OPD(nbif_3_simple_exception) + GLOBAL(CSYM(nbif_4_simple_exception)) +CSYM(nbif_4_simple_exception): + li r4, 4 /*FALLTHROUGH*/ .nbif_simple_exception: LOAD r3, P_FREASON(P) diff --git a/erts/emulator/hipe/hipe_sparc_asm.m4 b/erts/emulator/hipe/hipe_sparc_asm.m4 index c3c3bcb74a..8020104e40 100644 --- a/erts/emulator/hipe/hipe_sparc_asm.m4 +++ b/erts/emulator/hipe/hipe_sparc_asm.m4 @@ -176,6 +176,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(r1,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(r2,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(r3,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(r1,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(r2,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(r3,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(r3,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(r1,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(r2,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(r3,5,2)` */' @@ -200,6 +204,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index 2bfe3a4646..8dfb28c8e0 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -54,9 +54,10 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_3(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -146,6 +147,39 @@ $1: .type $1, #function #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov P, %o0 + NBIF_ARG(%o1,4,0) + NBIF_ARG(%o2,4,1) + NBIF_ARG(%o3,4,2) + NBIF_ARG(%o4,4,3) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_BIF + st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg + st %o2, [%o0+P_ARG1] + st %o3, [%o0+P_ARG2] + st %o4, [%o0+P_ARG3] + add %o0, P_ARG0, %o1 + CALL_BIF($2) + nop + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + TEST_GOT_EXN(4) + RESTORE_CONTEXT_BIF + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + .size $1, .-$1 + .type $1, #function +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 6c8c841194..094a87fd58 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -316,6 +316,7 @@ nbif_fail: .global nbif_1_gc_after_bif .global nbif_2_gc_after_bif .global nbif_3_gc_after_bif + .global nbif_4_gc_after_bif nbif_0_gc_after_bif: ba .gc_after_bif mov 0, %o1 /* delay slot */ @@ -326,7 +327,10 @@ nbif_2_gc_after_bif: ba .gc_after_bif mov 2, %o1 /* delay slot */ nbif_3_gc_after_bif: - mov 3, %o1 + ba .gc_after_bif + mov 3, %o1 /* delay slot */ +nbif_4_gc_after_bif: + mov 4, %o1 /*FALLTHROUGH*/ .gc_after_bif: st %o1, [P+P_NARITY] @@ -364,7 +368,11 @@ nbif_2_simple_exception: mov 2, %o1 /* delay slot */ .global nbif_3_simple_exception nbif_3_simple_exception: - mov 3, %o1 + ba .nbif_simple_exception + mov 3, %o1 /* delay slot */ + .global nbif_4_simple_exception +nbif_4_simple_exception: + mov 4, %o1 /*FALLTHROUGH*/ .nbif_simple_exception: ld [P+P_FREASON], %o0 diff --git a/erts/emulator/hipe/hipe_x86_asm.m4 b/erts/emulator/hipe/hipe_x86_asm.m4 index 39c5cb1044..436feca506 100644 --- a/erts/emulator/hipe/hipe_x86_asm.m4 +++ b/erts/emulator/hipe/hipe_x86_asm.m4 @@ -212,6 +212,7 @@ define(NBIF_COPY_NSP,`ifelse(eval($1 > NR_ARG_REGS),0,,`movl %esp, TEMP_NSP')')d `/* #define NBIF_COPY_NSP_1 'NBIF_COPY_NSP(1)` */' `/* #define NBIF_COPY_NSP_2 'NBIF_COPY_NSP(2)` */' `/* #define NBIF_COPY_NSP_3 'NBIF_COPY_NSP(3)` */' +`/* #define NBIF_COPY_NSP_4 'NBIF_COPY_NSP(4)` */' `/* #define NBIF_COPY_NSP_5 'NBIF_COPY_NSP(5)` */' dnl @@ -235,6 +236,10 @@ define(NBIF_ARG_OPND,`ifelse(eval($2 >= NR_ARG_REGS),0,`ARG'$2,BASE_OFFSET(eval( `/* #define NBIF_ARG_OPND_3_0 'NBIF_ARG_OPND(3,0)` */' `/* #define NBIF_ARG_OPND_3_1 'NBIF_ARG_OPND(3,1)` */' `/* #define NBIF_ARG_OPND_3_2 'NBIF_ARG_OPND(3,2)` */' +`/* #define NBIF_ARG_OPND_4_0 'NBIF_ARG_OPND(4,0)` */' +`/* #define NBIF_ARG_OPND_4_1 'NBIF_ARG_OPND(4,1)` */' +`/* #define NBIF_ARG_OPND_4_2 'NBIF_ARG_OPND(4,2)` */' +`/* #define NBIF_ARG_OPND_4_3 'NBIF_ARG_OPND(4,3)` */' `/* #define NBIF_ARG_OPND_5_0 'NBIF_ARG_OPND(5,0)` */' `/* #define NBIF_ARG_OPND_5_1 'NBIF_ARG_OPND(5,1)` */' `/* #define NBIF_ARG_OPND_5_2 'NBIF_ARG_OPND(5,2)` */' @@ -274,6 +279,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index a0f16efa33..b0064ee628 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -48,6 +48,7 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * * Generate native interface for a BIF with 0-3 parameters and @@ -158,6 +159,43 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + TEXT + .align 4 + GLOBAL(ASYM($1)) +ASYM($1): + /* copy native stack pointer */ + NBIF_COPY_NSP(4) + + /* switch to C stack */ + SWITCH_ERLANG_TO_C + + /* make the call on the C stack */ + NBIF_ARG_REG(0,P) + NBIF_ARG(2,4,0) + NBIF_ARG(3,4,1) + NBIF_ARG(4,4,2) + NBIF_ARG(5,4,3) + lea 8(%esp), %eax + NBIF_ARG_REG(1,%eax) /* BIF__ARGS */ + CALL_BIF($2) + TEST_GOT_MBUF + + /* switch to native stack */ + SWITCH_C_TO_ERLANG + + /* throw exception if failure, otherwise return */ + TEST_GOT_EXN + jz nbif_4_simple_exception + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index 9d38eaaafd..f124e36a26 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -299,6 +299,7 @@ ASYM(nbif_fail): GLOBAL(nbif_1_gc_after_bif) GLOBAL(nbif_2_gc_after_bif) GLOBAL(nbif_3_gc_after_bif) + GLOBAL(nbif_4_gc_after_bif) .align 4 nbif_0_gc_after_bif: xorl %edx, %edx @@ -314,6 +315,10 @@ nbif_2_gc_after_bif: .align 4 nbif_3_gc_after_bif: movl $3, %edx + jmp .gc_after_bif + .align 4 +nbif_4_gc_after_bif: + movl $4, %edx /*FALLTHROUGH*/ .align 4 .gc_after_bif: @@ -337,6 +342,7 @@ nbif_3_gc_after_bif: GLOBAL(nbif_1_simple_exception) GLOBAL(nbif_2_simple_exception) GLOBAL(nbif_3_simple_exception) + GLOBAL(nbif_4_simple_exception) .align 4 nbif_0_simple_exception: xorl %eax, %eax @@ -352,6 +358,10 @@ nbif_2_simple_exception: .align 4 nbif_3_simple_exception: movl $3, %eax + jmp .nbif_simple_exception + .align 4 +nbif_4_simple_exception: + movl $4, %eax /*FALLTHROUGH*/ .align 4 .nbif_simple_exception: diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 8783f189a4..9eaf8b9e59 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 5ebc67dcaa..61dc642536 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -126,7 +126,7 @@ -type zlevel() :: 'none' | 'default' | 'best_compression' | 'best_speed' | 0..9. -type zmethod() :: 'deflated'. --type zwindowbits() :: -15..-9 | 9..47. +-type zwindowbits() :: -15..-8 | 8..47. -type zmemlevel() :: 1..9. -type zstrategy() :: 'default' | 'filtered' | 'huffman_only' | 'rle'. @@ -531,8 +531,8 @@ arg_method(_) -> erlang:error(badarg). -spec arg_bitsz(zwindowbits()) -> zwindowbits(). arg_bitsz(Bits) when is_integer(Bits) andalso - ((8 < Bits andalso Bits < 48) orelse - (-15 =< Bits andalso Bits < -8)) -> + ((8 =< Bits andalso Bits < 48) orelse + (-15 =< Bits andalso Bits =< -8)) -> Bits; arg_bitsz(_) -> erlang:error(badarg). diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml index 45f01c12ec..f39f391818 100644 --- a/lib/common_test/doc/src/event_handler_chapter.xml +++ b/lib/common_test/doc/src/event_handler_chapter.xml @@ -59,6 +59,15 @@ Event handlers plugged into this manager will receive the events from all the test nodes as well as information from the CT Master server itself.</p> + + <p>User specific event handlers may be plugged into a Common Test event + manager, either by telling Common Test to install them before the test + run (see below), or by adding the handlers dynamically during the test + run by means of + <c>gen_event:add_handler/3</c> or <c>gen_event:add_sup_handler/3</c>. + In the latter scenario, the reference of the Common Test event manager is + required. To get it, call <c>ct:get_event_mgr_ref/0</c> or (on the CT + Master node) <c>ct_master:get_event_mgr_ref/0</c>.</p> </section> <section> <marker id="usage"></marker> diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 85afdc7834..9d8fce2789 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -52,6 +52,7 @@ -module(ct). -include("ct.hrl"). +-include("ct_util.hrl"). %% Command line user interface for running tests -export([install/1, run/1, run/2, run/3, @@ -77,6 +78,7 @@ %% Other interface functions -export([get_status/0, abort_current_testcase/1, + get_event_mgr_ref/0, encrypt_config_file/2, encrypt_config_file/3, decrypt_config_file/2, decrypt_config_file/3]). @@ -1005,6 +1007,18 @@ abort_current_testcase(Reason) -> test_server_ctrl:abort_current_testcase(Reason). %%%----------------------------------------------------------------- +%%% @spec get_event_mgr_ref() -> EvMgrRef +%%% EvMgrRef = atom() +%%% +%%% @doc <p>Call this function in order to get a reference to the +%%% CT event manager. The reference can be used to e.g. add +%%% a user specific event handler while tests are running. +%%% Example: +%%% <c>gen_event:add_handler(ct:get_event_mgr_ref(), my_ev_h, [])</c></p> +get_event_mgr_ref() -> + ?CT_EVMGR_REF. + +%%%----------------------------------------------------------------- %%% @spec encrypt_config_file(SrcFileName, EncryptFileName) -> %%% ok | {error,Reason} %%% SrcFileName = string() diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index ec525784ec..498950c9d1 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -686,18 +686,21 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> undefined -> %% send sync notification so that event handlers may print %% in the log file before it gets closed - ct_event:sync_notify(#event{name=tc_done, - node=node(), - data={Mod,FuncSpec, - tag_cth(FinalNotify)}}), + Event = #event{name=tc_done, + node=node(), + data={Mod,FuncSpec, + tag(FinalNotify)}}, + ct_event:sync_notify(Event), Result1; Fun -> %% send sync notification so that event handlers may print %% in the log file before it gets closed - ct_event:sync_notify(#event{name=tc_done, - node=node(), - data={Mod,FuncSpec, - tag(FinalNotify)}}), + Event = #event{name=tc_done, + node=node(), + data={Mod,FuncSpec, + tag({'$test_server_framework_test', + FinalNotify})}}, + ct_event:sync_notify(Event), Fun(end_tc, Return) end, @@ -770,44 +773,37 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> %% {error,Reason} | {skip,Reason} | {timetrap_timeout,TVal} | %% {testcase_aborted,Reason} | testcase_aborted_or_killed | -%% {'EXIT',Reason} | Other (ignored return value, e.g. 'ok') -tag({STag,Reason}) when STag == skip; STag == skipped -> - case Reason of - {failed,{_,init_per_testcase,_}} -> {auto_skipped,Reason}; - _ -> {skipped,Reason} - end; -tag({auto_skip,Reason}) -> - {auto_skipped,Reason}; -tag(E = {ETag,_}) when ETag == error; ETag == 'EXIT'; - ETag == timetrap_timeout; - ETag == testcase_aborted -> - {failed,E}; -tag(E = testcase_aborted_or_killed) -> - {failed,E}; -tag(Other) -> - Other. - -tag_cth({skipped,Reason={failed,{_,init_per_testcase,_}}}) -> +%% {'EXIT',Reason} | {fail,Reason} | {failed,Reason} | +%% {user_timetrap_error,Reason} | +%% Other (ignored return value, e.g. 'ok') +tag({'$test_server_framework_test',Result}) -> + case tag(Result) of + ok -> Result; + Failure -> Failure + end; +tag({skipped,Reason={failed,{_,init_per_testcase,_}}}) -> {auto_skipped,Reason}; -tag_cth({STag,Reason}) when STag == skip; STag == skipped -> +tag({STag,Reason}) when STag == skip; STag == skipped -> case Reason of {failed,{_,init_per_testcase,_}} -> {auto_skipped,Reason}; _ -> {skipped,Reason} end; -tag_cth({auto_skip,Reason}) -> +tag({auto_skip,Reason}) -> {auto_skipped,Reason}; -tag_cth({fail,Reason}) -> +tag({fail,Reason}) -> {failed,{error,Reason}}; -tag_cth(E = {ETag,_}) when ETag == error; ETag == 'EXIT'; +tag(Failed = {failed,_Reason}) -> + Failed; +tag(E = {ETag,_}) when ETag == error; ETag == 'EXIT'; ETag == timetrap_timeout; ETag == testcase_aborted -> {failed,E}; -tag_cth(E = testcase_aborted_or_killed) -> +tag(E = testcase_aborted_or_killed) -> {failed,E}; -tag_cth(List) when is_list(List) -> - ok; -tag_cth(Other) -> - Other. +tag(UserTimetrap = {user_timetrap_error,_Reason}) -> + UserTimetrap; +tag(_Other) -> + ok. %%%----------------------------------------------------------------- %%% @spec error_notification(Mod,Func,Args,Error) -> ok @@ -841,6 +837,8 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> io_lib:format("{test_case_failed,~p}", [Reason]); Result -> Result end; + {'EXIT',_Reason} = EXIT -> + io_lib:format("~P", [EXIT,5]); {Spec,_Reason} when is_atom(Spec) -> io_lib:format("~w", [Spec]); Other -> diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 23332ad268..dc118ed149 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -1905,6 +1905,17 @@ sort_all_runs(Dirs) -> {Date1,HH1,MM1,SS1} > {Date2,HH2,MM2,SS2} end, Dirs). +sort_ct_runs(Dirs) -> + %% Directory naming: <Prefix>.NodeName.Date_Time[/...] + %% Sort on Date_Time string: "YYYY-MM-DD_HH.MM.SS" + lists:sort(fun(Dir1,Dir2) -> + [_Prefix,_Node1,DateHH1,MM1,SS1] = + string:tokens(filename:dirname(Dir1),[$.]), + [_Prefix,_Node2,DateHH2,MM2,SS2] = + string:tokens(filename:dirname(Dir2),[$.]), + {DateHH1,MM1,SS1} =< {DateHH2,MM2,SS2} + end, Dirs). + dir_diff_all_runs(Dirs, LogCache) -> case LogCache#log_cache.all_runs of [] -> @@ -2217,7 +2228,8 @@ make_all_suites_index(When) when is_atom(When) -> end end, - LogDirs = filelib:wildcard(logdir_prefix()++".*/*"++?logdir_ext), + Wildcard = logdir_prefix()++".*/*"++?logdir_ext, + LogDirs = sort_ct_runs(filelib:wildcard(Wildcard)), LogCacheInfo = get_cache_data(UseCache), diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index b42ff73846..2cdb259899 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -25,6 +25,7 @@ -export([run/1,run/3,run/4]). -export([run_on_node/2,run_on_node/3]). -export([run_test/1,run_test/2]). +-export([get_event_mgr_ref/0]). -export([basic_html/1]). -export([abort/0,abort/1,progress/0]). @@ -292,6 +293,18 @@ progress() -> call(progress). %%%----------------------------------------------------------------- +%%% @spec get_event_mgr_ref() -> MasterEvMgrRef +%%% MasterEvMgrRef = atom() +%%% +%%% @doc <p>Call this function in order to get a reference to the +%%% CT master event manager. The reference can be used to e.g. +%%% add a user specific event handler while tests are running. +%%% Example: +%%% <c>gen_event:add_handler(ct_master:get_event_mgr_ref(), my_ev_h, [])</c></p> +get_event_mgr_ref() -> + ?CT_MEVMGR_REF. + +%%%----------------------------------------------------------------- %%% @spec basic_html(Bool) -> ok %%% Bool = true | false %%% diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index 14ee55703f..af82f2dcbf 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -794,8 +794,9 @@ action(Client,Action) -> Client :: client(), Action :: simple_xml(), Timeout :: timeout(), - Result :: {ok,[simple_xml()]} | {error,error_reason()}. -%% @doc Execute an action. + Result :: ok | {ok,[simple_xml()]} | {error,error_reason()}. +%% @doc Execute an action. If the return type is void, <c>ok</c> will +%% be returned instead of <c>{ok,[simple_xml()]}</c>. %% %% @end %%---------------------------------------------------------------------- @@ -1606,6 +1607,9 @@ decode_ok(Other) -> decode_data([{Tag,Attrs,Content}]) -> case get_local_name_atom(Tag) of + ok -> + %% when action has return type void + ok; data -> %% Since content of data has nothing from the netconf %% namespace, we remove the parent's xmlns attribute here diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl index 8ee12a2e4d..c2e06d866f 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl +++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl @@ -73,7 +73,7 @@ test_get_known_variable(_)-> test_localtime_update(_)-> Seconds = 5, LT1 = ct:get_config(localtime), - timer:sleep(Seconds*1000), + ct:sleep(Seconds*1000), LT2 = ct:reload_config(localtime), case is_diff_ok(LT1, LT2, Seconds) of {false, Actual, Exp}-> diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index ecf231529a..8464225284 100644 --- a/lib/common_test/test/ct_error_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE.erl @@ -1466,7 +1466,8 @@ test_events(misc_errors) -> {failed,{error,{suite_failed,this_is_expected}}}}}, {?eh,test_stats,{0,5,{0,0}}}, {?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_1}}, - {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_1,i_die_now}}, + {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_1, + {failed,{'EXIT',i_die_now}}}}, {?eh,test_stats,{0,6,{0,0}}}, {?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_2}}, {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_2, diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl index 806d3caf72..0ff8659269 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl @@ -26,10 +26,10 @@ init_per_testcase(_, Config) -> Config. end_per_testcase(tc2, _Config) -> - timer:sleep(2000), + ct:sleep(2000), exit(this_should_not_be_printed); end_per_testcase(tc4, _Config) -> - timer:sleep(2000), + ct:sleep(2000), exit(this_should_not_be_printed); end_per_testcase(_, _) -> ok. @@ -42,7 +42,7 @@ tc1() -> put('$test_server_framework_test', fun(init_tc, _Default) -> ct:pal("init_tc(~p): Night time...",[self()]), - timer:sleep(2000), + ct:sleep(2000), ct:pal("init_tc(~p): Day time!",[self()]), exit(this_should_not_be_printed); (_, Default) -> Default @@ -67,7 +67,7 @@ tc3(_) -> put('$test_server_framework_test', fun(end_tc, _Default) -> ct:pal("end_tc(~p): Night time...",[self()]), - timer:sleep(1000), + ct:sleep(1000), ct:pal("end_tc(~p): Day time!",[self()]); (_, Default) -> Default end), @@ -78,7 +78,7 @@ tc4() -> put('$test_server_framework_test', fun(end_tc, _Default) -> ct:pal("end_tc(~p): Night time...",[self()]), - timer:sleep(1000), + ct:sleep(1000), ct:pal("end_tc(~p): Day time!",[self()]); (_, Default) -> Default end), diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl index c8a3c1d15e..cfc0babb68 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl @@ -26,7 +26,7 @@ init_per_suite() -> put('$test_server_framework_test', fun(end_tc, _Default) -> ct:pal("end_tc(~p): Night time...",[self()]), - timer:sleep(1000), + ct:sleep(1000), ct:pal("end_tc(~p): Day time!",[self()]); (_, Default) -> Default end), diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl index 960d0f61b0..54b09e78c6 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl @@ -29,7 +29,7 @@ end_per_suite() -> put('$test_server_framework_test', fun(end_tc, _Default) -> ct:pal("end_tc(~p): Night time...",[self()]), - timer:sleep(1000), + ct:sleep(1000), ct:pal("end_tc(~p): Day time!",[self()]); (_, Default) -> Default end), diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl index 08c57887ef..0d93e46501 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl @@ -36,7 +36,7 @@ suite() -> %% Reason = term() %%-------------------------------------------------------------------- init_per_suite(Config) -> - timer:sleep(5000), + ct:sleep(5000), exit(shouldnt_happen). % Config. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_7_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_7_SUITE.erl index 9cd5b6ad29..d95f3b235b 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_7_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_7_SUITE.erl @@ -43,7 +43,7 @@ init_per_suite(Config) -> %% Config0 = Config1 = [tuple()] %%-------------------------------------------------------------------- end_per_suite(Config) -> - timer:sleep(5000), + ct:sleep(5000), ok. %%-------------------------------------------------------------------- diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_8_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_8_SUITE.erl index 25993833d7..d8f0c48034 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_8_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_8_SUITE.erl @@ -57,7 +57,7 @@ init_per_group(g1, Config) -> Config; init_per_group(g2, Config) -> ct:comment("init_per_group(g2) timeout"), - timer:sleep(5000), + ct:sleep(5000), Config; init_per_group(g3, _Config) -> badmatch = 42; @@ -80,7 +80,7 @@ end_per_group(g11, _Config) -> ok; end_per_group(g12, _Config) -> ct:comment("end_per_group(g6) timeout"), - timer:sleep(5000), + ct:sleep(5000), ok; end_per_group(_GroupName, _Config) -> ok. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl index a98382965f..1451a4119e 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl @@ -111,7 +111,7 @@ end_per_testcase1(tc2, Config) -> ct:pal("end_per_testcase(tc2): ~p", [Config]), tc2 = ?config(tc, Config), {failed,timetrap_timeout} = ?config(tc_status, Config), - timer:sleep(2000); + ct:sleep(2000); end_per_testcase1(tc3, Config) -> ct:pal("end_per_testcase(tc3): ~p", [Config]), @@ -123,7 +123,7 @@ end_per_testcase1(tc4, Config) -> ct:pal("end_per_testcase(tc4): ~p", [Config]), tc4 = ?config(tc, Config), {failed,{testcase_aborted,testing_end_conf}} = ?config(tc_status, Config), - timer:sleep(2000); + ct:sleep(2000); end_per_testcase1(tc5, Config) -> ct:pal("end_per_testcase(tc5): ~p", [Config]), @@ -182,29 +182,29 @@ all() -> [tc1, tc2, tc3, tc4, tc5, tc6, tc7, tc8, tc9]. tc1(_) -> - timer:sleep(2000), + ct:sleep(2000), ok. tc2(_) -> - timer:sleep(2000). + ct:sleep(2000). tc3(_) -> spawn(ct, abort_current_testcase, [testing_end_conf]), - timer:sleep(2000), + ct:sleep(2000), ok. tc4(_) -> spawn(ct, abort_current_testcase, [testing_end_conf]), - timer:sleep(2000), + ct:sleep(2000), ok. tc5(_) -> - timer:sleep(2000), + ct:sleep(2000), ok. tc6(_) -> spawn(ct, abort_current_testcase, [testing_end_conf]), - timer:sleep(2000). + ct:sleep(2000). tc7(_) -> sleep(2000), @@ -220,5 +220,5 @@ tc9(_) -> %%%----------------------------------------------------------------- sleep(T) -> - timer:sleep(T), + ct:sleep(T), ok. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl index 1389acca11..a9ea0be847 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl @@ -3,5 +3,5 @@ -export([sleep/1]). sleep(T) -> - timer:sleep(T), + ct:sleep(T), ok. diff --git a/lib/common_test/test/ct_event_handler_SUITE.erl b/lib/common_test/test/ct_event_handler_SUITE.erl index 30a5e650fe..b759424e46 100644 --- a/lib/common_test/test/ct_event_handler_SUITE.erl +++ b/lib/common_test/test/ct_event_handler_SUITE.erl @@ -29,6 +29,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/src/ct_util.hrl"). %-include_lib("common_test/include/ct_event.hrl"). @@ -59,7 +60,7 @@ end_per_testcase(TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [start_stop, results]. + [start_stop, results, event_mgrs]. groups() -> []. @@ -156,21 +157,28 @@ results(Config) when is_list(Config) -> TestEvents = [{eh_A,start_logging,{'DEF','RUNDIR'}}, {eh_A,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {eh_A,start_info,{1,1,4}}, + {eh_A,start_info,{1,1,5}}, {eh_A,tc_start,{eh_11_SUITE,init_per_suite}}, {eh_A,tc_done,{eh_11_SUITE,init_per_suite,ok}}, - {eh_A,tc_start,{eh_11_SUITE,tc1}}, - {eh_A,tc_done,{eh_11_SUITE,tc1,ok}}, - {eh_A,test_stats,{1,0,{0,0}}}, - {eh_A,tc_start,{eh_11_SUITE,tc2}}, - {eh_A,tc_done,{eh_11_SUITE,tc2,{skipped,"Skip"}}}, - {eh_A,test_stats,{1,0,{1,0}}}, - {eh_A,tc_start,{eh_11_SUITE,tc3}}, - {eh_A,tc_done,{eh_11_SUITE,tc3,{skipped,"Skipped"}}}, - {eh_A,test_stats,{1,0,{2,0}}}, - {eh_A,tc_start,{eh_11_SUITE,tc4}}, - {eh_A,tc_done,{eh_11_SUITE,tc4,{failed,{error,'Failing'}}}}, - {eh_A,test_stats,{1,1,{2,0}}}, + [{eh_A,tc_start,{eh_11_SUITE,{init_per_group,g1,[]}}}, + {eh_A,tc_done,{eh_11_SUITE,{init_per_group,g1,[]},ok}}, + {eh_A,tc_start,{eh_11_SUITE,tc1}}, + {eh_A,tc_done,{eh_11_SUITE,tc1,ok}}, + {eh_A,test_stats,{1,0,{0,0}}}, + {eh_A,tc_start,{eh_11_SUITE,tc2}}, + {eh_A,tc_done,{eh_11_SUITE,tc2,ok}}, + {eh_A,test_stats,{2,0,{0,0}}}, + {eh_A,tc_start,{eh_11_SUITE,tc3}}, + {eh_A,tc_done,{eh_11_SUITE,tc3,{skipped,"Skip"}}}, + {eh_A,test_stats,{2,0,{1,0}}}, + {eh_A,tc_start,{eh_11_SUITE,tc4}}, + {eh_A,tc_done,{eh_11_SUITE,tc4,{skipped,"Skipped"}}}, + {eh_A,test_stats,{2,0,{2,0}}}, + {eh_A,tc_start,{eh_11_SUITE,tc5}}, + {eh_A,tc_done,{eh_11_SUITE,tc5,{failed,{error,'Failing'}}}}, + {eh_A,test_stats,{2,1,{2,0}}}, + {eh_A,tc_start,{eh_11_SUITE,{end_per_group,g1,[]}}}, + {eh_A,tc_done,{eh_11_SUITE,{end_per_group,g1,[]},ok}}], {eh_A,tc_start,{eh_11_SUITE,end_per_suite}}, {eh_A,tc_done,{eh_11_SUITE,end_per_suite,ok}}, {eh_A,test_done,{'DEF','STOP_TIME'}}, @@ -179,5 +187,10 @@ results(Config) when is_list(Config) -> ok = ct_test_support:verify_events(TestEvents++TestEvents, Events, Config). +event_mgrs(_) -> + ?CT_EVMGR_REF = ct:get_event_mgr_ref(), + ?CT_MEVMGR_REF = ct_master:get_event_mgr_ref(). + + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS diff --git a/lib/common_test/test/ct_event_handler_SUITE_data/event_handling_1/test/eh_11_SUITE.erl b/lib/common_test/test/ct_event_handler_SUITE_data/event_handling_1/test/eh_11_SUITE.erl index a52fe96f30..14ea12d579 100644 --- a/lib/common_test/test/ct_event_handler_SUITE_data/event_handling_1/test/eh_11_SUITE.erl +++ b/lib/common_test/test/ct_event_handler_SUITE_data/event_handling_1/test/eh_11_SUITE.erl @@ -32,100 +32,36 @@ %% COMMON TEST CALLBACK FUNCTIONS %%-------------------------------------------------------------------- -%%-------------------------------------------------------------------- -%% Function: suite() -> Info -%% -%% Info = [tuple()] -%% List of key/value pairs. -%% -%% Description: Returns list of tuples to set default properties -%% for the suite. -%% -%% Note: The suite/0 function is only meant to be used to return -%% default data values, not perform any other operations. -%%-------------------------------------------------------------------- suite() -> [ {timetrap,{seconds,10}} ]. -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config0) -> -%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} -%% -%% Config0 = Config1 = [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Reason = term() -%% The reason for skipping the suite. -%% -%% Description: Initialization before the suite. -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- init_per_suite(Config) -> Config. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config0) -> void() | {save_config,Config1} -%% -%% Config0 = Config1 = [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Cleanup after the suite. -%%-------------------------------------------------------------------- end_per_suite(_Config) -> - ok. + %% should report ok as result to event handler + done. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + %% should report ok as result to event handler + void. -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config0) -> -%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} -%% -%% TestCase = atom() -%% Name of the test case that is about to run. -%% Config0 = Config1 = [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Reason = term() -%% The reason for skipping the test case. -%% -%% Description: Initialization before each test case. -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- init_per_testcase(_TestCase, Config) -> Config. -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config0) -> -%% void() | {save_config,Config1} -%% -%% TestCase = atom() -%% Name of the test case that is finished. -%% Config0 = Config1 = [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Cleanup after each test case. -%%-------------------------------------------------------------------- end_per_testcase(_TestCase, _Config) -> - ok. + true. -%%-------------------------------------------------------------------- -%% Function: all() -> TestCases | {skip,Reason} -%% -%% TestCases = [TestCase | {sequence,SeqName}] -%% TestCase = atom() -%% Name of a test case. -%% SeqName = atom() -%% Name of a test case sequence. -%% Reason = term() -%% The reason for skipping all test cases. -%% -%% Description: Returns the list of test cases that are to be executed. -%%-------------------------------------------------------------------- -all() -> - [tc1, tc2, tc3, tc4]. +groups() -> + [{g1, [], [tc1, tc2, tc3, tc4, tc5]}]. +all() -> + [{group,g1}]. %%-------------------------------------------------------------------- %% TEST CASES @@ -134,11 +70,15 @@ all() -> tc1(_Config) -> ok. -tc2(_Config) -> - {skip,"Skip"}. +tc2(_Config) -> + %% should report ok as result to event handler + 42. tc3(_Config) -> + {skip,"Skip"}. + +tc4(_Config) -> {skipped,"Skipped"}. -tc4(_Config) -> +tc5(_Config) -> exit('Failing'). diff --git a/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl b/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl index 1344878675..96dd80e4e8 100644 --- a/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl +++ b/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl @@ -73,23 +73,23 @@ handles_to_multi_conn_pids(_Config) -> {true,true} = {is_process_alive(Handle3),is_process_alive(ConnPid3)}, ok = proto:close(Handle1), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle1),is_process_alive(ConnPid1)}, {true,true} = {is_process_alive(Handle2),is_process_alive(ConnPid2)}, ok = proto:kill_conn_proc(Handle2), - timer:sleep(100), + ct:sleep(100), {true,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2)}, ConnPid2x = ct_gen_conn:get_conn_pid(Handle2), true = is_process_alive(ConnPid2x), ok = proto:close(Handle2), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2x)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle3),is_process_alive(ConnPid3)}, ok. @@ -116,23 +116,23 @@ handles_to_single_conn_pids(_Config) -> ct:pal("CONNS = ~n~p", [Conns]), ok = proto:close(Handle1), - timer:sleep(100), + ct:sleep(100), {false,true} = {is_process_alive(Handle1),is_process_alive(ConnPid)}, ok = proto:kill_conn_proc(Handle2), - timer:sleep(100), + ct:sleep(100), NewConnPid = ct_gen_conn:get_conn_pid(Handle2), NewConnPid = ct_gen_conn:get_conn_pid(Handle3), true = is_process_alive(Handle2), true = is_process_alive(Handle3), ok = proto:close(Handle2), - timer:sleep(100), + ct:sleep(100), {false,true} = {is_process_alive(Handle2),is_process_alive(NewConnPid)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle3),is_process_alive(NewConnPid)}, ok. @@ -158,29 +158,29 @@ names_to_multi_conn_pids(_Config) -> Handle1 = proto:open(mconn1), ok = proto:close(mconn1), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle1),is_process_alive(ConnPid1)}, ok = proto:kill_conn_proc(Handle2), - timer:sleep(100), + ct:sleep(100), Handle2 = proto:open(mconn2), % should've been reconnected already {true,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2)}, ConnPid2x = ct_gen_conn:get_conn_pid(Handle2), true = is_process_alive(ConnPid2x), ok = proto:close(mconn2), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2x)}, Handle2y = proto:open(mconn2), ConnPid2y = ct_gen_conn:get_conn_pid(Handle2y), {true,true} = {is_process_alive(Handle2y),is_process_alive(ConnPid2y)}, ok = proto:close(mconn2), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle2y),is_process_alive(ConnPid2y)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle3),is_process_alive(ConnPid3)}, ok. @@ -211,11 +211,11 @@ names_to_single_conn_pids(_Config) -> ct:pal("CONNS on ~p = ~n~p", [ConnPid,Conns]), ok = proto:close(sconn1), - timer:sleep(100), + ct:sleep(100), {false,true} = {is_process_alive(Handle1),is_process_alive(ConnPid)}, ok = proto:kill_conn_proc(Handle2), - timer:sleep(100), + ct:sleep(100), {true,false} = {is_process_alive(Handle2),is_process_alive(ConnPid)}, Handle2 = proto:open(sconn2), % should've been reconnected already NewConnPid = ct_gen_conn:get_conn_pid(Handle2), @@ -227,12 +227,12 @@ names_to_single_conn_pids(_Config) -> ct:pal("CONNS on ~p = ~n~p", [NewConnPid,Conns1]), ok = proto:close(sconn2), - timer:sleep(100), + ct:sleep(100), {false,true} = {is_process_alive(Handle2),is_process_alive(NewConnPid)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - timer:sleep(100), + ct:sleep(100), {false,false} = {is_process_alive(Handle3),is_process_alive(NewConnPid)}, ok. diff --git a/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl b/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl index 804f722081..bfdc78639e 100644 --- a/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl +++ b/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl @@ -258,14 +258,14 @@ gen_io(Label, N, Acc) -> %% (via ct logging functions) from an external process which has a %% different group leader than the test cases. unexp1(Config) -> - timer:sleep(1000), + ct:sleep(1000), gen_unexp_io(), - timer:sleep(1000), + ct:sleep(1000), check_unexp_io(Config), ok. unexp2(_) -> - timer:sleep(2000), + ct:sleep(2000), ok. gen_unexp_io() -> diff --git a/lib/common_test/test/ct_groups_test_1_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE.erl index e520a72227..d5de949554 100644 --- a/lib/common_test/test/ct_groups_test_1_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_1_SUITE.erl @@ -302,7 +302,7 @@ test_events(groups_suite_1) -> {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_11_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]; @@ -410,7 +410,7 @@ test_events(groups_suite_2) -> {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_12_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_12_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_12_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]; @@ -505,7 +505,7 @@ test_events(groups_suites_1) -> {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_11_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {?eh,tc_start,{groups_12_SUITE,init_per_suite}}, {?eh,tc_done,{groups_12_SUITE,init_per_suite,ok}}, @@ -596,7 +596,7 @@ test_events(groups_suites_1) -> {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_12_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_12_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_12_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]; @@ -691,7 +691,7 @@ test_events(groups_dir_1) -> {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_11_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {?eh,tc_start,{groups_12_SUITE,init_per_suite}}, {?eh,tc_done,{groups_12_SUITE,init_per_suite,ok}}, @@ -782,7 +782,7 @@ test_events(groups_dir_1) -> {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_12_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_12_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_12_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]; @@ -878,7 +878,7 @@ test_events(groups_dirs_1) -> {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_11_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {?eh,tc_start,{groups_12_SUITE,init_per_suite}}, {?eh,tc_done,{groups_12_SUITE,init_per_suite,ok}}, @@ -969,7 +969,7 @@ test_events(groups_dirs_1) -> {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_12_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_12_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_12_SUITE,end_per_suite,ok}}, {?eh,tc_start,{groups_21_SUITE,init_per_suite}}, {?eh,tc_done,{groups_21_SUITE,init_per_suite,ok}}, @@ -1089,7 +1089,7 @@ test_events(groups_dirs_1) -> {groups_21_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_21_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_21_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_21_SUITE,end_per_suite,ok}}, {?eh,tc_start,{groups_22_SUITE,init_per_suite}}, {?eh,tc_done,{groups_22_SUITE,init_per_suite,ok}}, @@ -1223,6 +1223,6 @@ test_events(groups_dirs_1) -> {groups_22_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_22_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_22_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_22_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]. diff --git a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl index ec90ef95d1..6f49f9a957 100644 --- a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl @@ -278,7 +278,7 @@ testcase_5a(Config) -> %% increase chance the done event will come %% during execution of subgroup (could be %% tricky to handle) - timer:sleep(3), + ct:sleep(3), ok. testcase_5b() -> []. diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl index 8b0de98709..f41395e028 100644 --- a/lib/common_test/test/ct_groups_test_2_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl @@ -302,7 +302,7 @@ test_events(empty_group) -> {?eh,tc_done, {groups_22_SUITE,{end_per_group,test_group_8,[]},ok}}], {?eh,tc_start,{groups_22_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_22_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_22_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]. diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl index 154c676d7e..80bb5ba69b 100644 --- a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl @@ -293,7 +293,7 @@ testcase_5a(Config) -> %% increase chance the done event will come %% during execution of subgroup (could be %% tricky to handle) - timer:sleep(3), + ct:sleep(3), ok. testcase_5b() -> []. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl index 18dd07e87e..80ce248418 100644 --- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl @@ -50,7 +50,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> Gen = proplists:get_value(gen, Config), exit(Gen, kill), - timer:sleep(100), + ct:sleep(100), ok. %%-------------------------------------------------------------------- diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl index 332e54d1a7..e7bbdc28a5 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -204,7 +204,7 @@ hello_required_exists(Config) -> ?NS:expect_do_reply('close-session',close,ok), ?ok = ct_netconfc:close_session(my_named_connection), - timer:sleep(500), + ct:sleep(500), %% Then check that it can be used again after the first is closed {ok,_Client2} = open_configured_success(my_named_connection,DataDir), @@ -486,8 +486,15 @@ action(Config) -> DataDir = ?config(data_dir,Config), {ok,Client} = open_success(DataDir), Data = [{myactionreturn,[{xmlns,"myns"}],["value"]}], - ?NS:expect_reply(action,{data,Data}), - {ok,Data} = ct_netconfc:action(Client,{myaction,[{xmlns,"myns"}],[]}), + %% test either to receive {data,Data} or {ok,Data}, + %% both need to be handled + {Reply,RetVal} = case element(3, now()) rem 2 of + 0 -> {{data,Data},{ok,Data}}; + 1 -> {{ok,Data},ok} + end, + ct:log("Client will receive {~w,Data}", [element(1,Reply)]), + ?NS:expect_reply(action,Reply), + RetVal = ct_netconfc:action(Client,{myaction,[{xmlns,"myns"}],[]}), ?NS:expect_do_reply('close-session',close,ok), ?ok = ct_netconfc:close_session(Client), ok. @@ -654,10 +661,10 @@ receive_chunked_data(Config) -> %% Spawn a process which will wait a bit for the client to send %% the request (below), then order the server to the chunks of the %% rpc-reply one by one. - spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), - timer:sleep(100),?NS:hupp(send,Part2), - timer:sleep(100),?NS:hupp(send,Part3), - timer:sleep(100),?NS:hupp(send,Part4) + spawn(fun() -> ct:sleep(500),?NS:hupp(send,Part1), + ct:sleep(100),?NS:hupp(send,Part2), + ct:sleep(100),?NS:hupp(send,Part3), + ct:sleep(100),?NS:hupp(send,Part4) end), %% Order server to expect a get - then the process above will make @@ -702,8 +709,8 @@ timeout_receive_chunked_data(Config) -> %% Spawn a process which will wait a bit for the client to send %% the request (below), then order the server to the chunks of the %% rpc-reply one by one. - spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), - timer:sleep(100),?NS:hupp(send,Part2) + spawn(fun() -> ct:sleep(500),?NS:hupp(send,Part1), + ct:sleep(100),?NS:hupp(send,Part2) end), %% Order server to expect a get - then the process above will make @@ -748,9 +755,9 @@ close_while_waiting_for_chunked_data(Config) -> %% Spawn a process which will wait a bit for the client to send %% the request (below), then order the server to the chunks of the %% rpc-reply one by one. - spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), - timer:sleep(100),?NS:hupp(send,Part2), - timer:sleep(100),?NS:hupp(kill) + spawn(fun() -> ct:sleep(500),?NS:hupp(send,Part1), + ct:sleep(100),?NS:hupp(send,Part2), + ct:sleep(100),?NS:hupp(kill) end), %% Order server to expect a get - then the process above will make @@ -766,7 +773,7 @@ connection_crash(Config) -> %% Test that if the test survives killing the connection %% process. Earlier this caused ct_util_server to terminate, and %% this aborting the complete test run. - spawn(fun() -> timer:sleep(500),exit(Client,kill) end), + spawn(fun() -> ct:sleep(500),exit(Client,kill) end), ?NS:expect(get), {error,{closed,killed}}=ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), ok. 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 f7c7b891bb..e4bc396536 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl @@ -351,7 +351,7 @@ check_expected(SessionId,ConnRef,Msg) -> do(ConnRef, Do), reply(ConnRef,Reply); error -> - timer:sleep(1000), + ct:sleep(1000), exit({error,{got_unexpected,SessionId,Msg,ets:tab2list(ns_tab)}}) end. @@ -548,8 +548,13 @@ make_msg({hello,SessionId,Stuff}) -> SessionIdXml/binary,"</hello>">>); make_msg(ok) -> xml(rpc_reply("<ok/>")); + +make_msg({ok,Data}) -> + xml(rpc_reply(from_simple({ok,Data}))); + make_msg({data,Data}) -> xml(rpc_reply(from_simple({data,Data}))); + make_msg(event) -> xml(<<"<notification xmlns=\"",?NETCONF_NOTIF_NAMESPACE,"\">" "<eventTime>2012-06-14T14:50:54+02:00</eventTime>" diff --git a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl index 5de1ecc2bd..1e6018f442 100644 --- a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl +++ b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl @@ -91,27 +91,27 @@ pre_post_io(Config) -> spawn(fun() -> ct:pal("CONTROLLER: Started!", []), %% --- test run 1 --- - timer:sleep(3000), + ct:sleep(3000), ct:pal("CONTROLLER: Handle remote events = true", []), ok = ct_test_support:ct_rpc({cth_log_redirect, handle_remote_events, [true]}, Config), - timer:sleep(2000), + ct:sleep(2000), ct:pal("CONTROLLER: Proceeding with test run #1!", []), ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config), - timer:sleep(6000), + ct:sleep(6000), ct:pal("CONTROLLER: Proceeding with shutdown #1!", []), ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config), %% --- test run 2 --- - timer:sleep(3000), + ct:sleep(3000), ct:pal("CONTROLLER: Handle remote events = true", []), ok = ct_test_support:ct_rpc({cth_log_redirect, handle_remote_events, [true]}, Config), - timer:sleep(2000), + ct:sleep(2000), ct:pal("CONTROLLER: Proceeding with test run #2!", []), ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config), - timer:sleep(6000), + ct:sleep(6000), ct:pal("CONTROLLER: Proceeding with shutdown #2!", []), ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config) end), diff --git a/lib/common_test/test/ct_repeat_1_SUITE.erl b/lib/common_test/test/ct_repeat_1_SUITE.erl index e37aeb196c..50e07608f6 100644 --- a/lib/common_test/test/ct_repeat_1_SUITE.erl +++ b/lib/common_test/test/ct_repeat_1_SUITE.erl @@ -220,8 +220,7 @@ test_events(repeat_cs_and_grs) -> {?eh,test_stats,{1,1,{0,0}}}, [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_result,[]},ok}}, {?eh,test_stats,{2,1,{0,0}}}, - {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_fail_result,[]},ok}}], {?eh,test_stats,{3,1,{0,0}}}, [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]}, {failed,{error,fails_on_purpose}}}}, @@ -242,8 +241,7 @@ test_events(repeat_cs_and_grs) -> {?eh,test_stats,{5,2,{0,1}}}, [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_result,[]},ok}}, {?eh,test_stats,{6,2,{0,1}}}, - {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_fail_result,[]},ok}}], {?eh,test_stats,{7,2,{0,1}}}, [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]}, {failed,{error,fails_on_purpose}}}}, @@ -289,8 +287,7 @@ test_events(repeat_seq) -> {init_per_group,gr_fail_result,[]},ok}}, {?eh,test_stats,{4,2,{0,2}}}, {?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result,[]},ok}}], {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_2}, {group_result,gr_fail_result,failed}}}, {?eh,test_stats,{4,2,{0,3}}}, @@ -402,8 +399,7 @@ test_events(repeat_gr_until_any_ok) -> [{?eh,tc_done,{repeat_1_SUITE, {init_per_group,gr_fail_result,[]},ok}}, {?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE,tc_fail_1, {failed,{error,{{badmatch,2},'_'}}}}}, {?eh,test_stats,{1,1,{0,0}}}, @@ -418,8 +414,7 @@ test_events(repeat_gr_until_any_ok) -> [{?eh,tc_done,{repeat_1_SUITE, {init_per_group,gr_fail_result_then_ok,[]},ok}}, {?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result_then_ok,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result_then_ok,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE, {end_per_group,repeat_gr_until_any_ok_1, [{repeat_until_any_ok,3}]},ok}}], @@ -441,8 +436,7 @@ test_events(repeat_gr_until_any_ok) -> {init_per_group,repeat_gr_until_any_ok_2, [{repeat_until_any_ok,3}]},ok}}, [{?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE,tc_fail_1, {failed,{error,{{badmatch,2},'_'}}}}}, {?eh,test_stats,{5,5,{0,2}}}, @@ -675,8 +669,7 @@ test_events(repeat_gr_until_any_fail) -> {repeat_1_SUITE,{end_per_group,gr_ok_then_fail_result,[]}}}, {?eh,tc_done, {repeat_1_SUITE, - {end_per_group,gr_ok_then_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_ok_then_fail_result,[]},ok}}], {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}}, {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}}, {?eh,test_stats,{8,0,{0,0}}}, @@ -938,8 +931,7 @@ test_events(repeat_gr_until_all_ok) -> {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}}, {?eh,test_stats,{3,1,{0,0}}}, {?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result_then_ok,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result_then_ok,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE, {end_per_group,repeat_gr_until_all_ok_1, [{repeat_until_all_ok,3}]},ok}}], @@ -1113,8 +1105,7 @@ test_events(repeat_gr_until_all_fail) -> gr_ok_then_fail_result,[]},ok}}, {?eh,test_stats,{3,3,{0,2}}}, {?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_ok_then_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_ok_then_fail_result,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE, {end_per_group,repeat_gr_until_all_fail_1, [{repeat_until_all_fail,2}]},ok}}], @@ -1148,8 +1139,7 @@ test_events(repeat_gr_until_all_fail) -> {init_per_group,repeat_gr_until_all_fail_3, [{repeat_until_all_fail,3}]},ok}}, [{?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}}, {?eh,test_stats,{6,5,{0,3}}}, {?eh,tc_done,{repeat_1_SUITE, @@ -1159,8 +1149,7 @@ test_events(repeat_gr_until_all_fail) -> {init_per_group,repeat_gr_until_all_fail_3, [{repeat_until_all_fail,2}]},ok}}, [{?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_fail_result,[]},ok}}], {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1, {failed,{error,failing_this_time}}}}, {?eh,test_stats,{7,6,{0,3}}}, @@ -1263,8 +1252,7 @@ test_events(repeat_seq_until_any_fail) -> {init_per_group,repeat_seq_until_any_fail_4, [{repeat_until_any_fail,2},sequence]},ok}}, [{?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_ok_then_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_ok_then_fail_result,[]},ok}}], {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1}, {group_result,gr_ok_then_fail_result,failed}}}, {?eh,test_stats,{19,1,{0,3}}}, @@ -1473,8 +1461,7 @@ test_events(repeat_shuffled_seq_until_any_fail) -> [{?eh,tc_start,{repeat_1_SUITE, {end_per_group,gr_ok_then_fail_result,[]}}}, {?eh,tc_done,{repeat_1_SUITE, - {end_per_group,gr_ok_then_fail_result,[]}, - {return_group_result,failed}}}], + {end_per_group,gr_ok_then_fail_result,[]},ok}}], {?eh,tc_start,{repeat_1_SUITE, {end_per_group,repeat_shuffled_seq_until_any_fail_4, [{shuffle,repeated},{repeat_until_any_fail,2},sequence]}}}, diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl index 3fd5943691..3d7049a9c4 100644 --- a/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl +++ b/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl @@ -68,7 +68,7 @@ end_per_testcase(_Case, Config) -> %%%----------------------------------------------------------------- %%% Test cases tc1(_Config) -> - timer:sleep(10000), + ct:sleep(10000), ok. tc2(_Config) -> diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl index dc9abc2863..e4f6e7dcc1 100644 --- a/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl +++ b/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl @@ -68,7 +68,7 @@ end_per_testcase(_Case, Config) -> %%%----------------------------------------------------------------- %%% Test cases tc1(_Config) -> - %% timer:sleep(3000), + %% ct:sleep(3000), ok. tc2(_Config) -> diff --git a/lib/common_test/test/ct_sequence_1_SUITE.erl b/lib/common_test/test/ct_sequence_1_SUITE.erl index 5a775a1117..4055cd789e 100644 --- a/lib/common_test/test/ct_sequence_1_SUITE.erl +++ b/lib/common_test/test/ct_sequence_1_SUITE.erl @@ -182,8 +182,7 @@ test_events(subgroup_return_fail) -> {?eh,test_stats,{0,1,{0,0}}}, {?eh,tc_start, {subgroups_1_SUITE,{end_per_group,return_fail,[]}}}, - {?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]}, - {return_group_result,failed}}}], + {?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},ok}}], {?eh,tc_auto_skip, {subgroups_1_SUITE,{ok_tc,ok_group}, {group_result,return_fail,failed}}}, @@ -191,8 +190,7 @@ test_events(subgroup_return_fail) -> {?eh,tc_start, {subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]}}}, {?eh,tc_done, - {subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]}, - {return_group_result,failed}}}], + {subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -221,8 +219,7 @@ test_events(subgroup_init_fail) -> {?eh,test_stats,{0,0,{0,2}}}, {?eh,tc_start,{subgroups_1_SUITE,{end_per_group,subgroup_init_fail,[sequence]}}}, {?eh,tc_done,{subgroups_1_SUITE, - {end_per_group,subgroup_init_fail,[sequence]}, - {return_group_result,failed}}}], + {end_per_group,subgroup_init_fail,[sequence]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -245,8 +242,7 @@ test_events(subgroup_after_failed_case) -> {?eh,tc_start,{subgroups_1_SUITE, {end_per_group,subgroup_after_failed_case,[sequence]}}}, {?eh,tc_done,{subgroups_1_SUITE, - {end_per_group,subgroup_after_failed_case,[sequence]}, - {return_group_result,failed}}}], + {end_per_group,subgroup_after_failed_case,[sequence]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -266,16 +262,14 @@ test_events(case_after_subgroup_return_fail) -> {?eh,tc_done,{subgroups_1_SUITE,failing_tc,{failed,{error,{{badmatch,3},'_'}}}}}, {?eh,test_stats,{0,1,{0,0}}}, {?eh,tc_start,{subgroups_1_SUITE,{end_per_group,return_fail,[]}}}, - {?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]}, - {return_group_result,failed}}}], + {?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},ok}}], {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,case_after_subgroup_return_fail}, {group_result,return_fail,failed}}}, {?eh,test_stats,{0,1,{0,1}}}, {?eh,tc_start,{subgroups_1_SUITE, {end_per_group,case_after_subgroup_return_fail,[sequence]}}}, {?eh,tc_done,{subgroups_1_SUITE, - {end_per_group,case_after_subgroup_return_fail,[sequence]}, - {return_group_result,failed}}}], + {end_per_group,case_after_subgroup_return_fail,[sequence]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -310,8 +304,7 @@ test_events(case_after_subgroup_fail_init) -> {?eh,tc_start,{subgroups_1_SUITE, {end_per_group,case_after_subgroup_fail_init,[sequence]}}}, {?eh,tc_done,{subgroups_1_SUITE, - {end_per_group,case_after_subgroup_fail_init,[sequence]}, - {return_group_result,failed}}}], + {end_per_group,case_after_subgroup_fail_init,[sequence]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]. diff --git a/lib/common_test/test/ct_shell_SUITE.erl b/lib/common_test/test/ct_shell_SUITE.erl index 4b8c43d800..70c0ab8127 100644 --- a/lib/common_test/test/ct_shell_SUITE.erl +++ b/lib/common_test/test/ct_shell_SUITE.erl @@ -93,7 +93,7 @@ start_interactive(Config) -> test_server:format(Level, "ct_util_server not stopped on ~p yet, waiting 5 s...~n", [CTNode]), - timer:sleep(5000), + ct:sleep(5000), undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server]) end, Events = ct_test_support:get_events(ERPid, Config), diff --git a/lib/common_test/test/ct_skip_SUITE_data/skip/test/auto_skip_4_SUITE.erl b/lib/common_test/test/ct_skip_SUITE_data/skip/test/auto_skip_4_SUITE.erl index 825846cd55..89e202a404 100644 --- a/lib/common_test/test/ct_skip_SUITE_data/skip/test/auto_skip_4_SUITE.erl +++ b/lib/common_test/test/ct_skip_SUITE_data/skip/test/auto_skip_4_SUITE.erl @@ -72,7 +72,7 @@ end_per_group(_GroupName, _Config) -> %% Reason = term() %%-------------------------------------------------------------------- init_per_testcase(tc1, Config) -> - timer:sleep(5000), + ct:sleep(5000), Config; init_per_testcase(_TestCase, Config) -> Config. diff --git a/lib/common_test/test/ct_smoke_test_SUITE.erl b/lib/common_test/test/ct_smoke_test_SUITE.erl index 49b38361e2..6077946c33 100644 --- a/lib/common_test/test/ct_smoke_test_SUITE.erl +++ b/lib/common_test/test/ct_smoke_test_SUITE.erl @@ -480,7 +480,7 @@ events(Test) when Test == dir1 ; Test == dir2 ; {Suite,tc4,{skipped,"Skipping this one"}}}, {?eh,test_stats,{7,0,{1,0}}}, {?eh,tc_start,{Suite,end_per_suite}}, - {?eh,tc_done,{Suite,end_per_suite,ips_data}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -517,7 +517,7 @@ events(Test) when Test == dir1_2 ; Test == suite11_21 -> {happy_11_SUITE,tc4,{skipped,"Skipping this one"}}}, {?eh,test_stats,{7,0,{1,0}}}, {?eh,tc_start,{happy_11_SUITE,end_per_suite}}, - {?eh,tc_done,{happy_11_SUITE,end_per_suite,ips_data}}, + {?eh,tc_done,{happy_11_SUITE,end_per_suite,ok}}, {?eh,tc_start,{happy_21_SUITE,init_per_suite}}, {?eh,tc_done,{happy_21_SUITE,init_per_suite,ok}}, {?eh,tc_start,{happy_21_SUITE,tc1}}, @@ -546,7 +546,7 @@ events(Test) when Test == dir1_2 ; Test == suite11_21 -> {happy_21_SUITE,tc4,{skipped,"Skipping this one"}}}, {?eh,test_stats,{14,0,{2,0}}}, {?eh,tc_start,{happy_21_SUITE,end_per_suite}}, - {?eh,tc_done,{happy_21_SUITE,end_per_suite,ips_data}}, + {?eh,tc_done,{happy_21_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -563,7 +563,7 @@ events(Test) when Test == tc111 ; Test == tc211 -> {?eh,tc_done,{Suite,tc1,ok}}, {?eh,test_stats,{1,0,{0,0}}}, {?eh,tc_start,{Suite,end_per_suite}}, - {?eh,tc_done,{Suite,end_per_suite,ips_data}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; @@ -582,7 +582,7 @@ events(tc111_112) -> {?eh,tc_done,{happy_11_SUITE,tc2,ok}}, {?eh,test_stats,{2,0,{0,0}}}, {?eh,tc_start,{happy_11_SUITE,end_per_suite}}, - {?eh,tc_done,{happy_11_SUITE,end_per_suite,ips_data}}, + {?eh,tc_done,{happy_11_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]. diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl index e20832e1e7..07f7bf02e4 100644 --- a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl +++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl @@ -117,12 +117,12 @@ break(_Config) -> start_stop(Config) -> ok = ct_snmp:start(Config,snmp1,snmp_app1), - timer:sleep(1000), + ct:sleep(1000), {snmp,_,_} = lists:keyfind(snmp,1,application:which_applications()), [_|_] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)), ok = ct_snmp:stop(Config), - timer:sleep(1000), + ct:sleep(1000), false = lists:keyfind(snmp,1,application:which_applications()), [] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)), ok. diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl index bd5d76266a..1d3f5918d2 100644 --- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl @@ -283,14 +283,14 @@ large_string(_) -> %% yield the same result as the single request case. ok = ct_telnet:send(Handle, "echo_sep "++BigString), - timer:sleep(1000), + ct:sleep(1000), {ok,Data1} = ct_telnet:get_data(Handle), ct:log("[GET DATA #1] Received ~w chars: ~s", [length(lists:flatten(Data1)),Data1]), VerifyStr = [C || C <- lists:flatten(Data1), C/=$ , C/=$\r, C/=$\n, C/=$>], ok = ct_telnet:send(Handle, "echo_sep "++BigString), - timer:sleep(50), + ct:sleep(50), {ok,Data2} = ct_telnet:get_data(Handle), ct:log("[GET DATA #2] Received ~w chars: ~s", [length(lists:flatten(Data2)),Data2]), VerifyStr = [C || C <- lists:flatten(Data2), C/=$ , C/=$\r, C/=$\n, C/=$>], @@ -316,7 +316,7 @@ server_speaks(_) -> "echo_no_prompt This is the second message"), %% Let ct_telnet_client get an idle timeout. This should print the %% two messages to the log. Note that the buffers are not flushed here! - timer:sleep(15000), + ct:sleep(15000), ok = ct_telnet_client:send_data(Backdoor, "echo_no_prompt This is the third message"), {ok,_} = ct_telnet:expect(Handle, ["first.*second.*third"], @@ -326,7 +326,7 @@ server_speaks(_) -> ok = ct_telnet_client:send_data(Backdoor, "echo_no_prompt This is the fourth message"), %% give the server time to respond - timer:sleep(2000), + ct:sleep(2000), %% closing the connection should print last message in log ok = ct_telnet:close(Handle), ok. @@ -338,11 +338,11 @@ server_disconnects(_) -> ok = ct_telnet:send(Handle, "disconnect_after 1500"), %% wait until the get_data operation (triggered by send/2) times out %% before sending the msg - timer:sleep(500), + ct:sleep(500), ok = ct_telnet:send(Handle, "echo_no_prompt This is the message"), %% when the server closes the connection, the last message should be %% printed in the log - timer:sleep(3000), + ct:sleep(3000), _ = ct_telnet:close(Handle), ok. diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl index 06fa6ac638..d30feb0bd2 100644 --- a/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl +++ b/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl @@ -76,7 +76,7 @@ end_per_group(_GroupName, _Config) -> %% Reason = term() %%-------------------------------------------------------------------- init_per_testcase(tc1, Config) -> - timer:sleep(5000), + ct:sleep(5000), Config; init_per_testcase(tc8, _Config) -> {skip,"tc8 skipped"}; @@ -92,7 +92,7 @@ init_per_testcase(_TestCase, Config) -> %% Config0 = Config1 = [tuple()] %%-------------------------------------------------------------------- end_per_testcase(tc2, Config) -> - timer:sleep(5000); + ct:sleep(5000); end_per_testcase(tc12, Config) -> ct:comment("end_per_testcase(tc12) called!"), ct:pal("end_per_testcase(tc12) called!", []), @@ -146,7 +146,7 @@ tc2(_) -> timeout_in_end_per_testcase. tc3(_) -> - timer:sleep(5000). + ct:sleep(5000). tc4(_) -> exit(failed_on_purpose). @@ -186,7 +186,7 @@ gtc2(_) -> tc12(_) -> F = fun() -> ct:abort_current_testcase('stopping tc12') end, spawn(F), - timer:sleep(1000), + ct:sleep(1000), exit(should_have_been_aborted). tc13(_) -> diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 2c1f98d63b..ca736fe400 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -51,12 +51,12 @@ init_per_suite(Config) -> init_per_suite(Config, 50). init_per_suite(Config, Level) -> + ScaleFactor = test_server:timetrap_scale_factor(), case os:type() of {win32, _} -> %% Extend timeout to 1 hour for windows as starting node %% can take a long time there - test_server:timetrap( 60*60*1000 * - test_server:timetrap_scale_factor()); + test_server:timetrap( 60*60*1000 * ScaleFactor ); _ -> ok end, @@ -67,6 +67,16 @@ init_per_suite(Config, Level) -> _ -> ok end, + + {Mult,Scale} = test_server_ctrl:get_timetrap_parameters(), + test_server:format(Level, "Timetrap multiplier: ~w~n", [Mult]), + if Scale == true -> + test_server:format(Level, "Timetrap scale factor: ~w~n", + [ScaleFactor]); + true -> + ok + end, + start_slave(Config, Level). start_slave(Config, Level) -> diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl index c2670316b6..bc19283a47 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE.erl @@ -795,7 +795,7 @@ test_events(skip_all_groups) -> {?eh,test_stats,{0,0,{12,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_4},"SKIPPED!"}}, {?eh,tc_start,{groups_11_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; @@ -840,7 +840,7 @@ test_events(skip_group) -> {?eh,test_stats,{2,0,{6,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_2}, "SKIPPED!"}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; @@ -876,7 +876,7 @@ test_events(skip_group_all_testcases) -> {?eh,test_stats,{0,0,{4,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b}, "SKIPPED!"}}, - {?eh,tc_done,{groups_11_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; @@ -1065,7 +1065,7 @@ test_events(skip_subgroup) -> {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_4,[]},ok}}], {?eh,tc_start,{groups_12_SUITE,end_per_suite}}, - {?eh,tc_done,{groups_12_SUITE,end_per_suite,init}}, + {?eh,tc_done,{groups_12_SUITE,end_per_suite,ok}}, {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl index 69c06f9b83..e5de5df1a8 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl @@ -285,7 +285,7 @@ testcase_5a(Config) -> %% increase chance the done event will come %% during execution of subgroup (could be %% tricky to handle) - timer:sleep(3), + ct:sleep(3), ok. testcase_5b() -> []. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl index cd517876df..dcd361d658 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl @@ -278,7 +278,7 @@ testcase_5a(Config) -> %% increase chance the done event will come %% during execution of subgroup (could be %% tricky to handle) - timer:sleep(3), + ct:sleep(3), ok. testcase_5b() -> []. diff --git a/lib/common_test/test/telnet_server.erl b/lib/common_test/test/telnet_server.erl index d25ee62d38..11959c3e12 100644 --- a/lib/common_test/test/telnet_server.erl +++ b/lib/common_test/test/telnet_server.erl @@ -59,7 +59,7 @@ init(Opts) -> accept(State), ok = gen_tcp:close(LSock), dbg("telnet_server closed the listen socket ~p\n", [LSock]), - timer:sleep(1000), + ct:sleep(1000), ok. listen(0, _Port, _Opts) -> @@ -68,7 +68,7 @@ listen(Retries, Port, Opts) -> case gen_tcp:listen(Port, Opts) of {error,eaddrinuse} -> dbg("Listen port not released, trying again..."), - timer:sleep(5000), + ct:sleep(5000), listen(Retries-1, Port, Opts); Ok = {ok,_LSock} -> Ok; @@ -220,7 +220,7 @@ do_handle_data("echo_sep " ++ Data,State) -> Msgs = string:tokens(Data," "), lists:foreach(fun(Msg) -> send(Msg,State), - timer:sleep(10) + ct:sleep(10) end, Msgs), send("\r\n> ",State), {ok,State}; @@ -292,7 +292,7 @@ send_loop(T0,T,Data,State) -> ok; true -> send(Data,State), - timer:sleep(500), + ct:sleep(500), send_loop(T0,T,Data,State) end. diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 2032392821..7c4cebdc28 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -159,6 +159,10 @@ $(EBIN)/beam_asm.beam: $(ESRC)/beam_asm.erl $(EGEN)/beam_opcodes.hrl $(EBIN)/cerl_inline.beam: $(ESRC)/cerl_inline.erl $(V_ERLC) $(ERL_COMPILE_FLAGS) +nowarn_shadow_vars -o$(EBIN) $< +# Inlining core_parse is slow and has no benefit. +$(EBIN)/core_parse.beam: $(EGEN)/core_parse.erl + $(V_ERLC) $(subst +inline,,$(ERL_COMPILE_FLAGS)) -o$(EBIN) $< + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 92f09e400c..5216f39296 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -252,13 +252,6 @@ combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) -> %% opt([Instruction]) -> [Instruction] %% Optimize the instruction stream inside a basic block. -opt([{set,[Dst],As,{bif,Bif,Fail}}=I1, - {set,[Dst],[Dst],{bif,'not',Fail}}=I2|Is]) -> - %% Get rid of the 'not' if the operation can be inverted. - case inverse_comp_op(Bif) of - none -> [I1,I2|opt(Is)]; - RevBif -> [{set,[Dst],As,{bif,RevBif,Fail}}|opt(Is)] - end; opt([{set,[X],[X],move}|Is]) -> opt(Is); opt([{set,_,_,{line,_}}=Line1, {set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1, @@ -428,18 +421,6 @@ x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N)); x_live([_|Rs], Regs) -> x_live(Rs, Regs); x_live([], Regs) -> Regs. -%% inverse_comp_op(Op) -> none|RevOp - -inverse_comp_op('=:=') -> '=/='; -inverse_comp_op('=/=') -> '=:='; -inverse_comp_op('==') -> '/='; -inverse_comp_op('/=') -> '=='; -inverse_comp_op('>') -> '=<'; -inverse_comp_op('<') -> '>='; -inverse_comp_op('>=') -> '<'; -inverse_comp_op('=<') -> '>'; -inverse_comp_op(_) -> none. - %%% %%% Evaluation of constant bit fields. %%% diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl index a452d30b61..5ed9c16d61 100644 --- a/lib/compiler/src/beam_bool.erl +++ b/lib/compiler/src/beam_bool.erl @@ -787,6 +787,9 @@ is_not_used(R, Is, Label, #st{ll=Ll}) -> initialized_regs(Is) -> initialized_regs(Is, ordsets:new()). +initialized_regs([{set,Dst,_Src,{alloc,Live,_}}|_], Regs0) -> + Regs = add_init_regs(free_vars_regs(Live), Regs0), + add_init_regs(Dst, Regs); initialized_regs([{set,Dst,Src,_}|Is], Regs) -> initialized_regs(Is, add_init_regs(Dst, add_init_regs(Src, Regs))); initialized_regs([{test,_,_,Src}|Is], Regs) -> diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl index 7cd07dc3be..f4515ba2a7 100644 --- a/lib/compiler/src/beam_dead.erl +++ b/lib/compiler/src/beam_dead.erl @@ -98,6 +98,12 @@ move_move_into_block([], Acc) -> reverse(Acc). forward(Is, Lc) -> forward(Is, gb_trees:empty(), Lc, []). +forward([{move,_,_}=Move|[{label,L}|_]=Is], D, Lc, Acc) -> + %% move/2 followed by jump/1 is optimized by backward/3. + forward([Move,{jump,{f,L}}|Is], D, Lc, Acc); +forward([{bif,_,_,_,_}=Bif|[{label,L}|_]=Is], D, Lc, Acc) -> + %% bif/4 followed by jump/1 is optimized by backward/3. + forward([Bif,{jump,{f,L}}|Is], D, Lc, Acc); forward([{block,[]}|Is], D, Lc, Acc) -> %% Empty blocks can prevent optimizations. forward(Is, D, Lc, Acc); @@ -124,6 +130,8 @@ forward([{label,Lbl}=LblI|[{move,Lit,Dst}|Is1]=Is0], D, Lc, Acc) -> _ -> Is0 %Keep move instruction. end, forward(Is, D, Lc, [LblI|Acc]); +forward([{test,is_eq_exact,_,[Same,Same]}|Is], D, Lc, Acc) -> + forward(Is, D, Lc, Acc); forward([{test,is_eq_exact,_,[Dst,Src]}=I, {block,[{set,[Dst],[Src],move}|Bl]}|Is], D, Lc, Acc) -> forward([I,{block,Bl}|Is], D, Lc, Acc); @@ -234,10 +242,8 @@ backward([{select,select_val,Reg,{f,Fail0},List0}|Is], D, Acc) -> Fail = shortcut_bs_test(Fail1, Is, D), Sel = {select,select_val,Reg,{f,Fail},List}, backward(Is, D, [Sel|Acc]); -backward([{jump,{f,To0}},{move,Src0,Reg}|Is], D, Acc) -> - To1 = shortcut_select_label(To0, Reg, Src0, D), - {To,Src} = shortcut_boolean_label(To1, Reg, Src0, D), - Move = {move,Src,Reg}, +backward([{jump,{f,To0}},{move,Src,Reg}=Move|Is], D, Acc) -> + To = shortcut_select_label(To0, Reg, Src, D), Jump = {jump,{f,To}}, case beam_utils:is_killed_at(Reg, To, D) of false -> backward([Move|Is], D, [Jump|Acc]); @@ -330,16 +336,6 @@ shortcut_label(To0, D) -> shortcut_select_label(To, Reg, Lit, D) -> shortcut_rel_op(To, is_ne_exact, [Reg,Lit], D). -shortcut_boolean_label(To0, Reg, {atom,Bool0}=Lit, D) when is_boolean(Bool0) -> - case beam_utils:code_at(To0, D) of - [{line,_},{bif,'not',_,[Reg],Reg},{jump,{f,To}}|_] -> - Bool = {atom,not Bool0}, - {shortcut_select_label(To, Reg, Bool, D),Bool}; - _ -> - {To0,Lit} - end; -shortcut_boolean_label(To, _, Bool, _) -> {To,Bool}. - %% Replace a comparison operator with a test instruction and a jump. %% For example, if we have this code: %% diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl index 97a8c7ba70..5abacc8d5d 100644 --- a/lib/compiler/src/beam_peep.erl +++ b/lib/compiler/src/beam_peep.erl @@ -108,14 +108,14 @@ peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) -> %% has succeeded. peep(Is, gb_sets:empty(), [I|Acc]); true -> - Test = {Op,Ops}, - case gb_sets:is_element(Test, SeenTests0) of + case is_test_redundant(Op, Ops, SeenTests0) of true -> - %% This test has already succeeded and + %% This test or a similar test has already succeeded and %% is therefore redundant. peep(Is, SeenTests0, Acc); false -> %% Remember that we have seen this test. + Test = {Op,Ops}, SeenTests = gb_sets:insert(Test, SeenTests0), peep(Is, SeenTests, [I|Acc]) end @@ -136,6 +136,15 @@ peep([I|Is], _, Acc) -> peep(Is, gb_sets:empty(), [I|Acc]); peep([], _, Acc) -> reverse(Acc). +is_test_redundant(Op, Ops, Seen) -> + gb_sets:is_element({Op,Ops}, Seen) orelse + is_test_redundant_1(Op, Ops, Seen). + +is_test_redundant_1(is_boolean, [R], Seen) -> + gb_sets:is_element({is_eq_exact,[R,{atom,false}]}, Seen) orelse + gb_sets:is_element({is_eq_exact,[R,{atom,true}]}, Seen); +is_test_redundant_1(_, _, _) -> false. + kill_seen(Dst, Seen0) -> gb_sets:from_ordset(kill_seen_1(gb_sets:to_list(Seen0), Dst)). diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 3d4b9ee0c6..8367a1e19e 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -138,7 +138,8 @@ ]). -export_type([c_binary/0, c_bitstr/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, - c_literal/0, c_map/0, c_map_pair/0, c_module/0, c_tuple/0, + c_let/0, c_literal/0, c_map/0, c_map_pair/0, + c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]). -include("core_parse.hrl"). diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index ea1959d0f8..0d020578f5 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -96,7 +96,7 @@ t=[], %Types in_guard=false}). %In guard or not. --type type_info() :: cerl:cerl() | 'bool'. +-type type_info() :: cerl:cerl() | 'bool' | 'integer'. -type yes_no_maybe() :: 'yes' | 'no' | 'maybe'. -type sub() :: #sub{}. @@ -297,7 +297,8 @@ expr(#c_seq{arg=Arg0,body=B0}=Seq0, Ctxt, Sub) -> false -> Seq0#c_seq{arg=Arg,body=B1} end end; -expr(#c_let{}=Let, Ctxt, Sub) -> +expr(#c_let{}=Let0, Ctxt, Sub) -> + Let = opt_case_in_let(Let0), case simplify_let(Let, Sub) of impossible -> %% The argument for the let is "simple", i.e. has no @@ -829,16 +830,16 @@ eval_rel_op(Call, '=:=', [Term,#c_literal{val=true}], Sub) -> maybe -> Call; no -> #c_literal{val=false} end; -eval_rel_op(Call, '==', Ops, _Sub) -> - case is_exact_eq_ok(Ops) of +eval_rel_op(Call, '==', Ops, Sub) -> + case is_exact_eq_ok(Ops, Sub) of true -> Name = #c_literal{anno=cerl:get_ann(Call),val='=:='}, Call#c_call{name=Name}; false -> Call end; -eval_rel_op(Call, '/=', Ops, _Sub) -> - case is_exact_eq_ok(Ops) of +eval_rel_op(Call, '/=', Ops, Sub) -> + case is_exact_eq_ok(Ops, Sub) of true -> Name = #c_literal{anno=cerl:get_ann(Call),val='=/='}, Call#c_call{name=Name}; @@ -847,11 +848,17 @@ eval_rel_op(Call, '/=', Ops, _Sub) -> end; eval_rel_op(Call, _, _, _) -> Call. -is_exact_eq_ok([#c_literal{val=Lit}|_]) -> +is_exact_eq_ok([A,B]=L, Sub) -> + case is_int_type(A, Sub) =:= yes andalso is_int_type(B, Sub) =:= yes of + true -> true; + false -> is_exact_eq_ok_1(L) + end. + +is_exact_eq_ok_1([#c_literal{val=Lit}|_]) -> is_non_numeric(Lit); -is_exact_eq_ok([_|T]) -> - is_exact_eq_ok(T); -is_exact_eq_ok([]) -> false. +is_exact_eq_ok_1([_|T]) -> + is_exact_eq_ok_1(T); +is_exact_eq_ok_1([]) -> false. is_non_numeric([H|T]) -> is_non_numeric(H) andalso is_non_numeric(T); @@ -963,7 +970,7 @@ eval_element(Call, #c_literal{val=Pos}, Tuple, Types) 1 =< Pos, Pos =< length(Es) -> El = lists:nth(Pos, Es), try - pat_to_expr(El) + cerl:set_ann(pat_to_expr(El), [compiler_generated]) catch throw:impossible -> Call @@ -1008,28 +1015,32 @@ eval_is_record(Call, _, _, _, _) -> Call. %% eval_setelement(Call, Pos, Tuple, NewVal) -> Core. %% Evaluates setelement/3 if position Pos is an integer -%% the shape of the tuple Tuple is known. +%% and the shape of the tuple Tuple is known. %% -eval_setelement(Call, Pos, Tuple, NewVal) -> - try - eval_setelement_1(Pos, Tuple, NewVal) - catch - error:_ -> - Call - end. - -eval_setelement_1(#c_literal{val=Pos}, #c_tuple{anno=A,es=Es}, NewVal) - when is_integer(Pos) -> - ann_c_tuple(A, eval_setelement_2(Pos, Es, NewVal)); -eval_setelement_1(#c_literal{val=Pos}, #c_literal{anno=A,val=Es0}, NewVal) +eval_setelement(Call, #c_literal{val=Pos}, Tuple, NewVal) when is_integer(Pos) -> - Es = [#c_literal{anno=A,val=E} || E <- tuple_to_list(Es0)], - ann_c_tuple(A, eval_setelement_2(Pos, Es, NewVal)). + case cerl:is_data(Tuple) of + false -> + Call; + true -> + Es0 = case cerl:is_c_tuple(Tuple) of + false -> []; + true -> cerl:tuple_es(Tuple) + end, + if + 1 =< Pos, Pos =< length(Es0) -> + Es = eval_setelement_1(Pos, Es0, NewVal), + cerl:update_c_tuple(Tuple, Es); + true -> + eval_failure(Call, badarg) + end + end; +eval_setelement(Call, _, _, _) -> Call. -eval_setelement_2(1, [_|T], NewVal) -> +eval_setelement_1(1, [_|T], NewVal) -> [NewVal|T]; -eval_setelement_2(Pos, [H|T], NewVal) when Pos > 1 -> - [H|eval_setelement_2(Pos-1, T, NewVal)]. +eval_setelement_1(Pos, [H|T], NewVal) when Pos > 1 -> + [H|eval_setelement_1(Pos-1, T, NewVal)]. %% eval_failure(Call, Reason) -> Core. %% Warn for a call that will fail and replace the call with @@ -1955,46 +1966,125 @@ letify(Bs, Body) -> cerl:ann_c_let(Ann, [V], Val, B) end, Body, Bs). -%% opt_case_in_let(LetExpr) -> LetExpr' +%% opt_not_in_let(Let) -> Cerl +%% Try to optimize away a 'not' operator in a 'let'. -opt_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let, Sub) -> - opt_case_in_let_0(Vs, Arg, B, Let, Sub). +-spec opt_not_in_let(cerl:c_let()) -> cerl:cerl(). -opt_case_in_let_0([#c_var{name=V}], Arg, - #c_case{arg=#c_var{name=V},clauses=Cs}=Case, Let, Sub) -> - case opt_case_in_let_1(V, Arg, Cs) of - impossible -> - case is_simple_case_arg(Arg) andalso - not core_lib:is_var_used(V, Case#c_case{arg=#c_literal{val=nil}}) of - true -> - expr(opt_bool_case(Case#c_case{arg=Arg,clauses=Cs}), sub_new(Sub)); - false -> - Let +opt_not_in_let(#c_let{vars=[_]=Vs0,arg=Arg0,body=Body0}=Let) -> + case opt_not_in_let(Vs0, Arg0, Body0) of + {[],#c_values{es=[]},Body} -> + Body; + {Vs,Arg,Body} -> + Let#c_let{vars=Vs,arg=Arg,body=Body} + end; +opt_not_in_let(Let) -> Let. + +%% opt_not_in_let(Vs, Arg, Body) -> {Vs',Arg',Body'} +%% Try to optimize away a 'not' operator in a 'let'. + +-spec opt_not_in_let([cerl:c_var()], cerl:cerl(), cerl:cerl()) -> + {[cerl:c_var()],cerl:cerl(),cerl:cerl()}. + +opt_not_in_let([#c_var{name=V}]=Vs0, Arg0, Body0) -> + case cerl:type(Body0) of + call -> + %% let <V> = Expr in not V ==> + %% let <> = <> in notExpr + case opt_not_in_let_1(V, Body0, Arg0) of + no -> + {Vs0,Arg0,Body0}; + {yes,Body} -> + {[],#c_values{es=[]},Body} end; - Expr -> Expr + 'let' -> + %% let <V> = Expr in let <Var> = not V in Body ==> + %% let <Var> = notExpr in Body + %% V must not be used in Body. + LetArg = cerl:let_arg(Body0), + case opt_not_in_let_1(V, LetArg, Arg0) of + no -> + {Vs0,Arg0,Body0}; + {yes,Arg} -> + LetBody = cerl:let_body(Body0), + case core_lib:is_var_used(V, LetBody) of + true -> + {Vs0,Arg0,Body0}; + false -> + LetVars = cerl:let_vars(Body0), + {LetVars,Arg,LetBody} + end + end; + _ -> + {Vs0,Arg0,Body0} end; -opt_case_in_let_0(_, _, _, Let, _) -> Let. - -opt_case_in_let_1(V, Arg, Cs) -> - try - opt_case_in_let_2(V, Arg, Cs) - catch - _:_ -> impossible +opt_not_in_let(Vs, Arg, Body) -> + {Vs,Arg,Body}. + +opt_not_in_let_1(V, Call, Body) -> + case Call of + #c_call{module=#c_literal{val=erlang}, + name=#c_literal{val='not'}, + args=[#c_var{name=V}]} -> + opt_not_in_let_2(Body); + _ -> + no end. -opt_case_in_let_2(V, Arg0, - [#c_clause{pats=[#c_tuple{es=Es}], - guard=#c_literal{val=true},body=B}|_]) -> +opt_not_in_let_2(#c_case{clauses=Cs0}=Case) -> + Vars = make_vars([], 1), + Body = #c_call{module=#c_literal{val=erlang}, + name=#c_literal{val='not'}, + args=Vars}, + Cs = [begin + Let = #c_let{vars=Vars,arg=B,body=Body}, + C#c_clause{body=opt_not_in_let(Let)} + end || #c_clause{body=B}=C <- Cs0], + {yes,Case#c_case{clauses=Cs}}; +opt_not_in_let_2(#c_call{}=Call0) -> + invert_call(Call0); +opt_not_in_let_2(_) -> no. + +invert_call(#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=Name0}, + args=[_,_]}=Call) -> + case inverse_rel_op(Name0) of + no -> no; + Name -> {yes,Call#c_call{name=#c_literal{val=Name}}} + end; +invert_call(#c_call{}) -> no. + +%% inverse_rel_op(Op) -> no | RevOp + +inverse_rel_op('=:=') -> '=/='; +inverse_rel_op('=/=') -> '=:='; +inverse_rel_op('==') -> '/='; +inverse_rel_op('/=') -> '=='; +inverse_rel_op('>') -> '=<'; +inverse_rel_op('<') -> '>='; +inverse_rel_op('>=') -> '<'; +inverse_rel_op('=<') -> '>'; +inverse_rel_op(_) -> no. - %% In {V1,V2,...} = case E of P -> ... {Val1,Val2,...}; ... end. - %% avoid building tuples, by converting tuples to multiple values. - %% (The optimisation is not done if the built tuple is used or returned.) - true = all(fun (#c_var{}) -> true; - (_) -> false end, Es), %Only variables in tuple - false = core_lib:is_var_used(V, B), %Built tuple must not be used. - Arg1 = tuple_to_values(Arg0, length(Es)), %Might fail. - #c_let{vars=Es,arg=Arg1,body=B}. +%% opt_bool_case_in_let(LetExpr, Sub) -> Core + +opt_bool_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let, Sub) -> + opt_case_in_let_1(Vs, Arg, B, Let, Sub). + +opt_case_in_let_1([#c_var{name=V}], Arg, + #c_case{arg=#c_var{name=V}}=Case0, Let, Sub) -> + case is_simple_case_arg(Arg) of + true -> + Case = opt_bool_case(Case0#c_case{arg=Arg}), + case core_lib:is_var_used(V, Case) of + false -> expr(Case, sub_new(Sub)); + true -> Let + end; + false -> + Let + end; +opt_case_in_let_1(_, _, _, Let, _) -> Let. %% is_simple_case_arg(Expr) -> true|false %% Determine whether the Expr is simple enough to be worth @@ -2036,7 +2126,7 @@ is_bool_expr(#c_clause{body=B}, Sub) -> is_bool_expr(B, Sub); is_bool_expr(#c_let{vars=[V],arg=Arg,body=B}, Sub0) -> Sub = case is_bool_expr(Arg, Sub0) of - true -> update_types(V, [#c_literal{val=true}], Sub0); + true -> update_types(V, [bool], Sub0); false -> Sub0 end, is_bool_expr(B, Sub); @@ -2122,38 +2212,6 @@ is_safe_bool_expr_list([C|Cs], Sub, BoolVars) -> end; is_safe_bool_expr_list([], _, _) -> true. -%% tuple_to_values(Expr, TupleArity) -> Expr' -%% Convert tuples in return position of arity TupleArity to values. -%% Throws an exception for constructs that are not handled. - -tuple_to_values(#c_tuple{es=Es}, Arity) when length(Es) =:= Arity -> - core_lib:make_values(Es); -tuple_to_values(#c_literal{val=Tuple}=Lit, Arity) when tuple_size(Tuple) =:= Arity -> - Es = [Lit#c_literal{val=E} || E <- tuple_to_list(Tuple)], - core_lib:make_values(Es); -tuple_to_values(#c_case{clauses=Cs0}=Case, Arity) -> - Cs1 = [tuple_to_values(E, Arity) || E <- Cs0], - Case#c_case{clauses=Cs1}; -tuple_to_values(#c_seq{body=B0}=Seq, Arity) -> - Seq#c_seq{body=tuple_to_values(B0, Arity)}; -tuple_to_values(#c_let{body=B0}=Let, Arity) -> - Let#c_let{body=tuple_to_values(B0, Arity)}; -tuple_to_values(#c_receive{clauses=Cs0,timeout=Timeout,action=A0}=Rec, Arity) -> - Cs = [tuple_to_values(E, Arity) || E <- Cs0], - A = case Timeout of - #c_literal{val=infinity} -> A0; - _ -> tuple_to_values(A0, Arity) - end, - Rec#c_receive{clauses=Cs,action=A}; -tuple_to_values(#c_clause{body=B0}=Clause, Arity) -> - B = tuple_to_values(B0, Arity), - Clause#c_clause{body=B}; -tuple_to_values(Expr, _) -> - case will_fail(Expr) of - true -> Expr; - false -> erlang:error({not_handled,Expr}) - end. - %% simplify_let(Let, Sub) -> Expr | impossible %% If the argument part of an let contains a complex expression, such %% as a let or a sequence, move the original let body into the complex @@ -2180,7 +2238,7 @@ move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner, Arg = body(Arg0, Sub0), ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}), {OuterVs,ScopeSub} = pattern_list(OuterVs0, ScopeSub0), - + OuterBody = body(OuterBody0, ScopeSub), {InnerVs,Sub} = pattern_list(InnerVs0, Sub0), @@ -2258,50 +2316,179 @@ move_let_into_expr(_Let, _Expr, _Sub) -> impossible. is_failing_clause(#c_clause{body=B}) -> will_fail(B). +%% opt_case_in_let(Let) -> Let' +%% Try to avoid building tuples that are immediately matched. +%% A common pattern is: +%% +%% {V1,V2,...} = case E of P -> ... {Val1,Val2,...}; ... end +%% +%% In Core Erlang the pattern would look like this: +%% +%% let <V> = case E of +%% ... -> ... {Val1,Val2} +%% ... +%% end, +%% in case V of +%% {A,B} -> ... <use A and B> ... +%% end +%% +%% Rewrite this to: +%% +%% let <V1,V2> = case E of +%% ... -> ... <Val1,Val2> +%% ... +%% end, +%% in +%% let <V> = {V1,V2} +%% in case V of +%% {A,B} -> ... <use A and B> ... +%% end +%% +%% Note that the second 'case' is unchanged. The other optimizations +%% in this module will eliminate the building of the tuple and +%% rewrite the second case to: +%% +%% case <V1,V2> of +%% <A,B> -> ... <use A and B> ... +%% end +%% + +opt_case_in_let(#c_let{vars=Vs,arg=Arg0,body=B}=Let0) -> + case matches_data(Vs, B) of + {yes,TypeSig} -> + case delay_build(Arg0, TypeSig) of + no -> + Let0; + {yes,Vars,Arg,Data} -> + InnerLet = Let0#c_let{arg=Data}, + Let0#c_let{vars=Vars,arg=Arg,body=InnerLet} + end; + no -> + Let0 + end. + +matches_data([#c_var{name=V}], #c_case{arg=#c_var{name=V}, + clauses=[#c_clause{pats=[P]}|_]}) -> + case cerl:is_data(P) of + false -> + no; + true -> + case cerl:data_type(P) of + {atomic,_} -> + no; + Type -> + {yes,{Type,cerl:data_arity(P)}} + end + end; +matches_data(_, _) -> no. + +delay_build(Core, TypeSig) -> + case cerl:is_data(Core) of + true -> no; + false -> delay_build_1(Core, TypeSig) + end. + +delay_build_1(Core0, TypeSig) -> + try delay_build_expr(Core0, TypeSig) of + Core -> + {Type,Arity} = TypeSig, + Vars = make_vars([], Arity), + Data = cerl:ann_make_data([compiler_generated], Type, Vars), + {yes,Vars,Core,Data} + catch + throw:impossible -> + no + end. + +delay_build_cs([#c_clause{body=B0}=C0|Cs], TypeSig) -> + B = delay_build_expr(B0, TypeSig), + C = C0#c_clause{body=B}, + [C|delay_build_cs(Cs, TypeSig)]; +delay_build_cs([], _) -> []. + +delay_build_expr(Core, {Type,Arity}=TypeSig) -> + case cerl:is_data(Core) of + false -> + delay_build_expr_1(Core, TypeSig); + true -> + case {cerl:data_type(Core),cerl:data_arity(Core)} of + {Type,Arity} -> + core_lib:make_values(cerl:data_es(Core)); + {_,_} -> + throw(impossible) + end + end. + +delay_build_expr_1(#c_case{clauses=Cs0}=Case, TypeSig) -> + Cs = delay_build_cs(Cs0, TypeSig), + Case#c_case{clauses=Cs}; +delay_build_expr_1(#c_let{body=B0}=Let, TypeSig) -> + B = delay_build_expr(B0, TypeSig), + Let#c_let{body=B}; +delay_build_expr_1(#c_receive{clauses=Cs0, + timeout=Timeout, + action=A0}=Rec, TypeSig) -> + Cs = delay_build_cs(Cs0, TypeSig), + A = case Timeout of + #c_literal{val=infinity} -> A0; + _ -> delay_build_expr(A0, TypeSig) + end, + Rec#c_receive{clauses=Cs,action=A}; +delay_build_expr_1(#c_seq{body=B0}=Seq, TypeSig) -> + B = delay_build_expr(B0, TypeSig), + Seq#c_seq{body=B}; +delay_build_expr_1(Core, _TypeSig) -> + case will_fail(Core) of + true -> Core; + false -> throw(impossible) + end. + %% opt_simple_let(#c_let{}, Context, Sub) -> CoreTerm %% Optimize a let construct that does not contain any lets in %% in its argument. -opt_simple_let(#c_let{arg=Arg0}=Let, Ctxt, Sub0) -> - Arg = body(Arg0, value, Sub0), %This is a body +opt_simple_let(Let0, Ctxt, Sub) -> + case opt_not_in_let(Let0) of + #c_let{}=Let -> + opt_simple_let_0(Let, Ctxt, Sub); + Expr -> + expr(Expr, Ctxt, Sub) + end. + +opt_simple_let_0(#c_let{arg=Arg0}=Let, Ctxt, Sub) -> + Arg = body(Arg0, value, Sub), %This is a body case will_fail(Arg) of true -> Arg; - false -> opt_simple_let_1(Let, Arg, Ctxt, Sub0) + false -> opt_simple_let_1(Let, Arg, Ctxt, Sub) end. opt_simple_let_1(#c_let{vars=Vs0,body=B0}=Let, Arg0, Ctxt, Sub0) -> %% Optimise let and add new substitutions. - {Vs,Args,Sub1} = let_substs(Vs0, Arg0, Sub0), - BodySub = case {Vs,Args} of - {[V],[A]} -> - case is_bool_expr(A, Sub0) of - true -> - update_types(V, [#c_literal{val=true}], Sub1); - false -> - Sub1 - end; - {_,_} -> Sub1 - end, - B = body(B0, Ctxt, BodySub), - Arg = core_lib:make_values(Args), - opt_simple_let_2(Let, Vs, Arg, B, Ctxt, Sub1). - -opt_simple_let_2(Let0, Vs0, Arg0, Body, Ctxt, Sub) -> + {Vs1,Args,Sub1} = let_substs(Vs0, Arg0, Sub0), + BodySub = update_let_types(Vs1, Args, Sub1), + B1 = body(B0, Ctxt, BodySub), + Arg1 = core_lib:make_values(Args), + {Vs,Arg,B} = opt_not_in_let(Vs1, Arg1, B1), + opt_simple_let_2(Let, Vs, Arg, B, B0, Ctxt, Sub1). + +opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) -> case {Vs0,Arg0,Body} of - {[#c_var{name=N1}],Arg,#c_var{name=N2}} -> + {[#c_var{name=N1}],Arg1,#c_var{name=N2}} -> case N1 =:= N2 of true -> %% let <Var> = Arg in <Var> ==> Arg - Arg; + Arg1; false -> %% let <Var> = Arg in <OtherVar> ==> seq Arg OtherVar + Arg = maybe_suppress_warnings(Arg1, Vs0, PrevBody, Ctxt), expr(#c_seq{arg=Arg,body=Body}, Ctxt, sub_new_preserve_types(Sub)) end; {[],#c_values{es=[]},_} -> %% No variables left. Body; - {_,Arg,#c_literal{}} -> + {Vs,Arg1,#c_literal{}} -> + Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody, Ctxt), E = case Ctxt of effect -> %% Throw away the literal body. @@ -2313,22 +2500,50 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, Ctxt, Sub) -> #c_seq{arg=Arg,body=Body} end, expr(E, Ctxt, sub_new_preserve_types(Sub)); - {Vs,Arg,Body} -> + {Vs,Arg1,Body} -> %% If none of the variables are used in the body, we can %% rewrite the let to a sequence: %% let <Var> = Arg in BodyWithoutVar ==> %% seq Arg BodyWithoutVar case is_any_var_used(Vs, Body) of false -> + Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody, Ctxt), expr(#c_seq{arg=Arg,body=Body}, Ctxt, sub_new_preserve_types(Sub)); true -> - Let1 = Let0#c_let{vars=Vs,arg=Arg,body=Body}, - Let2 = opt_case_in_let(Let1, Sub), + Let1 = Let0#c_let{vars=Vs,arg=Arg1,body=Body}, + Let2 = opt_bool_case_in_let(Let1, Sub), opt_case_in_let_arg(Let2, Ctxt, Sub) end end. +%% maybe_suppress_warnings(Arg, [#c_var{}], PreviousBody, Context) -> Arg' +%% Try to suppress false warnings when a variable is not used. +%% For instance, we don't expect a warning for useless building in: +%% +%% R = #r{}, %No warning expected. +%% R#r.f %Optimization would remove the reference to R. +%% +%% To avoid false warnings, we will check whether the variables were +%% referenced in the original unoptimized code. If they were, we will +%% consider the warning false and suppress it. + +maybe_suppress_warnings(Arg, _, _, effect) -> + %% Don't suppress any warnings in effect context. + Arg; +maybe_suppress_warnings(Arg, Vs, PrevBody, value) -> + case suppress_warning(Arg) of + true -> + Arg; %Already suppressed. + false -> + case is_any_var_used(Vs, PrevBody) of + true -> + cerl:set_ann(Arg, [compiler_generated]); + false -> + Arg + end + end. + move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg, body=InnerArg0}=Outer, clauses=InnerClauses}=Inner, Sub) -> @@ -2416,7 +2631,7 @@ move_case_into_arg(_, _) -> %% <> when 'true' -> %% let <Var> = Literal2 in LetBody %% end -%% +%% %% In the worst case, the size of the code could increase. %% In practice, though, substituting the literals into %% LetBody and doing constant folding will decrease the code @@ -2490,6 +2705,7 @@ is_boolean_type(Var, Sub) -> is_int_type(Var, Sub) -> case get_type(Var, Sub) of none -> maybe; + integer -> yes; C -> yes_no(cerl:is_c_int(C)) end. @@ -2504,8 +2720,58 @@ is_tuple_type(Var, Sub) -> yes_no(true) -> yes; yes_no(false) -> no. +%%% +%%% Update type information. +%%% + +update_let_types(Vs, Args, Sub) when is_list(Args) -> + update_let_types_1(Vs, Args, Sub); +update_let_types(_Vs, _Arg, Sub) -> + %% The argument is a complex expression (such as a 'case') + %% that returns multiple values. + Sub. + +update_let_types_1([#c_var{}=V|Vs], [A|As], Sub0) -> + Sub = update_types_from_expr(V, A, Sub0), + update_let_types_1(Vs, As, Sub); +update_let_types_1([], [], Sub) -> Sub. + +update_types_from_expr(V, Expr, Sub) -> + Type = extract_type(Expr, Sub), + update_types(V, [Type], Sub). + +extract_type(#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=Name}, + args=Args}=Call, Sub) -> + case returns_integer(Name, Args) of + true -> integer; + false -> extract_type_1(Call, Sub) + end; +extract_type(Expr, Sub) -> + extract_type_1(Expr, Sub). + +extract_type_1(Expr, Sub) -> + case is_bool_expr(Expr, Sub) of + false -> Expr; + true -> bool + end. + +returns_integer(bit_size, [_]) -> true; +returns_integer('bsl', [_,_]) -> true; +returns_integer('bsr', [_,_]) -> true; +returns_integer(byte_size, [_]) -> true; +returns_integer(length, [_]) -> true; +returns_integer('rem', [_,_]) -> true; +returns_integer(size, [_]) -> true; +returns_integer(tuple_size, [_]) -> true; +returns_integer(trunc, [_]) -> true; +returns_integer(_, _) -> false. + %% update_types(Expr, Pattern, Sub) -> Sub' %% Update the type database. + +-spec update_types(cerl:cerl(), [type_info()], sub()) -> sub(). + update_types(Expr, Pat, #sub{t=Tdb0}=Sub) -> Tdb = update_types_1(Expr, Pat, Tdb0), Sub#sub{t=Tdb}. @@ -2525,6 +2791,8 @@ update_types_2(V, [#c_tuple{}=P], Types) -> orddict:store(V, P, Types); update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) -> orddict:store(V, bool, Types); +update_types_2(V, [Type], Types) when is_atom(Type) -> + orddict:store(V, Type, Types); update_types_2(_, _, Types) -> Types. %% kill_types(V, Tdb) -> Tdb' @@ -2791,7 +3059,7 @@ bsm_ensure_no_partition_after([#c_clause{pats=Ps}|Cs], Pos) -> bsm_problem(P, bin_partition) end; bsm_ensure_no_partition_after([], _) -> ok. - + bsm_could_match_binary(#c_alias{pat=P}) -> bsm_could_match_binary(P); bsm_could_match_binary(#c_cons{}) -> false; bsm_could_match_binary(#c_tuple{}) -> false; diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index cbe50b93b0..7eec9dd62b 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -69,10 +69,8 @@ stk=[], %Stack table res=[]}). %Reserved regs: [{reserved,I,V}] -module({Mod,Exp,Attr,Forms}, Options) -> - put(?MODULE, Options), +module({Mod,Exp,Attr,Forms}, _Options) -> {Fs,St} = functions(Forms, {atom,Mod}), - erase(?MODULE), {ok,{Mod,Exp,Attr,Fs,St#cg.lcount}}. functions(Forms, AtomMod) -> @@ -924,7 +922,7 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) -> select_map(Scs, V, Tf, Vf, Bef, St0) -> Reg = fetch_var(V, Bef), {Is,Aft,St1} = - match_fmf(fun(#l{ke={val_clause,{map,_,Es},B},i=I,vdb=Vdb}, Fail, St1) -> + match_fmf(fun(#l{ke={val_clause,{map,exact,_,Es},B},i=I,vdb=Vdb}, Fail, St1) -> select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1) end, Vf, St0, Scs), {[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 3c19a209c0..c954d21e59 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -78,7 +78,7 @@ splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]). -import(ordsets, [add_element/2,del_element/2,is_element/2, union/1,union/2,intersection/2,subtract/2]). --import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1, +-import(cerl, [ann_c_cons/3,ann_c_tuple/2,c_tuple/1, ann_c_map/3]). -include("core_parse.hrl"). @@ -1660,48 +1660,55 @@ pat_segment({bin_element,_,Val,Size,[Type,{unit,Unit}|Flags]}, St) -> %% pat_alias(CorePat, CorePat) -> AliasPat. %% Normalise aliases. Trap bad aliases by throwing 'nomatch'. -pat_alias(#c_var{name=V1}, P2) -> #c_alias{var=#c_var{name=V1},pat=P2}; -pat_alias(P1, #c_var{name=V2}) -> #c_alias{var=#c_var{name=V2},pat=P1}; - -%% alias cons -pat_alias(#c_cons{}=Cons, #c_literal{anno=A,val=[H|T]}=S) -> - pat_alias(Cons, ann_c_cons_skel(A, #c_literal{anno=A,val=H}, - S#c_literal{val=T})); -pat_alias(#c_literal{anno=A,val=[H|T]}=S, #c_cons{}=Cons) -> - pat_alias(ann_c_cons_skel(A, #c_literal{anno=A,val=H}, - S#c_literal{val=T}), Cons); -pat_alias(#c_cons{anno=Anno,hd=H1,tl=T1}, #c_cons{hd=H2,tl=T2}) -> - ann_c_cons(Anno, pat_alias(H1, H2), pat_alias(T1, T2)); - -%% alias tuples -pat_alias(#c_tuple{anno=Anno,es=Es1}, #c_literal{val=T}) when is_tuple(T) -> - Es2 = [#c_literal{val=E} || E <- tuple_to_list(T)], - ann_c_tuple(Anno, pat_alias_list(Es1, Es2)); -pat_alias(#c_literal{anno=Anno,val=T}, #c_tuple{es=Es2}) when is_tuple(T) -> - Es1 = [#c_literal{val=E} || E <- tuple_to_list(T)], - ann_c_tuple(Anno, pat_alias_list(Es1, Es2)); -pat_alias(#c_tuple{anno=Anno,es=Es1}, #c_tuple{es=Es2}) -> - ann_c_tuple(Anno, pat_alias_list(Es1, Es2)); - -%% alias maps -%% There are no literals in maps patterns (patterns are always abstract) -pat_alias(#c_map{es=Es1}=M,#c_map{es=Es2}) -> - M#c_map{es=pat_alias_map_pairs(Es1++Es2)}; - -pat_alias(#c_alias{var=V1,pat=P1}, - #c_alias{var=V2,pat=P2}) -> - if V1 =:= V2 -> #c_alias{var=V1,pat=pat_alias(P1, P2)}; - true -> #c_alias{var=V1,pat=#c_alias{var=V2,pat=pat_alias(P1, P2)}} +pat_alias(#c_var{name=V1}=P, #c_var{name=V1}) -> P; +pat_alias(#c_var{name=V1}=Var, + #c_alias{var=#c_var{name=V2},pat=Pat}=Alias) -> + if + V1 =:= V2 -> + Alias; + true -> + Alias#c_alias{pat=pat_alias(Var, Pat)} + end; +pat_alias(#c_var{}=P1, P2) -> #c_alias{var=P1,pat=P2}; + +pat_alias(#c_alias{var=#c_var{name=V1}}=Alias, #c_var{name=V1}) -> + Alias; +pat_alias(#c_alias{var=#c_var{name=V1}=Var1,pat=P1}, + #c_alias{var=#c_var{name=V2}=Var2,pat=P2}) -> + Pat = pat_alias(P1, P2), + if + V1 =:= V2 -> + #c_alias{var=Var1,pat=Pat}; + true -> + pat_alias(Var1, pat_alias(Var2, Pat)) end; -pat_alias(#c_alias{var=V1,pat=P1}, P2) -> - #c_alias{var=V1,pat=pat_alias(P1, P2)}; -pat_alias(P1, #c_alias{var=V2,pat=P2}) -> - #c_alias{var=V2,pat=pat_alias(P1, P2)}; +pat_alias(#c_alias{var=#c_var{}=Var,pat=P1}, P2) -> + #c_alias{var=Var,pat=pat_alias(P1, P2)}; + +pat_alias(#c_map{es=Es1}=M, #c_map{es=Es2}) -> + M#c_map{es=pat_alias_map_pairs(Es1 ++ Es2)}; + +pat_alias(P1, #c_var{}=Var) -> + #c_alias{var=Var,pat=P1}; +pat_alias(P1, #c_alias{pat=P2}=Alias) -> + Alias#c_alias{pat=pat_alias(P1, P2)}; + pat_alias(P1, P2) -> - case {set_anno(P1, []),set_anno(P2, [])} of - {P,P} -> P; + %% Aliases between binaries are not allowed, so the only + %% legal patterns that remain are data patterns. + case cerl:is_data(P1) andalso cerl:is_data(P2) of + false -> throw(nomatch); + true -> ok + end, + Type = cerl:data_type(P1), + case cerl:data_type(P2) of + Type -> ok; _ -> throw(nomatch) - end. + end, + Es1 = cerl:data_es(P1), + Es2 = cerl:data_es(P2), + Es = pat_alias_list(Es1, Es2), + cerl:make_data(Type, Es). %% pat_alias_list([A1], [A2]) -> [A]. diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index cd4b5fd674..75bd188479 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -270,7 +270,7 @@ match(#k_select{anno=A,var=V,types=Kts}, Ls0, I, Ctxt, Vdb0) -> end, Vdb1 = use_vars(union(A#k.us, Ls1), I, Vdb0), Ts = [type_clause(Tc, Ls1, I+1, Ctxt, Vdb1) || Tc <- Kts], - #l{ke={select,literal2(V, Ctxt),Ts},i=I,vdb=Vdb1,a=Anno}; + #l{ke={select,literal(V, Ctxt),Ts},i=I,vdb=Vdb1,a=Anno}; match(#k_guard{anno=A,clauses=Kcs}, Ls, I, Ctxt, Vdb0) -> Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0), Cs = [guard_clause(G, Ls, I+1, Ctxt, Vdb1) || G <- Kcs], @@ -297,7 +297,7 @@ val_clause(#k_val_clause{anno=A,val=V,body=Kb}, Ls0, I, Ctxt0, Vdb0) -> _ -> Ctxt0 end, B = match(Kb, Ls1, I+1, Ctxt, Vdb1), - #l{ke={val_clause,literal2(V, Ctxt),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}. + #l{ke={val_clause,literal(V, Ctxt),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}. guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Ctxt, Vdb0) -> Vdb1 = use_vars(union(A#k.us, Ls), I+2, Vdb0), @@ -350,6 +350,7 @@ atomic_list(Ks) -> [atomic(K) || K <- Ks]. %% literal_list([Klit]) -> [Lit]. literal(#k_var{name=N}, _) -> {var,N}; +literal(#k_literal{val=I}, _) -> {literal,I}; literal(#k_int{val=I}, _) -> {integer,I}; literal(#k_float{val=F}, _) -> {float,F}; literal(#k_atom{val=N}, _) -> {atom,N}; @@ -358,58 +359,29 @@ literal(#k_nil{}, _) -> nil; literal(#k_cons{hd=H,tl=T}, Ctxt) -> {cons,[literal(H, Ctxt),literal(T, Ctxt)]}; literal(#k_binary{segs=V}, Ctxt) -> - {binary,literal(V, Ctxt)}; + {binary,literal(V, Ctxt)}; +literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=[]}, Ctxt) -> + %% Only occurs in patterns. + {bin_seg,Ctxt,literal(S, Ctxt),U,T,Fs,[literal(Seg, Ctxt)]}; literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}, Ctxt) -> {bin_seg,Ctxt,literal(S, Ctxt),U,T,Fs, [literal(Seg, Ctxt),literal(N, Ctxt)]}; +literal(#k_bin_int{size=S,unit=U,flags=Fs,val=Int,next=N}, Ctxt) -> + %% Only occurs in patterns. + {bin_int,Ctxt,literal(S, Ctxt),U,Fs,Int, + [literal(N, Ctxt)]}; literal(#k_bin_end{}, Ctxt) -> {bin_end,Ctxt}; literal(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list(Es, Ctxt)}; -literal(#k_map{op=Op,var=Var,es=Es}, Ctxt) -> - {map,Op,literal(Var, Ctxt),literal_list(Es, Ctxt)}; +literal(#k_map{op=Op,var=Var,es=Es0}, Ctxt) -> + {map,Op,literal(Var, Ctxt),literal_list(Es0, Ctxt)}; literal(#k_map_pair{key=K,val=V}, Ctxt) -> - {map_pair,literal(K, Ctxt),literal(V, Ctxt)}; -literal(#k_literal{val=V}, _Ctxt) -> - {literal,V}. + {map_pair,literal(K, Ctxt),literal(V, Ctxt)}. literal_list(Ks, Ctxt) -> [literal(K, Ctxt) || K <- Ks]. -literal2(#k_var{name=N}, _) -> {var,N}; -literal2(#k_literal{val=I}, _) -> {literal,I}; -literal2(#k_int{val=I}, _) -> {integer,I}; -literal2(#k_float{val=F}, _) -> {float,F}; -literal2(#k_atom{val=N}, _) -> {atom,N}; -%%literal2(#k_char{val=C}, _) -> {char,C}; -literal2(#k_nil{}, _) -> nil; -literal2(#k_cons{hd=H,tl=T}, Ctxt) -> - {cons,[literal2(H, Ctxt),literal2(T, Ctxt)]}; -literal2(#k_binary{segs=V}, Ctxt) -> - {binary,literal2(V, Ctxt)}; -literal2(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=[]}, Ctxt) -> - {bin_seg,Ctxt,literal2(S, Ctxt),U,T,Fs,[literal2(Seg, Ctxt)]}; -literal2(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}, Ctxt) -> - {bin_seg,Ctxt,literal2(S, Ctxt),U,T,Fs, - [literal2(Seg, Ctxt),literal2(N, Ctxt)]}; -literal2(#k_bin_int{size=S,unit=U,flags=Fs,val=Int,next=N}, Ctxt) -> - {bin_int,Ctxt,literal2(S, Ctxt),U,Fs,Int, - [literal2(N, Ctxt)]}; -literal2(#k_bin_end{}, Ctxt) -> - {bin_end,Ctxt}; -literal2(#k_tuple{es=Es}, Ctxt) -> - {tuple,literal_list2(Es, Ctxt)}; -literal2(#k_map{op=Op,es=Es}, Ctxt) -> - {map,Op,literal_list2(Es, Ctxt)}; -literal2(#k_map_pair{key=K,val=V}, Ctxt) -> - {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}. - -literal_list2(Ks, Ctxt) -> - [literal2(K, Ctxt) || K <- Ks]. - -%% literal_bin(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}) -> -%% {bin_seg,literal(S),U,T,Fs,[literal(Seg),literal(N)]} - %% is_gc_bif(Name, Arity) -> true|false %% Determines whether the BIF Name/Arity might do a GC. diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl index 3199440d84..4d7f444c4f 100644 --- a/lib/compiler/test/andor_SUITE.erl +++ b/lib/compiler/test/andor_SUITE.erl @@ -370,6 +370,11 @@ combined(Config) when is_list(Config) -> ?line true = ?COMB(false, blurf, true), ?line true = ?COMB(true, true, blurf), + false = simple_comb(false, false), + false = simple_comb(false, true), + false = simple_comb(true, false), + true = simple_comb(true, true), + ok. -undef(COMB). @@ -396,6 +401,13 @@ comb(A, B, C) -> end, id(Res). +simple_comb(A, B) -> + %% Use Res twice, to ensure that a careless optimization of 'not' + %% doesn't leave Res as a free variable. + Res = A andalso B, + _ = id(not Res), + Res. + %% Test that a boolean expression in a case expression is properly %% optimized (in particular, that the error behaviour is correct). in_case(Config) when is_list(Config) -> diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 512aada203..bc82eaf5aa 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -88,6 +88,7 @@ t_element(Config) when is_list(Config) -> {_,_,_}=Tup -> ?line {'EXIT',{badarg,_}} = (catch element(4, Tup)) end, + {'EXIT',{badarg,_}} = (catch element(1, tuple_size(Tuple))), ok. @@ -106,6 +107,7 @@ setelement(Config) when is_list(Config) -> ?line error = setelement_crash_2({a,b,c,d,e,f}, <<42>>), {'EXIT',{badarg,_}} = (catch setelement(1, not_a_tuple, New)), + {'EXIT',{badarg,_}} = (catch setelement(3, {a,b}, New)), ok. diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 7522ee985f..9aec0b3d4e 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -141,6 +141,13 @@ aliases(Config) when is_list(Config) -> ?line {a,b} = list_alias2([a,b]), ?line {a,b} = list_alias3([a,b]), + %% Non-matching aliases. + none = mixed_aliases(<<42>>), + none = mixed_aliases([b]), + none = mixed_aliases([d]), + none = mixed_aliases({a,42}), + none = mixed_aliases(42), + ok. str_alias(V) -> @@ -244,6 +251,12 @@ list_alias2([X,Y]=[a,b]) -> list_alias3([X,b]=[a,Y]) -> {X,Y}. +mixed_aliases(<<X:8>> = x) -> {a,X}; +mixed_aliases([b] = <<X:8>>) -> {b,X}; +mixed_aliases(<<X:8>> = {a,X}) -> {c,X}; +mixed_aliases([X] = <<X:8>>) -> {d,X}; +mixed_aliases(_) -> none. + %% OTP-7018. match_in_call(Config) when is_list(Config) -> diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index dcd3910926..d0b7c71be8 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -39,7 +39,7 @@ guard/1,bad_arith/1,bool_cases/1,bad_apply/1, files/1,effect/1,bin_opt_info/1,bin_construction/1, comprehensions/1,maps/1,redundant_boolean_clauses/1, - latin1_fallback/1,underscore/1]). + latin1_fallback/1,underscore/1,no_warnings/1]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(2)). @@ -65,7 +65,7 @@ groups() -> bad_arith,bool_cases,bad_apply,files,effect, bin_opt_info,bin_construction,comprehensions,maps, redundant_boolean_clauses,latin1_fallback, - underscore]}]. + underscore,no_warnings]}]. init_per_suite(Config) -> Config. @@ -281,7 +281,6 @@ bad_arith(Config) when is_list(Config) -> {3,sys_core_fold,{eval_failure,badarith}}, {9,sys_core_fold,nomatch_guard}, {9,sys_core_fold,{eval_failure,badarith}}, - {9,sys_core_fold,{no_effect,{erlang,is_integer,1}}}, {10,sys_core_fold,nomatch_guard}, {10,sys_core_fold,{eval_failure,badarith}}, {15,sys_core_fold,{eval_failure,badarith}} @@ -719,6 +718,27 @@ underscore(Config) when is_list(Config) -> ok. +no_warnings(Config) when is_list(Config) -> + Ts = [{no_warnings, + <<"-record(r, {s=ordsets:new(),a,b}). + + a() -> + R = #r{}, %No warning expected. + {R#r.a,R#r.b}. + + b(X) -> + T = true, + Var = [X], %No warning expected. + case T of + false -> Var; + true -> [] + end. + ">>, + [], + []}], + run(Config, Ts), + ok. + %%% %%% End of test cases. %%% diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index b68115cd82..ed35ee3a9c 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -103,7 +103,7 @@ filter() See present/1, substrings/2, <type> <v>Handle = handle()</v> <v>Options = ssl:ssl_options()</v> - <v>Timeout = inifinity | positive_integer()</v> + <v>Timeout = infinity | positive_integer()</v> </type> <desc> <p>Upgrade the connection associated with <c>Handle</c> to a tls connection if possible.</p> diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk index 432ba2e742..adca41ed63 100644 --- a/lib/eldap/vsn.mk +++ b/lib/eldap/vsn.mk @@ -1 +1 @@ -ELDAP_VSN = 1.1 +ELDAP_VSN = 1.1.1 diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl index ed306a84f5..35778d3ed5 100644 --- a/lib/inets/src/http_client/httpc_cookie.erl +++ b/lib/inets/src/http_client/httpc_cookie.erl @@ -115,8 +115,8 @@ maybe_dets_close(Db) -> %%-------------------------------------------------------------------- -%% Func: insert(CookieDb) -> ok -%% Purpose: Close the cookie db +%% Func: insert(CookieDb, Cookie) -> ok +%% Purpose: insert cookies into the cookie db %%-------------------------------------------------------------------- %% If no persistent cookie database is defined we diff --git a/lib/kernel/src/erl_distribution.erl b/lib/kernel/src/erl_distribution.erl index 25ad34357a..3c4429129e 100644 --- a/lib/kernel/src/erl_distribution.erl +++ b/lib/kernel/src/erl_distribution.erl @@ -22,7 +22,6 @@ -export([start_link/0,start_link/1,init/1,start/1,stop/0]). -%-define(DBG,io:format("~p:~p~n",[?MODULE,?LINE])). -define(DBG,erlang:display([?MODULE,?LINE])). start_link() -> diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 849013ac79..44a32fc1ec 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -88,10 +88,30 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(lookup_bad_search_option, Config) -> + Db = inet_db, + Key = res_lookup, + %% The bad option can not enter through inet_db:set_lookup/1, + %% but through e.g .inetrc. + Prev = ets:lookup(Db, Key), + ets:delete(Db, Key), + ets:insert(Db, {Key,[lookup_bad_search_option]}), + ?t:format("Misconfigured resolver lookup order", []), + Dog = test_server:timetrap(test_server:seconds(60)), + [{Key,Prev},{watchdog,Dog}|Config]; init_per_testcase(_Func, Config) -> Dog = test_server:timetrap(test_server:seconds(60)), [{watchdog,Dog}|Config]. +end_per_testcase(lookup_bad_search_option, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + Db = inet_db, + Key = res_lookup, + Prev = ?config(Key, Config), + ets:delete(Db, Key), + ets:insert(Db, Prev), + ?t:format("Restored resolver lookup order", []); end_per_testcase(_Func, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog). @@ -915,10 +935,8 @@ lookup_bad_search_option(suite) -> lookup_bad_search_option(doc) -> ["Test lookup with erroneously configured lookup option (OTP-12133)"]; lookup_bad_search_option(Config) when is_list(Config) -> - Db = inet_db, - %% The bad option can not enter through inet_db:set_lookup/1, - %% but through e.g .inetrc. - ets:insert(Db, {res_lookup,[lookup_bad_search_option]}), + %% Manipulation of resolver config is done in init_per_testcase + %% and end_per_testcase to ensure cleanup. {ok,Hostname} = inet:gethostname(), {ok,_Hent} = inet:gethostbyname(Hostname), % Will hang loop for this bug ok. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index e99151284f..41c19fce51 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -146,8 +146,6 @@ api_deflateInit(Config) when is_list(Config) -> ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-20,8,default)), ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-7,8,default)), ?m(?BARG, zlib:deflateInit(Z1,default,deflated,7,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-8,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,8,8,default)), ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,0,default)), ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,10,default)), @@ -169,7 +167,7 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok, zlib:deflateInit(Z12,default,deflated,-Wbits,8,default)), ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) - end, lists:seq(9, 15)), + end, lists:seq(8, 15)), lists:foreach(fun(MemLevel) -> ?line Z = zlib:open(), @@ -277,7 +275,7 @@ api_inflateInit(Config) when is_list(Config) -> ?m(ok, zlib:inflateInit(Z12,-Wbits)), ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) - end, lists:seq(9,15)), + end, lists:seq(8,15)), ?m(?BARG, zlib:inflateInit(gurka, -15)), ?m(?BARG, zlib:inflateInit(Z1, 7)), ?m(?BARG, zlib:inflateInit(Z1, -7)), diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index a0a87e5351..e8ff965982 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -114,7 +114,7 @@ pem_encode(PemEntries) when is_list(PemEntries) -> iolist_to_binary(pubkey_pem:encode(PemEntries)). %%-------------------------------------------------------------------- --spec pem_entry_decode(pem_entry(), [string()]) -> term(). +-spec pem_entry_decode(pem_entry(), string()) -> term(). % %% Description: Decodes a pem entry. pem_decode/1 returns a list of %% pem entries. @@ -146,14 +146,16 @@ pem_entry_decode({Asn1Type, CryptDer, {Cipher, #'PBES2-params'{}}} = PemEntry, pem_entry_decode({Asn1Type, CryptDer, {Cipher, {#'PBEParameter'{},_}}} = PemEntry, Password) when is_atom(Asn1Type) andalso is_binary(CryptDer) andalso - is_list(Cipher) -> + is_list(Cipher) andalso + is_list(Password) -> do_pem_entry_decode(PemEntry, Password); pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry, Password) when is_atom(Asn1Type) andalso is_binary(CryptDer) andalso is_list(Cipher) andalso is_binary(Salt) andalso - ((erlang:byte_size(Salt) == 8) or (erlang:byte_size(Salt) == 16)) -> + ((erlang:byte_size(Salt) == 8) or (erlang:byte_size(Salt) == 16)) andalso + is_list(Password) -> do_pem_entry_decode(PemEntry, Password). @@ -626,8 +628,12 @@ pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) -> % %% Description: Returns the issuer id. %%-------------------------------------------------------------------- -pkix_issuer_id(Cert, Signed)-> - pkix_issuer_id(Cert, Signed, decode). +pkix_issuer_id(#'OTPCertificate'{} = OtpCert, Signed) when (Signed == self) or + (Signed == other) -> + pubkey_cert:issuer_id(OtpCert, Signed); +pkix_issuer_id(Cert, Signed) when is_binary(Cert) -> + OtpCert = pkix_decode_cert(Cert, otp), + pkix_issuer_id(OtpCert, Signed). %%-------------------------------------------------------------------- -spec pkix_crl_issuer(CRL::binary()| #'CertificateList'{}) -> @@ -990,17 +996,3 @@ ec_key({PubKey, PrivateKey}, Params) -> parameters = Params, publicKey = {0, PubKey}}. -pkix_issuer_id(#'OTPCertificate'{} = OtpCert, Signed, decode) when (Signed == self) or - (Signed == other) -> - pubkey_cert:issuer_id(OtpCert, Signed); -pkix_issuer_id(#'OTPCertificate'{} = OtpCert, Signed, encode) when (Signed == self) or - (Signed == other) -> - case pubkey_cert:issuer_id(OtpCert, Signed) of - {ok, {Serial, Issuer}} -> - {ok, {Serial, pubkey_cert_records:transform(Issuer, encode)}}; - Error -> - Error - end; -pkix_issuer_id(Cert, Signed, Decode) when is_binary(Cert) -> - OtpCert = pkix_decode_cert(Cert, otp), - pkix_issuer_id(OtpCert, Signed, Decode). diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index c8cac3e852..bfebe2c60b 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 3.1 +SSH_VSN = 3.1.1 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index c9b02d44ec..47100c0d81 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -94,7 +94,7 @@ {log_alert, boolean()} | {server_name_indication, hostname() | disable} </c></p> - <p><c>transportoption() = {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}} + <p><c>transportoption() = {cb_info, {CallbackModule :: atom(), DataTag :: atom(), ClosedTag :: atom(), ErrTag:atom()}} - defaults to {gen_tcp, tcp, tcp_closed, tcp_error}. Can be used to customize the transport layer. The callback module must implement a reliable transport protocol and behave as gen_tcp and in addition have functions corresponding to @@ -303,20 +303,20 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo </taglist> </item> - <tag>{crl_check, boolean() | peer | best_effort )</tag> + <tag>{crl_check, boolean() | peer | best_effort }</tag> <item> Perform CRL (Certificate Revocation List) verification <seealso marker="public_key:public_key#pkix_crl_validate-3"> - public_key:pkix_crls_validate/3</seealso>, during the + (public_key:pkix_crls_validate/3)</seealso> on all the certificates during the path validation <seealso - marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3 </seealso> - invokation on all the certificates in the peer certificate chain. Defaults to - false. - + marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3) + </seealso> + of the certificate chain. Defaults to false. + <p><c>peer</c> - check is only performed on the peer certificate.</p> - <p><c>best_effort</c> - if certificate revokation status can not be determined + <p><c>best_effort</c> - if certificate revocation status can not be determined it will be accepted as valid.</p> <p>The CA certificates specified for the connection will be used to @@ -326,7 +326,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo <seealso marker="ssl:ssl_crl_cache_api">ssl_crl_cache_api(3)</seealso>.</p> </item> - <tag>{crl_cache, {Module::atom, {DbHandle::internal | term(), Args::list()}}</tag> + <tag>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</tag> <item> <p>Module defaults to ssl_crl_cache with <c> DbHandle </c> internal and an empty argument list. The following arguments may be specified for the internal cache.</p> diff --git a/lib/ssl/doc/src/ssl_crl_cache.xml b/lib/ssl/doc/src/ssl_crl_cache.xml index 1ed76d3e2a..b291c7b633 100644 --- a/lib/ssl/doc/src/ssl_crl_cache.xml +++ b/lib/ssl/doc/src/ssl_crl_cache.xml @@ -29,7 +29,7 @@ <p> Implements an internal CRL (Certificate Revocation List) cache. In addition to implementing the <seealso - marker="ssl_cache_crl_api"> ssl_cache_crl_api</seealso> + marker="ssl_cache_crl_api"> ssl_cache_crl_api</seealso> behaviour the following functions are available. </p> </description> diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml index 24365c9f59..3f518496be 100644 --- a/lib/ssl/doc/src/ssl_crl_cache_api.xml +++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml @@ -27,13 +27,15 @@ <modulesummary>API for a SSL/TLS CRL (Certificate Revocation List) cache.</modulesummary> <description> <p> - When SSL/TLS performs certificate path validation according to - <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> it should - also perform CRL validation checks. To enable the CRL checks the application - needs access to CRLs. A database of CRLs can be set up in many different ways. - This module provides an API to integrate an arbitrary CRL cache with the erlang - ssl application. It is also used by the application itself to provide a simple - default implementation of a CRL cache. + When SSL/TLS performs certificate path validation according to + <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> + it should also perform CRL validation checks. To enable the CRL + checks the application needs access to CRLs. A database of CRLs + can be set up in many different ways. This module provides the + behavior of the API needed to integrate an arbitrary CRL cache + with the erlang ssl application. It is also used by the + application itself to provide a simple default implementation of + a CRL cache. </p> </description> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 508983ddac..f177a8610d 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2014. All Rights Reserved. +%% Copyright Ericsson AB 2013-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -146,7 +146,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> Handshake = ssl_handshake:init_handshake_history(), TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), try ssl_config:init(SSLOpts0, Role) of - {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> + {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} -> Session = State0#state.session, State = State0#state{ tls_handshake_history = Handshake, @@ -155,6 +155,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> file_ref_db = FileRefHandle, cert_db_ref = Ref, cert_db = CertDbHandle, + crl_db = CRLDbInfo, session_cache = CacheHandle, private_key = Key, diffie_hellman_params = DHParams}, diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 764bd82de0..34e4a8b447 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -84,7 +84,7 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) - end. %%-------------------------------------------------------------------- --spec certificate_chain(undefined | binary(), db_handle(), certdb_ref()) -> +-spec certificate_chain(undefined | binary() | #'OTPCertificate'{} , db_handle(), certdb_ref()) -> {error, no_cert} | {ok, #'OTPCertificate'{} | undefined, [der_cert()]}. %% %% Description: Return the certificate chain to send to peer. diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl index b8761f0601..1a08d3c80a 100644 --- a/lib/ssl/src/ssl_crl.erl +++ b/lib/ssl/src/ssl_crl.erl @@ -73,8 +73,6 @@ verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) -> true -> throw({ok, ErlCertCandidate}); false -> - NotIssuer; - _ -> NotIssuer end; _ -> diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl index b2bdb19979..b9d6a61c3b 100644 --- a/lib/ssl/src/ssl_crl_cache.erl +++ b/lib/ssl/src/ssl_crl_cache.erl @@ -34,7 +34,7 @@ %% Cache callback API %%==================================================================== -lookup(#'DistributionPoint'{distributionPoint={fullName, Names}}, +lookup(#'DistributionPoint'{distributionPoint = {fullName, Names}}, CRLDbInfo) -> get_crls(Names, CRLDbInfo); lookup(_,_) -> @@ -48,8 +48,8 @@ select(Issuer, {{_Cache, Mapping},_}) -> CRLs end. -fresh_crl(DistributionPoint, CRL) -> - case get_crls(DistributionPoint, undefined) of +fresh_crl(#'DistributionPoint'{distributionPoint = {fullName, Names}}, CRL) -> + case get_crls(Names, undefined) of not_available -> CRL; [NewCRL] -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 6cab8eb7a1..5c5f386c6f 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1998,12 +1998,12 @@ crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _) - case dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) of [] -> valid; %% No relevant CRL existed - Dps -> - crl_check_same_issuer(OtpCert, Check, Dps, Options) + DpsAndCRls -> + crl_check_same_issuer(OtpCert, Check, DpsAndCRls, Options) end; - Dps -> %% This DP list may be empty if relevant CRLs existed + DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed %% but could not be retrived, will result in {bad_cert, revocation_status_undetermined} - case public_key:pkix_crls_validate(OtpCert, Dps, Options) of + case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of {bad_cert, revocation_status_undetermined} -> crl_check_same_issuer(OtpCert, Check, dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer), Options); diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 9c4b2a8bad..396013825e 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -101,8 +101,10 @@ start_link_dist(Opts) -> gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []). %%-------------------------------------------------------------------- --spec connection_init(binary()| {der, list()}, client | server, {Cb :: atom(), Handle:: term()}) -> - {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}. +-spec connection_init(binary()| {der, list()}, client | server, + {Cb :: atom(), Handle:: term()}) -> + {ok, certdb_ref(), db_handle(), db_handle(), + db_handle(), db_handle(), CRLInfo::term()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index b0b6d5a8e3..548ec4aebe 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -82,8 +82,7 @@ client_hello(Host, Port, ConnectionStates, boolean()) -> {tls_record:tls_version(), session_id(), #connection_states{}, binary() | undefined}| {tls_record:tls_version(), {resumed | new, #session{}}, #connection_states{}, - [binary()] | undefined, - [ssl_handshake:oid()] | undefined, [ssl_handshake:oid()] | undefined} | + #hello_extensions{}} | #alert{}. %% %% Description: Handles a recieved hello message diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 902a921fbf..6b9524ef63 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -1618,14 +1618,18 @@ true</pre> </func> <func> <name name="update_counter" arity="3" clause_i="1"/> + <name name="update_counter" arity="4" clause_i="1"/> <name name="update_counter" arity="3" clause_i="2"/> + <name name="update_counter" arity="4" clause_i="2"/> <name name="update_counter" arity="3" clause_i="3"/> + <name name="update_counter" arity="4" clause_i="3"/> <type variable="Tab"/> <type variable="Key"/> <type variable="UpdateOp" name_i="1"/> <type variable="Pos" name_i="1"/> <type variable="Threshold" name_i="1"/> <type variable="SetValue" name_i="1"/> + <type variable="Default"/> <fsummary>Update a counter object in an ETS table.</fsummary> <desc> <p>This function provides an efficient way to update one or more @@ -1667,12 +1671,22 @@ true</pre> <seealso marker="#lookup/2">lookup/2</seealso> and <seealso marker="#new/2">new/2</seealso> for details on the difference).</p> + <p>If a default object <c><anno>Default</anno></c> is given, it is used + as the object to be updated if the key is missing from the table. The + value in place of the key is ignored and replaced by the proper key + value. The return value is as if the default object had not been used, + that is a single updated element or a list of them.</p> <p>The function will fail with reason <c>badarg</c> if:</p> <list type="bulleted"> <item>the table is not of type <c>set</c> or <c>ordered_set</c>,</item> - <item>no object with the right key exists,</item> + <item>no object with the right key exists and no default object were + supplied,</item> <item>the object has the wrong arity,</item> + <item>the default object arity is smaller than + <c><![CDATA[<keypos>]]></c></item> + <item>any field from the default object being updated is not an + integer</item> <item>the element to update is not an integer,</item> <item>the element to update is also the key, or,</item> <item>any of <c><anno>Pos</anno></c>, <c><anno>Incr</anno></c>, <c><anno>Threshold</anno></c> or diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index f27a974242..9a0ff85038 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -173,7 +173,7 @@ <name name="init_ack" arity="2"/> <fsummary>Used by a process when it has started.</fsummary> <desc> - <p>This function must used by a process that has been started by + <p>This function must be used by a process that has been started by a <seealso marker="#start/3">start[_link]/3,4,5</seealso> function. It tells <c><anno>Parent</anno></c> that the process has initialized itself, has started, or has failed to initialize diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index 48b376743d..d201e81a79 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -135,6 +135,12 @@ <p>These options are described in <seealso marker="#zip_options">create/3</seealso>.</p> </desc> </datatype> + <datatype> + <name name="handle"/> + <desc> + <p>As returned by <seealso marker="#zip_open/2">zip_open/2</seealso>.</p> + </desc> + </datatype> </datatypes> <funcs> <func> @@ -430,6 +436,8 @@ means that subsequently reading files from the archive will be faster than unzipping files one at a time with <c>unzip</c>.</p> <p>The archive must be closed with <c>zip_close/1</c>.</p> + <p>The <c><anno>ZipHandle</anno></c> will be closed if the + process which originally opened the archive dies.</p> </desc> </func> <func> diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 09c8924650..1df069755d 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -72,7 +72,7 @@ select_count/2, select_delete/2, select_reverse/1, select_reverse/2, select_reverse/3, setopts/2, slot/2, take/2, - update_counter/3, update_element/3]). + update_counter/3, update_counter/4, update_element/3]). -spec all() -> [Tab] when Tab :: tab(). @@ -439,6 +439,38 @@ take(_, _) -> update_counter(_, _, _) -> erlang:nif_error(undef). +-spec update_counter(Tab, Key, UpdateOp, Default) -> Result when + Tab :: tab(), + Key :: term(), + UpdateOp :: {Pos, Incr} + | {Pos, Incr, Threshold, SetValue}, + Pos :: integer(), + Incr :: integer(), + Threshold :: integer(), + SetValue :: integer(), + Result :: integer(), + Default :: tuple(); + (Tab, Key, [UpdateOp], Default) -> [Result] when + Tab :: tab(), + Key :: term(), + UpdateOp :: {Pos, Incr} + | {Pos, Incr, Threshold, SetValue}, + Pos :: integer(), + Incr :: integer(), + Threshold :: integer(), + SetValue :: integer(), + Result :: integer(), + Default :: tuple(); + (Tab, Key, Incr, Default) -> Result when + Tab :: tab(), + Key :: term(), + Incr :: integer(), + Result :: integer(), + Default :: tuple(). + +update_counter(_, _, _, _) -> + erlang:nif_error(undef). + -spec update_element(Tab, Key, ElementSpec :: {Pos, Value}) -> boolean() when Tab :: tab(), Key :: term(), diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index b768c6d0b9..44e75ff15b 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -214,7 +214,9 @@ -type zip_comment() :: #zip_comment{}. -type zip_file() :: #zip_file{}. --export_type([create_option/0, filename/0]). +-opaque handle() :: pid(). + +-export_type([create_option/0, filename/0, handle/0]). %% Open a zip archive with options %% @@ -500,7 +502,7 @@ do_list_dir(F, Options) -> -spec(t(Archive) -> ok when Archive :: file:name() | binary() | ZipHandle, - ZipHandle :: pid()). + ZipHandle :: handle()). t(F) when is_pid(F) -> zip_t(F); t(F) when is_record(F, openzip) -> openzip_t(F); @@ -524,7 +526,7 @@ do_t(F, RawPrint) -> -spec(tt(Archive) -> ok when Archive :: file:name() | binary() | ZipHandle, - ZipHandle :: pid()). + ZipHandle :: handle()). tt(F) when is_pid(F) -> zip_tt(F); tt(F) when is_record(F, openzip) -> openzip_tt(F); @@ -1114,15 +1116,19 @@ local_file_header_from_info_method_name(#file_info{mtime = MTime}, file_name_length = length(Name), extra_field_length = 0}. +server_init(Parent) -> + %% we want to know if our parent dies + process_flag(trap_exit, true), + server_loop(Parent, not_open). %% small, simple, stupid zip-archive server -server_loop(OpenZip) -> +server_loop(Parent, OpenZip) -> receive {From, {open, Archive, Options}} -> case openzip_open(Archive, Options) of {ok, NewOpenZip} -> From ! {self(), {ok, self()}}, - server_loop(NewOpenZip); + server_loop(Parent, NewOpenZip); Error -> From ! {self(), Error} end; @@ -1130,43 +1136,47 @@ server_loop(OpenZip) -> From ! {self(), openzip_close(OpenZip)}; {From, get} -> From ! {self(), openzip_get(OpenZip)}, - server_loop(OpenZip); + server_loop(Parent, OpenZip); {From, {get, FileName}} -> From ! {self(), openzip_get(FileName, OpenZip)}, - server_loop(OpenZip); + server_loop(Parent, OpenZip); {From, list_dir} -> From ! {self(), openzip_list_dir(OpenZip)}, - server_loop(OpenZip); + server_loop(Parent, OpenZip); {From, {list_dir, Opts}} -> From ! {self(), openzip_list_dir(OpenZip, Opts)}, - server_loop(OpenZip); + server_loop(Parent, OpenZip); {From, get_state} -> From ! {self(), OpenZip}, - server_loop(OpenZip); + server_loop(Parent, OpenZip); + {'EXIT', Parent, Reason} -> + openzip_close(OpenZip), + exit({parent_died, Reason}); _ -> {error, bad_msg} end. -spec(zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} when Archive :: file:name() | binary(), - ZipHandle :: pid(), + ZipHandle :: handle(), Reason :: term()). zip_open(Archive) -> zip_open(Archive, []). -spec(zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} when Archive :: file:name() | binary(), - ZipHandle :: pid(), + ZipHandle :: handle(), Options :: [Option], Option :: cooked | memory | {cwd, CWD :: file:filename()}, Reason :: term()). zip_open(Archive, Options) -> - Pid = spawn(fun() -> server_loop(not_open) end), - request(self(), Pid, {open, Archive, Options}). + Self = self(), + Pid = spawn_link(fun() -> server_init(Self) end), + request(Self, Pid, {open, Archive, Options}). -spec(zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} when - ZipHandle :: pid(), + ZipHandle :: handle(), Result :: file:name() | {file:name(), binary()}, Reason :: term()). @@ -1174,14 +1184,14 @@ zip_get(Pid) when is_pid(Pid) -> request(self(), Pid, get). -spec(zip_close(ZipHandle) -> ok | {error, einval} when - ZipHandle :: pid()). + ZipHandle :: handle()). zip_close(Pid) when is_pid(Pid) -> request(self(), Pid, close). -spec(zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason} when FileName :: file:name(), - ZipHandle :: pid(), + ZipHandle :: handle(), Result :: file:name() | {file:name(), binary()}, Reason :: term()). @@ -1190,7 +1200,7 @@ zip_get(FileName, Pid) when is_pid(Pid) -> -spec(zip_list_dir(ZipHandle) -> {ok, Result} | {error, Reason} when Result :: [zip_comment() | zip_file()], - ZipHandle :: pid(), + ZipHandle :: handle(), Reason :: term()). zip_list_dir(Pid) when is_pid(Pid) -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 72f3c49b5a..9f552b5a6b 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -47,6 +47,7 @@ -export([ordered/1, ordered_match/1, interface_equality/1, 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([member/1]). -export([memory/1]). -export([select_fail/1]). @@ -99,7 +100,7 @@ misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1, 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, + types_do/1, sleeper/0, memory_do/1, update_counter_with_default_do/1, ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4 ]). @@ -136,7 +137,8 @@ all() -> {group, heavy}, ordered, ordered_match, interface_equality, fixtable_next, fixtable_insert, rename, rename_unnamed, evil_rename, update_element, - update_counter, evil_update_counter, partly_bound, + update_counter, evil_update_counter, + update_counter_with_default, partly_bound, 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, @@ -1761,6 +1763,14 @@ update_counter_do(Opts) -> OrdSet = ets_new(ordered_set,[ordered_set | Opts]), update_counter_for(Set), update_counter_for(OrdSet), + ets:delete_all_objects(Set), + ets:delete_all_objects(OrdSet), + ets:safe_fixtable(Set, true), + ets:safe_fixtable(OrdSet, true), + update_counter_for(Set), + update_counter_for(OrdSet), + ets:safe_fixtable(Set, false), + ets:safe_fixtable(OrdSet, false), ets:delete(Set), ets:delete(OrdSet), update_counter_neg(Opts). @@ -1780,10 +1790,14 @@ update_counter_for(T) -> ?line {NewObj, Ret} = uc_mimic(Obj,Arg3), ArgHash = erlang:phash2({T,a,Arg3}), %%io:format("update_counter(~p, ~p, ~p) expecting ~p\n",[T,a,Arg3,Ret]), + [DefaultObj] = ets:lookup(T, a), ?line Ret = ets:update_counter(T,a,Arg3), + Ret = ets:update_counter(T, b, Arg3, DefaultObj), % Use other key ?line ArgHash = erlang:phash2({T,a,Arg3}), %%io:format("NewObj=~p~n ",[NewObj]), ?line [NewObj] = ets:lookup(T,a), + true = ets:lookup(T, b) =:= [setelement(1, NewObj, b)], + ets:delete(T, b), Myself(NewObj,Times-1,Arg3,Myself) end, @@ -2008,6 +2022,44 @@ evil_counter_1(Iter, T) -> ets:update_counter(T, dracula, 1), evil_counter_1(Iter-1, T). +update_counter_with_default(Config) when is_list(Config) -> + repeat_for_opts(update_counter_with_default_do). + +update_counter_with_default_do(Opts) -> + T1 = ets_new(a, [set | Opts]), + %% Insert default object. + 3 = ets:update_counter(T1, foo, 2, {beaufort,1}), + %% Increment. + 5 = ets:update_counter(T1, foo, 2, {cabecou,1}), + %% Increment with list. + [9] = ets:update_counter(T1, foo, [{2,4}], {camembert,1}), + %% Same with non-immediate key. + 3 = ets:update_counter(T1, {foo,bar}, 2, {{chaource,chevrotin},1}), + 5 = ets:update_counter(T1, {foo,bar}, 2, {{cantal,comté},1}), + [9] = ets:update_counter(T1, {foo,bar}, [{2,4}], {{emmental,de,savoie},1}), + %% Same with ordered set. + T2 = ets_new(b, [ordered_set | Opts]), + 3 = ets:update_counter(T2, foo, 2, {maroilles,1}), + 5 = ets:update_counter(T2, foo, 2, {mimolette,1}), + [9] = ets:update_counter(T2, foo, [{2,4}], {morbier,1}), + 3 = ets:update_counter(T2, {foo,bar}, 2, {{laguiole},1}), + 5 = ets:update_counter(T2, {foo,bar}, 2, {{saint,nectaire},1}), + [9] = ets:update_counter(T2, {foo,bar}, [{2,4}], {{rocamadour},1}), + %% Arithmetically-equal keys. + 3 = ets:update_counter(T2, 1.0, 2, {1,1}), + 5 = ets:update_counter(T2, 1, 2, {1,1}), + 7 = ets:update_counter(T2, 1, 2, {1.0,1}), + %% Same with reversed type difference. + 3 = ets:update_counter(T2, 2, 2, {2.0,1}), + 5 = ets:update_counter(T2, 2.0, 2, {2.0,1}), + 7 = ets:update_counter(T2, 2.0, 2, {2,1}), + %% bar is not an integer. + {'EXIT',{badarg,_}} = (catch ets:update_counter(T1, qux, 3, {saint,félicien})), + %% No third element in default value. + {'EXIT',{badarg,_}} = (catch ets:update_counter(T1, qux, [{3,1}], {roquefort,1})), + + ok. + fixtable_next(doc) -> ["Check that a first-next sequence always works on a fixed table"]; fixtable_next(suite) -> diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl index a57641ef62..d168a9d9bc 100644 --- a/lib/stdlib/test/zip_SUITE.erl +++ b/lib/stdlib/test/zip_SUITE.erl @@ -23,7 +23,7 @@ bad_zip/1, unzip_from_binary/1, unzip_to_binary/1, zip_to_binary/1, unzip_options/1, zip_options/1, list_dir_options/1, aliases/1, - openzip_api/1, zip_api/1, unzip_jar/1, + openzip_api/1, zip_api/1, open_leak/1, unzip_jar/1, compress_control/1, foldl/1]). @@ -38,7 +38,7 @@ 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, unzip_jar, compress_control, foldl]. + zip_api, open_leak, unzip_jar, compress_control, foldl]. groups() -> []. @@ -318,8 +318,46 @@ zip_api(Config) when is_list(Config) -> %% Clean up. delete_files([Names]), + ok. + +open_leak(doc) -> + ["Test that zip doesn't leak processes and ports where the " + "controlling process dies without closing an zip opened with " + "zip:zip_open/1."]; +open_leak(suite) -> []; +open_leak(Config) when is_list(Config) -> + %% Create a zip archive + Zip = "zip.zip", + {ok, Zip} = zip:zip(Zip, [], []), + + %% Open archive in a another process that dies immediately. + ZipSrv = spawn_zip(Zip, [memory]), + + %% Expect the ZipSrv process to die soon after. + true = spawned_zip_dead(ZipSrv), + + %% Clean up. + delete_files([Zip]), + ok. +spawn_zip(Zip, Options) -> + Self = self(), + spawn(fun() -> Self ! zip:zip_open(Zip, Options) end), + receive + {ok, ZipSrv} -> + ZipSrv + end. + +spawned_zip_dead(ZipSrv) -> + Ref = monitor(process, ZipSrv), + receive + {'DOWN', Ref, _, ZipSrv, _} -> + true + after 1000 -> + false + end. + unzip_options(doc) -> ["Test options for unzip, only cwd and file_list currently"]; unzip_options(suite) -> diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index 0e685a2d8a..b5e2d06aa6 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -404,15 +404,6 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name, }). run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> - {ok,Cwd} = file:get_cwd(), - Args2Print = case Args of - [Args1] when is_list(Args1) -> - lists:keydelete(tc_group_result, 1, Args1); - _ -> - Args - end, - print(minor, "Test case started with:\n~w:~w(~tp)\n", [Mod,Func,Args2Print]), - print(minor, "Current directory is ~tp\n", [Cwd]), print_timestamp(minor,"Started at "), print(minor, "", [], internal_raw), TCCallback = get(test_server_testcase_callback), @@ -687,7 +678,7 @@ handle_tc_exit(Reason, #st{status=tc,config=Config0,mf={Mod,Func},pid=Pid}=St) Msg = {E,AbortReason}, {Msg,Loc0,Msg}; Other -> - {Other,unknown,Other} + {{'EXIT',Other},unknown,Other} end, Timeout = end_conf_timeout(Reason, St), Config = [{tc_status,{failed,F}}|Config0], @@ -701,7 +692,7 @@ handle_tc_exit(Reason, #st{config=Config,mf={Mod,Func0},pid=Pid, {testcase_aborted=E,AbortReason,Loc0} -> {{E,AbortReason},Loc0}; Other -> - {Other,St#st.last_known_loc} + {{'EXIT',Other},St#st.last_known_loc} end, Func = case Status of init_per_testcase=F -> {F,Func0}; @@ -738,18 +729,16 @@ do_call_end_conf(Starter,Mod,Func,Data,Conf,TVal) -> EndConfApply = fun() -> timetrap(TVal), - case catch apply(Mod, - end_per_testcase, - [Func,Conf]) of - {'EXIT',Why} -> + try apply(Mod,end_per_testcase,[Func,Conf]) of + _ -> ok + catch + _:Why -> timer:sleep(1), group_leader() ! {printout,12, "WARNING! " "~w:end_per_testcase(~w, ~p)" " crashed!\n\tReason: ~p\n", - [Mod,Func,Conf,Why]}; - _ -> - ok + [Mod,Func,Conf,Why]} end, Supervisor ! {self(),end_conf} end, @@ -778,13 +767,11 @@ spawn_fw_call(Mod,{init_per_testcase,Func},CurrConf,Pid, Skip = {skip,{failed,{Mod,init_per_testcase,Why}}}, %% if init_per_testcase fails, the test case %% should be skipped - case catch do_end_tc_call(Mod,Func, - {Pid,Skip,[CurrConf]}, - Why) of - {'EXIT',FwEndTCErr} -> - exit({fw_notify_done,end_tc,FwEndTCErr}); - _ -> - ok + try do_end_tc_call(Mod,Func, {Pid,Skip,[CurrConf]}, Why) of + _ -> ok + catch + _:FwEndTCErr -> + exit({fw_notify_done,end_tc,FwEndTCErr}) end, %% finished, report back SendTo ! {self(),fw_notify_done, @@ -812,12 +799,12 @@ spawn_fw_call(Mod,{end_per_testcase,Func},EndConf,Pid, " failed!\n\tReason: timetrap timeout" " after ~w ms!\n", [Mod,Func,EndConf,TVal]}, FailLoc = proplists:get_value(tc_fail_loc, EndConf), - case catch do_end_tc_call(Mod,Func, + try do_end_tc_call(Mod,Func, {Pid,Report,[EndConf]}, Why) of - {'EXIT',FwEndTCErr} -> - exit({fw_notify_done,end_tc,FwEndTCErr}); - _ -> - ok + _ -> ok + catch + _:FwEndTCErr -> + exit({fw_notify_done,end_tc,FwEndTCErr}) end, Warn = "<font color=\"red\">" "WARNING: end_per_testcase timed out!</font>", @@ -853,21 +840,21 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) -> end, FwCall = fun() -> - case catch fw_error_notify(Mod,Func1,[], - Error,Loc) of - {'EXIT',FwErrorNotifyErr} -> + try fw_error_notify(Mod,Func1,[], + Error,Loc) of + _ -> ok + catch + _:FwErrorNotifyErr -> exit({fw_notify_done,error_notification, - FwErrorNotifyErr}); - _ -> - ok + FwErrorNotifyErr}) end, Conf = [{tc_status,{failed,Error}}|CurrConf], - case catch do_end_tc_call(Mod,Func1, - {Pid,Error,[Conf]},Error) of - {'EXIT',FwEndTCErr} -> - exit({fw_notify_done,end_tc,FwEndTCErr}); - _ -> - ok + try do_end_tc_call(Mod,Func1, + {Pid,Error,[Conf]},Error) of + _ -> ok + catch + _:FwEndTCErr -> + exit({fw_notify_done,end_tc,FwEndTCErr}) end, %% finished, report back SendTo ! {self(),fw_notify_done,{died,Error,Loc,[],undefined}} @@ -1358,8 +1345,8 @@ fw_error_notify(Mod, Func, Args, Error, Loc) -> %% Just like io:format, except that depending on the Detail value, the output %% is directed to console, major and/or minor log files. -print(Detail,Format,Args) -> - test_server_ctrl:print(Detail, Format, Args). +%% print(Detail,Format,Args) -> +%% test_server_ctrl:print(Detail, Format, Args). print(Detail,Format,Args,Printer) -> test_server_ctrl:print(Detail, Format, Args, Printer). diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 488f38d05d..68b03a5987 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -1808,20 +1808,32 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA) -> put(test_server_minor_footer, Footer), io:put_chars(Fd, Header), + io:put_chars(Fd, "<a name=\"top\"></a>"), + io:put_chars(Fd, "<pre>\n"), + SrcListing = downcase(atom_to_list(Mod)) ++ ?src_listing_ext, + + {Info,Arity} = + if Func == init_per_suite; Func == end_per_suite -> + {"Config function: ", 1}; + Func == init_per_group; Func == end_per_group -> + {"Config function: ", 2}; + true -> + {"Test case: ", 1} + end, + case {filelib:is_file(filename:join(LogDir, SrcListing)), lists:member(no_src, get(test_server_logopts))} of {true,false} -> - print(Lev, "<a href=\"~ts#~ts\">source code for ~w:~w/1</a>\n", + print(Lev, Info ++ "<a href=\"~ts#~ts\">~w:~w/~w</a> " + "(click for source code)\n", [uri_encode(SrcListing), uri_encode(atom_to_list(Func)++"-1",utf8), - Mod,Func]); + Mod,Func,Arity]); _ -> - ok + print(Lev, Info ++ "~w:~w/~w\n", [Mod,Func,Arity]) end, - io:put_chars(Fd, "<pre>\n"), - AbsName. stop_minor_log_file() -> @@ -3076,13 +3088,11 @@ print_conf_time(ConfTime) -> print(major, "=group_time ~.3fs", [ConfTime]), print(minor, "~n=== Total execution time of group: ~.3fs~n", [ConfTime]). -print_props(_, []) -> +print_props([]) -> ok; -print_props(true, Props) -> +print_props(Props) -> print(major, "=group_props ~p", [Props]), - print(minor, "Group properties: ~p~n", [Props]); -print_props(_, _) -> - ok. + print(minor, "Group properties: ~p~n", [Props]). %% repeat N times: {repeat,N} %% repeat N times or until all successful: {repeat_until_all_ok,N} @@ -3687,7 +3697,6 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, print(major, "=case ~w:~w", [Mod, Func]), MinorName = start_minor_log_file(Mod, Func, self() /= Main), - print(minor, "<a name=\"top\"></a>", [], internal_raw), MinorBase = filename:basename(MinorName), print(major, "=logfile ~ts", [filename:basename(MinorName)]), @@ -3720,7 +3729,20 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, [tc_start,{{Mod,{Func,GrName}}, MinorName}]), - print_props((RunInit==skip_init), get_props(Mode)), + {ok,Cwd} = file:get_cwd(), + Args2Print = if is_list(UpdatedArgs) -> + lists:keydelete(tc_group_result, 1, UpdatedArgs); + true -> + UpdatedArgs + end, + if RunInit == skip_init -> + print_props(get_props(Mode)); + true -> + ok + end, + print(minor, "Config value:\n\n ~tp\n", [Args2Print]), + print(minor, "Current directory is ~tp\n", [Cwd]), + GrNameStr = case GrName of undefined -> ""; Name -> cast_to_list(Name) diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl index bc62015ac3..594e619fbc 100644 --- a/lib/test_server/src/ts_install.erl +++ b/lib/test_server/src/ts_install.erl @@ -18,7 +18,6 @@ %% -module(ts_install). - -export([install/2, platform_id/1]). -include("ts.hrl"). @@ -135,15 +134,63 @@ unix_autoconf(XConf) -> case filelib:is_file(Configure) of true -> OSXEnv = macosx_cflags(), + UnQuotedEnv = assign_vars(unquote(Env++OSXEnv)), io:format("Running ~s~nEnv: ~p~n", - [lists:flatten(Configure ++ Args),Env++OSXEnv]), + [lists:flatten(Configure ++ Args),UnQuotedEnv]), Port = open_port({spawn, lists:flatten(["\"",Configure,"\"",Args])}, - [stream, eof, {env,Env++OSXEnv}]), + [stream, eof, {env,UnQuotedEnv}]), ts_lib:print_data(Port); false -> {error, no_configure_script} end. +unquote([{Var,Val}|T]) -> + [{Var,unquote(Val)}|unquote(T)]; +unquote([]) -> + []; +unquote("\""++Rest) -> + lists:reverse(tl(lists:reverse(Rest))); +unquote(String) -> + String. + +assign_vars([]) -> + []; +assign_vars([{VAR,FlagsStr} | VARs]) -> + [{VAR,assign_vars(FlagsStr)} | assign_vars(VARs)]; +assign_vars(FlagsStr) -> + Flags = [assign_all_vars(Str,[]) || Str <- string:tokens(FlagsStr, [$ ])], + string:strip(lists:flatten(lists:map(fun(Flag) -> + Flag ++ " " + end, Flags)), right). + +assign_all_vars([$$ | Rest], FlagSoFar) -> + {VarName,Rest1} = get_var_name(Rest, []), + assign_all_vars(Rest1, FlagSoFar ++ assign_var(VarName)); +assign_all_vars([Char | Rest], FlagSoFar) -> + assign_all_vars(Rest, FlagSoFar ++ [Char]); +assign_all_vars([], Flag) -> + Flag. + +get_var_name([Ch | Rest] = Str, VarR) -> + case valid_char(Ch) of + true -> get_var_name(Rest, [Ch | VarR]); + false -> {lists:reverse(VarR),Str} + end; +get_var_name([], VarR) -> + {lists:reverse(VarR),[]}. + +assign_var(VarName) -> + case os:getenv(VarName) of + false -> ""; + Val -> Val + end. + +valid_char(Ch) when Ch >= $a, Ch =< $z -> true; +valid_char(Ch) when Ch >= $A, Ch =< $Z -> true; +valid_char(Ch) when Ch >= $0, Ch =< $9 -> true; +valid_char($_) -> true; +valid_char(_) -> false. + get_xcomp_flag(Flag, Flags) -> get_xcomp_flag(Flag, Flag, Flags). get_xcomp_flag(Flag, Tag, Flags) -> diff --git a/lib/tools/src/tags.erl b/lib/tools/src/tags.erl index e3cc51cdb2..e25db2eb1b 100644 --- a/lib/tools/src/tags.erl +++ b/lib/tools/src/tags.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -297,15 +297,16 @@ word_char(_) -> false. %% Check the options `outfile' and `outdir'. open_out(Options) -> + Opts = [write, {encoding, unicode}], case lists:keysearch(outfile, 1, Options) of {value, {outfile, File}} -> - file:open(File, [write]); + file:open(File, Opts); _ -> case lists:keysearch(outdir, 1, Options) of {value, {outdir, Dir}} -> - file:open(filename:join(Dir, "TAGS"), [write]); + file:open(filename:join(Dir, "TAGS"), Opts); _ -> - file:open("TAGS", [write]) + file:open("TAGS", Opts) end end. diff --git a/otp_patch_apply b/otp_patch_apply new file mode 100755 index 0000000000..947aa1e6ee --- /dev/null +++ b/otp_patch_apply @@ -0,0 +1,480 @@ +#!/bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +version="1.0.1" + +force= +lib_path= +orig_dir= +sdir= +idir="/broken/path/here" +cleanup=no +install_docs=yes + +invalid_src="does not seem to be a valid OTP source tree" +not_built="Source in has not been built" +doc_not_built="Documentation has not been built. Either build the +documentation and re-run 'otp_patch_apply', or re-run 'otp_patch_apply' +with the '-n' switch." + +print_usage() +{ + cat <<EOF +otp_patch_apply -s <Dir> -i <Dir> [-l <Dir>] [-c] [-f] [-h] [-n] [-v] \\ + <App1> [... <AppN>] + + -s <Dir> -- OTP source directory that contain build results. + -i <Dir> -- OTP installation directory to patch. + -l <Dir> -- Alternative OTP source library directory path(s) containing + build results of OTP applications. Multiple paths should be + colon separated. + -c -- Cleanup (remove) old versions of applications patched + in the installation. + -f -- Force patch of application(s) even though dependencies are + not fullfilled. + -h -- Print this help then exit. + -n -- Do not install documentation. + -v -- Print version then exit. + <AppX> -- Application to patch. + +Environment Variable: + ERL_LIBS -- Alternative OTP source library directory path(s) containing + build results of OTP applications. Multiple paths should be + colon separated. + +NOTE: + * Complete build environment is required while running otp_patch_apply. + * Before applying a patch you need to build all of OTP in the source + directory. + * All source directories identified by -s and -l should contain build + results of OTP applications. + +Version: $version + +EOF + +} + +error() +{ + echo "ERROR:" "$@" 1>&2 + exit 1 +} + +usage_error() +{ + echo "ERROR:" "$@" 1>&2 + echo "" 1>&2 + print_usage 1>&2 + exit 1 +} + +usage() +{ + print_usage + exit 0 +} + +alt_lib_path() +{ + app=$1 + save_ifs=$IFS + IFS=: + + cd "$orig_dir" || error "Cannot change directory to $orig_dir" + + for lib in $lib_path; do + # Want absolute path + case "$lib" in + /*) + ;; + *) + cd "$lib" || error "Cannot change directory to $lib" + lib=`pwd` + cd "$orig_dir" || error "Cannot change directory to $orig_dir" + esac + if [ -d "$lib/$app" ]; then + echo "$lib/$app" + IFS=$save_ifs + return 0 + fi + done + + IFS=$save_ifs + + return 1 +} + +prog_in_mod_path() +{ + chk_path="/bin:$PATH" + PROG=$1 + save_ifs=$IFS + IFS=: + if [ "X$TARGET" = "Xwin32" ]; then + for p in $chk_path; do + if [ -f "$p/$PROG.exe" ]; then + IFS=$save_ifs + echo "$p/$PROG.exe" + return 0 + fi + done + else + for p in $chk_path; do + if [ -x "$p/$PROG" ]; then + IFS=$save_ifs + echo "$p/$PROG" + return 0 + fi + done + fi + IFS=$save_ifs + return 1 +} + +find_prog() +{ + prog_in_mod_path "$1" + if [ $? -ne 0 ]; then + echo "$1" + fi + return 0 +} + +# Parse arguments + +while [ $# -gt 0 ]; do + case "$1" in + "-s") + shift + if [ ! $# -gt 0 ]; then + usage_error "Missing OTP source directory" + fi + sdir="$1";; + "-i") + shift + if [ ! $# -gt 0 ]; then + usage_error "Missing OTP install directory" + fi + idir="$1";; + "-l") + shift + if [ ! $# -gt 0 ]; then + usage_error "Missing OTP library directory" + fi + if [ "x$lib_path" = "x" ]; then + lib_path="$1" + else + lib_path="$lib_path:$1" + fi;; + "-f") + force="-force";; + "-c") + cleanup=yes;; + "-h") + usage;; + "-n") + install_docs=no;; + "-v") + echo "otp_patch_apply version $version" + exit 0;; + *) + app="$1" + applications="$applications $app";; + esac + shift +done + +# Check that we got mandatory arguments +test "x$sdir" != "x" || usage_error "Missing OTP source directory" +test "x$idir" != "x" || usage_error "Missing OTP install directory" +test "x$applications" != "x" || usage_error "Missing applications" + +orig_dir=`pwd` + +# Check that the source directory seems sane +cd "$sdir" 2>/dev/null || error "Cannot change directory to $sdir" + +# Want absolute path +case "$sdir" in + /*) ;; + *) sdir=`pwd`;; +esac + +export ERL_TOP="$sdir" +test -f "$sdir/otp_build" || error "$ERL_TOP" $invalid_src +test -f "$sdir/OTP_VERSION" || error "$ERL_TOP" $invalid_src +test -f "$sdir/otp_versions.table" || error "$ERL_TOP" $invalid_src +test -f "$sdir/erts/autoconf/config.guess" || error "$ERL_TOP" $invalid_src +test -f "$sdir/make/verify_runtime_dependencies" || error "$ERL_TOP" $invalid_src +test -x "$sdir/bootstrap/bin/erl" || error $not_built +test -x "$sdir/bootstrap/bin/erlc" || error $not_built +test -x "$sdir/bootstrap/bin/escript" || error $not_built +test -f "$sdir/make/otp_built" || error $not_built + +if [ $install_docs = yes ]; then + test -f "$sdir/make/otp_doc_built" || usage_error $doc_not_built +fi + +otp_rel=`sed 's|\([0-9]*\).*|\1|' < $ERL_TOP/OTP_VERSION` || \ + error "Failed to read $ERL_TOP/OTP_VERSION" + +case "$otp_rel" in + 1[7-9]|[2-9][0-9]) ;; # ok; release 17-99 + *) error "Invalid OTP release: $otp_rel";; +esac + +export PATH="$ERL_TOP/bootstrap/bin:$PATH" +erlc="$ERL_TOP/bootstrap/bin/erlc" +erl="$ERL_TOP/bootstrap/bin/erl" + +erl_otp_rel=`$erl -noshell -noinput -eval "io:format(\"~s~n\", [erlang:system_info(otp_release)]), erlang:halt(0)"` || \ + error "Failed to execute: $erl" + +test "$otp_rel" = "$erl_otp_rel" || error "Inconsistent source: $sdir" + +app_dirs= +for app in $applications; do + case "$app" in + "erts") + dir="$ERL_TOP/erts";; + *) + dir="$ERL_TOP/lib/$app";; + esac + if [ ! -d "$dir" ]; then + dir=`alt_lib_path "$app"` + if [ $? -ne 0 ]; then + error "Application missing in source: $app" + fi + fi + app_dirs="$app_dirs $dir" +done + +cd "$orig_dir" 2>/dev/null || error "Cannot change directory to $orig_dir" + +# Check that the install directory seems sane +cd "$idir" 2>/dev/null || error "Cannot change directory to $idir" + +# Want absolute path +case "$idir" in + /*) ;; + *) idir=`pwd`;; +esac + +test -d "$idir/releases/$otp_rel" || \ + error "No OTP-$otp_rel installation present in $idir" + +cd "$ERL_TOP" 2>/dev/null || error "Cannot change directory to $ERL_TOP" + +# Some tools we use +rm=`find_prog rm` +rmdir=`find_prog rmdir` +cp=`find_prog cp` +mv=`find_prog mv` +mkdir=`find_prog mkdir` + +# Setup build stuff +if [ "x$TARGET" = "x" ]; then + TARGET=`$ERL_TOP/erts/autoconf/config.guess` +fi +BUILDSYS=$TARGET +if [ -z "$MAKE" ]; then + case $TARGET in + win32) + MAKE=make;; + *) + prog_in_mod_path gmake >/dev/null + if [ $? -eq 0 ]; then + MAKE=gmake + else + MAKE=make + fi;; + esac +fi +if [ X`$MAKE is_cross_configured` = Xyes ]; then + TARGET=`$MAKE target_configured` +elif [ "x$OVERRIDE_TARGET" != "x" -a "x$OVERRIDE_TARGET" != "xwin32" ]; then + TARGET=$OVERRIDE_TARGET +fi + +# Check for cleanup +inst_app_vers="$idir/releases/$otp_rel/installed_application_versions" +rm_app_vers= +if [ $cleanup = yes ]; then + $mv "$inst_app_vers" "${inst_app_vers}.save" || \ + error "Failed to save $inst_app_vers" + for app in $applications; do + tmp=`grep "$app-*" "${inst_app_vers}.save"` + rm_app_vers="$rm_app_vers $tmp" + done + $cp "${inst_app_vers}.save" "$inst_app_vers" + for rm_app_ver in $rm_app_vers; do + $cp "$inst_app_vers" "${inst_app_vers}.tmp" + grep -v $rm_app_ver "${inst_app_vers}.tmp" > "$inst_app_vers" + done + $rm -f "${inst_app_vers}.tmp" +fi + +# Verify runtime dependencies +$ERL_TOP/make/verify_runtime_dependencies -release "$otp_rel" \ + -source "$ERL_TOP" -target "$idir" $force $applications || { + test ! -f "${inst_app_vers}.save" || \ + $mv "${inst_app_vers}.save" "$inst_app_vers" + exit 1 +} + +# Update OTP_VERSION in installation +otp_version=`cat "$idir/releases/$otp_rel/OTP_VERSION"` || { + test ! -f "${inst_app_vers}.save" || \ + $mv "${inst_app_vers}.save" "$inst_app_vers" + error "Not able to read $idir/releases/$otp_rel/OTP_VERSION" +} + +{ + echo "$otp_version" | sed "s|^\([^\*]*\)\**|\1\*\*|g" > \ + "$idir/releases/$otp_rel/OTP_VERSION" +} 2>/dev/null || { + test ! -f "${inst_app_vers}.save" || \ + $mv "${inst_app_vers}.save" "$inst_app_vers" + error "Not able to update $idir/releases/$otp_rel/OTP_VERSION" +} + +# Do actual cleanup +if [ "x$rm_app_vers" != "x" ]; then + for app_ver in $rm_app_vers; do + case x"$app_ver" in + x) + ;; + xerts-*) + $rm -rf "$idir/$app_ver" ;; + x*) + $rm -rf "$idir/lib/$app_ver" ;; + esac + done + $rm -f "${inst_app_vers}.save" +fi + +# Install application from built source +for app_dir in $app_dirs; do + (cd "$app_dir" && \ + $MAKE MAKE="$MAKE" TARGET=$TARGET RELEASE_ROOT="$idir" \ + RELEASE_PATH="$idir" TESTROOT="$idir" release) || exit 1 +done + +if [ $install_docs = yes ]; then +# Documentation have been built and should be installed + + for app_dir in $app_dirs; do + (cd "$app_dir" && \ + $MAKE MAKE="$MAKE" RELEASE_ROOT="$idir" RELEASE_PATH="$idir" \ + TESTROOT="$idir" release_docs) || exit 1 + done + + (cd "$sdir/system/doc/top" && $MAKE clean) + + (cd "$sdir/system/doc/top" && \ + $MAKE MAKE="$MAKE" RELEASE_ROOT="$idir" RELEASE_PATH="$idir" \ + TESTROOT="$idir" release_docs) || exit 1 + + echo "" + echo "*" + echo "* NOTE! In order to update pre-formatted man pages you" + echo "* need to run the 'Install' script located in:" + echo "* $idir" + echo "*" +fi + +# If erts, kernel, stdlib or sasl is included, find versions +for app in $applications; do + case "$app" in + erts) + erts_vsn=`grep '^VSN' erts/vsn.mk | sed "s|^VSN.*=[^0-9]*\([0-9].*\)$|\1|g"` + update_rel=true;; + kernel) + kernel_vsn=`sed "s|^KERNEL_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/kernel/vsn.mk` + update_rel=true;; + stdlib) + stdlib_vsn=`sed "s|^STDLIB_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/stdlib/vsn.mk` + update_rel=true;; + sasl) + sasl_vsn=`sed "s|^SASL_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/sasl/vsn.mk` + update_rel=true;; + *) + ;; + esac +done + +# and find the old versions for those not included +if [ "X$update_rel" != "X" ]; then + if [ "X$erts_vsn" = "X" ]; then + erts_vsns=`ls -d "$idir"/erts-* | sed "s|$idir/erts-\([0-9\.].*\)|\1|g"` + erts_vsn=`echo "$erts_vsns" | sort -t '.' -g | tail -n 1` + fi + if [ "X$kernel_vsn" = "X" ]; then + kernel_vsns=`ls -d "$idir"/lib/kernel-* | sed "s|$idir/lib/kernel-\([0-9\.].*\)|\1|g"` + kernel_vsn=`echo "$kernel_vsns" | sort -t '.' -g | tail -n 1` + fi + if [ "X$stdlib_vsn" = "X" ]; then + stdlib_vsns=`ls -d "$idir"/lib/stdlib-* | sed "s|$idir/lib/stdlib-\([0-9\.].*\)|\1|g"` + stdlib_vsn=`echo "$stdlib_vsns" | sort -t '.' -g | tail -n 1` + fi + if [ "X$sasl_vsn" = "X" ]; then + sasl_vsns=`ls -d "$idir"/lib/sasl-* | sed "s|$idir/lib/sasl-\([0-9\.].*\)|\1|g"` + sasl_vsn=`echo "$sasl_vsns" | sort -t '.' -g | tail -n 1` + fi + + # Generate .rel, .script and .boot - to tmp dir + start_clean="{release, {\"Erlang/OTP\",\"$otp_rel\"}, {erts, \"$erts_vsn\"}, [{kernel,\"$kernel_vsn\"}, {stdlib,\"$stdlib_vsn\"}]}." + start_sasl="{release, {\"Erlang/OTP\",\"$otp_rel\"}, {erts, \"$erts_vsn\"}, [{kernel,\"$kernel_vsn\"}, {stdlib,\"$stdlib_vsn\"}, {sasl,\"$sasl_vsn\"}]}." + + tmp_dir="$idir/tmp"; + if [ ! -d "$tmp_dir" ]; then + $mkdir "$tmp_dir" + fi + echo "$start_sasl" > "$tmp_dir/start_sasl.rel" + echo "$start_clean" > "$tmp_dir/start_clean.rel" + echo "$start_clean" > "$tmp_dir/no_dot_erlang.rel" + + $erlc -I"$idir"/lib/*/ebin -o"$tmp_dir" "$tmp_dir/start_sasl.rel" || exit 1 + $erlc -I"$idir"/lib/*/ebin -o"$tmp_dir" +no_warn_sasl "$tmp_dir/start_clean.rel" || exit 1 + $erlc -I"$idir"/lib/*/ebin -o"$tmp_dir" +no_warn_sasl +no_dot_erlang "$tmp_dir/no_dot_erlang.rel" || exit 1 + + # Generate RELEASES file + "$erl" -noinput +B -eval "release_handler:create_RELEASES(\"%ERL_ROOT%\", \"$tmp_dir\", \"$tmp_dir/start_sasl.rel\", []), halt()" || exit 1 + + # If all good so far, move generated files into target area + $mv "$tmp_dir/RELEASES" "$idir/releases/RELEASES.src" + $mv "$tmp_dir"/* "$idir/releases/$otp_rel" + $rmdir "$tmp_dir" + + # Remove old start scripts (forces a new run of Install) + $rm -f "$idir"/releases/RELEASES + $rm -f "$idir"/bin/*.script + $rm -f "$idir"/bin/*.boot + $rm -f "$idir"/bin/erl + + echo "" + echo "*" + echo "* NOTE! In order to get a runnable OTP system again you" + echo "* need to run the 'Install' script located in:" + echo "* $idir" + echo "*" +fi + diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml index 7b030115df..9d3a204999 100644 --- a/system/doc/design_principles/applications.xml +++ b/system/doc/design_principles/applications.xml @@ -29,55 +29,63 @@ <file>applications.xml</file> </header> <marker id="appl"></marker> - <p>This chapter should be read in conjunction with <c>app(4)</c> and - <c>application(3)</c>.</p> + <p>This section is to be read with the <c>app(4)</c> and + <c>application(3)</c> manual pages in Kernel.</p> <section> <title>Application Concept</title> - <p>When we have written code implementing some specific - functionality, we might want to make the code into an - <em>application</em>, that is a component that can be started and - stopped as a unit, and which can be re-used in other systems as - well.</p> - <p>To do this, we create an - <seealso marker="#callback_module">application callback module</seealso>, where we describe how the application should - be started and stopped.</p> + <p>When you have written code implementing some specific functionality + you might want to make the code into an <em>application</em>, + that is, a component that can be started and stopped as a unit, + and which can also be reused in other systems.</p> + <p>To do this, create an + <seealso marker="#callback_module">application callback module</seealso>, + and describe how the application is to be started and stopped.</p> <p>Then, an <em>application specification</em> is needed, which is - put in an <seealso marker="#appl_res_file">application resource file</seealso>. Among other things, we specify which - modules the application consists of and the name of the callback - module.</p> - <p>If we use <c>systools</c>, the Erlang/OTP tools for packaging code + put in an + <seealso marker="#appl_res_file">application resource file</seealso>. + Among other things, this file specifies which modules the application + consists of and the name of the callback module.</p> + <p>If you use <c>systools</c>, the Erlang/OTP tools for packaging code (see <seealso marker="release_structure">Releases</seealso>), - the code for each application is placed in a separate directory - following a pre-defined <seealso marker="#app_dir">directory structure</seealso>.</p> + the code for each application is placed in a + separate directory following a pre-defined + <seealso marker="#app_dir">directory structure</seealso>.</p> </section> <section> <marker id="callback_module"></marker> <title>Application Callback Module</title> - <p>How to start and stop the code for the application, i.e. + <p>How to start and stop the code for the application, that is, the supervision tree, is described by two callback functions:</p> <code type="none"> start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} -stop(State)</code> - <p><c>start</c> is called when starting the application and should - create the supervision tree by starting the top supervisor. - It is expected to return the pid of the top supervisor and an - optional term <c>State</c>, which defaults to []. This term is - passed as-is to <c>stop</c>.</p> - <p><c>StartType</c> is usually the atom <c>normal</c>. It has other +stop(State) + </code> + <list type="bulleted"> + <item><c>start</c> is called when starting the application and is to + create the supervision tree by starting the top supervisor. It is + expected to return the pid of the top supervisor and an optional + term, <c>State</c>, which defaults to <c>[]</c>. This term is passed + as is to <c>stop</c>.</item> + <item><c>StartType</c> is usually the atom <c>normal</c>. It has other values only in the case of a takeover or failover, see - <seealso marker="distributed_applications">Distributed Applications</seealso>. <c>StartArgs</c> is defined by the key - <c>mod</c> in the <seealso marker="#appl_res_file">application resource file</seealso> file.</p> - <p><c>stop/1</c> is called <em>after</em> the application has been - stopped and should do any necessary cleaning up. Note that - the actual stopping of the application, that is the shutdown of - the supervision tree, is handled automatically as described in - <seealso marker="#stopping">Starting and Stopping Applications</seealso>.</p> + <seealso marker="distributed_applications">Distributed Applications</seealso>. + </item> + <item><c>StartArgs</c> is defined by the key <c>mod</c> in the + <seealso marker="#appl_res_file">application + resource file</seealso>.</item> + <item><c>stop/1</c> is called <em>after</em> the application has been + stopped and is to do any necessary cleaning up. The actual stopping of + the application, that is, the shutdown of the supervision tree, is + handled automatically as described in + <seealso marker="#stopping">Starting and Stopping Applications</seealso>. + </item> + </list> <marker id="ch_app"></marker> <p>Example of an application callback module for packaging the supervision tree from - the <seealso marker="sup_princ#ex">Supervisor</seealso> chapter:</p> + <seealso marker="sup_princ#ex">Supervisor Behaviour</seealso>:</p> <code type="none"> -module(ch_app). -behaviour(application). @@ -89,44 +97,48 @@ start(_Type, _Args) -> stop(_State) -> ok.</code> - <p>A library application, which can not be started or stopped, - does not need any application callback module.</p> + <p>A library application that cannot be started or stopped, does not + need any application callback module.</p> </section> <section> <marker id="appl_res_file"></marker> <title>Application Resource File</title> - <p>To define an application, we create an <em>application specification</em> which is put in an <em>application resource file</em>, or in short <c>.app</c> file:</p> + <p>To define an application, an <em>application specification</em> is + created, which is put in an <em>application resource file</em>, or in + short an <c>.app</c> file:</p> <code type="none"> {application, Application, [Opt1,...,OptN]}.</code> - <p><c>Application</c>, an atom, is the name of the application. - The file must be named <c>Application.app</c>.</p> - <p>Each <c>Opt</c> is a tuple <c>{Key, Value}</c> which define a +<list> + <item><c>Application</c>, an atom, is the name of the application. + The file must be named <c>Application.app</c>.</item> + <item>Each <c>Opt</c> is a tuple <c>{Key,Value}</c>, which define a certain property of the application. All keys are optional. - Default values are used for any omitted keys.</p> + Default values are used for any omitted keys.</item> +</list> <p>The contents of a minimal <c>.app</c> file for a library - application <c>libapp</c> looks like this:</p> + application <c>libapp</c> looks as follows:</p> <code type="none"> {application, libapp, []}.</code> <p>The contents of a minimal <c>.app</c> file <c>ch_app.app</c> for - a supervision tree application like <c>ch_app</c> looks like this:</p> + a supervision tree application like <c>ch_app</c> looks as follows:</p> <code type="none"> {application, ch_app, [{mod, {ch_app,[]}}]}.</code> - <p>The key <c>mod</c> defines the callback module and start - argument of the application, in this case <c>ch_app</c> and - [], respectively. This means that</p> + <p>The key <c>mod</c> defines the callback module and start argument of + the application, in this case <c>ch_app</c> and <c>[]</c>, respectively. + This means that the following is called when the application is to be + started:</p> <code type="none"> ch_app:start(normal, [])</code> - <p>will be called when the application should be started and</p> + <p>The following is called when the application is stopped.</p> <code type="none"> ch_app:stop([])</code> - <p>will be called when the application has been stopped.</p> <p>When using <c>systools</c>, the Erlang/OTP tools for packaging - code (see <seealso marker="release_structure">Releases</seealso>), - the keys <c>description</c>, <c>vsn</c>, <c>modules</c>, - <c>registered</c> and <c>applications</c> should also be - specified:</p> + code (see Section + <seealso marker="release_structure">Releases</seealso>), the keys + <c>description</c>, <c>vsn</c>, <c>modules</c>, <c>registered</c>, + and <c>applications</c> are also to be specified:</p> <code type="none"> {application, ch_app, [{description, "Channel allocator"}, @@ -136,67 +148,54 @@ ch_app:stop([])</code> {applications, [kernel, stdlib, sasl]}, {mod, {ch_app,[]}} ]}.</code> - <taglist> - <tag><c>description</c></tag> - <item>A short description, a string. Defaults to "".</item> - <tag><c>vsn</c></tag> - <item>Version number, a string. Defaults to "".</item> - <tag><c>modules</c></tag> - <item>All modules <em>introduced</em> by this application. - <c>systools</c> uses this list when generating boot scripts and - tar files. A module must be defined in one and only one - application. Defaults to [].</item> - <tag><c>registered</c></tag> - <item>All names of registered processes in the application. - <c>systools</c> uses this list to detect name clashes - between applications. Defaults to [].</item> - <tag><c>applications</c></tag> - <item>All applications which must be started before this - application is started. <c>systools</c> uses this list to - generate correct boot scripts. Defaults to [], but note that - all applications have dependencies to at least <c>kernel</c> - and <c>stdlib</c>.</item> - </taglist> - <note><p>The syntax and contents of of the application resource file - are described in detail in the<seealso marker="kernel:app"> - Application resource file reference</seealso>.</p></note> + <list> + <item><c>description</c> - A short description, a string. Defaults to + "".</item> + <item><c>vsn</c> - Version number, a string. Defaults to "".</item> + <item><c>modules</c> - All modules <em>introduced</em> by this + application. <c>systools</c> uses this list when generating boot scripts + and tar files. A module must be defined in only one application. + Defaults to <c>[]</c>.</item> + <item><c>registered</c> - All names of registered processes in the + application. <c>systools</c> uses this list to detect name clashes + between applications. Defaults to <c>[]</c>.</item> + <item><c>applications</c> - All applications that must be + started before this application is started. <c>systools</c> uses this + list to generate correct boot scripts. Defaults to <c>[]</c>. Notice + that all applications have dependencies to at least Kernel + and STDLIB.</item> + </list> + <note><p>For details about the syntax and contents of the application + resource file, see the <seealso marker="kernel:app">app</seealso> + manual page in Kernel.</p></note> </section> <section> <marker id="app_dir"></marker> <title>Directory Structure</title> <p>When packaging code using <c>systools</c>, the code for each - application is placed in a separate directory + application is placed in a separate directory, <c>lib/Application-Vsn</c>, where <c>Vsn</c> is the version number.</p> - <p>This may be useful to know, even if <c>systools</c> is not used, - since Erlang/OTP itself is packaged according to the OTP principles + <p>This can be useful to know, even if <c>systools</c> is not used, + since Erlang/OTP is packaged according to the OTP principles and thus comes with this directory structure. The code server - (see <c>code(3)</c>) will automatically use code from - the directory with the highest version number, if there are - more than one version of an application present.</p> - <p>The application directory structure can of course be used in - the development environment as well. The version number may then + (see the <c>code(3)</c> manual page in Kernel) automatically + uses code from + the directory with the highest version number, if more than one + version of an application is present.</p> + <p>The application directory structure can also be used in the + development environment. The version number can then be omitted from the name.</p> - <p>The application directory have the following sub-directories:</p> + <p>The application directory has the following sub-directories:</p> <list type="bulleted"> - <item><c>src</c></item> - <item><c>ebin</c></item> - <item><c>priv</c></item> - <item><c>include</c></item> + <item><c>src</c> - Contains the Erlang source code.</item> + <item><c>ebin</c> - Contains the Erlang object code, the + <c>beam</c> files. The <c>.app</c> file is also placed here.</item> + <item><c>priv</c> - Used for application specific files. For + example, C executables are placed here. The function + <c>code:priv_dir/1</c> is to be used to access this directory.</item> + <item><c>include</c> - Used for include files.</item> </list> - <taglist> - <tag><c>src</c></tag> - <item>Contains the Erlang source code.</item> - <tag><c>ebin</c></tag> - <item>Contains the Erlang object code, the <c>beam</c> files. - The <c>.app</c> file is also placed here.</item> - <tag><c>priv</c></tag> - <item>Used for application specific files. For example, C - executables are placed here. The function <c>code:priv_dir/1</c> - should be used to access this directory.</item> - <tag><c>include</c></tag> - <item>Used for include files.</item> - </taglist> </section> <section> @@ -207,17 +206,17 @@ ch_app:stop([])</code> processes is the <em>application controller</em> process, registered as <c>application_controller</c>.</p> <p>All operations on applications are coordinated by the application - controller. It is interfaced through the functions in - the module <c>application</c>, see <c>application(3)</c>. - In particular, applications can be loaded, unloaded, started and - stopped.</p> + controller. It is interacted through the functions in + the module <c>application</c>, see the <c>application(3)</c> + manual page in Kernel. In particular, applications can be + loaded, unloaded, started, and stopped.</p> </section> <section> <title>Loading and Unloading Applications</title> <p>Before an application can be started, it must be <em>loaded</em>. The application controller reads and stores the information from - the <c>.app</c> file.</p> + the <c>.app</c> file:</p> <pre> 1> <input>application:load(ch_app).</input> ok @@ -236,7 +235,7 @@ ok {stdlib,"ERTS CXC 138 10","1.11.4.3"}]</pre> <note> <p>Loading/unloading an application does not load/unload the code - used by the application. Code loading is done the usual way.</p> + used by the application. Code loading is done the usual way.</p> </note> </section> @@ -252,13 +251,14 @@ ok {stdlib,"ERTS CXC 138 10","1.11.4.3"}, {ch_app,"Channel allocator","1"}]</pre> <p>If the application is not already loaded, the application - controller will first load it using <c>application:load/1</c>. It - will check the value of the <c>applications</c> key, to ensure - that all applications that should be started before this + controller first loads it using <c>application:load/1</c>. It + checks the value of the <c>applications</c> key, to ensure + that all applications that are to be started before this application are running.</p> <marker id="application_master"></marker> - <p>The application controller then creates an <em>application master</em> for the application. The application master is - the group leader of all the processes in the application. + <p>The application controller then creates an + <em>application master</em> for the application. The application master + is the group leader of all the processes in the application. The application master starts the application by calling the application callback function <c>start/2</c> in the module, and with the start argument, defined by the <c>mod</c> key in @@ -268,8 +268,8 @@ ok 7> <input>application:stop(ch_app).</input> ok</pre> <p>The application master stops the application by telling the top - supervisor to shutdown. The top supervisor tells all its child - processes to shutdown etc. and the entire tree is terminated in + supervisor to shut down. The top supervisor tells all its child + processes to shut down, and so on; the entire tree is terminated in reversed start order. The application master then calls the application callback function <c>stop/1</c> in the module defined by the <c>mod</c> key.</p> @@ -277,8 +277,10 @@ ok</pre> <section> <title>Configuring an Application</title> - <p>An application can be configured using <em>configuration parameters</em>. These are a list of <c>{Par, Val}</c> tuples - specified by a key <c>env</c> in the <c>.app</c> file.</p> + <p>An application can be configured using + <em>configuration parameters</em>. These are a list of + <c>{Par,Val}</c> tuples + specified by a key <c>env</c> in the <c>.app</c> file:</p> <code type="none"> {application, ch_app, [{description, "Channel allocator"}, @@ -289,11 +291,12 @@ ok</pre> {mod, {ch_app,[]}}, {env, [{file, "/usr/local/log"}]} ]}.</code> - <p><c>Par</c> should be an atom, <c>Val</c> is any term. + <p><c>Par</c> is to be an atom. <c>Val</c> is any term. The application can retrieve the value of a configuration parameter by calling <c>application:get_env(App, Par)</c> or a - number of similar functions, see <c>application(3)</c>.</p> - <p>Example:</p> + number of similar functions, see the <c>application(3)</c> + manual page in Kernel.</p> + <p><em>Example:</em></p> <pre> % <input>erl</input> Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] @@ -304,20 +307,21 @@ ok 2> <input>application:get_env(ch_app, file).</input> {ok,"/usr/local/log"}</pre> <p>The values in the <c>.app</c> file can be overridden by values - in a <em>system configuration file</em>. This is a file which + in a <em>system configuration file</em>. This is a file that contains configuration parameters for relevant applications:</p> <code type="none"> [{Application1, [{Par11,Val11},...]}, ..., {ApplicationN, [{ParN1,ValN1},...]}].</code> - <p>The system configuration should be called <c>Name.config</c> and - Erlang should be started with the command line argument - <c>-config Name</c>. See <c>config(4)</c> for more information.</p> - <p>Example: A file <c>test.config</c> is created with the following - contents:</p> + <p>The system configuration is to be called <c>Name.config</c> and + Erlang is to be started with the command-line argument + <c>-config Name</c>. For details, see the <c>config(4)</c> + manual page in Kernel.</p> + <p><em>Example:</em></p> + <p>A file <c>test.config</c> is created with the following contents:</p> <code type="none"> [{ch_app, [{file, "testlog"}]}].</code> - <p>The value of <c>file</c> will override the value of <c>file</c> + <p>The value of <c>file</c> overrides the value of <c>file</c> as defined in the <c>.app</c> file:</p> <pre> % <input>erl -config test</input> @@ -330,14 +334,14 @@ ok {ok,"testlog"}</pre> <p>If <seealso marker="release_handling#sys">release handling</seealso> - is used, exactly one system configuration file should be used and - that file should be called <c>sys.config</c></p> - <p>The values in the <c>.app</c> file, as well as the values in a - system configuration file, can be overridden directly from + is used, exactly one system configuration file is to be used and + that file is to be called <c>sys.config</c>.</p> + <p>The values in the <c>.app</c> file and the values in a + system configuration file can be overridden directly from the command line:</p> <pre> % <input>erl -ApplName Par1 Val1 ... ParN ValN</input></pre> - <p>Example:</p> + <p><em>Example:</em></p> <pre> % <input>erl -ch_app file '"testlog"'</input> Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] @@ -368,10 +372,10 @@ application:start(Application, Type)</code> <item>If a temporary application terminates, this is reported but no other applications are terminated.</item> </list> - <p>It is always possible to stop an application explicitly by + <p>An application can always be stopped explicitly by calling <c>application:stop/1</c>. Regardless of the mode, no - other applications will be affected.</p> - <p>Note that transient mode is of little practical use, since when + other applications are affected.</p> + <p>The transient mode is of little practical use, since when a supervision tree terminates, the reason is set to <c>shutdown</c>, not <c>normal</c>.</p> </section> diff --git a/system/doc/design_principles/appup_cookbook.xml b/system/doc/design_principles/appup_cookbook.xml index 22c48db855..63adea8a5c 100644 --- a/system/doc/design_principles/appup_cookbook.xml +++ b/system/doc/design_principles/appup_cookbook.xml @@ -28,15 +28,15 @@ <rev></rev> <file>appup_cookbook.xml</file> </header> - <p>This chapter contains examples of <c>.appup</c> files for - typical cases of upgrades/downgrades done in run-time.</p> + <marker id="appup cookbook"></marker> + <p>This section includes examples of <c>.appup</c> files for + typical cases of upgrades/downgrades done in runtime.</p> <section> <title>Changing a Functional Module</title> - <p>When a change has been made to a functional module, for example + <p>When a functional module has been changed, for example, if a new function has been added or a bug has been corrected, - simple code replacement is sufficient.</p> - <p>Example:</p> + simple code replacement is sufficient, for example:</p> <code type="none"> {"2", [{"1", [{load_module, m}]}], @@ -46,29 +46,31 @@ <section> <title>Changing a Residence Module</title> - <p>In a system implemented according to the OTP Design Principles, + <p>In a system implemented according to the OTP design principles, all processes, except system processes and special processes, reside in one of the behaviours <c>supervisor</c>, - <c>gen_server</c>, <c>gen_fsm</c> or <c>gen_event</c>. These + <c>gen_server</c>, <c>gen_fsm</c>, or <c>gen_event</c>. These belong to the STDLIB application and upgrading/downgrading normally requires an emulator restart.</p> - <p>OTP thus provides no support for changing residence modules - except in the case of <seealso marker="#spec">special processes</seealso>.</p> + <p>OTP thus provides no support for changing residence modules except + in the case of <seealso marker="#spec">special processes</seealso>.</p> </section> <section> <title>Changing a Callback Module</title> - <p>A callback module is a functional module, and for code + <p>A callback module is a functional module, and for code extensions simple code replacement is sufficient.</p> - <p>Example: When adding a function to <c>ch3</c> as described in - the example in <seealso marker="release_handling#appup">Release Handling</seealso>, <c>ch_app.appup</c> looks as follows:</p> + <p><em>Example:</em> When adding a function to <c>ch3</c>, + as described in the example in + <seealso marker="release_handling#appup">Release Handling</seealso>, + <c>ch_app.appup</c> looks as follows:</p> <code type="none"> {"2", [{"1", [{load_module, ch3}]}], [{"1", [{load_module, ch3}]}] }.</code> <p>OTP also supports changing the internal state of behaviour - processes, see <seealso marker="#int_state">Changing Internal State</seealso> below.</p> + processes, see <seealso marker="#int_state">Changing Internal State</seealso>.</p> </section> <section> @@ -77,26 +79,28 @@ <p>In this case, simple code replacement is not sufficient. The process must explicitly transform its state using the callback function <c>code_change</c> before switching to the new version - of the callback module. Thus synchronized code replacement is + of the callback module. Thus, synchronized code replacement is used.</p> - <p>Example: Consider the gen_server <c>ch3</c> from the chapter - about the <seealso marker="gen_server_concepts#ex">gen_server behaviour</seealso>. The internal state is a term <c>Chs</c> - representing the available channels. Assume we want add a counter - <c>N</c> which keeps track of the number of <c>alloc</c> requests - so far. This means we need to change the format to + <p><em>Example:</em> Consider <c>gen_server</c> <c>ch3</c> from + <seealso marker="gen_server_concepts#ex">gen_server Behaviour</seealso>. + The internal state is a term <c>Chs</c> + representing the available channels. Assume you want to add a counter + <c>N</c>, which keeps track of the number of <c>alloc</c> requests + so far. This means that the format must be changed to <c>{Chs,N}</c>.</p> - <p>The <c>.appup</c> file could look as follows:</p> + <p>The <c>.appup</c> file can look as follows:</p> <code type="none"> {"2", [{"1", [{update, ch3, {advanced, []}}]}], [{"1", [{update, ch3, {advanced, []}}]}] }.</code> <p>The third element of the <c>update</c> instruction is a tuple - <c>{advanced,Extra}</c> which says that the affected processes - should do a state transformation before loading the new version + <c>{advanced,Extra}</c>, which says that the affected processes + are to do a state transformation before loading the new version of the module. This is done by the processes calling the callback - function <c>code_change</c> (see <c>gen_server(3)</c>). The term - <c>Extra</c>, in this case [], is passed as-is to the function:</p> + function <c>code_change</c> (see the <c>gen_server(3)</c> manual + page in STDLIB). The term <c>Extra</c>, in this case + <c>[]</c>, is passed as is to the function:</p> <marker id="code_change"></marker> <code type="none"> -module(ch3). @@ -107,40 +111,41 @@ code_change({down, _Vsn}, {Chs, N}, _Extra) -> {ok, Chs}; code_change(_Vsn, Chs, _Extra) -> {ok, {Chs, 0}}.</code> - <p>The first argument is <c>{down,Vsn}</c> in case of a downgrade, - or <c>Vsn</c> in case of an upgrade. The term <c>Vsn</c> is - fetched from the 'original' version of the module, i.e. - the version we are upgrading from, or downgrading to.</p> + <p>The first argument is <c>{down,Vsn}</c> if there is a downgrade, + or <c>Vsn</c> if there is a upgrade. The term <c>Vsn</c> is + fetched from the 'original' version of the module, that is, + the version you are upgrading from, or downgrading to.</p> <p>The version is defined by the module attribute <c>vsn</c>, if any. There is no such attribute in <c>ch3</c>, so in this case - the version is the checksum (a huge integer) of the BEAM file, an - uninteresting value which is ignored.</p> - <p>(The other callback functions of <c>ch3</c> need to be modified - as well and perhaps a new interface function added, this is not - shown here).</p> + the version is the checksum (a huge integer) of the beam file, an + uninteresting value, which is ignored.</p> + <p>The other callback functions of <c>ch3</c> must also be modified + and perhaps a new interface function must be added, but this is not + shown here.</p> </section> <section> <title>Module Dependencies</title> - <p>Assume we extend a module by adding a new interface function, as - in the example in <seealso marker="release_handling#appup">Release Handling</seealso>, where a function <c>available/0</c> is - added to <c>ch3</c>.</p> - <p>If we also add a call to this function, say in the module - <c>m1</c>, a run-time error could occur during release upgrade if + <p>Assume that a module is extended by adding an interface function, + as in the example in + <seealso marker="release_handling#appup">Release Handling</seealso>, + where a function <c>available/0</c> is added to <c>ch3</c>.</p> + <p>If a call is added to this function, say in module + <c>m1</c>, a runtime error could can occur during release upgrade if the new version of <c>m1</c> is loaded first and calls <c>ch3:available/0</c> before the new version of <c>ch3</c> is loaded.</p> - <p>Thus, <c>ch3</c> must be loaded before <c>m1</c> is, in - the upgrade case, and vice versa in the downgrade case. We say - that <c>m1</c> <em>is dependent on</em> <c>ch3</c>. In a release - handling instruction, this is expressed by the element - <c>DepMods</c>:</p> + <p>Thus, <c>ch3</c> must be loaded before <c>m1</c>, in + the upgrade case, and conversely in the downgrade case. + <c>m1</c> is said to be <em>dependent on</em> <c>ch3</c>. In a release + handling instruction, this is expressed by the + <c>DepMods</c> element:</p> <code type="none"> {load_module, Module, DepMods} {update, Module, {advanced, Extra}, DepMods}</code> <p><c>DepMods</c> is a list of modules, on which <c>Module</c> is dependent.</p> - <p>Example: The module <c>m1</c> in the application <c>myapp</c> is + <p><em>Example:</em> The module <c>m1</c> in application <c>myapp</c> is dependent on <c>ch3</c> when upgrading from "1" to "2", or downgrading from "2" to "1":</p> <code type="none"> @@ -157,8 +162,8 @@ ch_app.appup: [{"1", [{load_module, ch3}]}], [{"1", [{load_module, ch3}]}] }.</code> - <p>If <c>m1</c> and <c>ch3</c> had belonged to the same application, - the <c>.appup</c> file could have looked like this:</p> + <p>If instead <c>m1</c> and <c>ch3</c> belong to the same application, + the <c>.appup</c> file can look as follows:</p> <code type="none"> {"2", [{"1", @@ -168,48 +173,48 @@ ch_app.appup: [{load_module, ch3}, {load_module, m1, [ch3]}]}] }.</code> - <p>Note that it is <c>m1</c> that is dependent on <c>ch3</c> also + <p><c>m1</c> is dependent on <c>ch3</c> also when downgrading. <c>systools</c> knows the difference between - up- and downgrading and will generate a correct <c>relup</c>, - where <c>ch3</c> is loaded before <c>m1</c> when upgrading but + up- and downgrading and generates a correct <c>relup</c>, + where <c>ch3</c> is loaded before <c>m1</c> when upgrading, but <c>m1</c> is loaded before <c>ch3</c> when downgrading.</p> </section> <section> <marker id="spec"></marker> - <title>Changing Code For a Special Process</title> + <title>Changing Code for a Special Process</title> <p>In this case, simple code replacement is not sufficient. When a new version of a residence module for a special process is loaded, the process must make a fully qualified call to - its loop function to switch to the new code. Thus synchronized + its loop function to switch to the new code. Thus, synchronized code replacement must be used.</p> <note> <p>The name(s) of the user-defined residence module(s) must be listed in the <c>Modules</c> part of the child specification - for the special process, in order for the release handler to + for the special process. Otherwise the release handler cannot find the process.</p> </note> - <p>Example. Consider the example <c>ch4</c> from the chapter about + <p><em>Example:</em> Consider the example <c>ch4</c> in <seealso marker="spec_proc#ex">sys and proc_lib</seealso>. - When started by a supervisor, the child specification could look - like this:</p> + When started by a supervisor, the child specification can look + as follows:</p> <code type="none"> {ch4, {ch4, start_link, []}, permanent, brutal_kill, worker, [ch4]}</code> <p>If <c>ch4</c> is part of the application <c>sp_app</c> and a new - version of the module should be loaded when upgrading from - version "1" to "2" of this application, <c>sp_app.appup</c> could - look like this:</p> + version of the module is to be loaded when upgrading from + version "1" to "2" of this application, <c>sp_app.appup</c> can + look as follows:</p> <code type="none"> {"2", [{"1", [{update, ch4, {advanced, []}}]}], [{"1", [{update, ch4, {advanced, []}}]}] }.</code> <p>The <c>update</c> instruction must contain the tuple - <c>{advanced,Extra}</c>. The instruction will make the special + <c>{advanced,Extra}</c>. The instruction makes the special process call the callback function <c>system_code_change/4</c>, a function the user must implement. The term <c>Extra</c>, in this - case [], is passed as-is to <c>system_code_change/4</c>:</p> + case <c>[]</c>, is passed as is to <c>system_code_change/4</c>:</p> <code type="none"> -module(ch4). ... @@ -218,39 +223,43 @@ ch_app.appup: system_code_change(Chs, _Module, _OldVsn, _Extra) -> {ok, Chs}.</code> - <p>The first argument is the internal state <c>State</c> passed from - the function <c>sys:handle_system_msg(Request, From, Parent, Module, Deb, State)</c>, called by the special process when - a system message is received. In <c>ch4</c>, the internal state is - the set of available channels <c>Chs</c>.</p> - <p>The second argument is the name of the module (<c>ch4</c>).</p> - <p>The third argument is <c>Vsn</c> or <c>{down,Vsn}</c> as - described for - <seealso marker="#code_change">gen_server:code_change/3</seealso>.</p> + <list type="bulleted"> + <item>The first argument is the internal state <c>State</c>, + passed from function + <c>sys:handle_system_msg(Request, From, Parent, Module, Deb, State)</c>, + and called by the special process when a system message is received. + In <c>ch4</c>, the internal state is the set of available channels + <c>Chs</c>.</item> + <item>The second argument is the name of the module + (<c>ch4</c>).</item> + <item>The third argument is <c>Vsn</c> or <c>{down,Vsn}</c>, as + described for <c>gen_server:code_change/3</c> in + <seealso marker="#code_change">Changing Internal State</seealso>.</item> + </list> <p>In this case, all arguments but the first are ignored and the function simply returns the internal state again. This is - enough if the code only has been extended. If we had wanted to - change the internal state (similar to the example in + enough if the code only has been extended. If instead the + internal state is changed (similar to the example in <seealso marker="#int_state">Changing Internal State</seealso>), - it would have been done in this function and - <c>{ok,Chs2}</c> returned.</p> + this is done in this function and <c>{ok,Chs2}</c> returned.</p> </section> <section> <marker id="sup"></marker> <title>Changing a Supervisor</title> <p>The supervisor behaviour supports changing the internal state, - i.e. changing restart strategy and maximum restart intensity - properties, as well as changing existing child specifications.</p> - <p>Adding and deleting child processes are also possible, but not + that is, changing the restart strategy and maximum restart frequency + properties, as well as changing the existing child specifications.</p> + <p>Child processes can be added or deleted, but this is not handled automatically. Instructions must be given by in the <c>.appup</c> file.</p> <section> <title>Changing Properties</title> - <p>Since the supervisor should change its internal state, + <p>Since the supervisor is to change its internal state, synchronized code replacement is required. However, a special <c>update</c> instruction must be used.</p> - <p>The new version of the callback module must be loaded first + <p>First, the new version of the callback module must be loaded, both in the case of upgrade and downgrade. Then the new return value of <c>init/1</c> can be checked and the internal state be changed accordingly.</p> @@ -258,10 +267,11 @@ system_code_change(Chs, _Module, _OldVsn, _Extra) -> supervisors:</p> <code type="none"> {update, Module, supervisor}</code> - <p>Example: Assume we want to change the restart strategy of - <c>ch_sup</c> from the <seealso marker="sup_princ#ex">Supervisor Behaviour</seealso> chapter from one_for_one to one_for_all. - We change the callback function <c>init/1</c> in - <c>ch_sup.erl</c>:</p> + <p><em>Example:</em> To change the restart strategy of + <c>ch_sup</c> (from + <seealso marker="sup_princ#ex">Supervisor Behaviour</seealso>) + from <c>one_for_one</c> to <c>one_for_all</c>, change the callback + function <c>init/1</c> in <c>ch_sup.erl</c>:</p> <code type="none"> -module(ch_sup). ... @@ -280,7 +290,7 @@ init(_Args) -> <title>Changing Child Specifications</title> <p>The instruction, and thus the <c>.appup</c> file, when changing an existing child specification, is the same as when - changing properties as described above:</p> + changing properties as described earlier:</p> <code type="none"> {"2", [{"1", [{update, ch_sup, supervisor}]}], @@ -288,25 +298,25 @@ init(_Args) -> }.</code> <p>The changes do not affect existing child processes. For example, changing the start function only specifies how - the child process should be restarted, if needed later on.</p> - <p>Note that the id of the child specification cannot be changed.</p> - <p>Note also that changing the <c>Modules</c> field of the child - specification may affect the release handling process itself, + the child process is to be restarted, if needed later on.</p> + <p>The id of the child specification cannot be changed.</p> + <p>Changing the <c>Modules</c> field of the child + specification can affect the release handling process itself, as this field is used to identify which processes are affected when doing a synchronized code replacement.</p> </section> <marker id="sup_add"></marker> <section> - <title>Adding And Deleting Child Processes</title> - <p>As stated above, changing child specifications does not affect + <title>Adding and Deleting Child Processes</title> + <p>As stated earlier, changing child specifications does not affect existing child processes. New child specifications are - automatically added, but not deleted. Also, child processes are - not automatically started or terminated. Instead, this must be - done explicitly using <c>apply</c> instructions.</p> - <p>Example: Assume we want to add a new child process <c>m1</c> to - <c>ch_sup</c> when upgrading <c>ch_app</c> from "1" to "2". - This means <c>m1</c> should be deleted when downgrading from + automatically added, but not deleted. Child processes are + not automatically started or terminated, this must be + done using <c>apply</c> instructions.</p> + <p><em>Example:</em> Assume a new child process <c>m1</c> is to be + added to <c>ch_sup</c> when upgrading <c>ch_app</c> from "1" to "2". + This means <c>m1</c> is to be deleted when downgrading from "2" to "1":</p> <code type="none"> {"2", @@ -320,13 +330,13 @@ init(_Args) -> {update, ch_sup, supervisor} ]}] }.</code> - <p>Note that the order of the instructions is important.</p> - <p>Note also that the supervisor must be registered as + <p>The order of the instructions is important.</p> + <p>The supervisor must be registered as <c>ch_sup</c> for the script to work. If the supervisor is not registered, it cannot be accessed directly from the script. Instead a help function that finds the pid of the supervisor - and calls <c>supervisor:restart_child</c> etc. must be written, - and it is this function that should be called from the script + and calls <c>supervisor:restart_child</c>, and so on, must be + written. This function is then to be called from the script using the <c>apply</c> instruction.</p> <p>If the module <c>m1</c> is introduced in version "2" of <c>ch_app</c>, it must also be loaded when upgrading and @@ -345,18 +355,18 @@ init(_Args) -> {delete_module, m1} ]}] }.</code> - <p>Note again that the order of the instructions is important. - When upgrading, <c>m1</c> must be loaded and the supervisor's + <p>As stated earlier, the order of the instructions is important. + When upgrading, <c>m1</c> must be loaded, and the supervisor child specification changed, before the new child process can be started. When downgrading, the child process must be - terminated before child specification is changed and the module + terminated before the child specification is changed and the module is deleted.</p> </section> </section> <section> <title>Adding or Deleting a Module</title> - <p>Example: A new functional module <c>m</c> is added to + <p><em>Example:</em> A new functional module <c>m</c> is added to <c>ch_app</c>:</p> <code type="none"> {"2", @@ -367,15 +377,16 @@ init(_Args) -> <section> <title>Starting or Terminating a Process</title> <p>In a system structured according to the OTP design principles, - any process would be a child process belonging to a supervisor, - see <seealso marker="#sup_add">Adding and Deleting Child Processes</seealso> above.</p> + any process would be a child process belonging to a supervisor, see + <seealso marker="#sup_add">Adding and Deleting Child Processes</seealso> + in Changing a Supervisor.</p> </section> <section> <title>Adding or Removing an Application</title> <p>When adding or removing an application, no <c>.appup</c> file is needed. When generating <c>relup</c>, the <c>.rel</c> files - are compared and <c>add_application</c> and + are compared and the <c>add_application</c> and <c>remove_application</c> instructions are added automatically.</p> </section> @@ -383,11 +394,11 @@ init(_Args) -> <title>Restarting an Application</title> <p>Restarting an application is useful when a change is too complicated to be made without restarting the processes, for - example if the supervisor hierarchy has been restructured.</p> - <p>Example: When adding a new child <c>m1</c> to <c>ch_sup</c>, as - in the <seealso marker="#sup_add">example above</seealso>, an - alternative to updating the supervisor is to restart the entire - application:</p> + example, if the supervisor hierarchy has been restructured.</p> + <p><em>Example:</em> When adding a child <c>m1</c> to <c>ch_sup</c>, as in + <seealso marker="#sup_add">Adding and Deleting Child Processes</seealso> + in Changing a Supervisor, an alternative to updating + the supervisor is to restart the entire application:</p> <code type="none"> {"2", [{"1", [{restart_application, ch_app}]}], @@ -400,7 +411,7 @@ init(_Args) -> <title>Changing an Application Specification</title> <p>When installing a release, the application specifications are automatically updated before evaluating the <c>relup</c> script. - Hence, no instructions are needed in the <c>.appup</c> file:</p> + Thus, no instructions are needed in the <c>.appup</c> file:</p> <pre> {"2", [{"1", []}], @@ -412,28 +423,29 @@ init(_Args) -> <title>Changing Application Configuration</title> <p>Changing an application configuration by updating the <c>env</c> key in the <c>.app</c> file is an instance of changing an - application specification, <seealso marker="#app_spec">see above</seealso>.</p> + application specification, see the previous section.</p> <p>Alternatively, application configuration parameters can be added or updated in <c>sys.config</c>.</p> </section> <section> <title>Changing Included Applications</title> - <p>The release handling instructions for adding, removing and + <p>The release handling instructions for adding, removing, and restarting applications apply to primary applications only. There are no corresponding instructions for included applications. However, since an included application is really a supervision tree with a topmost supervisor, started as a child process to a supervisor in the including application, a <c>relup</c> file can be manually created.</p> - <p>Example: Assume we have a release containing an application - <c>prim_app</c> which have a supervisor <c>prim_sup</c> in its + <p><em>Example:</em> Assume there is a release containing an application + <c>prim_app</c>, which have a supervisor <c>prim_sup</c> in its supervision tree.</p> - <p>In a new version of the release, our example application - <c>ch_app</c> should be included in <c>prim_app</c>. That is, - its topmost supervisor <c>ch_sup</c> should be started as a child + <p>In a new version of the release, the application <c>ch_app</c> + is to be included in <c>prim_app</c>. That is, + its topmost supervisor <c>ch_sup</c> is to be started as a child process to <c>prim_sup</c>.</p> - <p>1) Edit the code for <c>prim_sup</c>:</p> + <p>The workflow is as follows:</p> + <p><em>Step 1)</em> Edit the code for <c>prim_sup</c>:</p> <code type="none"> init(...) -> {ok, {...supervisor flags..., @@ -441,7 +453,7 @@ init(...) -> {ch_sup, {ch_sup,start_link,[]}, permanent,infinity,supervisor,[ch_sup]}, ...]}}.</code> - <p>2) Edit the <c>.app</c> file for <c>prim_app</c>:</p> + <p><em>Step 2)</em> Edit the <c>.app</c> file for <c>prim_app</c>:</p> <code type="none"> {application, prim_app, [..., @@ -450,27 +462,29 @@ init(...) -> {included_applications, [ch_app]}, ... ]}.</code> - <p>3) Create a new <c>.rel</c> file, including <c>ch_app</c>:</p> + <p><em>Step 3)</em> Create a new <c>.rel</c> file, including + <c>ch_app</c>:</p> <code type="none"> {release, ..., [..., {prim_app, "2"}, {ch_app, "1"}]}.</code> + <p>The included application can be started in two ways. + This is described in the next two sections.</p> <section> <title>Application Restart</title> - <p>4a) One way to start the included application is to restart - the entire <c>prim_app</c> application. Normally, we would then - use the <c>restart_application</c> instruction in - the <c>.appup</c> file for <c>prim_app</c>.</p> - <p>However, if we did this and then generated a <c>relup</c> file, - not only would it contain instructions for restarting (i.e. + <p><em>Step 4a)</em> One way to start the included application is to + restart the entire <c>prim_app</c> application. Normally, the + <c>restart_application</c> instruction in the <c>.appup</c> file + for <c>prim_app</c> would be used.</p> + <p>However, if this is done and a <c>relup</c> file is generated, + not only would it contain instructions for restarting (that is, removing and adding) <c>prim_app</c>, it would also contain instructions for starting <c>ch_app</c> (and stopping it, in - the case of downgrade). This is due to the fact that - <c>ch_app</c> is included in the new <c>.rel</c> file, but not - in the old one.</p> + the case of downgrade). This is because <c>ch_app</c> is included + in the new <c>.rel</c> file, but not in the old one.</p> <p>Instead, a correct <c>relup</c> file can be created manually, either from scratch or by editing the generated version. The instructions for starting/stopping <c>ch_app</c> are @@ -512,7 +526,8 @@ init(...) -> <section> <title>Supervisor Change</title> - <p>4b) Another way to start the included application (or stop it + <p><em>Step 4b)</em> Another way to start the included + application (or stop it in the case of downgrade) is by combining instructions for adding and removing child processes to/from <c>prim_sup</c> with instructions for loading/unloading all <c>ch_app</c> code and @@ -521,7 +536,7 @@ init(...) -> scratch or by editing a generated version. Load all code for <c>ch_app</c> first, and also load the application specification, before <c>prim_sup</c> is updated. When - downgrading, <c>prim_sup</c> should be updated first, before + downgrading, <c>prim_sup</c> is to updated first, before the code for <c>ch_app</c> and its application specification are unloaded.</p> <code type="none"> @@ -560,10 +575,10 @@ init(...) -> <section> <title>Changing Non-Erlang Code</title> <p>Changing code for a program written in another programming - language than Erlang, for example a port program, is very - application dependent and OTP provides no special support for it.</p> - <p>Example, changing code for a port program: Assume that - the Erlang process controlling the port is a gen_server + language than Erlang, for example, a port program, is + application-dependent and OTP provides no special support for it.</p> + <p><em>Example:</em> When changing code for a port program, assume that + the Erlang process controlling the port is a <c>gen_server</c> <c>portc</c> and that the port is opened in the callback function <c>init/1</c>:</p> <code type="none"> @@ -573,10 +588,11 @@ init(...) -> Port = open_port({spawn,PortPrg}, [...]), ..., {ok, #state{port=Port, ...}}.</code> - <p>If the port program should be updated, we can extend the code for - the gen_server with a <c>code_change</c> function which closes - the old port and opens a new port. (If necessary, the gen_server - may first request data that needs to be saved from the port + <p>If the port program is to be updated, the code for the + <c>gen_server</c> can be extended with a <c>code_change</c> function, + which closes the old port and opens a new port. + (If necessary, the <c>gen_server</c> can + first request data that must be saved from the port program and pass this data to the new port):</p> <code type="none"> code_change(_OldVsn, State, port) -> @@ -595,8 +611,8 @@ code_change(_OldVsn, State, port) -> [{"1", [{update, portc, {advanced,port}}]}], [{"1", [{update, portc, {advanced,port}}]}] ].</code> - <p>Make sure the <c>priv</c> directory where the C program is - located is included in the new release package:</p> + <p>Ensure that the <c>priv</c> directory, where the C program is + located, is included in the new release package:</p> <pre> 1> <input>systools:make_tar("my_release", [{dirs,[priv]}]).</input> ...</pre> @@ -604,28 +620,29 @@ code_change(_OldVsn, State, port) -> <section> <title>Emulator Restart and Upgrade</title> - <p>There are two upgrade instructions that will restart the emulator:</p> - <taglist> - <tag><c>restart_new_emulator</c></tag> - <item>Intended for when erts, kernel, stdlib or sasl is - upgraded. It is automatically added when the relup file is - generated by <c>systools:make_relup/3,4</c>. It is executed - before all other upgrade instructions. See - <seealso marker="release_handling#restart_new_emulator_instr">Release - Handling</seealso> for more information about this - instruction.</item> - <tag><c>restart_emulator</c></tag> - <item>Used when a restart of the emulator is required after all - other upgrade instructions are executed. See - <seealso marker="release_handling#restart_emulator_instr">Release - Handling</seealso> for more information about this - instruction.</item> - </taglist> - + <p>Two upgrade instructions restart the emulator:</p> + <list type="bulleted"> + <item><p><c>restart_new_emulator</c></p> + <p>Intended when ERTS, Kernel, STDLIB, or + SASL is upgraded. It is automatically added when the + <c>relup</c> file is generated by <c>systools:make_relup/3,4</c>. + It is executed before all other upgrade instructions. + For more information about this instruction, see + restart_new_emulator (Low-Level) in + <seealso marker="release_handling#restart_new_emulator_instr">Release Handling Instructions</seealso>. + </p></item> + <item><p><c>restart_emulator</c></p> + <p>Used when a restart of the emulator is required after all + other upgrade instructions are executed. + For more information about this instruction, see + restart_emulator (Low-Level) in + <seealso marker="release_handling#restart_emulator_instr">Release Handling Instructions</seealso>. + </p></item> + </list> <p>If an emulator restart is necessary and no upgrade instructions - are needed, i.e. if the restart itself is enough for the - upgraded applications to start running the new versions, a very - simple <c>.relup</c> file can be created manually:</p> + are needed, that is, if the restart itself is enough for the + upgraded applications to start running the new versions, a + simple <c>relup</c> file can be created manually:</p> <code type="none"> {"B", [{"A", @@ -637,26 +654,27 @@ code_change(_OldVsn, State, port) -> }.</code> <p>In this case, the release handler framework with automatic packing and unpacking of release packages, automatic path - updates etc. can be used without having to specify <c>.appup</c> - files.</p> + updates, and so on, can be used without having to specify + <c>.appup</c> files.</p> </section> <section> - <title>Emulator Upgrade from pre OTP R15</title> + <title>Emulator Upgrade From Pre OTP R15</title> <p>From OTP R15, an emulator upgrade is performed by restarting the emulator with new versions of the core applications - (<c>kernel</c>, <c>stdlib</c> and <c>sasl</c>) before loading code + (Kernel, STDLIB, and SASL) before loading code and running upgrade instruction for other applications. For this - to work, the release to upgrade from must includes OTP R15 or - later. For the case where the release to upgrade from includes an - earlier emulator version, <c>systools:make_relup</c> will create a + to work, the release to upgrade from must include OTP R15 or + later.</p> + <p>For the case where the release to upgrade from includes an + earlier emulator version, <c>systools:make_relup</c> creates a backwards compatible relup file. This means that all upgrade - instructions will be executed before the emulator is - restarted. The new application code will therefore be loaded into + instructions are executed before the emulator is + restarted. The new application code is therefore loaded into the old emulator. If the new code is compiled with the new - emulator, there might be cases where the beam format has changed - and beam files can not be loaded. To overcome this problem, the - new code should be compiled with the old emulator.</p> + emulator, there can be cases where the beam format has changed + and beam files cannot be loaded. To overcome this problem, compile + the new code with the old emulator.</p> </section> </chapter> diff --git a/system/doc/design_principles/des_princ.xml b/system/doc/design_principles/des_princ.xml index e8f289b905..77c61eafb0 100644 --- a/system/doc/design_principles/des_princ.xml +++ b/system/doc/design_principles/des_princ.xml @@ -28,50 +28,52 @@ <rev></rev> <file>des_princ.xml</file> </header> - <p>The <em>OTP Design Principles</em> is a set of principles for how - to structure Erlang code in terms of processes, modules and - directories.</p> + <marker id="otp design principles"></marker> + <p>The <em>OTP design principles</em> define how to + structure Erlang code in terms of processes, modules, + and directories.</p> <section> <title>Supervision Trees</title> <p>A basic concept in Erlang/OTP is the <em>supervision tree</em>. This is a process structuring model based on the idea of - <em>workers</em> and <em>supervisors</em>.</p> + <em>workers</em> and <em>supervisors</em>:</p> <list type="bulleted"> - <item>Workers are processes which perform computations, that is, + <item>Workers are processes that perform computations, that is, they do the actual work.</item> - <item>Supervisors are processes which monitor the behaviour of + <item>Supervisors are processes that monitor the behaviour of workers. A supervisor can restart a worker if something goes wrong.</item> <item>The supervision tree is a hierarchical arrangement of - code into supervisors and workers, making it possible to + code into supervisors and workers, which makes it possible to design and program fault-tolerant software.</item> </list> + <p>In the following figure, square boxes represents supervisors and + circles represent workers:</p> <marker id="sup6"></marker> <image file="../design_principles/sup6.gif"> <icaption>Supervision Tree</icaption> </image> - <p>In the figure above, square boxes represents supervisors and - circles represent workers.</p> </section> <section> <title>Behaviours</title> <p>In a supervision tree, many of the processes have similar structures, they follow similar patterns. For example, - the supervisors are very similar in structure. The only difference - between them is which child processes they supervise. Also, many - of the workers are servers in a server-client relation, finite - state machines, or event handlers such as error loggers.</p> + the supervisors are similar in structure. The only difference + between them is which child processes they supervise. Many + of the workers are servers in a server-client relation, + finite-state machines, or event handlers such as error loggers.</p> <p><em>Behaviours</em> are formalizations of these common patterns. The idea is to divide the code for a process in a generic part - (a behaviour module) and a specific part (a <em>callback module</em>).</p> + (a behaviour module) and a specific part (a + <em>callback module</em>).</p> <p>The behaviour module is part of Erlang/OTP. To implement a process such as a supervisor, the user only has to implement - the callback module which should export a pre-defined set of + the callback module which is to export a pre-defined set of functions, the <em>callback functions</em>.</p> - <p>An example to illustrate how code can be divided into a generic - and a specific part: Consider the following code (written in + <p>The following example illustrate how code can be divided into a + generic and a specific part. Consider the following code (written in plain Erlang) for a simple server, which keeps track of a number of "channels". Other processes can allocate and free the channels by calling the functions <c>alloc/0</c> and <c>free/1</c>, @@ -149,7 +151,7 @@ loop(Mod, State) -> State2 = Mod:handle_cast(Req, State), loop(Mod, State2) end.</code> - <p>and a callback module <c>ch2.erl</c>:</p> + <p>And a callback module <c>ch2.erl</c>:</p> <code type="none"> -module(ch2). -export([start/0]). @@ -173,27 +175,27 @@ handle_call(alloc, Chs) -> handle_cast({free, Ch}, Chs) -> free(Ch, Chs). % => Chs2</code> - <p>Note the following:</p> + <p>Notice the following:</p> <list type="bulleted"> - <item>The code in <c>server</c> can be re-used to build many + <item>The code in <c>server</c> can be reused to build many different servers.</item> - <item>The name of the server, in this example the atom - <c>ch2</c>, is hidden from the users of the client functions. - This means the name can be changed without affecting them.</item> + <item>The server name, in this example the atom + <c>ch2</c>, is hidden from the users of the client functions. This + means that the name can be changed without affecting them.</item> <item>The protcol (messages sent to and received from the server) - is hidden as well. This is good programming practice and allows - us to change the protocol without making changes to code using + is also hidden. This is good programming practice and allows + one to change the protocol without changing the code using the interface functions.</item> - <item>We can extend the functionality of <c>server</c>, without + <item>The functionality of <c>server</c> can be extended without having to change <c>ch2</c> or any other callback module.</item> </list> - <p>(In <c>ch1.erl</c> and <c>ch2.erl</c> above, the implementation - of <c>channels/0</c>, <c>alloc/1</c> and <c>free/2</c> has been + <p>In <c>ch1.erl</c> and <c>ch2.erl</c> above, the implementation + of <c>channels/0</c>, <c>alloc/1</c>, and <c>free/2</c> has been intentionally left out, as it is not relevant to the example. For completeness, one way to write these functions are given - below. Note that this is an example only, a realistic + below. This is an example only, a realistic implementation must be able to handle situations like running out - of channels to allocate etc.)</p> + of channels to allocate, and so on.</p> <code type="none"> channels() -> {_Allocated = [], _Free = lists:seq(1,100)}. @@ -208,30 +210,30 @@ free(Ch, {Alloc, Free} = Channels) -> false -> Channels end. </code> - <p>Code written without making use of behaviours may be more - efficient, but the increased efficiency will be at the expense of + <p>Code written without using behaviours can be more + efficient, but the increased efficiency is at the expense of generality. The ability to manage all applications in the system - in a consistent manner is very important.</p> + in a consistent manner is important.</p> <p>Using behaviours also makes it easier to read and understand - code written by other programmers. Ad hoc programming structures, + code written by other programmers. Improvised programming structures, while possibly more efficient, are always more difficult to understand.</p> - <p>The module <c>server</c> corresponds, greatly simplified, + <p>The <c>server</c> module corresponds, greatly simplified, to the Erlang/OTP behaviour <c>gen_server</c>.</p> <p>The standard Erlang/OTP behaviours are:</p> - <taglist> - <tag><seealso marker="gen_server_concepts">gen_server</seealso></tag> - <item>For implementing the server of a client-server relation.</item> - <tag><seealso marker="fsm">gen_fsm</seealso></tag> - <item>For implementing finite state machines.</item> - <tag><seealso marker="events">gen_event</seealso></tag> - <item>For implementing event handling functionality.</item> - <tag><seealso marker="sup_princ">supervisor</seealso></tag> - <item>For implementing a supervisor in a supervision tree.</item> - </taglist> + <list type="bulleted"> + <item><p><seealso marker="gen_server_concepts">gen_server</seealso></p> + <p>For implementing the server of a client-server relation</p></item> + <item><p><seealso marker="fsm">gen_fsm</seealso></p> + <p>For implementing finite-state machines</p></item> + <item><p><seealso marker="events">gen_event</seealso></p> + <p>For implementing event handling functionality</p></item> + <item><p><seealso marker="sup_princ">supervisor</seealso></p> + <p>For implementing a supervisor in a supervision tree</p></item> + </list> <p>The compiler understands the module attribute <c>-behaviour(Behaviour)</c> and issues warnings about - missing callback functions. Example:</p> + missing callback functions, for example:</p> <code type="none"> -module(chs3). -behaviour(gen_server). @@ -248,13 +250,17 @@ free(Ch, {Alloc, Free} = Channels) -> some specific functionality. Components are with Erlang/OTP terminology called <em>applications</em>. Examples of Erlang/OTP applications are Mnesia, which has everything needed for - programming database services, and Debugger which is used to - debug Erlang programs. The minimal system based on Erlang/OTP - consists of the applications Kernel and STDLIB.</p> + programming database services, and Debugger, which is used + to debug Erlang programs. The minimal system based on Erlang/OTP + consists of the following two applications:</p> + <list type="bulleted"> + <item>Kernel - Functionality necessary to run Erlang</item> + <item>STDLIB - Erlang standard libraries</item> + </list> <p>The application concept applies both to program structure (processes) and directory structure (modules).</p> - <p>The simplest kind of application does not have any processes, - but consists of a collection of functional modules. Such an + <p>The simplest applications do not have any processes, + but consist of a collection of functional modules. Such an application is called a <em>library application</em>. An example of a library application is STDLIB.</p> <p>An application with processes is easiest implemented as a @@ -266,12 +272,11 @@ free(Ch, {Alloc, Free} = Channels) -> <section> <title>Releases</title> <p>A <em>release</em> is a complete system made out from a subset of - the Erlang/OTP applications and a set of user-specific - applications.</p> + Erlang/OTP applications and a set of user-specific applications.</p> <p>How to program releases is described in <seealso marker="release_structure">Releases</seealso>.</p> <p>How to install a release in a target environment is described - in the chapter about Target Systems in System Principles.</p> + in the section about target systems in Section 2 System Principles.</p> </section> <section> diff --git a/system/doc/design_principles/distributed_applications.xml b/system/doc/design_principles/distributed_applications.xml index 4d4ba3136e..f40a24fdf5 100644 --- a/system/doc/design_principles/distributed_applications.xml +++ b/system/doc/design_principles/distributed_applications.xml @@ -28,71 +28,73 @@ <rev></rev> <file>distributed_applications.xml</file> </header> + <marker id="distributed appl"></marker> <section> - <title>Definition</title> - <p>In a distributed system with several Erlang nodes, there may be - a need to control applications in a distributed manner. If + <title>Introduction</title> + <p>In a distributed system with several Erlang nodes, it can be + necessary to control applications in a distributed manner. If the node, where a certain application is running, goes down, - the application should be restarted at another node.</p> + the application is to be restarted at another node.</p> <p>Such an application is called a <em>distributed application</em>. - Note that it is the control of the application which is - distributed, all applications can of course be distributed in - the sense that they, for example, use services on other nodes.</p> - <p>Because a distributed application may move between nodes, some + Notice that it is the control of the application that is distributed. + All applications can be distributed in the sense that they, + for example, use services on other nodes.</p> + <p>Since a distributed application can move between nodes, some addressing mechanism is required to ensure that it can be addressed by other applications, regardless on which node it currently executes. This issue is not addressed here, but the - Kernel modules <c>global</c> or <c>pg2</c> can be - used for this purpose.</p> + <c>global</c> or <c>pg2</c> modules in Kernel + can be used for this purpose.</p> </section> <section> <title>Specifying Distributed Applications</title> <p>Distributed applications are controlled by both the application - controller and a distributed application controller process, - <c>dist_ac</c>. Both these processes are part of the <c>kernel</c> - application. Therefore, distributed applications are specified by - configuring the <c>kernel</c> application, using the following - configuration parameter (see also <c>kernel(6)</c>):</p> - <taglist> - <tag><c>distributed = [{Application, [Timeout,] NodeDesc}]</c></tag> - <item> - <p>Specifies where the application <c>Application = atom()</c> - may execute. <c>NodeDesc = [Node | {Node,...,Node}]</c> is - a list of node names in priority order. The order between - nodes in a tuple is undefined.</p> - <p><c>Timeout = integer()</c> specifies how many milliseconds to - wait before restarting the application at another node. - Defaults to 0.</p> - </item> - </taglist> + controller and a distributed application controller process, + <c>dist_ac</c>. Both these processes are part of the Kernel + application. Distributed applications are thus specified by + configuring the Kernel application, using the following + configuration parameter (see also <c>kernel(6)</c>):</p> + <p><c>distributed = [{Application, [Timeout,] NodeDesc}]</c></p> + <list type="bulleted"> + <item>Specifies where the application <c>Application = atom()</c> + can execute.</item> + <item>><c>NodeDesc = [Node | {Node,...,Node}]</c> is a list of + node names in priority order. The order between nodes in a tuple + is undefined.</item> + <item><c>Timeout = integer()</c> specifies how many milliseconds + to wait before restarting the application at another node. It + defaults to 0.</item> + </list> <p>For distribution of application control to work properly, - the nodes where a distributed application may run must contact + the nodes where a distributed application can run must contact each other and negotiate where to start the application. This is - done using the following <c>kernel</c> configuration parameters:</p> - <taglist> - <tag><c>sync_nodes_mandatory = [Node]</c></tag> - <item>Specifies which other nodes must be started (within - the timeout specified by <c>sync_nodes_timeout</c>.</item> - <tag><c>sync_nodes_optional = [Node]</c></tag> - <item>Specifies which other nodes can be started (within - the timeout specified by <c>sync_nodes_timeout</c>.</item> - <tag><c>sync_nodes_timeout = integer() | infinity</c></tag> - <item>Specifies how many milliseconds to wait for the other nodes - to start.</item> - </taglist> - <p>When started, the node will wait for all nodes specified by + done using the following configuration parameters in + Kernel:</p> + <list type="bulleted"> + <item><c>sync_nodes_mandatory = [Node]</c> - Specifies which + other nodes must be started (within the time-out specified by + <c>sync_nodes_timeout</c>).</item> + <item><c>sync_nodes_optional = [Node]</c> - Specifies which + other nodes can be started (within the time-out specified by + <c>sync_nodes_timeout</c>).</item> + <item><c>sync_nodes_timeout = integer() | infinity</c> - + Specifies how many milliseconds to wait for the other nodes to + start.</item> + </list> + <p>When started, the node waits for all nodes specified by <c>sync_nodes_mandatory</c> and <c>sync_nodes_optional</c> to - come up. When all nodes have come up, or when all mandatory nodes - have come up and the time specified by <c>sync_nodes_timeout</c> - has elapsed, all applications will be started. If not all - mandatory nodes have come up, the node will terminate.</p> - <p>Example: An application <c>myapp</c> should run at the node - <c>cp1@cave</c>. If this node goes down, <c>myapp</c> should + come up. When all nodes are up, or when all mandatory nodes + are up and the time specified by <c>sync_nodes_timeout</c> + has elapsed, all applications start. If not all + mandatory nodes are up, the node terminates.</p> + <p><em>Example:</em></p> + <p>An application <c>myapp</c> is to run at the node + <c>cp1@cave</c>. If this node goes down, <c>myapp</c> is to be restarted at <c>cp2@cave</c> or <c>cp3@cave</c>. A system - configuration file <c>cp1.config</c> for <c>cp1@cave</c> could - look like:</p> + configuration file <c>cp1.config</c> for <c>cp1@cave</c> can + look as follows:</p> <code type="none"> [{kernel, [{distributed, [{myapp, 5000, [cp1@cave, {cp2@cave, cp3@cave}]}]}, @@ -103,13 +105,13 @@ ].</code> <p>The system configuration files for <c>cp2@cave</c> and <c>cp3@cave</c> are identical, except for the list of mandatory - nodes which should be <c>[cp1@cave, cp3@cave]</c> for + nodes, which is to be <c>[cp1@cave, cp3@cave]</c> for <c>cp2@cave</c> and <c>[cp1@cave, cp2@cave]</c> for <c>cp3@cave</c>.</p> <note> <p>All involved nodes must have the same value for - <c>distributed</c> and <c>sync_nodes_timeout</c>, or - the behaviour of the system is undefined.</p> + <c>distributed</c> and <c>sync_nodes_timeout</c>. + Otherwise the system behaviour is undefined.</p> </note> </section> @@ -117,28 +119,29 @@ <title>Starting and Stopping Distributed Applications</title> <p>When all involved (mandatory) nodes have been started, the distributed application can be started by calling - <c>application:start(Application)</c> at <em>all of these nodes.</em></p> - <p>It is of course also possible to use a boot script (see - <seealso marker="release_structure">Releases</seealso>) which - automatically starts the application.</p> - <p>The application will be started at the first node, specified - by the <c>distributed</c> configuration parameter, which is up - and running. The application is started as usual. That is, an - application master is created and calls the application callback - function:</p> + <c>application:start(Application)</c> at <em>all of these + nodes.</em></p> + <p>A boot script (see + <seealso marker="release_structure">Releases</seealso>) + can be used that automatically starts the application.</p> + <p>The application is started at the first operational node that + is listed in the list of nodes in the <c>distributed</c> + configuration parameter. The application is started as usual. + That is, an application master is created and calls the + application callback function:</p> <code type="none"> Module:start(normal, StartArgs)</code> - <p>Example: Continuing the example from the previous section, - the three nodes are started, specifying the system configuration - file:</p> + <p>Example:</p> + <p>Continuing the example from the previous section, the three nodes + are started, specifying the system configuration file:</p> <pre> > <input>erl -sname cp1 -config cp1</input> > <input>erl -sname cp2 -config cp2</input> > <input>erl -sname cp3 -config cp3</input></pre> - <p>When all nodes are up and running, <c>myapp</c> can be started. + <p>When all nodes are operational, <c>myapp</c> can be started. This is achieved by calling <c>application:start(myapp)</c> at all three nodes. It is then started at <c>cp1</c>, as shown in - the figure below.</p> + the following figure:</p> <marker id="dist1"></marker> <image file="../design_principles/dist1.gif"> <icaption>Application myapp - Situation 1</icaption> @@ -150,31 +153,33 @@ Module:start(normal, StartArgs)</code> <section> <title>Failover</title> <p>If the node where the application is running goes down, - the application is restarted (after the specified timeout) at - the first node, specified by the <c>distributed</c> configuration - parameter, which is up and running. This is called a + the application is restarted (after the specified time-out) at + the first operational node that is listed in the list of nodes + in the <c>distributed</c> configuration parameter. This is called a <em>failover</em>.</p> <p>The application is started the normal way at the new node, that is, by the application master calling:</p> <code type="none"> Module:start(normal, StartArgs)</code> - <p>Exception: If the application has the <c>start_phases</c> key - defined (see <seealso marker="included_applications">Included Applications</seealso>), then the application is instead started - by calling:</p> + <p>An exception is if the application has the <c>start_phases</c> + key defined + (see <seealso marker="included_applications">Included Applications</seealso>). + The application is then instead started by calling:</p> <code type="none"> Module:start({failover, Node}, StartArgs)</code> - <p>where <c>Node</c> is the terminated node.</p> - <p>Example: If <c>cp1</c> goes down, the system checks which one of + <p>Here <c>Node</c> is the terminated node.</p> + <p><em>Example:</em></p> + <p> If <c>cp1</c> goes down, the system checks which one of the other nodes, <c>cp2</c> or <c>cp3</c>, has the least number of running applications, but waits for 5 seconds for <c>cp1</c> to restart. If <c>cp1</c> does not restart and <c>cp2</c> runs fewer - applications than <c>cp3,</c> then <c>myapp</c> is restarted on + applications than <c>cp3</c>, <c>myapp</c> is restarted on <c>cp2</c>.</p> <marker id="dist2"></marker> <image file="../design_principles/dist2.gif"> <icaption>Application myapp - Situation 2</icaption> </image> - <p>Suppose now that <c>cp2</c> goes down as well and does not + <p>Suppose now that <c>cp2</c> goes also down and does not restart within 5 seconds. <c>myapp</c> is now restarted on <c>cp3</c>.</p> <marker id="dist3"></marker> @@ -186,28 +191,29 @@ Module:start({failover, Node}, StartArgs)</code> <section> <title>Takeover</title> <p>If a node is started, which has higher priority according - to <c>distributed</c>, than the node where a distributed - application is currently running, the application will be - restarted at the new node and stopped at the old node. This is + to <c>distributed</c> than the node where a distributed + application is running, the application is restarted at the + new node and stopped at the old node. This is called a <em>takeover</em>.</p> <p>The application is started by the application master calling:</p> <code type="none"> Module:start({takeover, Node}, StartArgs)</code> - <p>where <c>Node</c> is the old node.</p> - <p>Example: If <c>myapp</c> is running at <c>cp3</c>, and if - <c>cp2</c> now restarts, it will not restart <c>myapp</c>, - because the order between nodes <c>cp2</c> and <c>cp3</c> is + <p>Here <c>Node</c> is the old node.</p> + <p><em>Example: </em></p> + <p>If <c>myapp</c> is running at <c>cp3</c>, and if + <c>cp2</c> now restarts, it does not restart <c>myapp</c>, + as the order between the <c>cp2</c> and <c>cp3</c> nodes is undefined.</p> <marker id="dist4"></marker> <image file="../design_principles/dist4.gif"> <icaption>Application myapp - Situation 4</icaption> </image> - <p>However, if <c>cp1</c> restarts as well, the function + <p>However, if <c>cp1</c> also restarts, the function <c>application:takeover/2</c> moves <c>myapp</c> to <c>cp1</c>, - because <c>cp1</c> has a higher priority than <c>cp3</c> for this - application. In this case, - <c>Module:start({takeover, cp3@cave}, StartArgs)</c> is executed - at <c>cp1</c> to start the application.</p> + as <c>cp1</c> has a higher priority than <c>cp3</c> for this + application. In this case, + <c>Module:start({takeover, cp3@cave}, StartArgs)</c> is + executed at <c>cp1</c> to start the application.</p> <marker id="dist5"></marker> <image file="../design_principles/dist5.gif"> <icaption>Application myapp - Situation 5</icaption> diff --git a/system/doc/design_principles/events.xml b/system/doc/design_principles/events.xml index 529e12c216..6e5afb939e 100644 --- a/system/doc/design_principles/events.xml +++ b/system/doc/design_principles/events.xml @@ -21,7 +21,7 @@ </legalnotice> - <title>Gen_Event Behaviour</title> + <title>gen_event Behaviour</title> <prepared></prepared> <docno></docno> <date></date> @@ -29,35 +29,36 @@ <file>events.xml</file> </header> <marker id="gen_event"></marker> - <p>This chapter should be read in conjunction with - <c>gen_event(3)</c>, where all interface functions and callback - functions are described in detail.</p> + <p>This section is to be read with the <c>gen_event(3)</c> manual + page in STDLIB, where all interface functions and callback + functions are described in detail.</p> <section> <title>Event Handling Principles</title> <p>In OTP, an <em>event manager</em> is a named object to which - events can be sent. An <em>event</em> could be, for example, - an error, an alarm or some information that should be logged.</p> - <p>In the event manager, zero, one or several <em>event handlers</em> are installed. When the event manager is notified - about an event, the event will be processed by all the installed + events can be sent. An <em>event</em> can be, for example, + an error, an alarm, or some information that is to be logged.</p> + <p>In the event manager, zero, one, or many <em>event handlers</em> + are installed. When the event manager is notified + about an event, the event is processed by all the installed event handlers. For example, an event manager for handling errors - can by default have a handler installed which writes error + can by default have a handler installed, which writes error messages to the terminal. If the error messages during a certain - period should be saved to a file as well, the user adds another - event handler which does this. When logging to file is no longer - necessary, this event handler is deleted.</p> + period is to be saved to a file as well, the user adds another + event handler that does this. When logging to the file is no + longer necessary, this event handler is deleted.</p> <p>An event manager is implemented as a process and each event handler is implemented as a callback module.</p> <p>The event manager essentially maintains a list of <c>{Module, State}</c> pairs, where each <c>Module</c> is an - event handler, and <c>State</c> the internal state of that event - handler.</p> + event handler, and <c>State</c> is the internal state of that + event handler.</p> </section> <section> <title>Example</title> <p>The callback module for the event handler writing error messages - to the terminal could look like:</p> + to the terminal can look as follows:</p> <code type="none"> -module(terminal_logger). -behaviour(gen_event). @@ -74,7 +75,7 @@ handle_event(ErrorMsg, State) -> terminate(_Args, _State) -> ok.</code> <p>The callback module for the event handler writing error messages - to a file could look like:</p> + to a file can look as follows:</p> <code type="none"> -module(file_logger). -behaviour(gen_event). @@ -98,29 +99,28 @@ terminate(_Args, Fd) -> <marker id="mgr"></marker> <title>Starting an Event Manager</title> <p>To start an event manager for handling errors, as described in - the example above, call the following function:</p> + the previous example, call the following function:</p> <code type="none"> gen_event:start_link({local, error_man})</code> <p>This function spawns and links to a new process, an event manager.</p> - <p>The argument, <c>{local, error_man}</c> specifies the name. In - this case, the event manager will be locally registered as - <c>error_man</c>.</p> + <p>The argument, <c>{local, error_man}</c> specifies the name. The + event manager is then locally registered as <c>error_man</c>.</p> <p>If the name is omitted, the event manager is not registered. - Instead its pid must be used. The name could also be given + Instead its pid must be used. The name can also be given as <c>{global, Name}</c>, in which case the event manager is registered using <c>global:register_name/2</c>.</p> <p><c>gen_event:start_link</c> must be used if the event manager is - part of a supervision tree, i.e. is started by a supervisor. - There is another function <c>gen_event:start</c> to start a - stand-alone event manager, i.e. an event manager which is not + part of a supervision tree, that is, started by a supervisor. + There is another function, <c>gen_event:start</c>, to start a + standalone event manager, that is, an event manager that is not part of a supervision tree.</p> </section> <section> <title>Adding an Event Handler</title> - <p>Here is an example using the shell on how to start an event - manager and add an event handler to it:</p> + <p>The following example shows how to start an event manager and + add an event handler to it by using the shell:</p> <pre> 1> <input>gen_event:start({local, error_man}).</input> {ok,<0.31.0>} @@ -128,16 +128,16 @@ gen_event:start_link({local, error_man})</code> ok</pre> <p>This function sends a message to the event manager registered as <c>error_man</c>, telling it to add the event handler - <c>terminal_logger</c>. The event manager will call the callback - function <c>terminal_logger:init([])</c>, where the argument [] - is the third argument to <c>add_handler</c>. <c>init</c> is - expected to return <c>{ok, State}</c>, where <c>State</c> is + <c>terminal_logger</c>. The event manager calls the callback + function <c>terminal_logger:init([])</c>, where the argument + <c>[]</c> is the third argument to <c>add_handler</c>. <c>init</c> + is expected to return <c>{ok, State}</c>, where <c>State</c> is the internal state of the event handler.</p> <code type="none"> init(_Args) -> {ok, []}.</code> <p>Here, <c>init</c> does not need any input data and ignores its - argument. Also, for <c>terminal_logger</c> the internal state is + argument. For <c>terminal_logger</c>, the internal state is not used. For <c>file_logger</c>, the internal state is used to save the open file descriptor.</p> <code type="none"> @@ -147,7 +147,7 @@ init(File) -> </section> <section> - <title>Notifying About Events</title> + <title>Notifying about Events</title> <pre> 3> <input>gen_event:notify(error_man, no_reply).</input> ***Error*** no_reply @@ -158,7 +158,7 @@ ok</pre> When the event is received, the event manager calls <c>handle_event(Event, State)</c> for each installed event handler, in the same order as they were added. The function is - expected to return a tuple <c>{ok, State1}</c>, where + expected to return a tuple <c>{ok,State1}</c>, where <c>State1</c> is a new value for the state of the event handler.</p> <p>In <c>terminal_logger</c>:</p> <code type="none"> @@ -179,17 +179,17 @@ handle_event(ErrorMsg, Fd) -> ok</pre> <p>This function sends a message to the event manager registered as <c>error_man</c>, telling it to delete the event handler - <c>terminal_logger</c>. The event manager will call the callback + <c>terminal_logger</c>. The event manager calls the callback function <c>terminal_logger:terminate([], State)</c>, where - the argument [] is the third argument to <c>delete_handler</c>. - <c>terminate</c> should be the opposite of <c>init</c> and do any + the argument <c>[]</c> is the third argument to <c>delete_handler</c>. + <c>terminate</c> is to be the opposite of <c>init</c> and do any necessary cleaning up. Its return value is ignored.</p> <p>For <c>terminal_logger</c>, no cleaning up is necessary:</p> <code type="none"> terminate(_Args, _State) -> ok.</code> <p>For <c>file_logger</c>, the file descriptor opened in <c>init</c> - needs to be closed:</p> + must be closed:</p> <code type="none"> terminate(_Args, Fd) -> file:close(Fd).</code> @@ -197,20 +197,22 @@ terminate(_Args, Fd) -> <section> <title>Stopping</title> - <p>When an event manager is stopped, it will give each of + <p>When an event manager is stopped, it gives each of the installed event handlers the chance to clean up by calling <c>terminate/2</c>, the same way as when deleting a handler.</p> <section> <title>In a Supervision Tree</title> <p>If the event manager is part of a supervision tree, no stop - function is needed. The event manager will automatically be + function is needed. The event manager is automatically terminated by its supervisor. Exactly how this is done is - defined by a <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> set in the supervisor.</p> + defined by a + <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> + set in the supervisor.</p> </section> <section> - <title>Stand-Alone Event Managers</title> + <title>Standalone Event Managers</title> <p>An event manager can also be stopped by calling:</p> <pre> > <input>gen_event:stop(error_man).</input> @@ -219,16 +221,17 @@ ok</pre> </section> <section> <title>Handling Other Messages</title> - <p>If the gen_event should be able to receive other messages than - events, the callback function <c>handle_info(Info, StateName, StateData)</c> - must be implemented to handle them. Examples of - other messages are exit messages, if the gen_event is linked to + <p>If the <c>gen_event</c> is to be able to receive other messages + than events, the callback function + <c>handle_info(Info, StateName, StateData)</c> + must be implemented to handle them. Examples of other + messages are exit messages, if the <c>gen_event</c> is linked to other processes (than the supervisor) and trapping exit signals.</p> <code type="none"> handle_info({'EXIT', Pid, Reason}, State) -> ..code to handle exits here.. {ok, NewState}.</code> - <p>The code_change method also has to be implemented.</p> + <p>The <c>code_change</c> method must also be implemented.</p> <code type="none"> code_change(OldVsn, State, Extra) -> ..code to convert state (and more) during code change diff --git a/system/doc/design_principles/fsm.xml b/system/doc/design_principles/fsm.xml index 9dce159dca..ef961f5fad 100644 --- a/system/doc/design_principles/fsm.xml +++ b/system/doc/design_principles/fsm.xml @@ -21,32 +21,33 @@ </legalnotice> - <title>Gen_Fsm Behaviour</title> + <title>gen_fsm Behaviour</title> <prepared></prepared> <docno></docno> <date></date> <rev></rev> <file>fsm.xml</file> </header> - <p>This chapter should be read in conjunction with <c>gen_fsm(3)</c>, - where all interface functions and callback functions are described - in detail.</p> + <marker id="gen_fsm behaviour"></marker> + <p>This section is to be read with the <c>gen_fsm(3)</c> manual page + in STDLIB, where all interface functions and callback + functions are described in detail.</p> <section> - <title>Finite State Machines</title> - <p>A finite state machine, FSM, can be described as a set of + <title>Finite-State Machines</title> + <p>A Finite-State Machine (FSM) can be described as a set of relations of the form:</p> <pre> State(S) x Event(E) -> Actions(A), State(S')</pre> <p>These relations are interpreted as meaning:</p> <quote> - <p>If we are in state <c>S</c> and the event <c>E</c> occurs, we - should perform the actions <c>A</c> and make a transition to - the state <c>S'</c>.</p> + <p>If we are in state <c>S</c> and event <c>E</c> occurs, we + are to perform actions <c>A</c> and make a transition to + state <c>S'</c>.</p> </quote> <p>For an FSM implemented using the <c>gen_fsm</c> behaviour, the state transition rules are written as a number of Erlang - functions which conform to the following convention:</p> + functions, which conform to the following convention:</p> <pre> StateName(Event, StateData) -> .. code for actions here ... @@ -55,16 +56,16 @@ StateName(Event, StateData) -> <section> <title>Example</title> - <p>A door with a code lock could be viewed as an FSM. Initially, + <p>A door with a code lock can be viewed as an FSM. Initially, the door is locked. Anytime someone presses a button, this generates an event. Depending on what buttons have been pressed - before, the sequence so far may be correct, incomplete or wrong.</p> - <p>If it is correct, the door is unlocked for 30 seconds (30000 ms). + before, the sequence so far can be correct, incomplete, or wrong.</p> + <p>If it is correct, the door is unlocked for 30 seconds (30,000 ms). If it is incomplete, we wait for another button to be pressed. If it is is wrong, we start all over, waiting for a new button sequence.</p> <p>Implementing the code lock FSM using <c>gen_fsm</c> results in - this callback module:</p> + the following callback module:</p> <marker id="ex"></marker> <code type="none"><![CDATA[ -module(code_lock). @@ -101,85 +102,88 @@ open(timeout, State) -> </section> <section> - <title>Starting a Gen_Fsm</title> - <p>In the example in the previous section, the gen_fsm is started by - calling <c>code_lock:start_link(Code)</c>:</p> + <title>Starting gen_fsm</title> + <p>In the example in the previous section, the <c>gen_fsm</c> is + started by calling <c>code_lock:start_link(Code)</c>:</p> <code type="none"> start_link(Code) -> gen_fsm:start_link({local, code_lock}, code_lock, lists:reverse(Code), []). </code> - <p><c>start_link</c> calls the function <c>gen_fsm:start_link/4</c>. - This function spawns and links to a new process, a gen_fsm.</p> + <p><c>start_link</c> calls the function <c>gen_fsm:start_link/4</c>, + which spawns and links to a new process, a <c>gen_fsm</c>.</p> <list type="bulleted"> <item> - <p>The first argument <c>{local, code_lock}</c> specifies - the name. In this case, the gen_fsm will be locally registered - as <c>code_lock</c>.</p> - <p>If the name is omitted, the gen_fsm is not registered. - Instead its pid must be used. The name could also be given as - <c>{global, Name}</c>, in which case the gen_fsm is registered - using <c>global:register_name/2</c>.</p> + <p>The first argument, <c>{local, code_lock}</c>, specifies + the name. In this case, the <c>gen_fsm</c> is locally + registered as <c>code_lock</c>.</p> + <p>If the name is omitted, the <c>gen_fsm</c> is not registered. + Instead its pid must be used. The name can also be given + as <c>{global, Name}</c>, in which case the <c>gen_fsm</c> is + registered using <c>global:register_name/2</c>.</p> </item> <item> <p>The second argument, <c>code_lock</c>, is the name of - the callback module, that is the module where the callback + the callback module, that is, the module where the callback functions are located.</p> - <p>In this case, the interface functions (<c>start_link</c> and - <c>button</c>) are located in the same module as the callback - functions (<c>init</c>, <c>locked</c> and <c>open</c>). This + <p>The interface functions (<c>start_link</c> and <c>button</c>) + are then located in the same module as the callback + functions (<c>init</c>, <c>locked</c>, and <c>open</c>). This is normally good programming practice, to have the code corresponding to one process contained in one module.</p> </item> <item> - <p>The third argument, <c>Code</c>, is a list of digits which is passed - reversed to the callback function <c>init</c>. Here, <c>init</c> + <p>The third argument, <c>Code</c>, is a list of digits that + which is passed reversed to the callback function <c>init</c>. + Here, <c>init</c> gets the correct code for the lock as indata.</p> </item> <item> - <p>The fourth argument, [], is a list of options. See - <c>gen_fsm(3)</c> for available options.</p> + <p>The fourth argument, <c>[]</c>, is a list of options. See + the <c>gen_fsm(3)</c> manual page for available options.</p> </item> </list> - <p>If name registration succeeds, the new gen_fsm process calls + <p>If name registration succeeds, the new <c>gen_fsm</c> process calls the callback function <c>code_lock:init(Code)</c>. This function is expected to return <c>{ok, StateName, StateData}</c>, where - <c>StateName</c> is the name of the initial state of the gen_fsm. - In this case <c>locked</c>, assuming the door is locked to begin - with. <c>StateData</c> is the internal state of the gen_fsm. (For - gen_fsms, the internal state is often referred to 'state data' to + <c>StateName</c> is the name of the initial state of the + <c>gen_fsm</c>. In this case <c>locked</c>, assuming the door is + locked to begin with. <c>StateData</c> is the internal state of + the <c>gen_fsm</c>. (For <c>gen_fsm</c>, the internal state is + often referred to 'state data' to distinguish it from the state as in states of a state machine.) In this case, the state data is the button sequence so far (empty to begin with) and the correct code of the lock.</p> <code type="none"> init(Code) -> {ok, locked, {[], Code}}.</code> - <p>Note that <c>gen_fsm:start_link</c> is synchronous. It does not - return until the gen_fsm has been initialized and is ready to + <p><c>gen_fsm:start_link</c> is synchronous. It does not return until + the <c>gen_fsm</c> has been initialized and is ready to receive notifications.</p> - <p><c>gen_fsm:start_link</c> must be used if the gen_fsm is part of - a supervision tree, i.e. is started by a supervisor. There is - another function <c>gen_fsm:start</c> to start a stand-alone - gen_fsm, i.e. a gen_fsm which is not part of a supervision tree.</p> + <p><c>gen_fsm:start_link</c> must be used if the <c>gen_fsm</c> is + part of a supervision tree, that is, started by a supervisor. There + is another function, <c>gen_fsm:start</c>, to start a standalone + <c>gen_fsm</c>, that is, a <c>gen_fsm</c> that is not part of a + supervision tree.</p> </section> <section> - <title>Notifying About Events</title> + <title>Notifying about Events</title> <p>The function notifying the code lock about a button event is implemented using <c>gen_fsm:send_event/2</c>:</p> <code type="none"> button(Digit) -> gen_fsm:send_event(code_lock, {button, Digit}).</code> - <p><c>code_lock</c> is the name of the gen_fsm and must agree with - the name used to start it. <c>{button, Digit}</c> is the actual - event.</p> - <p>The event is made into a message and sent to the gen_fsm. When - the event is received, the gen_fsm calls - <c>StateName(Event, StateData)</c> which is expected to return a - tuple <c>{next_state, StateName1, StateData1}</c>. + <p><c>code_lock</c> is the name of the <c>gen_fsm</c> and must + agree with the name used to start it. + <c>{button, Digit}</c> is the actual event.</p> + <p>The event is made into a message and sent to the <c>gen_fsm</c>. + When the event is received, the <c>gen_fsm</c> calls + <c>StateName(Event, StateData)</c>, which is expected to return a + tuple <c>{next_state,StateName1,StateData1}</c>. <c>StateName</c> is the name of the current state and <c>StateName1</c> is the name of the next state to go to. <c>StateData1</c> is a new value for the state data of - the gen_fsm.</p> + the <c>gen_fsm</c>.</p> <code type="none"><![CDATA[ locked({button, Digit}, {SoFar, Code}) -> case [Digit|SoFar] of @@ -198,20 +202,21 @@ open(timeout, State) -> <p>If the door is locked and a button is pressed, the complete button sequence so far is compared with the correct code for the lock and, depending on the result, the door is either unlocked - and the gen_fsm goes to state <c>open</c>, or the door remains in - state <c>locked</c>.</p> + and the <c>gen_fsm</c> goes to state <c>open</c>, or the door + remains in state <c>locked</c>.</p> </section> <section> - <title>Timeouts</title> + <title>Time-Outs</title> <p>When a correct code has been given, the door is unlocked and the following tuple is returned from <c>locked/2</c>:</p> <code type="none"> {next_state, open, {[], Code}, 30000};</code> - <p>30000 is a timeout value in milliseconds. After 30000 ms, i.e. - 30 seconds, a timeout occurs. Then <c>StateName(timeout, StateData)</c> is called. In this case, the timeout occurs when - the door has been in state <c>open</c> for 30 seconds. After that - the door is locked again:</p> + <p>30,000 is a time-out value in milliseconds. After this time, + that is, 30 seconds, a time-out occurs. Then, + <c>StateName(timeout, StateData)</c> is called. The time-out + then occurs when the door has been in state <c>open</c> for 30 + seconds. After that the door is locked again:</p> <code type="none"> open(timeout, State) -> do_lock(), @@ -220,7 +225,7 @@ open(timeout, State) -> <section> <title>All State Events</title> - <p>Sometimes an event can arrive at any state of the gen_fsm. + <p>Sometimes an event can arrive at any state of the <c>gen_fsm</c>. Instead of sending the message with <c>gen_fsm:send_event/2</c> and writing one clause handling the event for each state function, the message can be sent with <c>gen_fsm:send_all_state_event/2</c> @@ -245,15 +250,16 @@ handle_event(stop, _StateName, StateData) -> <section> <title>In a Supervision Tree</title> - <p>If the gen_fsm is part of a supervision tree, no stop function - is needed. The gen_fsm will automatically be terminated by its - supervisor. Exactly how this is done is defined by a - <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> + <p>If the <c>gen_fsm</c> is part of a supervision tree, no stop + function is needed. The <c>gen_fsm</c> is automatically + terminated by its supervisor. Exactly how this is done is + defined by a + <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> set in the supervisor.</p> <p>If it is necessary to clean up before termination, the shutdown - strategy must be a timeout value and the gen_fsm must be set to - trap exit signals in the <c>init</c> function. When ordered - to shutdown, the gen_fsm will then call the callback function + strategy must be a time-out value and the <c>gen_fsm</c> must be + set to trap exit signals in the <c>init</c> function. When ordered + to shutdown, the <c>gen_fsm</c> then calls the callback function <c>terminate(shutdown, StateName, StateData)</c>:</p> <code type="none"> init(Args) -> @@ -270,9 +276,9 @@ terminate(shutdown, StateName, StateData) -> </section> <section> - <title>Stand-Alone Gen_Fsms</title> - <p>If the gen_fsm is not part of a supervision tree, a stop - function may be useful, for example:</p> + <title>Standalone gen_fsm</title> + <p>If the <c>gen_fsm</c> is not part of a supervision tree, a stop + function can be useful, for example:</p> <code type="none"> ... -export([stop/0]). @@ -290,26 +296,28 @@ handle_event(stop, _StateName, StateData) -> terminate(normal, _StateName, _StateData) -> ok.</code> <p>The callback function handling the <c>stop</c> event returns a - tuple <c>{stop,normal,StateData1}</c>, where <c>normal</c> + tuple, <c>{stop,normal,StateData1}</c>, where <c>normal</c> specifies that it is a normal termination and <c>StateData1</c> - is a new value for the state data of the gen_fsm. This will - cause the gen_fsm to call + is a new value for the state data of the <c>gen_fsm</c>. This + causes the <c>gen_fsm</c> to call <c>terminate(normal,StateName,StateData1)</c> and then - terminate gracefully:</p> + it terminates gracefully:</p> </section> </section> <section> <title>Handling Other Messages</title> - <p>If the gen_fsm should be able to receive other messages than - events, the callback function <c>handle_info(Info, StateName, StateData)</c> must be implemented to handle them. Examples of - other messages are exit messages, if the gen_fsm is linked to + <p>If the <c>gen_fsm</c> is to be able to receive other messages + than events, the callback function + <c>handle_info(Info, StateName, StateData)</c> must be implemented + to handle them. Examples of + other messages are exit messages, if the <c>gen_fsm</c> is linked to other processes (than the supervisor) and trapping exit signals.</p> <code type="none"> handle_info({'EXIT', Pid, Reason}, StateName, StateData) -> ..code to handle exits here.. {next_state, StateName1, StateData1}.</code> - <p>The code_change method also has to be implemented.</p> + <p>The code_change method must also be implemented.</p> <code type="none"> code_change(OldVsn, StateName, StateData, Extra) -> ..code to convert state (and more) during code change diff --git a/system/doc/design_principles/gen_server_concepts.xml b/system/doc/design_principles/gen_server_concepts.xml index d24d87aa03..d721845c6d 100644 --- a/system/doc/design_principles/gen_server_concepts.xml +++ b/system/doc/design_principles/gen_server_concepts.xml @@ -21,7 +21,7 @@ </legalnotice> - <title>Gen_Server Behaviour</title> + <title>gen_server Behaviour</title> <prepared></prepared> <docno></docno> <date></date> @@ -29,16 +29,16 @@ <file>gen_server_concepts.xml</file> </header> <marker id="gen_server"></marker> - <p>This chapter should be read in conjunction with - <seealso marker="stdlib:gen_server">gen_server(3)</seealso>, - where all interface functions and callback - functions are described in detail.</p> + <p>This section is to be read with the + <seealso marker="stdlib:gen_server">gen_server(3)</seealso> + manual page in <c>stdblib</c>, where all interface functions and + callback functions are described in detail.</p> <section> <title>Client-Server Principles</title> <p>The client-server model is characterized by a central server and an arbitrary number of clients. The client-server model is - generally used for resource management operations, where several + used for resource management operations, where several different clients want to share a common resource. The server is responsible for managing this resource.</p> <marker id="clientserver"></marker> @@ -49,9 +49,10 @@ <section> <title>Example</title> - <p>An example of a simple server written in plain Erlang was - given in <seealso marker="des_princ#ch1">Overview</seealso>. - The server can be re-implemented using <c>gen_server</c>, + <p>An example of a simple server written in plain Erlang is + provided in + <seealso marker="des_princ#ch1">Overview</seealso>. + The server can be reimplemented using <c>gen_server</c>, resulting in this callback module:</p> <marker id="ex"></marker> <code type="none"> @@ -86,61 +87,60 @@ handle_cast({free, Ch}, Chs) -> <section> <title>Starting a Gen_Server</title> - <p>In the example in the previous section, the gen_server is started - by calling <c>ch3:start_link()</c>:</p> + <p>In the example in the previous section, <c>gen_server</c> is + started by calling <c>ch3:start_link()</c>:</p> <code type="none"> start_link() -> gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}</code> - <p><c>start_link</c> calls the function - <c>gen_server:start_link/4</c>. This function spawns and links to - a new process, a gen_server.</p> + <p><c>start_link</c> calls function <c>gen_server:start_link/4</c>. + This function spawns and links to a new process, a + <c>gen_server</c>.</p> <list type="bulleted"> <item> - <p>The first argument <c>{local, ch3}</c> specifies the name. In - this case, the gen_server will be locally registered as - <c>ch3</c>.</p> - <p>If the name is omitted, the gen_server is not registered. - Instead its pid must be used. The name could also be given - as <c>{global, Name}</c>, in which case the gen_server is + <p>The first argument, <c>{local, ch3}</c>, specifies the name. + The gen_server is then locally registered as <c>ch3</c>.</p> + <p>If the name is omitted, the <c>gen_server</c> is not registered. + Instead its pid must be used. The name can also be given + as <c>{global, Name}</c>, in which case the <c>gen_server</c> is registered using <c>global:register_name/2</c>.</p> </item> <item> <p>The second argument, <c>ch3</c>, is the name of the callback - module, that is the module where the callback functions are + module, that is, the module where the callback functions are located.</p> - <p>In this case, the interface functions (<c>start_link</c>, - <c>alloc</c> and <c>free</c>) are located in the same module - as the callback functions (<c>init</c>, <c>handle_call</c> and + <p>The interface functions (<c>start_link</c>, <c>alloc</c>, + and <c>free</c>) are then located in the same module + as the callback functions (<c>init</c>, <c>handle_call</c>, and <c>handle_cast</c>). This is normally good programming practice, to have the code corresponding to one process contained in one module.</p> </item> <item> - <p>The third argument, [], is a term which is passed as-is to - the callback function <c>init</c>. Here, <c>init</c> does not + <p>The third argument, <c>[]</c>, is a term that is passed as is + to the callback function <c>init</c>. Here, <c>init</c> does not need any indata and ignores the argument.</p> </item> <item> - <p>The fourth argument, [], is a list of options. See - <c>gen_server(3)</c> for available options.</p> + <p>The fourth argument, <c>[]</c>, is a list of options. See the + <c>gen_server(3)</c> manual page for available options.</p> </item> </list> - <p>If name registration succeeds, the new gen_server process calls - the callback function <c>ch3:init([])</c>. <c>init</c> is expected - to return <c>{ok, State}</c>, where <c>State</c> is the internal - state of the gen_server. In this case, the state is the available - channels.</p> + <p>If name registration succeeds, the new <c>gen_server</c> process + calls the callback function <c>ch3:init([])</c>. <c>init</c> is + expected to return <c>{ok, State}</c>, where <c>State</c> is the + internal state of the <c>gen_server</c>. In this case, the state + is the available channels.</p> <code type="none"> init(_Args) -> {ok, channels()}.</code> - <p>Note that <c>gen_server:start_link</c> is synchronous. It does - not return until the gen_server has been initialized and is ready + <p><c>gen_server:start_link</c> is synchronous. It does not return + until the <c>gen_server</c> has been initialized and is ready to receive requests.</p> - <p><c>gen_server:start_link</c> must be used if the gen_server is - part of a supervision tree, i.e. is started by a supervisor. - There is another function <c>gen_server:start</c> to start a - stand-alone gen_server, i.e. a gen_server which is not part of a - supervision tree.</p> + <p><c>gen_server:start_link</c> must be used if the <c>gen_server</c> + is part of a supervision tree, that is, started by a supervisor. + There is another function, <c>gen_server:start</c>, to start a + standalone <c>gen_server</c>, that is, a <c>gen_server</c> that + is not part of a supervision tree.</p> </section> <section> @@ -150,14 +150,17 @@ init(_Args) -> <code type="none"> alloc() -> gen_server:call(ch3, alloc).</code> - <p><c>ch3</c> is the name of the gen_server and must agree with - the name used to start it. <c>alloc</c> is the actual request.</p> - <p>The request is made into a message and sent to the gen_server. - When the request is received, the gen_server calls - <c>handle_call(Request, From, State)</c> which is expected to - return a tuple <c>{reply, Reply, State1}</c>. <c>Reply</c> is - the reply which should be sent back to the client, and - <c>State1</c> is a new value for the state of the gen_server.</p> + <p><c>ch3</c> is the name of the <c>gen_server</c> and must agree + with the name used to start it. <c>alloc</c> is the actual + request.</p> + <p>The request is made into a message and sent to the + <c>gen_server</c>. When the request is received, the + <c>gen_server</c> calls + <c>handle_call(Request, From, State)</c>, which is expected to + return a tuple <c>{reply,Reply,State1}</c>. <c>Reply</c> is + the reply that is to be sent back to the client, and + <c>State1</c> is a new value for the state of the + <c>gen_server</c>.</p> <code type="none"> handle_call(alloc, _From, Chs) -> {Ch, Chs2} = alloc(Chs), @@ -166,8 +169,8 @@ handle_call(alloc, _From, Chs) -> the new state is the set of remaining available channels <c>Chs2</c>.</p> <p>Thus, the call <c>ch3:alloc()</c> returns the allocated channel - <c>Ch</c> and the gen_server then waits for new requests, now - with an updated list of available channels.</p> + <c>Ch</c> and the <c>gen_server</c> then waits for new requests, + now with an updated list of available channels.</p> </section> <section> @@ -177,20 +180,21 @@ handle_call(alloc, _From, Chs) -> <code type="none"> free(Ch) -> gen_server:cast(ch3, {free, Ch}).</code> - <p><c>ch3</c> is the name of the gen_server. <c>{free, Ch}</c> is - the actual request.</p> - <p>The request is made into a message and sent to the gen_server. + <p><c>ch3</c> is the name of the <c>gen_server</c>. + <c>{free, Ch}</c> is the actual request.</p> + <p>The request is made into a message and sent to the + <c>gen_server</c>. <c>cast</c>, and thus <c>free</c>, then returns <c>ok</c>.</p> - <p>When the request is received, the gen_server calls - <c>handle_cast(Request, State)</c> which is expected to - return a tuple <c>{noreply, State1}</c>. <c>State1</c> is a new - value for the state of the gen_server.</p> + <p>When the request is received, the <c>gen_server</c> calls + <c>handle_cast(Request, State)</c>, which is expected to + return a tuple <c>{noreply,State1}</c>. <c>State1</c> is a new + value for the state of the <c>gen_server</c>.</p> <code type="none"> handle_cast({free, Ch}, Chs) -> Chs2 = free(Ch, Chs), {noreply, Chs2}.</code> <p>In this case, the new state is the updated list of available - channels <c>Chs2</c>. The gen_server is now ready for new + channels <c>Chs2</c>. The <c>gen_server</c> is now ready for new requests.</p> </section> @@ -199,15 +203,17 @@ handle_cast({free, Ch}, Chs) -> <section> <title>In a Supervision Tree</title> - <p>If the gen_server is part of a supervision tree, no stop - function is needed. The gen_server will automatically be + <p>If the <c>gen_server</c> is part of a supervision tree, no stop + function is needed. The <c>gen_server</c> is automatically terminated by its supervisor. Exactly how this is done is - defined by a <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> set in the supervisor.</p> + defined by a + <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> + set in the supervisor.</p> <p>If it is necessary to clean up before termination, the shutdown - strategy must be a timeout value and the gen_server must be set - to trap exit signals in the <c>init</c> function. When ordered - to shutdown, the gen_server will then call the callback function - <c>terminate(shutdown, State)</c>:</p> + strategy must be a time-out value and the <c>gen_server</c> must + be set to trap exit signals in function <c>init</c>. When ordered + to shutdown, the <c>gen_server</c> then calls the callback + function <c>terminate(shutdown, State)</c>:</p> <code type="none"> init(Args) -> ..., @@ -223,9 +229,9 @@ terminate(shutdown, State) -> </section> <section> - <title>Stand-Alone Gen_Servers</title> - <p>If the gen_server is not part of a supervision tree, a stop - function may be useful, for example:</p> + <title>Standalone Gen_Servers</title> + <p>If the <c>gen_server</c> is not part of a supervision tree, a + stop function can be useful, for example:</p> <code type="none"> ... export([stop/0]). @@ -245,26 +251,26 @@ handle_cast({free, Ch}, State) -> terminate(normal, State) -> ok.</code> <p>The callback function handling the <c>stop</c> request returns - a tuple <c>{stop, normal, State1}</c>, where <c>normal</c> + a tuple <c>{stop,normal,State1}</c>, where <c>normal</c> specifies that it is a normal termination and <c>State1</c> is - a new value for the state of the gen_server. This will cause - the gen_server to call <c>terminate(normal,State1)</c> and then - terminate gracefully.</p> + a new value for the state of the <c>gen_server</c>. This causes + the <c>gen_server</c> to call <c>terminate(normal, State1)</c> + and then it terminates gracefully.</p> </section> </section> <section> <title>Handling Other Messages</title> - <p>If the gen_server should be able to receive other messages than - requests, the callback function <c>handle_info(Info, State)</c> + <p>If the <c>gen_server</c> is to be able to receive other messages + than requests, the callback function <c>handle_info(Info, State)</c> must be implemented to handle them. Examples of other messages - are exit messages, if the gen_server is linked to other processes - (than the supervisor) and trapping exit signals.</p> + are exit messages, if the <c>gen_server</c> is linked to other + processes (than the supervisor) and trapping exit signals.</p> <code type="none"> handle_info({'EXIT', Pid, Reason}, State) -> ..code to handle exits here.. {noreply, State1}.</code> - <p>The code_change method also has to be implemented.</p> + <p>The <c>code_change</c> method must also be implemented.</p> <code type="none"> code_change(OldVsn, State, Extra) -> ..code to convert state (and more) during code change diff --git a/system/doc/design_principles/included_applications.xml b/system/doc/design_principles/included_applications.xml index 3aa43fd595..7f139edf76 100644 --- a/system/doc/design_principles/included_applications.xml +++ b/system/doc/design_principles/included_applications.xml @@ -28,35 +28,36 @@ <rev></rev> <file>included_applications.xml</file> </header> + <marker id="included appl"></marker> <section> - <title>Definition</title> + <title>Introduction</title> <p>An application can <em>include</em> other applications. An <em>included application</em> has its own application directory and <c>.app</c> file, but it is started as part of the supervisor tree of another application.</p> <p>An application can only be included by one other application.</p> <p>An included application can include other applications.</p> - <p>An application which is not included by any other application is + <p>An application that is not included by any other application is called a <em>primary application</em>.</p> <marker id="inclappls"></marker> <image file="../design_principles/inclappls.gif"> - <icaption>Primary Application and Included Applications.</icaption> + <icaption>Primary Application and Included Applications</icaption> </image> - <p>The application controller will automatically load any included - applications when loading a primary application, but not start + <p>The application controller automatically loads any included + applications when loading a primary application, but does not start them. Instead, the top supervisor of the included application must be started by a supervisor in the including application.</p> <p>This means that when running, an included application is in fact - part of the primary application and a process in an included - application will consider itself belonging to the primary + part of the primary application, and a process in an included + application considers itself belonging to the primary application.</p> </section> <section> <title>Specifying Included Applications</title> <p>Which applications to include is defined by - the <c>included_applications</c> key in the <c>.app</c> file.</p> + the <c>included_applications</c> key in the <c>.app</c> file:</p> <pre> {application, prim_app, [{description, "Tree application"}, @@ -71,7 +72,7 @@ </section> <section> - <title>Synchronizing Processes During Startup</title> + <title>Synchronizing Processes during Startup</title> <p>The supervisor tree of an included application is started as part of the supervisor tree of the including application. If there is a need for synchronization between processes in @@ -79,12 +80,12 @@ by using <em>start phases</em>.</p> <p>Start phases are defined by the <c>start_phases</c> key in the <c>.app</c> file as a list of tuples <c>{Phase,PhaseArgs}</c>, - where <c>Phase</c> is an atom and <c>PhaseArgs</c> is a term. - Also, the value of the <c>mod</c> key of the including application + where <c>Phase</c> is an atom and <c>PhaseArgs</c> is a term.</p> + <p>The value of the <c>mod</c> key of the including application must be set to <c>{application_starter,[Module,StartArgs]}</c>, - where <c>Module</c> as usual is the application callback module - and <c>StartArgs</c> a term provided as argument to the callback - function <c>Module:start/2</c>.</p> + where <c>Module</c> as usual is the application callback module. + <c>StartArgs</c> is a term provided as argument to the callback + function <c>Module:start/2</c>:</p> <code type="none"> {application, prim_app, [{description, "Tree application"}, @@ -108,36 +109,38 @@ {mod, {incl_app_cb,[]}} ]}.</code> <p>When starting a primary application with included applications, - the primary application is started the normal way: - The application controller creates an application master for - the application, and the application master calls + the primary application is started the normal way, that is:</p> + <list type="bulleted"> + <item>The application controller creates an application master for + the application</item> + <item>The application master calls <c>Module:start(normal, StartArgs)</c> to start the top - supervisor.</p> + supervisor.</item> + </list> <p>Then, for the primary application and each included application in top-down, left-to-right order, the application master calls <c>Module:start_phase(Phase, Type, PhaseArgs)</c> for each phase - defined for for the primary application, in that order. - Note that if a phase is not defined for an included application, + defined for the primary application, in that order. If a phase + is not defined for an included application, the function is not called for this phase and application.</p> <p>The following requirements apply to the <c>.app</c> file for an included application:</p> <list type="bulleted"> - <item>The <c>{mod, {Module,StartArgs}}</c> option must be - included. This option is used to find the callback module - <c>Module</c> of the application. <c>StartArgs</c> is ignored, - as <c>Module:start/2</c> is called only for the primary - application.</item> + <item>The <c>{mod, {Module,StartArgs}}</c> option must be included. + This option is used to find the callback module <c>Module</c> of the + application. <c>StartArgs</c> is ignored, as <c>Module:start/2</c> + is called only for the primary application.</item> <item>If the included application itself contains included - applications, instead the option - <c>{mod, {application_starter, [Module,StartArgs]}}</c> must be - included.</item> + applications, instead the + <c>{mod, {application_starter, [Module,StartArgs]}}</c> option + must be included.</item> <item>The <c>{start_phases, [{Phase,PhaseArgs}]}</c> option must - be included, and the set of specified phases must be a subset - of the set of phases specified for the primary application.</item> + be included, and the set of specified phases must be a subset + of the set of phases specified for the primary application.</item> </list> <p>When starting <c>prim_app</c> as defined above, the application - controller will call the following callback functions, before - <c>application:start(prim_app)</c> returns a value:</p> + controller calls the following callback functions before + <c>application:start(prim_app)</c> returns a value:</p> <code type="none"> application:start(prim_app) => prim_app_cb:start(normal, []) diff --git a/system/doc/design_principles/release_handling.xml b/system/doc/design_principles/release_handling.xml index 9d1e2c8669..eeb71125b6 100644 --- a/system/doc/design_principles/release_handling.xml +++ b/system/doc/design_principles/release_handling.xml @@ -28,128 +28,126 @@ <rev></rev> <file>release_handling.xml</file> </header> + <marker id="release handling"></marker> <section> <title>Release Handling Principles</title> <p>An important feature of the Erlang programming language is - the ability to change module code in run-time, <em>code replacement</em>, as described in <em>Erlang Reference Manual</em>.</p> + the ability to change module code in runtime, + <em>code replacement</em>, as described in the Erlang + Reference Manual.</p> <p>Based on this feature, the OTP application SASL provides a framework for upgrading and downgrading between different - versions of an entire release in run-time. This is what we call + versions of an entire release in runtime. This is called <em>release handling</em>.</p> - <p>The framework consists of off-line support (<c>systools</c>) for - generating scripts and building release packages, and on-line - support (<c>release_handler</c>) for unpacking and installing - release packages.</p> - <p>Note that the minimal system based on Erlang/OTP, enabling - release handling, thus consists of Kernel, STDLIB and SASL.</p> - <list type="ordered"> - <item> - <p>A release is created as described in the previous chapter - <seealso marker="release_structure">Releases</seealso>. - The release is transferred to and installed at target - environment. Refer to <em>System Principles</em> for - information of how to install the first target system.</p> - </item> - <item> - <p>Modifications, for example error corrections, are made to - the code in the development environment.</p> - </item> - <item> - <p>At some point it is time to make a new version of release. - The relevant <c>.app</c> files are updated and a new - <c>.rel</c> file is written.</p> - </item> - <item> - <p>For each modified application, an - <seealso marker="#appup">application upgrade file</seealso>, - <c>.appup</c>, is created. In this file, it is described how - to upgrade and/or downgrade between the old and new version of - the application.</p> - </item> - <item> - <p>Based on the <c>.appup</c> files, a - <seealso marker="#relup">release upgrade file</seealso> called - <c>relup</c>, is created. This file describes how to upgrade - and/or downgrade between the old and new version of - the entire release.</p> - </item> - <item> - <p>A new release package is made and transferred to - the target system.</p> - </item> - <item> - <p>The new release package is unpacked using the release - handler.</p> - </item> - <item> - <p>The new version of the release is installed, also using - the release handler. This is done by evaluating - the instructions in <c>relup</c>. Modules may be added, - deleted or re-loaded, applications may be started, stopped or - re-started etc. In some cases, it is even necessary to restart - the entire emulator.</p> - <p>If the installation fails, the system may be rebooted. - The old release version is then automatically used.</p> - </item> - <item> - <p>If the installation succeeds, the new version is made - the default version, which should now be used in case of a - system reboot.</p> - </item> - </list> - <p>The next chapter, <seealso marker="appup_cookbook">Appup Cookbook</seealso>, contains examples of <c>.appup</c> files - for typical cases of upgrades/downgrades that are normally easy - to handle in run-time. However, there are a many aspects that can - make release handling complicated. To name a few examples:</p> + <p>The framework consists of:</p> <list type="bulleted"> - <item> - <p>Complicated or circular dependencies can make it difficult - or even impossible to decide in which order things must be - done without risking run-time errors during an upgrade or - downgrade. Dependencies may be:</p> - <list type="bulleted"> - <item>between nodes,</item> - <item>between processes, and</item> - <item>between modules.</item> - </list> - </item> - <item> - <p>During release handling, non-affected processes continue - normal execution. This may lead to timeouts or other problems. - For example, new processes created in the time window between - suspending processes using a certain module and loading a new - version of this module, may execute old code.</p> - </item> + <item>Offline support - <c>systools</c> for generating scripts + and building release packages</item> + <item>Online support - <c>release_handler</c> for unpacking and + installing release packages</item> </list> - <p>It is therefore recommended that code is changed in as small + <p>The minimal system based on Erlang/OTP, enabling release handling, + thus consists of the Kernel, STDLIB, and SASL + applications.</p> + + <section> + <title>Release Handling Workflow</title> + <p><em>Step 1</em>) A release is created as described in + <seealso marker="release_structure">Releases</seealso>.</p> + <p><em>Step 2</em>) The release is transferred to and installed at + target environment. For information of how to install the first + target system, see System Principles.</p> + <p><em>Step 3</em>) Modifications, for example, error corrections, + are made to the code in the development environment.</p> + <p><em>Step 4</em>) At some point, it is time to make a new version + of release. The relevant <c>.app</c> files are updated and a new + <c>.rel</c> file is written.</p> + <p><em>Step 5</em>) For each modified application, an + <seealso marker="#appup">application upgrade file</seealso>, + <c>.appup</c>, is created. In this file, it is described how to + upgrade and/or downgrade between the old and new version of the + application.</p> + <p><em>Step 6</em>) Based on the <c>.appup</c> files, a + <seealso marker="#relup">release upgrade file</seealso> called + <c>relup</c>, is created. This file describes how to upgrade and/or + downgrade between the old and new version of the entire release.</p> + <p><em>Step 7</em>) A new release package is made and transferred to + the target system.</p> + <p><em>Step 8</em>) The new release package is unpacked using the + release handler.</p> + <p><em>Step 9</em>) The new version of the release is installed, + also using the release handler. This is done by evaluating the + instructions in <c>relup</c>. Modules can be added, deleted, or + reloaded, applications can be started, stopped, or restarted, and so + on. In some cases, it is even necessary to restart the entire + emulator.</p> + <list type="bulleted"> + <item>If the installation fails, the system can be rebooted. + The old release version is then automatically used.</item> + <item>If the installation succeeds, the new version is made + the default version, which is to now be used if there is a + system reboot.</item> + </list> + </section> + + <section> + <title>Release Handling Aspects</title> + <p><seealso marker="appup_cookbook">Appup Cookbook</seealso>, + contains examples of <c>.appup</c> files + for typical cases of upgrades/downgrades that are normally easy to + handle in runtime. However, many aspects can make release handling + complicated, for example:</p> + <list type="bulleted"> + <item> + <p>Complicated or circular dependencies can make it difficult + or even impossible to decide in which order things must be + done without risking runtime errors during an upgrade or + downgrade. Dependencies can be:</p> + <list type="bulleted"> + <item>Between nodes</item> + <item>Between processes</item> + <item>Between modules</item> + </list> + </item> + <item> + <p>During release handling, non-affected processes continue + normal execution. This can lead to time-outs or other problems. + For example, new processes created in the time window between + suspending processes using a certain module, and loading a new + version of this module, can execute old code.</p> + </item> + </list> + <p>It is thus recommended that code is changed in as small steps as possible, and always kept backwards compatible.</p> + </section> </section> <section> <marker id="req"></marker> <title>Requirements</title> - <p>For release handling to work properly, the runtime system needs - to have knowledge about which release it is currently running. It - must also be able to change (in run-time) which boot script and - system configuration file should be used if the system is - rebooted, for example by <c>heart</c> after a failure. - Therefore, Erlang must be started as an embedded system, see - <em>Embedded System</em> for information on how to do this.</p> + <p>For release handling to work properly, the runtime system must + have knowledge about which release it is running. It + must also be able to change (in runtime) which boot script and + system configuration file to use if the system is + rebooted, for example, by <c>heart</c> after a failure. + Thus, Erlang must be started as an embedded system; for + information on how to do this, see Embedded System.</p> <p>For system reboots to work properly, it is also required that - the system is started with heart beat monitoring, see - <c>erl(1)</c> and <c>heart(3)</c>.</p> + the system is started with heartbeat monitoring, see the + <c>erl(1)</c> manual page in ERTS and the <c>heart(3)</c> + manual page in Kernel</p> <p>Other requirements:</p> <list type="bulleted"> <item> <p>The boot script included in a release package must be generated from the same <c>.rel</c> file as the release package itself.</p> - <p>Information about applications are fetched from the script + <p>Information about applications is fetched from the script when an upgrade or downgrade is performed.</p> </item> <item> - <p>The system must be configured using one and only one system + <p>The system must be configured using only one system configuration file, called <c>sys.config</c>.</p> <p>If found, this file is automatically included when a release package is created.</p> @@ -165,13 +163,13 @@ <section> <title>Distributed Systems</title> - <p>If the system consists of several Erlang nodes, each node may use + <p>If the system consists of several Erlang nodes, each node can use its own version of the release. The release handler is a locally registered process and must be called at each node where an - upgrade or downgrade is required. There is a release handling - instruction that can be used to synchronize the release handler - processes at a number of nodes: <c>sync_nodes</c>. See - <c>appup(4)</c>.</p> + upgrade or downgrade is required. A release handling + instruction, <c>sync_nodes</c>, can be used to synchronize the + release handler processes at a number of nodes, see the + <c>appup(4)</c> manual page in SASL.</p> </section> <section> @@ -183,31 +181,26 @@ instructions. To make it easier for the user, there are also a number of <em>high-level</em> instructions, which are translated to low-level instructions by <c>systools:make_relup</c>.</p> - <p>Here, some of the most frequently used instructions are - described. The complete list of instructions can be found in - <c>appup(4)</c>.</p> + <p>Some of the most frequently used instructions are described in + this section. The complete list of instructions is included in the + <c>appup(4)</c> manual page in SASL.</p> <p>First, some definitions:</p> - <taglist> - <tag><em>Residence module</em></tag> - <item> - <p>The module where a process has its tail-recursive loop - function(s). If the tail-recursive loop functions are - implemented in several modules, all those modules are residence - modules for the process.</p> - </item> - <tag><em>Functional module</em></tag> - <item> - <p>A module which is not a residence module for any process.</p> - </item> - </taglist> - <p>Note that for a process implemented using an OTP behaviour, - the behaviour module is the residence module for that process. - The callback module is a functional module.</p> + <list type="bulleted"> + <item><em>Residence module</em> - The module where a process has + its tail-recursive loop function(s). If these functions are + implemented in several modules, all those modules are residence + modules for the process.</item> + <item><em>Functional module</em> - A module that is not a + residence module for any process.</item> + </list> + <p>For a process implemented using an OTP behaviour, the behaviour + module is the residence module for that process. + The callback module is a functional module.</p> <section> <title>load_module</title> <p>If a simple extension has been made to a functional module, it - is sufficient to simply load the new version of the module into + is sufficient to load the new version of the module into the system, and remove the old version. This is called <em>simple code replacement</em> and for this the following instruction is used:</p> @@ -217,44 +210,50 @@ <section> <title>update</title> - <p>If a more complex change has been made, for example a change - to the format of the internal state of a gen_server, simple code - replacement is not sufficient. Instead it is necessary to - suspend the processes using the module (to avoid that they try - to handle any requests before the code replacement is - completed), ask them to transform the internal state format and - switch to the new version of the module, remove the old version - and last, resume the processes. This is called <em>synchronized code replacement</em> and for this the following instructions - are used:</p> + <p>If a more complex change has been made, for example, a change + to the format of the internal state of a <c>gen_server</c>, simple + code replacement is not sufficient. Instead, it is necessary to:</p> + <list type="bulleted"> + <item>Suspend the processes using the module (to avoid that + they try to handle any requests before the code replacement is + completed).</item> + <item>Ask them to transform the internal state format and + switch to the new version of the module.</item> + <item>Remove the old version.</item> + <item>Resume the processes.</item> + </list> + <p>This is called <em>synchronized code replacement</em> and for + this the following instructions are used:</p> <code type="none"> {update, Module, {advanced, Extra}} {update, Module, supervisor}</code> <p><c>update</c> with argument <c>{advanced,Extra}</c> is used when changing the internal state of a behaviour as described - above. It will cause behaviour processes to call the callback + above. It causes behaviour processes to call the callback function <c>code_change</c>, passing the term <c>Extra</c> and - some other information as arguments. See the man pages for + some other information as arguments. See the manual pages for the respective behaviours and - <seealso marker="appup_cookbook#int_state">Appup Cookbook</seealso>.</p> + <seealso marker="appup_cookbook#int_state">Appup Cookbook</seealso>.</p> <p><c>update</c> with argument <c>supervisor</c> is used when changing the start specification of a supervisor. See - <seealso marker="appup_cookbook#sup">Appup Cookbook</seealso>.</p> + <seealso marker="appup_cookbook#sup">Appup Cookbook</seealso>.</p> <p>When a module is to be updated, the release handler finds which processes that are <em>using</em> the module by traversing the supervision tree of each running application and checking all the child specifications:</p> <code type="none"> {Id, StartFunc, Restart, Shutdown, Type, Modules}</code> - <p>A process is using a module if the name is listed in + <p>A process uses a module if the name is listed in <c>Modules</c> in the child specification for the process.</p> <p>If <c>Modules=dynamic</c>, which is the case for event managers, the event manager process informs the release handler - about the list of currently installed event handlers (gen_fsm) - and it is checked if the module name is in this list instead.</p> + about the list of currently installed event handlers + (<c>gen_fsm</c>), and it is checked if the module name is in + this list instead.</p> <p>The release handler suspends, asks for code change, and resumes processes by calling the functions - <c>sys:suspend/1,2</c>, <c>sys:change_code/4,5</c> and - <c>sys:resume/1,2</c> respectively.</p> + <c>sys:suspend/1,2</c>, <c>sys:change_code/4,5</c>, and + <c>sys:resume/1,2</c>, respectively.</p> </section> <section> @@ -263,39 +262,39 @@ used:</p> <code type="none"> {add_module, Module}</code> - <p>The instruction loads the module and is absolutely necessary + <p>The instruction loads the module and is necessary when running Erlang in embedded mode. It is not strictly required when running Erlang in interactive (default) mode, since the code server then automatically searches for and loads unloaded modules.</p> - <p>The opposite of <c>add_module</c> is <c>delete_module</c> which + <p>The opposite of <c>add_module</c> is <c>delete_module</c>, which unloads a module:</p> <code type="none"> {delete_module, Module}</code> - <p>Note that any process, in any application, with <c>Module</c> + <p>Any process, in any application, with <c>Module</c> as residence module, is killed when the instruction is - evaluated. The user should therefore ensure that all such + evaluated. The user must therefore ensure that all such processes are terminated before deleting the module, to avoid - a possible situation with failing supervisor restarts.</p> + a situation with failing supervisor restarts.</p> </section> <section> <title>Application Instructions</title> - <p>Instruction for adding an application:</p> + <p>The following is the instruction for adding an application:</p> <code type="none"> {add_application, Application}</code> <p>Adding an application means that the modules defined by the <c>modules</c> key in the <c>.app</c> file are loaded using - a number of <c>add_module</c> instructions, then the application + a number of <c>add_module</c> instructions, and then the application is started.</p> - <p>Instruction for removing an application:</p> + <p>The following is the instruction for removing an application:</p> <code type="none"> {remove_application, Application}</code> <p>Removing an application means that the application is stopped, the modules are unloaded using a number of <c>delete_module</c> - instructions and then the application specification is unloaded + instructions, and then the application specification is unloaded from the application controller.</p> - <p>Instruction for restarting an application:</p> + <p>The following is the instruction for restarting an application:</p> <code type="none"> {restart_application, Application}</code> <p>Restarting an application means that the application is stopped @@ -305,46 +304,48 @@ </section> <section> - <title>apply (low-level)</title> + <title>apply (Low-Level)</title> <p>To call an arbitrary function from the release handler, the following instruction is used:</p> <code type="none"> {apply, {M, F, A}}</code> - <p>The release handler will evaluate <c>apply(M, F, A)</c>.</p> + <p>The release handler evalutes <c>apply(M, F, A)</c>.</p> </section> <section> <marker id="restart_new_emulator_instr"></marker> - <title>restart_new_emulator (low-level)</title> + <title>restart_new_emulator (Low-Level)</title> <p>This instruction is used when changing to a new emulator - version, or when any of the core applications kernel, stdlib - or sasl is upgraded. If a system reboot is needed for some - other reason, the <c>restart_emulator</c> instruction should - be used instead.</p> - <p>Requires that the system is started with heart beat - monitoring, see <c>erl(1)</c> and <c>heart(3)</c>.</p> - <p>The <c>restart_new_emulator</c> instruction shall always be - the very first instruction in a relup. If the relup is - generated by <c>systools:make_relup/3,4</c> this is + version, or when any of the core applications Kernel, + STDLIB, or SASL is upgraded. If a system reboot + is needed for another reason, the <c>restart_emulator</c> + instruction is to be used instead.</p> + <p>This instruction requires that the system is started with + heartbeat monitoring, see the <c>erl(1)</c> manual page in + ERTS and the <c>heart(3)</c> manual page in Kernel.</p> + <p>The <c>restart_new_emulator</c> instruction must always be + the first instruction in a relup. If the relup is + generated by <c>systools:make_relup/3,4</c>, this is automatically ensured.</p> <p>When the release handler encounters the instruction, it first generates a temporary boot file, which starts the new versions of the emulator and the core applications, and the old version of all other applications. Then it shuts down - the current emulator by calling <c>init:reboot()</c>, see - <c>init(3)</c>. All processes are terminated gracefully and - the system is rebooted by the heart program, using the + the current emulator by calling <c>init:reboot()</c>, see the + <c>init(3)</c> manual page in Kernel. + All processes are terminated gracefully and + the system is rebooted by the <c>heart</c> program, using the temporary boot file. After the reboot, the rest of the relup instructions are executed. This is done as a part of the temporary boot script.</p> <warning> - <p>Since this mechanism causes the new versions of the - emulator and core applications to run with the old version of - other applications during startup, extra care must be taken to + <p>This mechanism causes the new versions of the emulator and + core applications to run with the old version of other + applications during startup. Thus, take extra care to avoid incompatibility. Incompatible changes in the core - applications may in some situations be necessary. If possible, + applications can in some situations be necessary. If possible, such changes are preceded by deprecation over two major - releases before the actual change. To make sure your + releases before the actual change. To ensure the application is not crashed by an incompatible change, always remove any call to deprecated functions as soon as possible.</p> @@ -352,35 +353,36 @@ <p>An info report is written when the upgrade is completed. To programmatically find out if the upgrade is complete, call <c>release_handler:which_releases(current)</c> and check - if it returns the expected (i.e. the new) release.</p> + if it returns the expected (that is, the new) release.</p> <p>The new release version must be made permanent when the new - emulator is up and running. Otherwise, the old version will be - used in case of a new system reboot.</p> - <p>On UNIX, the release handler tells the heart program which - command to use to reboot the system. Note that the environment - variable <c>HEART_COMMAND</c>, normally used by the heart - program, in this case is ignored. The command instead defaults - to <c>$ROOT/bin/start</c>. Another command can be set - by using the SASL configuration parameter <c>start_prg</c>, see - <c>sasl(6)</c>.</p> + emulator is operational. Otherwise, the old version will be + used if there is a new system reboot.</p> + <p>On UNIX, the release handler tells the <c>heart</c> program + which command to use to reboot the system. The environment + variable <c>HEART_COMMAND</c>, normally used by the <c>heart</c> + program, is ignored in this case. The command instead defaults + to <c>$ROOT/bin/start</c>. Another command can be set by using + the SASL configuration parameter <c>start_prg</c>, see + the <c>sasl(6)</c> manual page.</p> </section> <section> <marker id="restart_emulator_instr"></marker> - <title>restart_emulator (low-level)</title> - <p>This instruction is not related to upgrades of erts or any of - the core applications. It can be used by any application to + <title>restart_emulator (Low-Level)</title> + <p>This instruction is not related to upgrades of ERTS or any + of the core applications. It can be used by any application to force a restart of the emulator after all upgrade instructions are executed.</p> - <p>There can only be one <c>restart_emulator</c> instruction in - a relup script, and it shall always be placed at the end. If - the relup is generated by <c>systools:make_relup/3,4</c> this + <p>A relup script can only have one <c>restart_emulator</c> + instruction and it must always be placed at the end. If + the relup is generated by <c>systools:make_relup/3,4</c>, this is automatically ensured.</p> <p>When the release handler encounters the instruction, it shuts - down the emulator by calling <c>init:reboot()</c>, see - <c>init(3)</c>. All processes are terminated gracefully and - the system can then be rebooted by the heart program using the - new release version. No more upgrade instruction will be + down the emulator by calling <c>init:reboot()</c>, see the + <c>init(3)</c> manual page in Kernel. + All processes are terminated gracefully and the system + can then be rebooted by the <c>heart</c> program using the + new release version. No more upgrade instruction is executed after the restart.</p> </section> </section> @@ -389,10 +391,11 @@ <marker id="appup"></marker> <title>Application Upgrade File</title> <p>To define how to upgrade/downgrade between the current version - and previous versions of an application, we create an - <em>application upgrade file</em>, or in short <c>.appup</c> file. - The file should be called <c>Application.appup</c>, where - <c>Application</c> is the name of the application:</p> + and previous versions of an application, an + <em>application upgrade file</em>, or in short an <c>.appup</c> + file is created. + The file is to be called <c>Application.appup</c>, where + <c>Application</c> is the application name:</p> <code type="none"> {Vsn, [{UpFromVsn1, InstructionsU1}, @@ -401,24 +404,27 @@ [{DownToVsn1, InstructionsD1}, ..., {DownToVsnK, InstructionsDK}]}.</code> - <p><c>Vsn</c>, a string, is the current version of the application, - as defined in the <c>.app</c> file. Each <c>UpFromVsn</c> - is a previous version of the application to upgrade from, and each - <c>DownToVsn</c> is a previous version of the application to - downgrade to. Each <c>Instructions</c> is a list of release - handling instructions.</p> - <p>The syntax and contents of the <c>appup</c> file are described - in detail in <c>appup(4)</c>.</p> - <p>In the chapter <seealso marker="appup_cookbook">Appup Cookbook</seealso>, examples of <c>.appup</c> files for typical - upgrade/downgrade cases are given.</p> - <p>Example: Consider the release <c>ch_rel-1</c> from - the <seealso marker="release_structure#ch_rel">Releases</seealso> - chapter. Assume we want to add a function <c>available/0</c> to - the server <c>ch3</c> which returns the number of available - channels:</p> - <p>(Hint: When trying out the example, make the changes in a copy of - the original directory, so that the first versions are still - available.)</p> + <list type="bulleted"> + <item><c>Vsn</c>, a string, is the current version of the application, + as defined in the <c>.app</c> file.</item> + <item>Each <c>UpFromVsn</c> is a previous version of the application + to upgrade from.</item> + <item>Each <c>DownToVsn</c> is a previous version of the application + to downgrade to.</item> + <item>Each <c>Instructions</c> is a list of release handling + instructions.</item> + </list> + <p>For information about the syntax and contents of the <c>.appup</c> + file, see the <c>appup(4)</c> manual page in SASL.</p> + <p><seealso marker="appup_cookbook">Appup Cookbook</seealso> + includes examples of <c>.appup</c> files for typical upgrade/downgrade + cases.</p> + <p><em>Example:</em> Consider the release <c>ch_rel-1</c> from + <seealso marker="release_structure#ch_rel">Releases</seealso>. + Assume you want to add a function <c>available/0</c> to server + <c>ch3</c>, which returns the number of available channels (when + trying out the example, change in a copy of the original + directory, so that the first versions are still available):</p> <code type="none"> -module(ch3). -behaviour(gen_server). @@ -465,9 +471,9 @@ handle_cast({free, Ch}, Chs) -> {mod, {ch_app,[]}} ]}.</code> <p>To upgrade <c>ch_app</c> from <c>"1"</c> to <c>"2"</c> (and - to downgrade from <c>"2"</c> to <c>"1"</c>), we simply need to + to downgrade from <c>"2"</c> to <c>"1"</c>), you only need to load the new (old) version of the <c>ch3</c> callback module. - We create the application upgrade file <c>ch_app.appup</c> in + Create the application upgrade file <c>ch_app.appup</c> in the <c>ebin</c> directory:</p> <code type="none"> {"2", @@ -480,25 +486,25 @@ handle_cast({free, Ch}, Chs) -> <marker id="relup"></marker> <title>Release Upgrade File</title> <p>To define how to upgrade/downgrade between the new version and - previous versions of a release, we create a <em>release upgrade file</em>, or in short <c>relup</c> file.</p> + previous versions of a release, a <em>release upgrade file</em>, + or in short <c>relup</c> file, is to be created.</p> <p>This file does not need to be created manually, it can be generated by <c>systools:make_relup/3,4</c>. The relevant versions - of the <c>.rel</c> file, <c>.app</c> files and <c>.appup</c> files - are used as input. It is deducted which applications should be - added and deleted, and which applications that need to be upgraded - and/or downgraded. The instructions for this is fetched from + of the <c>.rel</c> file, <c>.app</c> files, and <c>.appup</c> files + are used as input. It is deducted which applications are to be + added and deleted, and which applications that must be upgraded + and/or downgraded. The instructions for this are fetched from the <c>.appup</c> files and transformed into a single list of low-level instructions in the right order.</p> <p>If the <c>relup</c> file is relatively simple, it can be created - manually. Remember that it should only contain low-level - instructions.</p> - <p>The syntax and contents of the release upgrade file are - described in detail in <c>relup(4)</c>.</p> - <p>Example, continued from the previous section. We have a new - version "2" of <c>ch_app</c> and an <c>.appup</c> file. We also - need a new version of the <c>.rel</c> file. This time the file is - called <c>ch_rel-2.rel</c> and the release version string is - changed changed from "A" to "B":</p> + manually. It it only to contain low-level instructions.</p> + <p>For details about the syntax and contents of the release upgrade + file, see the <c>relup(4)</c> manual page in SASL.</p> + <p><em>Example, continued from the previous section:</em> You have a + new version "2" of <c>ch_app</c> and an <c>.appup</c> file. A new + version of the <c>.rel</c> file is also needed. This time the file + is called <c>ch_rel-2.rel</c> and the release version string is + changed from "A" to "B":</p> <code type="none"> {release, {"ch_rel", "B"}, @@ -512,13 +518,13 @@ handle_cast({free, Ch}, Chs) -> <pre> 1> <input>systools:make_relup("ch_rel-2", ["ch_rel-1"], ["ch_rel-1"]).</input> ok</pre> - <p>This will generate a <c>relup</c> file with instructions for + <p>This generates a <c>relup</c> file with instructions for how to upgrade from version "A" ("ch_rel-1") to version "B" ("ch_rel-2") and how to downgrade from version "B" to version "A".</p> - <p>Note that both the old and new versions of the <c>.app</c> and - <c>.rel</c> files must be in the code path, as well as - the <c>.appup</c> and (new) <c>.beam</c> files. It is possible - to extend the code path by using the option <c>path</c>:</p> + <p>Both the old and new versions of the <c>.app</c> and + <c>.rel</c> files must be in the code path, as well as the + <c>.appup</c> and (new) <c>.beam</c> files. The code path can be + extended by using the option <c>path</c>:</p> <pre> 1> <input>systools:make_relup("ch_rel-2", ["ch_rel-1"], ["ch_rel-1"],</input> <input>[{path,["../ch_rel-1",</input> @@ -529,31 +535,34 @@ ok</pre> <section> <marker id="rel_handler"></marker> <title>Installing a Release</title> - <p>When we have made a new version of a release, a release package + <p>When you have made a new version of a release, a release package can be created with this new version and transferred to the target environment.</p> - <p>To install the new version of the release in run-time, + <p>To install the new version of the release in runtime, the <em>release handler</em> is used. This is a process belonging - to the SASL application, that handles unpacking, installation, - and removal of release packages. It is interfaced through - the module <c>release_handler</c>, which is described in detail in - <c>release_handler(3)</c>.</p> - <p>Assuming there is a target system up and running with + to the SASL application, which handles unpacking, installation, + and removal of release packages. It is communicated through + the <c>release_handler</c> module. For details, see the + <c>release_handler(3)</c> manual page in SASL.</p> + <p>Assuming there is an operational target system with installation root directory <c>$ROOT</c>, the release package with - the new version of the release should be copied to + the new version of the release is to be copied to <c>$ROOT/releases</c>.</p> - <p>The first action is to <em>unpack</em> the release package, - the files are then extracted from the package:</p> + <p>First, <em>unpack</em> the release package. + The files are then extracted from the package:</p> <code type="none"> release_handler:unpack_release(ReleaseName) => {ok, Vsn}</code> - <p><c>ReleaseName</c> is the name of the release package except - the <c>.tar.gz</c> extension. <c>Vsn</c> is the version of - the unpacked release, as defined in its <c>.rel</c> file.</p> - <p>A directory <c>$ROOT/lib/releases/Vsn</c> will be created, where + <list type="bulleted"> + <item><c>ReleaseName</c> is the name of the release package except + the <c>.tar.gz</c> extension.</item> + <item><c>Vsn</c> is the version of the unpacked release, as + defined in its <c>.rel</c> file.</item> + </list> + <p>A directory <c>$ROOT/lib/releases/Vsn</c> is created, where the <c>.rel</c> file, the boot script <c>start.boot</c>, - the system configuration file <c>sys.config</c> and <c>relup</c> + the system configuration file <c>sys.config</c>, and <c>relup</c> are placed. For applications with new version numbers, - the application directories will be placed under <c>$ROOT/lib</c>. + the application directories are placed under <c>$ROOT/lib</c>. Unchanged applications are not affected.</p> <p>An unpacked release can be <em>installed</em>. The release handler then evaluates the instructions in <c>relup</c>, step by @@ -563,11 +572,11 @@ release_handler:install_release(Vsn) => {ok, FromVsn, []}</code> <p>If an error occurs during the installation, the system is rebooted using the old version of the release. If installation succeeds, the system is afterwards using the new version of - the release, but should anything happen and the system is - rebooted, it would start using the previous version again. To be - made the default version, the newly installed release must be made - <em>permanent</em>, which means the previous version becomes - <em>old</em>:</p> + the release, but if anything happens and the system is + rebooted, it starts using the previous version again.</p> + <p>To be made the default version, the newly installed release + must be made <em>permanent</em>, which means the previous + version becomes <em>old</em>:</p> <code type="none"> release_handler:make_permanent(Vsn) => ok</code> <p>The system keeps information about which versions are old and @@ -579,41 +588,44 @@ release_handler:make_permanent(Vsn) => ok</code> release_handler:install_release(FromVsn) => {ok, Vsn, []}</code> <p>An installed, but not permanent, release can be <em>removed</em>. Information about the release is then deleted from - <c>$ROOT/releases/RELEASES</c> and the release specific code, - that is the new application directories and + <c>$ROOT/releases/RELEASES</c> and the release-specific code, + that is, the new application directories and the <c>$ROOT/releases/Vsn</c> directory, are removed.</p> <code type="none"> release_handler:remove_release(Vsn) => ok</code> - <p>Example, continued from the previous sections:</p> - <p>1) Create a target system as described in <em>System Principles</em> of the first version <c>"A"</c> of <c>ch_rel</c> - from - the <seealso marker="release_structure#ch_rel">Releases</seealso> - chapter. This time <c>sys.config</c> must be included in - the release package. If no configuration is needed, the file - should contain the empty list:</p> - <code type="none"> + + <section> + <title>Example (continued from the previous sections)</title> + <p><em>Step 1)</em> Create a target system as described in + System Principles of the first version <c>"A"</c> + of <c>ch_rel</c> from + <seealso marker="release_structure#ch_rel">Releases</seealso>. + This time <c>sys.config</c> must be included in the release package. + If no configuration is needed, the file is to contain the empty + list:</p> + <code type="none"> [].</code> - <p>2) Start the system as a simple target system. Note that in - reality, it should be started as an embedded system. However, - using <c>erl</c> with the correct boot script and config file is - enough for illustration purposes:</p> - <pre> + <p><em>Step 2)</em> Start the system as a simple target system. In + reality, it is to be started as an embedded system. However, using + <c>erl</c> with the correct boot script and config file is enough for + illustration purposes:</p> + <pre> % <input>cd $ROOT</input> % <input>bin/erl -boot $ROOT/releases/A/start -config $ROOT/releases/A/sys</input> ...</pre> <p><c>$ROOT</c> is the installation directory of the target system.</p> - <p>3) In another Erlang shell, generate start scripts and create a - release package for the new version <c>"B"</c>. Remember to - include (a possible updated) <c>sys.config</c> and - the <c>relup</c> file, see <seealso marker="#relup">Release Upgrade File</seealso> above.</p> - <pre> + <p><em>Step 3)</em> In another Erlang shell, generate start scripts and + create a release package for the new version <c>"B"</c>. Remember to + include (a possible updated) <c>sys.config</c> and the <c>relup</c> file, + see <seealso marker="#relup">Release Upgrade File</seealso>.</p> + <pre> 1> <input>systools:make_script("ch_rel-2").</input> ok 2> <input>systools:make_tar("ch_rel-2").</input> ok</pre> - <p>The new release package now contains version "2" of <c>ch_app</c> - and the <c>relup</c> file as well:</p> - <code type="none"> + <p>The new release package now also contains version "2" of <c>ch_app</c> + and the <c>relup</c> file:</p> + <code type="none"> % tar tf ch_rel-2.tar lib/kernel-2.9/ebin/kernel.app lib/kernel-2.9/ebin/application.beam @@ -633,28 +645,30 @@ releases/B/relup releases/B/sys.config releases/B/ch_rel-2.rel releases/ch_rel-2.rel</code> - <p>4) Copy the release package <c>ch_rel-2.tar.gz</c> to - the <c>$ROOT/releases</c> directory.</p> - <p>5) In the running target system, unpack the release package:</p> - <pre> + <p><em>Step 4)</em> Copy the release package <c>ch_rel-2.tar.gz</c> + to the <c>$ROOT/releases</c> directory.</p> + <p><em>Step 5)</em> In the running target system, unpack the release + package:</p> + <pre> 1> <input>release_handler:unpack_release("ch_rel-2").</input> {ok,"B"}</pre> <p>The new application version <c>ch_app-2</c> is installed under <c>$ROOT/lib</c> next to <c>ch_app-1</c>. The <c>kernel</c>, - <c>stdlib</c> and <c>sasl</c> directories are not affected, as + <c>stdlib</c>, and <c>sasl</c> directories are not affected, as they have not changed.</p> <p>Under <c>$ROOT/releases</c>, a new directory <c>B</c> is created, containing <c>ch_rel-2.rel</c>, <c>start.boot</c>, - <c>sys.config</c> and <c>relup</c>.</p> - <p>6) Check if the function <c>ch3:available/0</c> is available:</p> - <pre> + <c>sys.config</c>, and <c>relup</c>.</p> + <p><em>Step 6)</em> Check if the function <c>ch3:available/0</c> is + available:</p> + <pre> 2> <input>ch3:available().</input> ** exception error: undefined function ch3:available/0</pre> - <p>7) Install the new release. The instructions in + <p><em>Step 7)</em> Install the new release. The instructions in <c>$ROOT/releases/B/relup</c> are executed one by one, resulting in the new version of <c>ch3</c> being loaded. The function <c>ch3:available/0</c> is now available:</p> - <pre> + <pre> 3> <input>release_handler:install_release("B").</input> {ok,"A",[]} 4> <input>ch3:available().</input> @@ -663,15 +677,16 @@ releases/ch_rel-2.rel</code> ".../lib/ch_app-2/ebin/ch3.beam" 6> <input>code:which(ch_sup).</input> ".../lib/ch_app-1/ebin/ch_sup.beam"</pre> - <p>Note that processes in <c>ch_app</c> for which code have not - been updated, for example the supervisor, are still evaluating + <p>Processes in <c>ch_app</c> for which code have not + been updated, for example, the supervisor, are still evaluating code from <c>ch_app-1</c>.</p> - <p>8) If the target system is now rebooted, it will use version "A" - again. The "B" version must be made permanent, in order to be + <p><em>Step 8)</em> If the target system is now rebooted, it uses + version "A" again. The "B" version must be made permanent, to be used when the system is rebooted.</p> - <pre> + <pre> 7> <input>release_handler:make_permanent("B").</input> ok</pre> + </section> </section> <section> @@ -681,37 +696,40 @@ ok</pre> specifications are automatically updated for all loaded applications.</p> <note> - <p>The information about the new application specifications are + <p>The information about the new application specifications is fetched from the boot script included in the release package. - It is therefore important that the boot script is generated from + Thus, it is important that the boot script is generated from the same <c>.rel</c> file as is used to build the release package itself.</p> </note> <p>Specifically, the application configuration parameters are automatically updated according to (in increasing priority order):</p> - <list type="ordered"> + <list type="bulleted"> <item>The data in the boot script, fetched from the new application resource file <c>App.app</c></item> <item>The new <c>sys.config</c></item> - <item>Command line arguments <c>-App Par Val</c></item> + <item>Command-line arguments <c>-App Par Val</c></item> </list> <p>This means that parameter values set in the other system - configuration files, as well as values set using - <c>application:set_env/3</c>, are disregarded.</p> + configuration files and values set using + <c>application:set_env/3</c> are disregarded.</p> <p>When an installed release is made permanent, the system process <c>init</c> is set to point out the new <c>sys.config</c>.</p> - <p>After the installation, the application controller will compare + <p>After the installation, the application controller compares the old and new configuration parameters for all running applications and call the callback function:</p> <code type="none"> Module:config_change(Changed, New, Removed)</code> - <p><c>Module</c> is the application callback module as defined by - the <c>mod</c> key in the <c>.app</c> file. <c>Changed</c> and - <c>New</c> are lists of <c>{Par,Val}</c> for all changed and - added configuration parameters, respectively. <c>Removed</c> is - a list of all parameters <c>Par</c> that have been removed.</p> - <p>The function is optional and may be omitted when implementing an + <list type="bulleted"> + <item><c>Module</c> is the application callback module as defined + by the <c>mod</c> key in the <c>.app</c> file.</item> + <item><c>Changed</c> and <c>New</c> are lists of <c>{Par,Val}</c> for + all changed and added configuration parameters, respectively.</item> + <item><c>Removed</c> is a list of all parameters <c>Par</c> that have + been removed.</item> + </list> + <p>The function is optional and can be omitted when implementing an application callback module.</p> </section> </chapter> diff --git a/system/doc/design_principles/release_structure.xml b/system/doc/design_principles/release_structure.xml index cec33f42e3..aa04f5e6a3 100644 --- a/system/doc/design_principles/release_structure.xml +++ b/system/doc/design_principles/release_structure.xml @@ -28,21 +28,23 @@ <rev></rev> <file>release_structure.xml</file> </header> - <p>This chapter should be read in conjuction with <c>rel(4)</c>, - <c>systools(3)</c> and <c>script(4)</c>.</p> + <marker id="releases section"></marker> + <p>This section is to be read with the <c>rel(4)</c>, <c>systools(3)</c>, + and <c>script(4)</c> manual pages in SASL.</p> <section> <title>Release Concept</title> - <p>When we have written one or more applications, we might want to - create a complete system consisting of these applications and a + <p>When you have written one or more applications, you might want + to create a complete system with these applications and a subset of the Erlang/OTP applications. This is called a <em>release</em>.</p> - <p>To do this, we create a <seealso marker="#res_file">release resource file</seealso> which defines which applications - are included in the release.</p> + <p>To do this, create a + <seealso marker="#res_file">release resource file</seealso> that + defines which applications are included in the release.</p> <p>The release resource file is used to generate <seealso marker="#boot">boot scripts</seealso> and <seealso marker="#pack">release packages</seealso>. A system - which is transfered to and installed at another site is called a + that is transferred to and installed at another site is called a <em>target system</em>. How to use a release package to create a target system is described in System Principles.</p> </section> @@ -50,29 +52,30 @@ <section> <marker id="res_file"></marker> <title>Release Resource File</title> - <p>To define a release, we create a <em>release resource file</em>, - or in short <c>.rel</c> file, where we specify the name and - version of the release, which ERTS version it is based on, and - which applications it consists of:</p> + <p>To define a release, create a <em>release resource file</em>, + or in short a <c>.rel</c> file. In the file, specify the name and + version of the release, which ERTS version it is based on, + and which applications it consists of:</p> <code type="none"> {release, {Name,Vsn}, {erts, EVsn}, [{Application1, AppVsn1}, ... {ApplicationN, AppVsnN}]}.</code> + <p><c>Name</c>, <c>Vsn</c>, <c>EVsn</c>, and <c>AppVsn</c> are + strings.</p> <p>The file must be named <c>Rel.rel</c>, where <c>Rel</c> is a unique name.</p> - <p><c>Name</c>, <c>Vsn</c> and <c>EVsn</c> are strings.</p> - <p>Each <c>Application</c> (atom) and <c>AppVsn</c> (string) is + <p>Each <c>Application</c> (atom) and <c>AppVsn</c> is the name and version of an application included in the release. - Note that the minimal release based on Erlang/OTP consists of - the <c>kernel</c> and <c>stdlib</c> applications, so these + The minimal release based on Erlang/OTP consists of + the Kernel and STDLIB applications, so these applications must be included in the list.</p> <p>If the release is to be upgraded, it must also include - the <c>sasl</c> application.</p> + the SASL application.</p> <marker id="ch_rel"></marker> - <p>Example: We want to make a release of <c>ch_app</c> from - the <seealso marker="applications#ch_app">Applications</seealso> - chapter. It has the following <c>.app</c> file:</p> + <p><em>Example: </em> A release of <c>ch_app</c> from + <seealso marker="applications#ch_app">Applications</seealso> + has the following <c>.app</c> file:</p> <code type="none"> {application, ch_app, [{description, "Channel allocator"}, @@ -83,8 +86,8 @@ {mod, {ch_app,[]}} ]}.</code> <p>The <c>.rel</c> file must also contain <c>kernel</c>, - <c>stdlib</c> and <c>sasl</c>, since these applications are - required by <c>ch_app</c>. We call the file <c>ch_rel-1.rel</c>:</p> + <c>stdlib</c>, and <c>sasl</c>, as these applications are required + by <c>ch_app</c>. The file is called <c>ch_rel-1.rel</c>:</p> <code type="none"> {release, {"ch_rel", "A"}, @@ -99,24 +102,28 @@ <section> <marker id="boot"></marker> <title>Generating Boot Scripts</title> - <p>There are tools in the SASL module <c>systools</c> available to - build and check releases. The functions read the <c>.rel</c> and + <p><c>systools</c> in the SASL application includes tools to + build and check releases. The functions read the <c>rel</c> and <c>.app</c> files and performs syntax and dependency checks. - The function <c>systools:make_script/1,2</c> is used to generate - a boot script (see System Principles).</p> + The <c>systools:make_script/1,2</c> function is used to generate + a boot script (see System Principles):</p> <pre> 1> <input>systools:make_script("ch_rel-1", [local]).</input> ok</pre> - <p>This creates a boot script, both the readable version - <c>ch_rel-1.script</c> and the binary version used by the runtime - system, <c>ch_rel-1.boot</c>. <c>"ch_rel-1"</c> is the name of - the <c>.rel</c> file, minus the extension. <c>local</c> is an - option that means that the directories where the applications are - found are used in the boot script, instead of <c>$ROOT/lib</c>. - (<c>$ROOT</c> is the root directory of the installed release.) - This is a useful way to test a generated boot script locally.</p> + <p>This creates a boot script, both the readable version, + <c>ch_rel-1.script</c>, and the binary version, <c>ch_rel-1.boot</c>, + used by the runtime system.</p> + <list type="bulleted"> + <item><c>"ch_rel-1"</c> is the name of the <c>.rel</c> file, + minus the extension.</item> + <item><c>local</c> is an option that means that the directories + where the applications are found are used in the boot script, + instead of <c>$ROOT/lib</c> (<c>$ROOT</c> is the root directory + of the installed release).</item> + </list> + <p> This is a useful way to test a generated boot script locally.</p> <p>When starting Erlang/OTP using the boot script, all applications - from the <c>.rel</c> file are automatically loaded and started:</p> + from the <c>.rel</c> file are automatically loaded and started:</p> <pre> % <input>erl -boot ch_rel-1</input> Erlang (BEAM) emulator version 5.3 @@ -147,18 +154,24 @@ Eshell V5.3 (abort with ^G) <section> <marker id="pack"></marker> <title>Creating a Release Package</title> - <p>There is a function <c>systools:make_tar/1,2</c> which takes - a <c>.rel</c> file as input and creates a zipped tar-file with - the code for the specified applications, a <em>release package</em>.</p> + <p>The <c>systools:make_tar/1,2</c> function takes a <c>.rel</c> file + as input and creates a zipped tar file with the code for the specified + applications, a <em>release package</em>:</p> <pre> 1> <input>systools:make_script("ch_rel-1").</input> ok 2> <input>systools:make_tar("ch_rel-1").</input> ok</pre> - <p>The release package by default contains the <c>.app</c> files and - object code for all applications, structured according to - the <seealso marker="applications#app_dir">application directory structure</seealso>, the binary boot script renamed to - <c>start.boot</c>, and the <c>.rel</c> file.</p> + <p>The release package by default contains:</p> + <list type="bulleted"> + <item>The <c>.app</c> files</item> + <item>The <c>.rel</c> file</item> + <item>The object code for all applications, structured according + to the + <seealso marker="applications#app_dir">application directory + structure</seealso></item> + <item>The binary boot script renamed to <c>start.boot</c></item> + </list> <pre> % <input>tar tf ch_rel-1.tar</input> lib/kernel-2.9/ebin/kernel.app @@ -177,40 +190,39 @@ lib/ch_app-1/ebin/ch3.beam releases/A/start.boot releases/A/ch_rel-1.rel releases/ch_rel-1.rel</pre> - <p>Note that a new boot script was generated, without + <p>A new boot script was generated, without the <c>local</c> option set, before the release package was made. In the release package, all application directories are placed - under <c>lib</c>. Also, we do not know where the release package - will be installed, so we do not want any hardcoded absolute paths - in the boot script here.</p> + under <c>lib</c>. You do not know where the release package + will be installed, so no hard-coded absolute paths are allowed.</p> <p>The release resource file <c>mysystem.rel</c> is duplicated in the tar file. Originally, this file was only stored in - the <c>releases</c> directory in order to make it possible for + the <c>releases</c> directory to make it possible for the <c>release_handler</c> to extract this file separately. After unpacking the tar file, <c>release_handler</c> would automatically copy the file to <c>releases/FIRST</c>. However, sometimes the tar file is - unpacked without involving the <c>release_handler</c> (e.g. when - unpacking the first target system) and therefore the file is now - instead duplicated in the tar file so no manual copying is - necessary.</p> + unpacked without involving the <c>release_handler</c> (for + example, when unpacking the first target system) and the file + is therefore now instead duplicated in the tar file so no manual + copying is necessary.</p> <p>If a <c>relup</c> file and/or a system configuration file called - <c>sys.config</c> is found, these files are included in - the release package as well. See + <c>sys.config</c> is found, these files are also included in + the release package. See <seealso marker="release_handling#req">Release Handling</seealso>.</p> <p>Options can be set to make the release package include source code and the ERTS binary as well.</p> - <p>Refer to System Principles for how to install the first target - system, using a release package, and to - <seealso marker="release_handling">Release Handling</seealso> for - how to install a new release package in an existing system.</p> + <p>For information on how to install the first target system, using + a release package, see System Principles. For information + on how to install a new release package in an existing system, see + <seealso marker="release_handling">Release Handling</seealso>.</p> </section> <section> <marker id="reldir"></marker> <title>Directory Structure</title> - <p>Directory structure for the code installed by the release handler - from a release package:</p> + <p>The directory structure for the code installed by the release handler + from a release package is as follows:</p> <code type="none"> $ROOT/lib/App1-AVsn1/ebin /priv @@ -222,24 +234,18 @@ $ROOT/lib/App1-AVsn1/ebin /erts-EVsn/bin /releases/Vsn /bin</code> - <taglist> - <tag><c>lib</c></tag> - <item>Application directories.</item> - <tag><c>erts-EVsn/bin</c></tag> - <item>Erlang runtime system executables.</item> - <tag><c>releases/Vsn</c></tag> - <item><c>.rel</c> file and boot script <c>start.boot</c>. <br></br> - - If present in the release package, <br></br> -<c>relup</c> and/or <c>sys.config</c>.</item> - <tag><c>bin</c></tag> - <item>Top level Erlang runtime system executables.</item> - </taglist> - <p>Applications are not required to be located under the - <c>$ROOT/lib</c> directory. Accordingly, several installation - directories may exist which contain different parts of a - system. For example, the previous example could be extended as - follows:</p> + <list type="bulleted"> + <item><c>lib</c> - Application directories</item> + <item><c>erts-EVsn/bin</c> - Erlang runtime system executables</item> + <item><c>releases/Vsn</c> - <c>.rel</c> file and boot script + <c>start.boot</c>; if present in the release package, <c>relup</c> + and/or <c>sys.config</c></item> + <item><c>bin</c> - Top-level Erlang runtime system executables</item> + </list> + <p>Applications are not required to be located under directory + <c>$ROOT/lib</c>. Several installation directories, which contain + different parts of a system, can thus exist. + For example, the previous example can be extended as follows:</p> <pre> $SECOND_ROOT/.../SApp1-SAVsn1/ebin /priv @@ -256,24 +262,24 @@ $THIRD_ROOT/TApp1-TAVsn1/ebin ... /TAppN-TAVsnN/ebin /priv</pre> - <p>The <c>$SECOND_ROOT</c> and <c>$THIRD_ROOT</c> are introduced as + <p><c>$SECOND_ROOT</c> and <c>$THIRD_ROOT</c> are introduced as <c>variables</c> in the call to the <c>systools:make_script/2</c> function.</p> <section> <title>Disk-Less and/or Read-Only Clients</title> - <p>If a complete system consists of some disk-less and/or - read-only client nodes, a <c>clients</c> directory should be - added to the <c>$ROOT</c> directory. By a read-only node we - mean a node with a read-only file system.</p> - <p>The <c>clients</c> directory should have one sub-directory + <p>If a complete system consists of disk-less and/or + read-only client nodes, a <c>clients</c> directory is to be + added to the <c>$ROOT</c> directory. A read-only node is + a node with a read-only file system.</p> + <p>The <c>clients</c> directory is to have one subdirectory per supported client node. The name of each client directory - should be the name of the corresponding client node. As a - minimum, each client directory should contain the <c>bin</c> and - <c>releases</c> sub-directories. These directories are used to + is to be the name of the corresponding client node. As a + minimum, each client directory is to contain the <c>bin</c> and + <c>releases</c> subdirectories. These directories are used to store information about installed releases and to appoint the - current release to the client. Accordingly, the <c>$ROOT</c> - directory contains the following:</p> + current release to the client. The <c>$ROOT</c> + directory thus contains the following:</p> <code type="none"> $ROOT/... /clients/ClientName1/bin @@ -283,14 +289,14 @@ $ROOT/... ... /ClientNameN/bin /releases/Vsn</code> - <p>This structure should be used if all clients are running + <p>This structure is to be used if all clients are running the same type of Erlang machine. If there are clients running different types of Erlang machines, or on different operating - systems, the <c>clients</c> directory could be divided into one - sub-directory per type of Erlang machine. Alternatively, you - can set up one <c>$ROOT</c> per type of machine. For each + systems, the <c>clients</c> directory can be divided into one + subdirectory per type of Erlang machine. Alternatively, one + <c>$ROOT</c> can be set up per type of machine. For each type, some of the directories specified for the <c>$ROOT</c> - directory should be included:</p> + directory are to be included:</p> <code type="none"> $ROOT/... /clients/Type1/lib diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml index e849388a38..aceb5ba99e 100644 --- a/system/doc/design_principles/spec_proc.xml +++ b/system/doc/design_principles/spec_proc.xml @@ -21,30 +21,31 @@ </legalnotice> - <title>Sys and Proc_Lib</title> + <title>sys and proc_lib</title> <prepared></prepared> <docno></docno> <date></date> <rev></rev> <file>spec_proc.xml</file> </header> - <p>The module <c>sys</c> contains functions for simple debugging of - processes implemented using behaviours.</p> - <p>There are also functions that, together with functions in - the module <c>proc_lib</c>, can be used to implement a - <em>special process</em>, a process which comply to the OTP design - principles without making use of a standard behaviour. They can - also be used to implement user defined (non-standard) behaviours.</p> - <p>Both <c>sys</c> and <c>proc_lib</c> belong to the STDLIB - application.</p> + <marker id="sys and proc_lib"></marker> + <p>The <c>sys</c> module has functions for simple debugging of + processes implemented using behaviours. It also has functions that, + together with functions in the <c>proc_lib</c> module, can be used + to implement a <em>special process</em> that complies to the OTP + design principles without using a standard behaviour. These + functions can also be used to implement user-defined (non-standard) + behaviours.</p> + <p>Both <c>sys</c> and <c>proc_lib</c> belong to the STDLIB + application.</p> <section> <title>Simple Debugging</title> - <p>The module <c>sys</c> contains some functions for simple debugging - of processes implemented using behaviours. We use the - <c>code_lock</c> example from - the <seealso marker="fsm#ex">gen_fsm</seealso> chapter to - illustrate this:</p> + <p>The <c>sys</c> module has functions for simple debugging of + processes implemented using behaviours. The <c>code_lock</c> + example from + <seealso marker="fsm#ex">gen_fsm Behaviour</seealso> + is used to illustrate this:</p> <pre> % <input>erl</input> Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] @@ -102,16 +103,18 @@ ok <section> <title>Special Processes</title> - <p>This section describes how to write a process which comply to - the OTP design principles, without making use of a standard - behaviour. Such a process should:</p> + <p>This section describes how to write a process that complies to + the OTP design principles, without using a standard behaviour. + Such a process is to:</p> <list type="bulleted"> - <item>be started in a way that makes the process fit into a - supervision tree,</item> - <item>support the <c>sys</c> <seealso marker="#debug">debug facilities</seealso>, and</item> - <item>take care of <seealso marker="#msg">system messages</seealso>.</item> + <item>Be started in a way that makes the process fit into a + supervision tree</item> + <item>Support the <c>sys</c> + <seealso marker="#debug">debug facilities</seealso></item> + <item>Take care of + <seealso marker="#msg">system messages</seealso>.</item> </list> - <p>System messages are messages with special meaning, used in + <p>System messages are messages with a special meaning, used in the supervision tree. Typical system messages are requests for trace output, and requests to suspend or resume process execution (used during release handling). Processes implemented using @@ -120,9 +123,9 @@ ok <section> <title>Example</title> <p>The simple server from - the <seealso marker="des_princ#ch1">Overview</seealso> chapter, - implemented using <c>sys</c> and <c>proc_lib</c> so it fits into - a supervision tree:</p> + <seealso marker="des_princ#ch1">Overview</seealso>, + implemented using <c>sys</c> and <c>proc_lib</c> so it fits into a + supervision tree:</p> <marker id="ex"></marker> <pre> -module(ch4). @@ -190,8 +193,8 @@ system_replace_state(StateFun, Chs) -> write_debug(Dev, Event, Name) -> io:format(Dev, "~p event = ~p~n", [Name, Event]).</pre> - <p>Example on how the simple debugging functions in <c>sys</c> can - be used for <c>ch4</c> as well:</p> + <p>Example on how the simple debugging functions in the <c>sys</c> + module can also be used for <c>ch4</c>:</p> <pre> % <input>erl</input> Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] @@ -230,31 +233,32 @@ ok <section> <title>Starting the Process</title> - <p>A function in the <c>proc_lib</c> module should be used to - start the process. There are several possible functions, for - example <c>spawn_link/3,4</c> for asynchronous start and + <p>A function in the <c>proc_lib</c> module is to be used to + start the process. Several functions are available, for + example, <c>spawn_link/3,4</c> for asynchronous start and <c>start_link/3,4,5</c> for synchronous start.</p> - <p>A process started using one of these functions will store - information that is needed for a process in a supervision tree, - for example about the ancestors and initial call.</p> - <p>Also, if the process terminates with another reason than - <c>normal</c> or <c>shutdown</c>, a crash report (see SASL - User's Guide) is generated.</p> - <p>In the example, synchronous start is used. The process is - started by calling <c>ch4:start_link()</c>:</p> + <p>A process started using one of these functions stores + information (for example, about the ancestors and initial call) + that is needed for a process in a supervision tree.</p> + <p>If the process terminates with another reason than + <c>normal</c> or <c>shutdown</c>, a crash report is generated. + For more information about the crash report, see the SASL + User's Guide.</p> + <p>In the example, synchronous start is used. The process + starts by calling <c>ch4:start_link()</c>:</p> <code type="none"> start_link() -> proc_lib:start_link(ch4, init, [self()]).</code> <p><c>ch4:start_link</c> calls the function <c>proc_lib:start_link</c>. This function takes a module name, - a function name and an argument list as arguments and spawns + a function name, and an argument list as arguments, spawns, and links to a new process. The new process starts by executing - the given function, in this case <c>ch4:init(Pid)</c>, where - <c>Pid</c> is the pid (<c>self()</c>) of the first process, that - is the parent process.</p> - <p>In <c>init</c>, all initialization including name registration - is done. The new process must also acknowledge that it has been - started to the parent:</p> + the given function, here <c>ch4:init(Pid)</c>, where + <c>Pid</c> is the pid (<c>self()</c>) of the first process, + which is the parent process.</p> + <p>All initialization, including name registration, is done in + <c>init</c>. The new process must also acknowledge that it has + been started to the parent:</p> <code type="none"> init(Parent) -> ... @@ -267,8 +271,8 @@ init(Parent) -> <section> <marker id="debug"></marker> <title>Debugging</title> - <p>To support the debug facilites in <c>sys</c>, we need a - <em>debug structure</em>, a term <c>Deb</c> which is + <p>To support the debug facilites in <c>sys</c>, a + <em>debug structure</em> is needed. The <c>Deb</c> term is initialized using <c>sys:debug_options/1</c>:</p> <code type="none"> init(Parent) -> @@ -278,50 +282,41 @@ init(Parent) -> loop(Chs, Parent, Deb).</code> <p><c>sys:debug_options/1</c> takes a list of options as argument. Here the list is empty, which means no debugging is enabled - initially. See <c>sys(3)</c> for information about possible - options.</p> - <p>Then for each <em>system event</em> that we want to be logged - or traced, the following function should be called.</p> + initially. For information about the possible options, see the + <c>sys(3)</c> manual page in STDLIB.</p> + <p>Then, for each <em>system event</em> to be logged + or traced, the following function is to be called.</p> <code type="none"> sys:handle_debug(Deb, Func, Info, Event) => Deb1</code> +<p>Here:</p> <list type="bulleted"> - <item> - <p><c>Deb</c> is the debug structure.</p> - </item> - <item> - <p><c>Func</c> is a fun specifying - a (user defined) function used to format + <item><c>Deb</c> is the debug structure.</item> + <item><c>Func</c> is a fun specifying + a (user-defined) function used to format trace output. For each system event, the format function is - called as <c>Func(Dev, Event, Info)</c>, where:</p> + called as <c>Func(Dev, Event, Info)</c>, where: <list type="bulleted"> - <item> - <p><c>Dev</c> is the IO device to which the output should - be printed. See <c>io(3)</c>.</p> - </item> - <item> - <p><c>Event</c> and <c>Info</c> are passed as-is from - <c>handle_debug</c>.</p> - </item> + <item><c>Dev</c> is the I/O device to which the output is to + be printed. See the <c>io(3)</c> manual page in + STDLIB.</item> + <item><c>Event</c> and <c>Info</c> are passed as is from + <c>handle_debug</c>.</item> </list> </item> - <item> - <p><c>Info</c> is used to pass additional information to - <c>Func</c>, it can be any term and is passed as-is.</p> - </item> - <item> - <p><c>Event</c> is the system event. It is up to the user to - define what a system event is and how it should be - represented, but typically at least incoming and outgoing + <item><c>Info</c> is used to pass more information to + <c>Func</c>. It can be any term and is passed as is.</item> + <item><c>Event</c> is the system event. It is up to the user to + define what a system event is and how it is to be + represented. Typically at least incoming and outgoing messages are considered system events and represented by the tuples <c>{in,Msg[,From]}</c> and <c>{out,Msg,To}</c>, - respectively.</p> - </item> + respectively.</item> </list> <p><c>handle_debug</c> returns an updated debug structure <c>Deb1</c>.</p> <p>In the example, <c>handle_debug</c> is called for each incoming and outgoing message. The format function <c>Func</c> is - the function <c>ch4:write_debug/3</c> which prints the message + the function <c>ch4:write_debug/3</c>, which prints the message using <c>io:format/3</c>.</p> <code type="none"> loop(Chs, Parent, Deb) -> @@ -354,22 +349,22 @@ write_debug(Dev, Event, Name) -> {system, From, Request}</code> <p>The content and meaning of these messages do not need to be interpreted by the process. Instead the following function - should be called:</p> + is to be called:</p> <code type="none"> sys:handle_system_msg(Request, From, Parent, Module, Deb, State)</code> - <p>This function does not return. It will handle the system - message and then call:</p> + <p>This function does not return. It handles the system + message and then either calls the following if process execution is + to continue:</p> <code type="none"> Module:system_continue(Parent, Deb, State)</code> - <p>if process execution should continue, or:</p> + <p>Or calls the following if the process is to terminate:</p> <code type="none"> Module:system_terminate(Reason, Parent, Deb, State)</code> - <p>if the process should terminate. Note that a process in a - supervision tree is expected to terminate with the same reason as - its parent.</p> + <p>A process in a supervision tree is expected to terminate with + the same reason as its parent.</p> <list type="bulleted"> - <item><c>Request</c> and <c>From</c> should be passed as-is from - the system message to the call to <c>handle_system_msg</c>.</item> + <item><c>Request</c> and <c>From</c> are to be passed as is from + the system message to the call to <c>handle_system_msg</c>.</item> <item><c>Parent</c> is the pid of the parent.</item> <item><c>Module</c> is the name of the module.</item> <item><c>Deb</c> is the debug structure.</item> @@ -377,10 +372,12 @@ Module:system_terminate(Reason, Parent, Deb, State)</code> is passed to <c>system_continue</c>/<c>system_terminate</c>/ <c>system_get_state</c>/<c>system_replace_state</c>.</item> </list> - <p>If the process should return its state <c>handle_system_msg</c> will call:</p> + <p>If the process is to return its state, <c>handle_system_msg</c> + calls:</p> <code type="none"> Module:system_get_state(State)</code> - <p>or if the process should replace its state using the fun <c>StateFun</c>:</p> + <p>If the process is to replace its state using the fun <c>StateFun</c>, + <c>handle_system_msg</c> calls:</p> <code type="none"> Module:system_replace_state(StateFun, State)</code> <p>In the example:</p> @@ -407,9 +404,9 @@ system_replace_state(StateFun, Chs) -> NChs = StateFun(Chs), {ok, NChs, NChs}. </code> - <p>If the special process is set to trap exits, note that if - the parent process terminates, the expected behavior is to - terminate with the same reason:</p> + <p>If the special process is set to trap exits and if the parent + process terminates, the expected behavior is to terminate with + the same reason:</p> <code type="none"> init(...) -> ..., @@ -431,28 +428,23 @@ loop(...) -> <section> <title>User-Defined Behaviours</title> - <p><marker id="behaviours"/>To implement a user-defined behaviour, - write code similar to code for a special process but calling - functions in a callback module for handling specific tasks.</p> - <p>If it is desired that the compiler should warn for missing - callback functions, as it does for the OTP behaviours, add - <c>-callback</c> attributes in the behaviour module to describe - the expected callback functions:</p> - + write code similar to + code for a special process, but call functions in a callback + module for handling specific tasks.</p> + <p>If the compiler is to warn for missing callback functions, as it + does for the OTP behaviours, add <c>-callback</c> attributes in the + behaviour module to describe the expected callbacks:</p> <code type="none"> -callback Name1(Arg1_1, Arg1_2, ..., Arg1_N1) -> Res1. -callback Name2(Arg2_1, Arg2_2, ..., Arg2_N2) -> Res2. ... -callback NameM(ArgM_1, ArgM_2, ..., ArgM_NM) -> ResM.</code> - - <p>where each <c>Name</c> is the name of a callback function and - <c>Arg</c> and <c>Res</c> are types as described in - Specifications for functions in <seealso - marker="../reference_manual/typespec">Types and Function - Specifications</seealso>. The whole syntax of the - <c>-spec</c> attribute is supported by <c>-callback</c> - attribute.</p> + <p><c>NameX</c> are the names of the expected callbacks. + <c>ArgX_Y</c> and <c>ResX</c> are types as they are described in + <seealso marker="../reference_manual/typespec">Types and + Function Specifications</seealso>. The whole syntax of the <c>-spec</c> + attribute is supported by the <c>-callback</c> attribute.</p> <p>Callback functions that are optional for the user of the behaviour to implement are specified by use of the <c>-optional_callbacks</c> attribute:</p> @@ -487,10 +479,10 @@ behaviour_info(callbacks) -> generated by the compiler using the <c>-callback</c> attributes.</p> <p>When the compiler encounters the module attribute - <c>-behaviour(Behaviour).</c> in a module <c>Mod</c>, it will - call <c>Behaviour:behaviour_info(callbacks)</c> and compare the + <c>-behaviour(Behaviour).</c> in a module <c>Mod</c>, it + calls <c>Behaviour:behaviour_info(callbacks)</c> and compares the result with the set of functions actually exported from - <c>Mod</c>, and issue a warning if any callback function is + <c>Mod</c>, and issues a warning if any callback function is missing.</p> <p>Example:</p> <code type="none"> diff --git a/system/doc/design_principles/sup_princ.xml b/system/doc/design_principles/sup_princ.xml index 3d7b53e339..9583ca5c55 100644 --- a/system/doc/design_principles/sup_princ.xml +++ b/system/doc/design_principles/sup_princ.xml @@ -28,15 +28,16 @@ <rev></rev> <file>sup_princ.xml</file> </header> - <p>This section should be read in conjunction with - <seealso marker="stdlib:supervisor">supervisor(3)</seealso>, where - all details about the supervisor behaviour are described.</p> + <p>This section should be read with the + <seealso marker="stdlib:supervisor">supervisor(3)</seealso> manual page + in STDLIB, where all details about the supervisor + behaviour is given.</p> <section> <title>Supervision Principles</title> <p>A supervisor is responsible for starting, stopping, and monitoring its child processes. The basic idea of a supervisor is - that it shall keep its child processes alive by restarting them + that it is to keep its child processes alive by restarting them when necessary.</p> <p>Which child processes to start and monitor is specified by a list of <seealso marker="#spec">child specifications</seealso>. @@ -47,8 +48,8 @@ <section> <title>Example</title> <p>The callback module for a supervisor starting the server from - the <seealso marker="gen_server_concepts#ex">gen_server chapter</seealso> - could look like this:</p> + <seealso marker="gen_server_concepts#ex">gen_server Behaviour</seealso> + can look as follows:</p> <marker id="ex"></marker> <code type="none"> -module(ch_sup). @@ -79,6 +80,7 @@ init(_Args) -> <section> <title>Supervisor Flags</title> + <marker id="flags"/> <p>This is the type definition for the supervisor flags:</p> <code type="none"><![CDATA[ sup_flags() = #{strategy => strategy(), % optional @@ -136,9 +138,9 @@ SupFlags = #{strategy => Strategy, ...}</code> <section> <title>rest_for_one</title> - <p>If a child process terminates, the 'rest' of the child - processes -- i.e. the child processes after the terminated - process in start order -- are terminated. Then the terminated + <p>If a child process terminates, the rest of the child + processes (that is, the child processes after the terminated + process in start order) are terminated. Then the terminated child process and the rest of the child processes are restarted.</p> </section> @@ -162,7 +164,7 @@ SupFlags = #{intensity => MaxR, period => MaxT, ...}</code> <p>If more than <c>MaxR</c> number of restarts occur in the last <c>MaxT</c> seconds, the supervisor terminates all the child processes and then itself.</p> - <p>When the supervisor terminates, the next higher level + <p>When the supervisor terminates, then the next higher-level supervisor takes some action. It either restarts the terminated supervisor or terminates itself.</p> <p>The intention of the restart mechanism is to prevent a situation @@ -176,14 +178,14 @@ SupFlags = #{intensity => MaxR, period => MaxT, ...}</code> <section> <marker id="spec"></marker> <title>Child Specification</title> - <p>This is the type definition for a child specification:</p> + <p>The type definition for a child specification is as follows:</p> <code type="none"><![CDATA[ child_spec() = #{id => child_id(), % mandatory start => mfargs(), % mandatory restart => restart(), % optional shutdown => shutdown(), % optional type => worker(), % optional - modules => modules()} % optional</pre> + modules => modules()} % optional child_id() = term() mfargs() = {M :: module(), F :: atom(), A :: [term()]} modules() = [module()] | dynamic @@ -195,7 +197,7 @@ child_spec() = #{id => child_id(), % mandatory <p><c>id</c> is used to identify the child specification internally by the supervisor.</p> <p>The <c>id</c> key is mandatory.</p> - <p>Note that this identifier on occations has been called + <p>Note that this identifier occasionally has been called "name". As far as possible, the terms "identifier" or "id" are now used but in order to keep backwards compatibility, some occurences of "name" can still be found, for example @@ -205,24 +207,28 @@ child_spec() = #{id => child_id(), % mandatory <p><c>start</c> defines the function call used to start the child process. It is a module-function-arguments tuple used as <c>apply(M, F, A)</c>.</p> - <p>It should be (or result in) a call to - <c>supervisor:start_link</c>, <c>gen_server:start_link</c>, - <c>gen_fsm:start_link</c>, or <c>gen_event:start_link</c>. - (Or a function compliant with these functions, see - <c>supervisor(3)</c> for details.</p> + <p>It is to be (or result in) a call to any of the following:</p> + <list type="bulleted"> + <item><c>supervisor:start_link</c></item> + <item><c>gen_server:start_link</c></item> + <item><c>gen_fsm:start_link</c></item> + <item><c>gen_event:start_link</c></item> + <item>A function compliant with these functions. For details, + see the <c>supervisor(3)</c> manual page.</item> + </list> <p>The <c>start</c> key is mandatory.</p> </item> <item> - <p><c>restart</c> defines when a terminated child process shall + <p><c>restart</c> defines when a terminated child process is to be restarted.</p> <list type="bulleted"> <item>A <c>permanent</c> child process is always restarted.</item> <item>A <c>temporary</c> child process is never restarted - (not even when the supervisor's restart strategy - is <c>rest_for_one</c> or <c>one_for_all</c> and a sibling's + (not even when the supervisor restart strategy + is <c>rest_for_one</c> or <c>one_for_all</c> and a sibling death causes the temporary process to be terminated).</item> <item>A <c>transient</c> child process is restarted only if it - terminates abnormally, i.e. with another exit reason than + terminates abnormally, that is, with another exit reason than <c>normal</c>, <c>shutdown</c>, or <c>{shutdown,Term}</c>.</item> </list> <p>The <c>restart</c> key is optional. If it is not given, the @@ -230,27 +236,27 @@ child_spec() = #{id => child_id(), % mandatory </item> <item> <marker id="shutdown"></marker> - <p><c>shutdown</c> defines how a child process shall be + <p><c>shutdown</c> defines how a child process is to be terminated.</p> <list type="bulleted"> - <item><c>brutal_kill</c> means the child process is + <item><c>brutal_kill</c> means that the child process is unconditionally terminated using <c>exit(Child, kill)</c>.</item> - <item>An integer timeout value means that the supervisor tells + <item>An integer time-out value means that the supervisor tells the child process to terminate by calling <c>exit(Child, shutdown)</c> and then waits for an exit signal back. If no exit signal is received within the specified time, the child process is unconditionally terminated using <c>exit(Child, kill)</c>.</item> - <item>If the child process is another supervisor, it should be + <item>If the child process is another supervisor, it is to be set to <c>infinity</c> to give the subtree enough time to shut down. It is also allowed to set it to <c>infinity</c>, - if the child process is a worker.</item> + if the child process is a worker. See the warning below:</item> </list> <warning> <p>Be careful when setting the shutdown time to <c>infinity</c> when the child process is a worker. Because, in this situation, the termination of the supervision tree depends on the - child process, it must be implemented in a safe way and its cleanup + child process; it must be implemented in a safe way and its cleanup procedure must always return.</p> </warning> <p>The <c>shutdown</c> key is optional. If it is not given, @@ -266,7 +272,7 @@ child_spec() = #{id => child_id(), % mandatory default value <c>worker</c> will be used.</p> </item> <item> - <p><c>modules</c> should be a list with one element + <p><c>modules</c> are to be a list with one element <c>[Module]</c>, where <c>Module</c> is the name of the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is a gen_event, @@ -279,8 +285,8 @@ child_spec() = #{id => child_id(), % mandatory child's start <c>{M,F,A}</c>.</p> </item> </list> - <p>Example: The child specification to start the server <c>ch3</c> - in the example above looks like:</p> + <p><em>Example:</em> The child specification to start the server + <c>ch3</c> in the previous example look as follows:</p> <code type="none"> #{id => ch3, start => {ch3, start_link, []}, @@ -301,11 +307,11 @@ child_spec() = #{id => child_id(), % mandatory start => {gen_event, start_link, [{local, error_man}]}, modules => dynamic}</code> <p>Both server and event manager are registered processes which - can be expected to be accessible at all times, thus they are + can be expected to be always accessible. Thus they are specified to be <c>permanent</c>.</p> <p><c>ch3</c> does not need to do any cleaning up before - termination, thus no shutdown time is needed but - <c>brutal_kill</c> should be sufficient. <c>error_man</c> may + termination. Thus, no shutdown time is needed, but + <c>brutal_kill</c> is sufficient. <c>error_man</c> can need some time for the event handlers to clean up, thus the shutdown time is set to 5000 ms (which is the default value).</p> @@ -320,19 +326,20 @@ child_spec() = #{id => child_id(), % mandatory <section> <marker id="super_tree"></marker> <title>Starting a Supervisor</title> - <p>In the example above, the supervisor is started by calling + <p>In the previous example, the supervisor is started by calling <c>ch_sup:start_link()</c>:</p> <code type="none"> start_link() -> supervisor:start_link(ch_sup, []).</code> - <p><c>ch_sup:start_link</c> calls the function - <c>supervisor:start_link/2</c>. This function spawns and links to - a new process, a supervisor.</p> + <p><c>ch_sup:start_link</c> calls function + <c>supervisor:start_link/2</c>, which spawns and links to a new + process, a supervisor.</p> <list type="bulleted"> <item>The first argument, <c>ch_sup</c>, is the name of - the callback module, that is the module where the <c>init</c> + the callback module, that is, the module where the <c>init</c> callback function is located.</item> - <item>The second argument, [], is a term which is passed as-is to + <item>The second argument, <c>[]</c>, is a term that is passed + as is to the callback function <c>init</c>. Here, <c>init</c> does not need any indata and ignores the argument.</item> </list> @@ -351,26 +358,27 @@ init(_Args) -> shutdown => brutal_kill}], {ok, {SupFlags, ChildSpecs}}.</code> <p>The supervisor then starts all its child processes according to - the given child specifications. In this case there, is one child - process, <c>ch3</c>.</p> - <p>Note that <c>supervisor:start_link</c> is synchronous. It does + the child specifications in the start specification. In this case + there is one child process, <c>ch3</c>.</p> + <p><c>supervisor:start_link</c> is synchronous. It does not return until all child processes have been started.</p> </section> <section> <title>Adding a Child Process</title> - <p>In addition to the static supervision tree, we can also add - dynamic child processes to an existing supervisor with - the following call:</p> + <p>In addition to the static supervision tree, dynamic child + processes can be added to an existing supervisor with the following + call:</p> <code type="none"> supervisor:start_child(Sup, ChildSpec)</code> <p><c>Sup</c> is the pid, or name, of the supervisor. - <c>ChildSpec</c> is a <seealso marker="#spec">child specification</seealso>.</p> + <c>ChildSpec</c> is a + <seealso marker="#spec">child specification</seealso>.</p> <p>Child processes added using <c>start_child/2</c> behave in - the same manner as the other child processes, with the following - important exception: If a supervisor dies and is re-created, then - all child processes which were dynamically added to the supervisor - will be lost.</p> + the same way as the other child processes, with the an important + exception: if a supervisor dies and is recreated, then + all child processes that were dynamically added to the supervisor + are lost.</p> </section> <section> @@ -393,11 +401,12 @@ supervisor:delete_child(Sup, Id)</code> <marker id="simple"/> <section> - <title>Simple-One-For-One Supervisors</title> + <title>Simplified one_for_one Supervisors</title> <p>A supervisor with restart strategy <c>simple_one_for_one</c> is - a simplified one_for_one supervisor, where all child processes are - dynamically added instances of the same child specification.</p> - <p>Example of a callback module for a simple_one_for_one supervisor:</p> + a simplified <c>one_for_one</c> supervisor, where all child + processes are dynamically added instances of the same process.</p> + <p>The following is an example of a callback module for a + <c>simple_one_for_one</c> supervisor:</p> <code type="none"> -module(simple_sup). -behaviour(supervisor). @@ -416,12 +425,12 @@ init(_Args) -> start => {call, start_link, []}, shutdown => brutal_kill}], {ok, {SupFlags, ChildSpecs}}.</code> - <p>When started, the supervisor will not start any child processes. + <p>When started, the supervisor does not start any child processes. Instead, all child processes are added dynamically by calling:</p> <code type="none"> supervisor:start_child(Sup, List)</code> <p><c>Sup</c> is the pid, or name, of the supervisor. - <c>List</c> is an arbitrary list of terms which will be added to + <c>List</c> is an arbitrary list of terms, which are added to the list of arguments specified in the child specification. If the start function is specified as <c>{M, F, A}</c>, the child process is started by calling @@ -429,17 +438,17 @@ supervisor:start_child(Sup, List)</code> <p>For example, adding a child to <c>simple_sup</c> above:</p> <code type="none"> supervisor:start_child(Pid, [id1])</code> - <p>results in the child process being started by calling + <p>The result is that the child process is started by calling <c>apply(call, start_link, []++[id1])</c>, or actually:</p> <code type="none"> call:start_link(id1)</code> - <p>A child under a <c>simple_one_for_one</c> supervisor can be terminated - with</p> + <p>A child under a <c>simple_one_for_one</c> supervisor can be + terminated with the following:</p> <code type="none"> supervisor:terminate_child(Sup, Pid)</code> - <p>where <c>Sup</c> is the pid, or name, of the supervisor and + <p><c>Sup</c> is the pid, or name, of the supervisor and <c>Pid</c> is the pid of the child.</p> - <p>Because a <c>simple_one_for_one</c> supervisor could have many + <p>Because a <c>simple_one_for_one</c> supervisor can have many children, it shuts them all down asynchronously. This means that the children will do their cleanup in parallel and therefore the order in which they are stopped is not defined.</p> @@ -447,11 +456,11 @@ supervisor:terminate_child(Sup, Pid)</code> <section> <title>Stopping</title> - <p>Since the supervisor is part of a supervision tree, it will - automatically be terminated by its supervisor. When asked to - shutdown, it will terminate all child processes in reversed start + <p>Since the supervisor is part of a supervision tree, it is + automatically terminated by its supervisor. When asked to + shut down, it terminates all child processes in reversed start order according to the respective shutdown specifications, and - then terminate itself.</p> + then terminates itself.</p> </section> </chapter> diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml index 51f1b2612c..3513a91e34 100644 --- a/system/doc/efficiency_guide/advanced.xml +++ b/system/doc/efficiency_guide/advanced.xml @@ -18,7 +18,6 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> <title>Advanced</title> @@ -31,175 +30,216 @@ <section> <title>Memory</title> - <p>A good start when programming efficiently is to have knowledge about + <p>A good start when programming efficiently is to know how much memory different data types and operations require. It is implementation-dependent how much memory the Erlang data types and - other items consume, but here are some figures for the - erts-5.2 system (OTP release R9B). (There have been no significant - changes in R13.)</p> + other items consume, but the following table shows some figures for + the <c>erts-5.2</c> system in R9B. There have been no significant + changes in R13.</p> - <p>The unit of measurement is memory words. There exists both a 32-bit - and a 64-bit implementation, and a word is therefore, 4 bytes or + <p>The unit of measurement is memory words. There exists both a + 32-bit and a 64-bit implementation. A word is therefore 4 bytes or 8 bytes, respectively.</p> <table> <row> - <cell align="center" valign="middle">Data type</cell> - <cell align="center" valign="middle">Memory size</cell> + <cell><em>Data Type</em></cell> + <cell><em>Memory Size</em></cell> </row> <row> - <cell align="left" valign="middle">Small integer</cell> - <cell align="left" valign="middle">1 word<br></br> -On 32-bit architectures: -134217729 < i < 134217728 (28 bits)<br></br> -On 64-bit architectures: -576460752303423489 < i < 576460752303423488 (60 bits)</cell> + <cell>Small integer</cell> + <cell>1 word.<br></br> + On 32-bit architectures: -134217729 < i < 134217728 + (28 bits).<br></br> + On 64-bit architectures: -576460752303423489 < i < + 576460752303423488 (60 bits).</cell> </row> <row> - <cell align="left" valign="middle">Big integer</cell> - <cell align="left" valign="middle">3..N words</cell> + <cell>Large integer</cell> + <cell>3..N words.</cell> </row> <row> - <cell align="left" valign="middle">Atom</cell> - <cell align="left" valign="middle">1 word. Note: an atom refers into - an atom table which also consumes memory. + <cell>Atom</cell> + <cell>1 word.<br></br> + An atom refers into an atom table, which also consumes memory. The atom text is stored once for each unique atom in this table. The atom table is <em>not</em> garbage-collected.</cell> </row> <row> - <cell align="left" valign="middle">Float</cell> - <cell align="left" valign="middle">On 32-bit architectures: 4 words <br></br> -On 64-bit architectures: 3 words</cell> + <cell>Float</cell> + <cell>On 32-bit architectures: 4 words.<br></br> + On 64-bit architectures: 3 words.</cell> </row> <row> - <cell align="left" valign="middle">Binary</cell> - <cell align="left" valign="middle">3..6 + data (can be shared)</cell> + <cell>Binary</cell> + <cell>3..6 words + data (can be shared).</cell> </row> <row> - <cell align="left" valign="middle">List</cell> - <cell align="left" valign="middle">1 word + 1 word per element + the size of each element</cell> + <cell>List</cell> + <cell>1 word + 1 word per element + the size of each element.</cell> </row> <row> - <cell align="left" valign="middle">String (is the same as a list of integers)</cell> - <cell align="left" valign="middle">1 word + 2 words per character</cell> + <cell>String (is the same as a list of integers)</cell> + <cell>1 word + 2 words per character.</cell> </row> <row> - <cell align="left" valign="middle">Tuple</cell> - <cell align="left" valign="middle">2 words + the size of each element</cell> + <cell>Tuple</cell> + <cell>2 words + the size of each element.</cell> </row> <row> - <cell align="left" valign="middle">Pid</cell> - <cell align="left" valign="middle">1 word for a process identifier from the current local node, and 5 words for a process identifier from another node. Note: a process identifier refers into a process table and a node table which also consumes memory.</cell> + <cell>Pid</cell> + <cell>1 word for a process identifier from the current local node + + 5 words for a process identifier from another node.<br></br> + A process identifier refers into a process table and a node table, + which also consumes memory.</cell> </row> <row> - <cell align="left" valign="middle">Port</cell> - <cell align="left" valign="middle">1 word for a port identifier from the current local node, and 5 words for a port identifier from another node. Note: a port identifier refers into a port table and a node table which also consumes memory.</cell> + <cell>Port</cell> + <cell>1 word for a port identifier from the current local node + + 5 words for a port identifier from another node.<br></br> + A port identifier refers into a port table and a node table, + which also consumes memory.</cell> </row> <row> - <cell align="left" valign="middle">Reference</cell> - <cell align="left" valign="middle">On 32-bit architectures: 5 words for a reference from the current local node, and 7 words for a reference from another node. <br></br> -On 64-bit architectures: 4 words for a reference from the current local node, and 6 words for a reference from another node. Note: a reference refers into a node table which also consumes memory.</cell> + <cell>Reference</cell> + <cell>On 32-bit architectures: 5 words for a reference from the + current local node + 7 words for a reference from another + node.<br></br> + On 64-bit architectures: 4 words for a reference from the current + local node + 6 words for a reference from another node.<br></br> + A reference refers into a node table, which also consumes + memory.</cell> </row> <row> - <cell align="left" valign="middle">Fun</cell> - <cell align="left" valign="middle">9..13 words + size of environment. Note: a fun refers into a fun table which also consumes memory.</cell> + <cell>Fun</cell> + <cell>9..13 words + the size of environment.<br></br> + A fun refers into a fun table, which also consumes memory.</cell> </row> <row> - <cell align="left" valign="middle">Ets table</cell> - <cell align="left" valign="middle">Initially 768 words + the size of each element (6 words + size of Erlang data). The table will grow when necessary.</cell> + <cell>Ets table</cell> + <cell>Initially 768 words + the size of each element (6 words + + the size of Erlang data). The table grows when necessary.</cell> </row> <row> - <cell align="left" valign="middle">Erlang process</cell> - <cell align="left" valign="middle">327 words when spawned including a heap of 233 words.</cell> + <cell>Erlang process</cell> + <cell>327 words when spawned, including a heap of 233 words.</cell> </row> - <tcaption>Memory size of different data types</tcaption> + <tcaption>Memory Size of Different Data Types</tcaption> </table> </section> <section> - <title>System limits</title> - <p>The Erlang language specification puts no limits on number of processes, - length of atoms etc., but for performance and memory saving reasons, - there will always be limits in a practical implementation of the Erlang - language and execution environment.</p> - <taglist> - <tag><em>Processes</em></tag> - <item> - <p>The maximum number of simultaneously alive Erlang processes is - by default 32768. This limit can be configured at startup, - for more information see the - <seealso marker="erts:erl#max_processes"><c>+P</c></seealso> - command line flag of - <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p> - </item> - <tag><em>Distributed nodes</em></tag> - <item> - <taglist> - <tag>Known nodes</tag> - <item> - <p>A remote node Y has to be known to node X if there exist - any pids, ports, references, or funs (Erlang data types) from Y - on X, or if X and Y are connected. The maximum number of remote - nodes simultaneously/ever known to a node is limited by the - <seealso marker="#atoms">maximum number of atoms</seealso> - available for node names. All data concerning remote nodes, - except for the node name atom, are garbage-collected.</p> - </item> - <tag>Connected nodes</tag> - <item>The maximum number of simultaneously connected nodes is limited by - either the maximum number of simultaneously known remote nodes, - <seealso marker="#ports">the maximum number of (Erlang) ports</seealso> - available, or - <seealso marker="#files_sockets">the maximum number of sockets</seealso> - available.</item> - </taglist> - </item> - <tag><em>Characters in an atom</em></tag> - <item>255</item> - <tag><em>Atoms </em></tag> - <item> <marker id="atoms"></marker> - By default, the maximum number of atoms is 1048576. - This limit can be raised or lowered using the <c>+t</c> option.</item> - <tag><em>Ets-tables</em></tag> - <item>The default is 1400, can be changed with the environment variable <c>ERL_MAX_ETS_TABLES</c>.</item> - <tag><em>Elements in a tuple</em></tag> - <item>The maximum number of elements in a tuple is 67108863 (26 bit unsigned integer). Other factors - such as the available memory can of course make it hard to create a tuple of that size. </item> - <tag><em>Size of binary</em></tag> - <item>In the 32-bit implementation of Erlang, 536870911 bytes is the - largest binary that can be constructed or matched using the bit syntax. - (In the 64-bit implementation, the maximum size is 2305843009213693951 bytes.) - If the limit is exceeded, bit syntax construction will fail with a - <c>system_limit</c> exception, while any attempt to match a binary that is - too large will fail. - This limit is enforced starting with the R11B-4 release; in earlier releases, - operations on too large binaries would in general either fail or give incorrect - results. - In future releases of Erlang/OTP, other operations that create binaries (such as - <c>list_to_binary/1</c>) will probably also enforce the same limit.</item> - <tag><em>Total amount of data allocated by an Erlang node</em></tag> - <item>The Erlang runtime system can use the complete 32 (or 64) bit address space, - but the operating system often limits a single process to use less than that.</item> - <tag><em>Length of a node name</em></tag> - <item>An Erlang node name has the form host@shortname or host@longname. The node name is - used as an atom within the system so the maximum size of 255 holds for the node name too.</item> - <tag><em>Open ports</em></tag> - <item> - <marker id="ports"></marker> - <p>The maximum number of simultaneously open Erlang ports is - often by default 16384. This limit can be configured at startup, - for more information see the - <seealso marker="erts:erl#max_ports"><c>+Q</c></seealso> - command line flag of - <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p> - </item> - <tag><em>Open files, and sockets</em></tag> - <item> <marker id="files_sockets"></marker> + <title>System Limits</title> + <p>The Erlang language specification puts no limits on the number of + processes, length of atoms, and so on. However, for performance and + memory saving reasons, there will always be limits in a practical + implementation of the Erlang language and execution environment.</p> - The maximum number of simultaneously open files and sockets - depend on - <seealso marker="#ports">the maximum number of Erlang ports</seealso> - available, and operating system specific settings and limits.</item> - <tag><em>Number of arguments to a function or fun</em></tag> - <item>255</item> - </taglist> + <table> + <row> + <cell>Processes</cell> + <cell>The maximum number of simultaneously alive Erlang processes + is by default 32,768. This limit can be configured at startup. + For more information, see the + <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> + </row> + <row> + <cell>Known nodes</cell> + <cell>A remote node Y must be known to node X if there exists + any pids, ports, references, or funs (Erlang data types) from Y + on X, or if X and Y are connected. The maximum number of remote + nodes simultaneously/ever known to a node is limited by the + <seealso marker="#atoms">maximum number of atoms</seealso> + available for node names. All data concerning remote nodes, + except for the node name atom, are garbage-collected.</cell> + </row> + <row> + <cell>Connected nodes</cell> + <cell>The maximum number of simultaneously connected nodes is + limited by either the maximum number of simultaneously known + remote nodes, + <seealso marker="#ports">the maximum number of (Erlang) ports</seealso> + available, or + <seealso marker="#files_sockets">the maximum number of sockets</seealso> + available.</cell> + </row> + <row> + <cell>Characters in an atom</cell> + <cell>255.</cell> + </row> + <row> + <cell><marker id="atoms"></marker>Atoms</cell> + <cell>By default, the maximum number of atoms is 1,048,576. This + limit can be raised or lowered using the <c>+t</c> option.</cell> + </row> + <row> + <cell>Ets tables</cell> + <cell>Default is 1400. It can be changed with the environment + variable <c>ERL_MAX_ETS_TABLES</c>.</cell> + </row> + <row> + <cell>Elements in a tuple</cell> + <cell>The maximum number of elements in a tuple is 67,108,863 + (26-bit unsigned integer). Clearly, other factors such as the + available memory can make it difficult to create a tuple of + that size.</cell> + </row> + <row> + <cell>Size of binary</cell> + <cell>In the 32-bit implementation of Erlang, 536,870,911 + bytes is the largest binary that can be constructed or matched + using the bit syntax. In the 64-bit implementation, the maximum + size is 2,305,843,009,213,693,951 bytes. If the limit is + exceeded, bit syntax construction fails with a + <c>system_limit</c> exception, while any attempt to match a + binary that is too large fails. This limit is enforced starting + in R11B-4.<br></br> + In earlier Erlang/OTP releases, operations on too large + binaries in general either fail or give incorrect results. In + future releases, other operations that create binaries (such as + <c>list_to_binary/1</c>) will probably also enforce the same + limit.</cell> + </row> + <row> + <cell>Total amount of data allocated by an Erlang node</cell> + <cell>The Erlang runtime system can use the complete 32-bit + (or 64-bit) address space, but the operating system often + limits a single process to use less than that.</cell> + </row> + <row> + <cell>Length of a node name</cell> + <cell>An Erlang node name has the form host@shortname or + host@longname. The node name is used as an atom within + the system, so the maximum size of 255 holds also for the + node name.</cell> + </row> + <row> + <cell><marker id="ports"></marker>Open ports</cell> + <cell>The maximum number of simultaneously open Erlang ports is + often by default 16,384. This limit can be configured at startup. + For more information, see the + <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> + </row> + <row> + <cell><marker id="files_sockets"></marker>Open files and + sockets</cell> + <cell>The maximum number of simultaneously open files and + sockets depends on + <seealso marker="#ports">the maximum number of Erlang ports</seealso> + available, as well as on operating system-specific settings + and limits.</cell> + </row> + <row> + <cell>Number of arguments to a function or fun</cell> + <cell>255</cell> + </row> + <tcaption>System Limits</tcaption> + </table> </section> </chapter> diff --git a/system/doc/efficiency_guide/binaryhandling.xml b/system/doc/efficiency_guide/binaryhandling.xml index 4ba1378059..0ac1a7ee32 100644 --- a/system/doc/efficiency_guide/binaryhandling.xml +++ b/system/doc/efficiency_guide/binaryhandling.xml @@ -23,7 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>Constructing and matching binaries</title> + <title>Constructing and Matching Binaries</title> <prepared>Bjorn Gustavsson</prepared> <docno></docno> <date>2007-10-12</date> @@ -31,10 +31,10 @@ <file>binaryhandling.xml</file> </header> - <p>In R12B, the most natural way to write binary construction and matching is now + <p>In R12B, the most natural way to construct and match binaries is significantly faster than in earlier releases.</p> - <p>To construct at binary, you can simply write</p> + <p>To construct a binary, you can simply write as follows:</p> <p><em>DO</em> (in R12B) / <em>REALLY DO NOT</em> (in earlier releases)</p> <code type="erl"><![CDATA[ @@ -46,13 +46,12 @@ my_list_to_binary([H|T], Acc) -> my_list_to_binary([], Acc) -> Acc.]]></code> - <p>In releases before R12B, <c>Acc</c> would be copied in every iteration. - In R12B, <c>Acc</c> will be copied only in the first iteration and extra - space will be allocated at the end of the copied binary. In the next iteration, - <c>H</c> will be written in to the extra space. When the extra space runs out, - the binary will be reallocated with more extra space.</p> - - <p>The extra space allocated (or reallocated) will be twice the size of the + <p>In releases before R12B, <c>Acc</c> is copied in every iteration. + In R12B, <c>Acc</c> is copied only in the first iteration and extra + space is allocated at the end of the copied binary. In the next iteration, + <c>H</c> is written into the extra space. When the extra space runs out, + the binary is reallocated with more extra space. The extra space allocated + (or reallocated) is twice the size of the existing binary data, or 256, whichever is larger.</p> <p>The most natural way to match binaries is now the fastest:</p> @@ -64,55 +63,79 @@ my_binary_to_list(<<H,T/binary>>) -> my_binary_to_list(<<>>) -> [].]]></code> <section> - <title>How binaries are implemented</title> + <title>How Binaries are Implemented</title> <p>Internally, binaries and bitstrings are implemented in the same way. - In this section, we will call them <em>binaries</em> since that is what + In this section, they are called <em>binaries</em> because that is what they are called in the emulator source code.</p> - <p>There are four types of binary objects internally. Two of them are - containers for binary data and two of them are merely references to - a part of a binary.</p> - - <p>The binary containers are called <em>refc binaries</em> - (short for <em>reference-counted binaries</em>) and <em>heap binaries</em>.</p> + <p>Four types of binary objects are available internally:</p> + <list type="bulleted"> + <item><p>Two are containers for binary data and are called:</p> + <list type="bulleted"> + <item><em>Refc binaries</em> (short for + <em>reference-counted binaries</em>)</item> + <item><em>Heap binaries</em></item> + </list></item> + <item><p>Two are merely references to a part of a binary and + are called:</p> + <list type="bulleted"> + <item><em>sub binaries</em></item> + <item><em>match contexts</em></item> + </list></item> + </list> - <p><marker id="refc_binary"></marker><em>Refc binaries</em> - consist of two parts: an object stored on - the process heap, called a <em>ProcBin</em>, and the binary object itself - stored outside all process heaps.</p> + <section> + <marker id="refc_binary"></marker> + <title>Refc Binaries</title> + <p>Refc binaries consist of two parts:</p> + <list type="bulleted"> + <item>An object stored on the process heap, called a + <em>ProcBin</em></item> + <item>The binary object itself, stored outside all process + heaps</item> + </list> <p>The binary object can be referenced by any number of ProcBins from any - number of processes; the object contains a reference counter to keep track + number of processes. The object contains a reference counter to keep track of the number of references, so that it can be removed when the last reference disappears.</p> <p>All ProcBin objects in a process are part of a linked list, so that the garbage collector can keep track of them and decrement the reference counters in the binary when a ProcBin disappears.</p> + </section> - <p><marker id="heap_binary"></marker><em>Heap binaries</em> are small binaries, - up to 64 bytes, that are stored directly on the process heap. - They will be copied when the process - is garbage collected and when they are sent as a message. They don't + <section> + <marker id="heap_binary"></marker> + <title>Heap Binaries</title> + <p>Heap binaries are small binaries, up to 64 bytes, and are stored + directly on the process heap. They are copied when the process is + garbage-collected and when they are sent as a message. They do not require any special handling by the garbage collector.</p> + </section> - <p>There are two types of reference objects that can reference part of - a refc binary or heap binary. They are called <em>sub binaries</em> and - <em>match contexts</em>.</p> + <section> + <title>Sub Binaries</title> + <p>The reference objects <em>sub binaries</em> and + <em>match contexts</em> can reference part of + a refc binary or heap binary.</p> <p><marker id="sub_binary"></marker>A <em>sub binary</em> is created by <c>split_binary/2</c> and when a binary is matched out in a binary pattern. A sub binary is a reference - into a part of another binary (refc or heap binary, never into a another + into a part of another binary (refc or heap binary, but never into another sub binary). Therefore, matching out a binary is relatively cheap because the actual binary data is never copied.</p> + </section> - <p><marker id="match_context"></marker>A <em>match context</em> is - similar to a sub binary, but is optimized - for binary matching; for instance, it contains a direct pointer to the binary - data. For each field that is matched out of a binary, the position in the - match context will be incremented.</p> + <section> + <title>Match Context</title> + <marker id="match_context"></marker> + <p>A <em>match context</em> is similar to a sub binary, but is + optimized for binary matching. For example, it contains a direct + pointer to the binary data. For each field that is matched out of + a binary, the position in the match context is incremented.</p> <p>In R11B, a match context was only used during a binary matching operation.</p> @@ -122,27 +145,28 @@ my_binary_to_list(<<>>) -> [].]]></code> context and discard the sub binary. Instead of creating a sub binary, the match context is kept.</p> - <p>The compiler can only do this optimization if it can know for sure + <p>The compiler can only do this optimization if it knows that the match context will not be shared. If it would be shared, the functional properties (also called referential transparency) of Erlang would break.</p> + </section> </section> <section> - <title>Constructing binaries</title> - - <p>In R12B, appending to a binary or bitstring</p> + <title>Constructing Binaries</title> + <p>In R12B, appending to a binary or bitstring + is specially optimized by the <em>runtime system</em>:</p> <code type="erl"><![CDATA[ <<Binary/binary, ...>> <<Binary/bitstring, ...>>]]></code> - <p>is specially optimized by the <em>run-time system</em>. - Because the run-time system handles the optimization (instead of + <p>As the runtime system handles the optimization (instead of the compiler), there are very few circumstances in which the optimization - will not work.</p> + does not work.</p> - <p>To explain how it works, we will go through this code</p> + <p>To explain how it works, let us examine the following code line + by line:</p> <code type="erl"><![CDATA[ Bin0 = <<0>>, %% 1 @@ -152,81 +176,81 @@ Bin3 = <<Bin2/binary,7,8,9>>, %% 4 Bin4 = <<Bin1/binary,17>>, %% 5 !!! {Bin4,Bin3} %% 6]]></code> - <p>line by line.</p> - - <p>The first line (marked with the <c>%% 1</c> comment), assigns + <list type="bulleted"> + <item>Line 1 (marked with the <c>%% 1</c> comment), assigns a <seealso marker="#heap_binary">heap binary</seealso> to - the variable <c>Bin0</c>.</p> + the <c>Bin0</c> variable.</item> - <p>The second line is an append operation. Since <c>Bin0</c> + <item>Line 2 is an append operation. As <c>Bin0</c> has not been involved in an append operation, a new <seealso marker="#refc_binary">refc binary</seealso> - will be created and the contents of <c>Bin0</c> will be copied - into it. The <em>ProcBin</em> part of the refc binary will have + is created and the contents of <c>Bin0</c> is copied + into it. The <em>ProcBin</em> part of the refc binary has its size set to the size of the data stored in the binary, while - the binary object will have extra space allocated. - The size of the binary object will be either twice the + the binary object has extra space allocated. + The size of the binary object is either twice the size of <c>Bin0</c> or 256, whichever is larger. In this case - it will be 256.</p> + it is 256.</item> - <p>It gets more interesting in the third line. + <item>Line 3 is more interesting. <c>Bin1</c> <em>has</em> been used in an append operation, - and it has 255 bytes of unused storage at the end, so the three new bytes - will be stored there.</p> + and it has 255 bytes of unused storage at the end, so the 3 new + bytes are stored there.</item> - <p>Same thing in the fourth line. There are 252 bytes left, - so there is no problem storing another three bytes.</p> + <item>Line 4. The same applies here. There are 252 bytes left, + so there is no problem storing another 3 bytes.</item> - <p>But in the fifth line something <em>interesting</em> happens. - Note that we don't append to the previous result in <c>Bin3</c>, - but to <c>Bin1</c>. We expect that <c>Bin4</c> will be assigned - the value <c><<0,1,2,3,17>></c>. We also expect that + <item>Line 5. Here, something <em>interesting</em> happens. Notice + that the result is not appended to the previous result in <c>Bin3</c>, + but to <c>Bin1</c>. It is expected that <c>Bin4</c> will be assigned + the value <c><<0,1,2,3,17>></c>. It is also expected that <c>Bin3</c> will retain its value (<c><<0,1,2,3,4,5,6,7,8,9>></c>). - Clearly, the run-time system cannot write the byte <c>17</c> into the binary, + Clearly, the runtime system cannot write byte <c>17</c> into the binary, because that would change the value of <c>Bin3</c> to - <c><<0,1,2,3,4,17,6,7,8,9>></c>.</p> - - <p>What will happen?</p> + <c><<0,1,2,3,4,17,6,7,8,9>></c>.</item> + </list> - <p>The run-time system will see that <c>Bin1</c> is the result + <p>The runtime system sees that <c>Bin1</c> is the result from a previous append operation (not from the latest append operation), - so it will <em>copy</em> the contents of <c>Bin1</c> to a new binary - and reserve extra storage and so on. (We will not explain here how the - run-time system can know that it is not allowed to write into <c>Bin1</c>; + so it <em>copies</em> the contents of <c>Bin1</c> to a new binary, + reserve extra storage, and so on. (Here is not explained how the + runtime system can know that it is not allowed to write into <c>Bin1</c>; it is left as an exercise to the curious reader to figure out how it is done by reading the emulator sources, primarily <c>erl_bits.c</c>.)</p> <section> - <title>Circumstances that force copying</title> + <title>Circumstances That Force Copying</title> <p>The optimization of the binary append operation requires that there is a <em>single</em> ProcBin and a <em>single reference</em> to the ProcBin for the binary. The reason is that the binary object can be - moved (reallocated) during an append operation, and when that happens + moved (reallocated) during an append operation, and when that happens, the pointer in the ProcBin must be updated. If there would be more than one ProcBin pointing to the binary object, it would not be possible to find and update all of them.</p> - <p>Therefore, certain operations on a binary will mark it so that + <p>Therefore, certain operations on a binary mark it so that any future append operation will be forced to copy the binary. In most cases, the binary object will be shrunk at the same time to reclaim the extra space allocated for growing.</p> - <p>When appending to a binary</p> + <p>When appending to a binary as follows, only the binary returned + from the latest append operation will support further cheap append + operations:</p> <code type="erl"><![CDATA[ Bin = <<Bin0,...>>]]></code> - <p>only the binary returned from the latest append operation will - support further cheap append operations. In the code fragment above, + <p>In the code fragment in the beginning of this section, appending to <c>Bin</c> will be cheap, while appending to <c>Bin0</c> will force the creation of a new binary and copying of the contents of <c>Bin0</c>.</p> <p>If a binary is sent as a message to a process or port, the binary will be shrunk and any further append operation will copy the binary - data into a new binary. For instance, in the following code fragment</p> + data into a new binary. For example, in the following code fragment + <c>Bin1</c> will be copied in the third line:</p> <code type="erl"><![CDATA[ Bin1 = <<Bin0,...>>, @@ -234,12 +258,12 @@ PortOrPid ! Bin1, Bin = <<Bin1,...>> %% Bin1 will be COPIED ]]></code> - <p><c>Bin1</c> will be copied in the third line.</p> - - <p>The same thing happens if you insert a binary into an <em>ets</em> - table or send it to a port using <c>erlang:port_command/2</c> or pass it to + <p>The same happens if you insert a binary into an Ets + table, send it to a port using <c>erlang:port_command/2</c>, or + pass it to <seealso marker="erts:erl_nif#enif_inspect_binary">enif_inspect_binary</seealso> in a NIF.</p> + <p>Matching a binary will also cause it to shrink and the next append operation will copy the binary data:</p> @@ -249,22 +273,23 @@ Bin1 = <<Bin0,...>>, Bin = <<Bin1,...>> %% Bin1 will be COPIED ]]></code> - <p>The reason is that a <seealso marker="#match_context">match context</seealso> + <p>The reason is that a + <seealso marker="#match_context">match context</seealso> contains a direct pointer to the binary data.</p> - <p>If a process simply keeps binaries (either in "loop data" or in the process - dictionary), the garbage collector may eventually shrink the binaries. - If only one such binary is kept, it will not be shrunk. If the process later - appends to a binary that has been shrunk, the binary object will be reallocated - to make place for the data to be appended.</p> + <p>If a process simply keeps binaries (either in "loop data" or in the + process + dictionary), the garbage collector can eventually shrink the binaries. + If only one such binary is kept, it will not be shrunk. If the process + later appends to a binary that has been shrunk, the binary object will + be reallocated to make place for the data to be appended.</p> </section> - </section> <section> - <title>Matching binaries</title> + <title>Matching Binaries</title> - <p>We will revisit the example shown earlier</p> + <p>Let us revisit the example in the beginning of the previous section:</p> <p><em>DO</em> (in R12B)</p> <code type="erl"><![CDATA[ @@ -272,36 +297,35 @@ my_binary_to_list(<<H,T/binary>>) -> [H|my_binary_to_list(T)]; my_binary_to_list(<<>>) -> [].]]></code> - <p>too see what is happening under the hood.</p> - - <p>The very first time <c>my_binary_to_list/1</c> is called, + <p>The first time <c>my_binary_to_list/1</c> is called, a <seealso marker="#match_context">match context</seealso> - will be created. The match context will point to the first - byte of the binary. One byte will be matched out and the match context - will be updated to point to the second byte in the binary.</p> + is created. The match context points to the first + byte of the binary. 1 byte is matched out and the match context + is updated to point to the second byte in the binary.</p> - <p>In R11B, at this point a <seealso marker="#sub_binary">sub binary</seealso> + <p>In R11B, at this point a + <seealso marker="#sub_binary">sub binary</seealso> would be created. In R12B, the compiler sees that there is no point in creating a sub binary, because there will soon be a call to a function (in this case, - to <c>my_binary_to_list/1</c> itself) that will immediately + to <c>my_binary_to_list/1</c> itself) that immediately will create a new match context and discard the sub binary.</p> - <p>Therefore, in R12B, <c>my_binary_to_list/1</c> will call itself + <p>Therefore, in R12B, <c>my_binary_to_list/1</c> calls itself with the match context instead of with a sub binary. The instruction - that initializes the matching operation will basically do nothing + that initializes the matching operation basically does nothing when it sees that it was passed a match context instead of a binary.</p> <p>When the end of the binary is reached and the second clause matches, the match context will simply be discarded (removed in the next - garbage collection, since there is no longer any reference to it).</p> + garbage collection, as there is no longer any reference to it).</p> <p>To summarize, <c>my_binary_to_list/1</c> in R12B only needs to create <em>one</em> match context and no sub binaries. In R11B, if the binary contains <em>N</em> bytes, <em>N+1</em> match contexts and <em>N</em> - sub binaries will be created.</p> + sub binaries are created.</p> - <p>In R11B, the fastest way to match binaries is:</p> + <p>In R11B, the fastest way to match binaries is as follows:</p> <p><em>DO NOT</em> (in R12B)</p> <code type="erl"><![CDATA[ @@ -317,13 +341,14 @@ my_complicated_binary_to_list(Bin, Skip) -> end.]]></code> <p>This function cleverly avoids building sub binaries, but it cannot - avoid building a match context in each recursion step. Therefore, in both R11B and R12B, + avoid building a match context in each recursion step. + Therefore, in both R11B and R12B, <c>my_complicated_binary_to_list/1</c> builds <em>N+1</em> match - contexts. (In a future release, the compiler might be able to generate code - that reuses the match context, but don't hold your breath.)</p> + contexts. (In a future Erlang/OTP release, the compiler might be able + to generate code that reuses the match context.)</p> - <p>Returning to <c>my_binary_to_list/1</c>, note that the match context was - discarded when the entire binary had been traversed. What happens if + <p>Returning to <c>my_binary_to_list/1</c>, notice that the match context + was discarded when the entire binary had been traversed. What happens if the iteration stops before it has reached the end of the binary? Will the optimization still work?</p> @@ -336,29 +361,23 @@ after_zero(<<>>) -> <<>>. ]]></code> - <p>Yes, it will. The compiler will remove the building of the sub binary in the - second clause</p> + <p>Yes, it will. The compiler will remove the building of the sub binary in + the second clause:</p> <code type="erl"><![CDATA[ -. -. -. +... after_zero(<<_,T/binary>>) -> after_zero(T); -. -. -.]]></code> +...]]></code> - <p>but will generate code that builds a sub binary in the first clause</p> + <p>But it will generate code that builds a sub binary in the first clause:</p> <code type="erl"><![CDATA[ after_zero(<<0,T/binary>>) -> T; -. -. -.]]></code> +...]]></code> - <p>Therefore, <c>after_zero/1</c> will build one match context and one sub binary + <p>Therefore, <c>after_zero/1</c> builds one match context and one sub binary (assuming it is passed a binary that contains a zero byte).</p> <p>Code like the following will also be optimized:</p> @@ -371,12 +390,14 @@ all_but_zeroes_to_list(<<0,T/binary>>, Acc, Remaining) -> all_but_zeroes_to_list(<<Byte,T/binary>>, Acc, Remaining) -> all_but_zeroes_to_list(T, [Byte|Acc], Remaining-1).]]></code> - <p>The compiler will remove building of sub binaries in the second and third clauses, - and it will add an instruction to the first clause that will convert <c>Buffer</c> - from a match context to a sub binary (or do nothing if <c>Buffer</c> already is a binary).</p> + <p>The compiler removes building of sub binaries in the second and third + clauses, and it adds an instruction to the first clause that converts + <c>Buffer</c> from a match context to a sub binary (or do nothing if + <c>Buffer</c> is a binary already).</p> - <p>Before you begin to think that the compiler can optimize any binary patterns, - here is a function that the compiler (currently, at least) is not able to optimize:</p> + <p>Before you begin to think that the compiler can optimize any binary + patterns, the following function cannot be optimized by the compiler + (currently, at least):</p> <code type="erl"><![CDATA[ non_opt_eq([H|T1], <<H,T2/binary>>) -> @@ -386,43 +407,43 @@ non_opt_eq([_|_], <<_,_/binary>>) -> non_opt_eq([], <<>>) -> true.]]></code> - <p>It was briefly mentioned earlier that the compiler can only delay creation of - sub binaries if it can be sure that the binary will not be shared. In this case, - the compiler cannot be sure.</p> + <p>It was mentioned earlier that the compiler can only delay creation of + sub binaries if it knows that the binary will not be shared. In this case, + the compiler cannot know.</p> - <p>We will soon show how to rewrite <c>non_opt_eq/2</c> so that the delayed sub binary - optimization can be applied, and more importantly, we will show how you can find out - whether your code can be optimized.</p> + <p>Soon it is shown how to rewrite <c>non_opt_eq/2</c> so that the delayed + sub binary optimization can be applied, and more importantly, it is shown + how you can find out whether your code can be optimized.</p> <section> - <title>The bin_opt_info option</title> + <title>Option bin_opt_info</title> <p>Use the <c>bin_opt_info</c> option to have the compiler print a lot of - information about binary optimizations. It can be given either to the compiler or - <c>erlc</c></p> + information about binary optimizations. It can be given either to the + compiler or <c>erlc</c>:</p> <code type="erl"><![CDATA[ erlc +bin_opt_info Mod.erl]]></code> - <p>or passed via an environment variable</p> + <p>or passed through an environment variable:</p> <code type="erl"><![CDATA[ export ERL_COMPILER_OPTIONS=bin_opt_info]]></code> - <p>Note that the <c>bin_opt_info</c> is not meant to be a permanent option added - to your <c>Makefile</c>s, because it is not possible to eliminate all messages that - it generates. Therefore, passing the option through the environment is in most cases - the most practical approach.</p> + <p>Notice that the <c>bin_opt_info</c> is not meant to be a permanent + option added to your <c>Makefile</c>s, because all messages that it + generates cannot be eliminated. Therefore, passing the option through + the environment is in most cases the most practical approach.</p> - <p>The warnings will look like this:</p> + <p>The warnings look as follows:</p> <code type="erl"><![CDATA[ ./efficiency_guide.erl:60: Warning: NOT OPTIMIZED: sub binary is used or returned ./efficiency_guide.erl:62: Warning: OPTIMIZED: creation of sub binary delayed]]></code> - <p>To make it clearer exactly what code the warnings refer to, - in the examples that follow, the warnings are inserted as comments - after the clause they refer to:</p> + <p>To make it clearer exactly what code the warnings refer to, the + warnings in the following examples are inserted as comments + after the clause they refer to, for example:</p> <code type="erl"><![CDATA[ after_zero(<<0,T/binary>>) -> @@ -434,12 +455,12 @@ after_zero(<<_,T/binary>>) -> after_zero(<<>>) -> <<>>.]]></code> - <p>The warning for the first clause tells us that it is not possible to - delay the creation of a sub binary, because it will be returned. - The warning for the second clause tells us that a sub binary will not be + <p>The warning for the first clause says that the creation of a sub + binary cannot be delayed, because it will be returned. + The warning for the second clause says that a sub binary will not be created (yet).</p> - <p>It is time to revisit the earlier example of the code that could not + <p>Let us revisit the earlier example of the code that could not be optimized and find out why:</p> <code type="erl"><![CDATA[ @@ -456,16 +477,16 @@ non_opt_eq([_|_], <<_,_/binary>>) -> non_opt_eq([], <<>>) -> true.]]></code> - <p>The compiler emitted two warnings. The <c>INFO</c> warning refers to the function - <c>non_opt_eq/2</c> as a callee, indicating that any functions that call <c>non_opt_eq/2</c> - will not be able to make delayed sub binary optimization. - There is also a suggestion to change argument order. - The second warning (that happens to refer to the same line) refers to the construction of - the sub binary itself.</p> + <p>The compiler emitted two warnings. The <c>INFO</c> warning refers + to the function <c>non_opt_eq/2</c> as a callee, indicating that any + function that call <c>non_opt_eq/2</c> cannot make delayed sub binary + optimization. There is also a suggestion to change argument order. + The second warning (that happens to refer to the same line) refers to + the construction of the sub binary itself.</p> - <p>We will soon show another example that should make the distinction between <c>INFO</c> - and <c>NOT OPTIMIZED</c> warnings somewhat clearer, but first we will heed the suggestion - to change argument order:</p> + <p>Soon another example will show the difference between the + <c>INFO</c> and <c>NOT OPTIMIZED</c> warnings somewhat clearer, but + let us first follow the suggestion to change argument order:</p> <code type="erl"><![CDATA[ opt_eq(<<H,T1/binary>>, [H|T2]) -> @@ -485,15 +506,13 @@ match_body([0|_], <<H,_/binary>>) -> %% sub binary optimization; %% SUGGEST changing argument order done; -. -. -.]]></code> +...]]></code> <p>The warning means that <em>if</em> there is a call to <c>match_body/2</c> (from another clause in <c>match_body/2</c> or another function), the - delayed sub binary optimization will not be possible. There will be additional - warnings for any place where a sub binary is matched out at the end of and - passed as the second argument to <c>match_body/2</c>. For instance:</p> + delayed sub binary optimization will not be possible. More warnings will + occur for any place where a sub binary is matched out at the end of and + passed as the second argument to <c>match_body/2</c>, for example:</p> <code type="erl"><![CDATA[ match_head(List, <<_:10,Data/binary>>) -> @@ -504,10 +523,10 @@ match_head(List, <<_:10,Data/binary>>) -> </section> <section> - <title>Unused variables</title> + <title>Unused Variables</title> - <p>The compiler itself figures out if a variable is unused. The same - code is generated for each of the following functions</p> + <p>The compiler figures out if a variable is unused. The same + code is generated for each of the following functions:</p> <code type="erl"><![CDATA[ count1(<<_,T/binary>>, Count) -> count1(T, Count+1); @@ -519,11 +538,9 @@ count2(<<>>, Count) -> Count. count3(<<_H,T/binary>>, Count) -> count3(T, Count+1); count3(<<>>, Count) -> Count.]]></code> - <p>In each iteration, the first 8 bits in the binary will be skipped, not matched out.</p> - + <p>In each iteration, the first 8 bits in the binary will be skipped, + not matched out.</p> </section> - </section> - </chapter> diff --git a/system/doc/efficiency_guide/commoncaveats.xml b/system/doc/efficiency_guide/commoncaveats.xml index 551b0a03e6..71991d342f 100644 --- a/system/doc/efficiency_guide/commoncaveats.xml +++ b/system/doc/efficiency_guide/commoncaveats.xml @@ -18,7 +18,6 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> <title>Common Caveats</title> @@ -29,49 +28,50 @@ <file>commoncaveats.xml</file> </header> - <p>Here we list a few modules and BIFs to watch out for, and not only + <p>This section lists a few modules and BIFs to watch out for, not only from a performance point of view.</p> <section> - <title>The timer module</title> + <title>Timer Module</title> <p>Creating timers using <seealso marker="erts:erlang#erlang:send_after/3">erlang:send_after/3</seealso> - and <seealso marker="erts:erlang#erlang:start_timer/3">erlang:start_timer/3</seealso> + and + <seealso marker="erts:erlang#erlang:start_timer/3">erlang:start_timer/3</seealso> +, is much more efficient than using the timers provided by the - <seealso marker="stdlib:timer">timer</seealso> module. The - <c>timer</c> module uses a separate process to manage the timers, - and that process can easily become overloaded if many processes + <seealso marker="stdlib:timer">timer</seealso> module in STDLIB. + The <c>timer</c> module uses a separate process to manage the timers. + That process can easily become overloaded if many processes create and cancel timers frequently (especially when using the SMP emulator).</p> - <p>The functions in the <c>timer</c> module that do not manage timers (such as - <c>timer:tc/3</c> or <c>timer:sleep/1</c>), do not call the timer-server process - and are therefore harmless.</p> + <p>The functions in the <c>timer</c> module that do not manage timers + (such as <c>timer:tc/3</c> or <c>timer:sleep/1</c>), do not call the + timer-server process and are therefore harmless.</p> </section> <section> <title>list_to_atom/1</title> - <p>Atoms are not garbage-collected. Once an atom is created, it will never - be removed. The emulator will terminate if the limit for the number - of atoms (1048576 by default) is reached.</p> + <p>Atoms are not garbage-collected. Once an atom is created, it is never + removed. The emulator terminates if the limit for the number + of atoms (1,048,576 by default) is reached.</p> - <p>Therefore, converting arbitrary input strings to atoms could be - dangerous in a system that will run continuously. - If only certain well-defined atoms are allowed as input, you can use + <p>Therefore, converting arbitrary input strings to atoms can be + dangerous in a system that runs continuously. + If only certain well-defined atoms are allowed as input, <seealso marker="erts:erlang#list_to_existing_atom/1">list_to_existing_atom/1</seealso> + can be used to to guard against a denial-of-service attack. (All atoms that are allowed - must have been created earlier, for instance by simply using all of them + must have been created earlier, for example, by simply using all of them in a module and loading that module.)</p> <p>Using <c>list_to_atom/1</c> to construct an atom that is passed to - <c>apply/3</c> like this</p> - + <c>apply/3</c> as follows, is quite expensive and not recommended + in time-critical code:</p> <code type="erl"> -apply(list_to_atom("some_prefix"++Var), foo, Args)</code> - - <p>is quite expensive and is not recommended in time-critical code.</p> +apply(list_to_atom("some_prefix"++Var), foo, Args)</code> </section> <section> @@ -81,25 +81,25 @@ apply(list_to_atom("some_prefix"++Var), foo, Args)</code> length of the list, as opposed to <c>tuple_size/1</c>, <c>byte_size/1</c>, and <c>bit_size/1</c>, which all execute in constant time.</p> - <p>Normally you don't have to worry about the speed of <c>length/1</c>, - because it is efficiently implemented in C. In time critical-code, though, - you might want to avoid it if the input list could potentially be very long.</p> + <p>Normally, there is no need to worry about the speed of <c>length/1</c>, + because it is efficiently implemented in C. In time-critical code, + you might want to avoid it if the input list could potentially be very + long.</p> <p>Some uses of <c>length/1</c> can be replaced by matching. - For instance, this code</p> - + For example, the following code:</p> <code type="erl"> foo(L) when length(L) >= 3 -> ...</code> - <p>can be rewritten to</p> + <p>can be rewritten to:</p> <code type="erl"> foo([_,_,_|_]=L) -> ...</code> - <p>(One slight difference is that <c>length(L)</c> will fail if the <c>L</c> - is an improper list, while the pattern in the second code fragment will - accept an improper list.)</p> + <p>One slight difference is that <c>length(L)</c> fails if <c>L</c> + is an improper list, while the pattern in the second code fragment + accepts an improper list.</p> </section> <section> @@ -107,50 +107,49 @@ foo([_,_,_|_]=L) -> <p><seealso marker="erts:erlang#setelement/3">setelement/3</seealso> copies the tuple it modifies. Therefore, updating a tuple in a loop - using <c>setelement/3</c> will create a new copy of the tuple every time.</p> + using <c>setelement/3</c> creates a new copy of the tuple every time.</p> <p>There is one exception to the rule that the tuple is copied. If the compiler clearly can see that destructively updating the tuple would - give exactly the same result as if the tuple was copied, the call to - <c>setelement/3</c> will be replaced with a special destructive setelement - instruction. In the following code sequence</p> - + give the same result as if the tuple was copied, the call to + <c>setelement/3</c> is replaced with a special destructive <c>setelement</c> + instruction. In the following code sequence, the first <c>setelement/3</c> + call copies the tuple and modifies the ninth element:</p> <code type="erl"> multiple_setelement(T0) -> T1 = setelement(9, T0, bar), T2 = setelement(7, T1, foobar), setelement(5, T2, new_value).</code> - <p>the first <c>setelement/3</c> call will copy the tuple and modify the - ninth element. The two following <c>setelement/3</c> calls will modify + <p>The two following <c>setelement/3</c> calls modify the tuple in place.</p> - <p>For the optimization to be applied, <em>all</em> of the followings conditions + <p>For the optimization to be applied, <em>all</em> the followings conditions must be true:</p> <list type="bulleted"> <item>The indices must be integer literals, not variables or expressions.</item> <item>The indices must be given in descending order.</item> - <item>There must be no calls to other function in between the calls to + <item>There must be no calls to another function in between the calls to <c>setelement/3</c>.</item> <item>The tuple returned from one <c>setelement/3</c> call must only be used in the subsequent call to <c>setelement/3</c>.</item> </list> - <p>If it is not possible to structure the code as in the <c>multiple_setelement/1</c> + <p>If the code cannot be structured as in the <c>multiple_setelement/1</c> example, the best way to modify multiple elements in a large tuple is to - convert the tuple to a list, modify the list, and convert the list back to + convert the tuple to a list, modify the list, and convert it back to a tuple.</p> </section> <section> <title>size/1</title> - <p><c>size/1</c> returns the size for both tuples and binary.</p> + <p><c>size/1</c> returns the size for both tuples and binaries.</p> - <p>Using the new BIFs <c>tuple_size/1</c> and <c>byte_size/1</c> introduced - in R12B gives the compiler and run-time system more opportunities for - optimization. A further advantage is that the new BIFs could help Dialyzer + <p>Using the new BIFs <c>tuple_size/1</c> and <c>byte_size/1</c>, introduced + in R12B, gives the compiler and the runtime system more opportunities for + optimization. Another advantage is that the new BIFs can help Dialyzer to find more bugs in your program.</p> </section> @@ -159,22 +158,21 @@ multiple_setelement(T0) -> <p>It is usually more efficient to split a binary using matching instead of calling the <c>split_binary/2</c> function. Furthermore, mixing bit syntax matching and <c>split_binary/2</c> - may prevent some optimizations of bit syntax matching.</p> + can prevent some optimizations of bit syntax matching.</p> <p><em>DO</em></p> <code type="none"><![CDATA[ <<Bin1:Num/binary,Bin2/binary>> = Bin,]]></code> <p><em>DO NOT</em></p> <code type="none"> - {Bin1,Bin2} = split_binary(Bin, Num) - </code> + {Bin1,Bin2} = split_binary(Bin, Num)</code> </section> <section> - <title>The '--' operator</title> - <p>Note that the '<c>--</c>' operator has a complexity - proportional to the product of the length of its operands, - meaning that it will be very slow if both of its operands + <title>Operator "--"</title> + <p>The "<c>--</c>" operator has a complexity + proportional to the product of the length of its operands. + This means that the operator is very slow if both of its operands are long lists:</p> <p><em>DO NOT</em></p> @@ -182,42 +180,39 @@ multiple_setelement(T0) -> HugeList1 -- HugeList2]]></code> <p>Instead use the <seealso marker="stdlib:ordsets">ordsets</seealso> - module:</p> + module in STDLIB:</p> <p><em>DO</em></p> <code type="none"> HugeSet1 = ordsets:from_list(HugeList1), HugeSet2 = ordsets:from_list(HugeList2), - ordsets:subtract(HugeSet1, HugeSet2) - </code> + ordsets:subtract(HugeSet1, HugeSet2)</code> - <p>Obviously, that code will not work if the original order + <p>Obviously, that code does not work if the original order of the list is important. If the order of the list must be - preserved, do like this:</p> + preserved, do as follows:</p> <p><em>DO</em></p> <code type="none"><![CDATA[ Set = gb_sets:from_list(HugeList2), [E || E <- HugeList1, not gb_sets:is_element(E, Set)]]]></code> - <p>Subtle note 1: This code behaves differently from '<c>--</c>' - if the lists contain duplicate elements. (One occurrence - of an element in HugeList2 will remove <em>all</em> + <note><p>This code behaves differently from "<c>--</c>" + if the lists contain duplicate elements (one occurrence + of an element in HugeList2 removes <em>all</em> occurrences in HugeList1.)</p> + <p>Also, this code compares lists elements using the + "<c>==</c>" operator, while "<c>--</c>" uses the "<c>=:=</c>" operator. + If that difference is important, <c>sets</c> can be used instead of + <c>gb_sets</c>, but <c>sets:from_list/1</c> is much + slower than <c>gb_sets:from_list/1</c> for long lists.</p></note> - <p>Subtle note 2: This code compares lists elements using the - '<c>==</c>' operator, while '<c>--</c>' uses the '<c>=:=</c>'. If - that difference is important, <c>sets</c> can be used instead of - <c>gb_sets</c>, but note that <c>sets:from_list/1</c> is much - slower than <c>gb_sets:from_list/1</c> for long lists.</p> - - <p>Using the '<c>--</c>' operator to delete an element + <p>Using the "<c>--</c>" operator to delete an element from a list is not a performance problem:</p> <p><em>OK</em></p> <code type="none"> - HugeList1 -- [Element] - </code> + HugeList1 -- [Element]</code> </section> diff --git a/system/doc/efficiency_guide/drivers.xml b/system/doc/efficiency_guide/drivers.xml index dfc49bdf21..33d6333e7d 100644 --- a/system/doc/efficiency_guide/drivers.xml +++ b/system/doc/efficiency_guide/drivers.xml @@ -18,7 +18,6 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> <title>Drivers</title> @@ -29,26 +28,26 @@ <file>drivers.xml</file> </header> - <p>This chapter provides a (very) brief overview on how to write efficient - drivers. It is assumed that you already have a good understanding of + <p>This section provides a brief overview on how to write efficient drivers.</p> + <p>It is assumed that you have a good understanding of drivers.</p> <section> - <title>Drivers and concurrency</title> + <title>Drivers and Concurrency</title> - <p>The run-time system will always take a lock before running + <p>The runtime system always takes a lock before running any code in a driver.</p> - <p>By default, that lock will be at the driver level, meaning that + <p>By default, that lock is at the driver level, that is, if several ports have been opened to the same driver, only code for one port at the same time can be running.</p> - <p>A driver can be configured to instead have one lock for each port.</p> + <p>A driver can be configured to have one lock for each port instead.</p> - <p>If a driver is used in a functional way (i.e. it holds no state, + <p>If a driver is used in a functional way (that is, holds no state, but only does some heavy calculation and returns a result), several - ports with registered names can be opened beforehand and the port to - be used can be chosen based on the scheduler ID like this:</p> + ports with registered names can be opened beforehand, and the port to + be used can be chosen based on the scheduler ID as follows:</p> <code type="none"> -define(PORT_NAMES(), @@ -67,82 +66,82 @@ client_port() -> </section> <section> - <title>Avoiding copying of binaries when calling a driver</title> + <title>Avoiding Copying Binaries When Calling a Driver</title> <p>There are basically two ways to avoid copying a binary that is - sent to a driver.</p> - - <p>If the <c>Data</c> argument for - <seealso marker="erts:erlang#port_control/3">port_control/3</seealso> - is a binary, the driver will be passed a pointer to the contents of - the binary and the binary will not be copied. - If the <c>Data</c> argument is an iolist (list of binaries and lists), - all binaries in the iolist will be copied.</p> - - <p>Therefore, if you want to send both a pre-existing binary and some - additional data to a driver without copying the binary, you must call - <c>port_control/3</c> twice; once with the binary and once with the - additional data. However, that will only work if there is only one - process communicating with the port (because otherwise another process - could call the driver in-between the calls).</p> - - <p>Another way to avoid copying binaries is to implement an <c>outputv</c> - callback (instead of an <c>output</c> callback) in the driver. - If a driver has an <c>outputv</c> callback, refc binaries passed - in an iolist in the <c>Data</c> argument for - <seealso marker="erts:erlang#port_command/2">port_command/2</seealso> - will be passed as references to the driver.</p> + sent to a driver:</p> + + <list type="bulleted"> + <item><p>If the <c>Data</c> argument for + <seealso marker="erts:erlang#port_control/3">port_control/3</seealso> + is a binary, the driver will be passed a pointer to the contents of + the binary and the binary will not be copied. If the <c>Data</c> + argument is an iolist (list of binaries and lists), all binaries in + the iolist will be copied.</p> + + <p>Therefore, if you want to send both a pre-existing binary and some + extra data to a driver without copying the binary, you must call + <c>port_control/3</c> twice; once with the binary and once with the + extra data. However, that will only work if there is only one + process communicating with the port (because otherwise another process + can call the driver in-between the calls).</p></item> + + <item><p>Implement an <c>outputv</c> callback (instead of an + <c>output</c> callback) in the driver. If a driver has an + <c>outputv</c> callback, refc binaries passed in an iolist + in the <c>Data</c> argument for + <seealso marker="erts:erlang#port_command/2">port_command/2</seealso> + will be passed as references to the driver.</p></item> + </list> </section> <section> - <title>Returning small binaries from a driver</title> + <title>Returning Small Binaries from a Driver</title> - <p>The run-time system can represent binaries up to 64 bytes as - heap binaries. They will always be copied when sent in a messages, - but they will require less memory if they are not sent to another + <p>The runtime system can represent binaries up to 64 bytes as + heap binaries. They are always copied when sent in messages, + but they require less memory if they are not sent to another process and garbage collection is cheaper.</p> - <p>If you know that the binaries you return are always small, - you should use driver API calls that do not require a pre-allocated - binary, for instance + <p>If you know that the binaries you return are always small, you + are advised to use driver API calls that do not require a pre-allocated + binary, for example, <seealso marker="erts:erl_driver#driver_output">driver_output()</seealso> - or - <seealso marker="erts:erl_driver#erl_drv_output_term">erl_drv_output_term()</seealso> + or + <seealso marker="erts:erl_driver#erl_drv_output_term">erl_drv_output_term()</seealso>, using the <c>ERL_DRV_BUF2BINARY</c> format, - to allow the run-time to construct a heap binary.</p> + to allow the runtime to construct a heap binary.</p> </section> <section> - <title>Returning big binaries without copying from a driver</title> + <title>Returning Large Binaries without Copying from a Driver</title> - <p>To avoid copying data when a big binary is sent or returned from + <p>To avoid copying data when a large binary is sent or returned from the driver to an Erlang process, the driver must first allocate the binary and then send it to an Erlang process in some way.</p> - <p>Use <seealso marker="erts:erl_driver#driver_alloc_binary">driver_alloc_binary()</seealso> to allocate a binary.</p> + <p>Use + <seealso marker="erts:erl_driver#driver_alloc_binary">driver_alloc_binary()</seealso> + to allocate a binary.</p> <p>There are several ways to send a binary created with - <c>driver_alloc_binary()</c>.</p> + <c>driver_alloc_binary()</c>:</p> <list type="bulleted"> - <item><p>From the <c>control</c> callback, a binary can be returned provided - that - <seealso marker="erts:erl_driver#set_port_control_flags">set_port_control_flags()</seealso> - has been called with the flag value <c>PORT_CONTROL_FLAG_BINARY</c>.</p> - </item> + <item>From the <c>control</c> callback, a binary can be returned if + <seealso marker="erts:erl_driver#set_port_control_flags">set_port_control_flags()</seealso> + has been called with the flag value <c>PORT_CONTROL_FLAG_BINARY</c>.</item> - <item><p>A single binary can be sent with - <seealso marker="erts:erl_driver#driver_output_binary">driver_output_binary()</seealso>.</p></item> + <item>A single binary can be sent with + <seealso marker="erts:erl_driver#driver_output_binary">driver_output_binary()</seealso>.</item> - <item><p>Using + <item>Using <seealso marker="erts:erl_driver#erl_drv_output_term">erl_drv_output_term()</seealso> or <seealso marker="erts:erl_driver#erl_drv_send_term">erl_drv_send_term()</seealso>, - a binary can be included in an Erlang term.</p> - </item> + a binary can be included in an Erlang term.</item> </list> </section> - </chapter> diff --git a/system/doc/efficiency_guide/functions.xml b/system/doc/efficiency_guide/functions.xml index ec1a45eaa9..bd23c9d90d 100644 --- a/system/doc/efficiency_guide/functions.xml +++ b/system/doc/efficiency_guide/functions.xml @@ -18,7 +18,6 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> <title>Functions</title> @@ -30,17 +29,18 @@ </header> <section> - <title>Pattern matching</title> - <p>Pattern matching in function head and in <c>case</c> and <c>receive</c> - clauses are optimized by the compiler. With a few exceptions, there is nothing - to gain by rearranging clauses.</p> + <title>Pattern Matching</title> + <p>Pattern matching in function head as well as in <c>case</c> and + <c>receive</c> clauses are optimized by the compiler. With a few + exceptions, there is nothing to gain by rearranging clauses.</p> <p>One exception is pattern matching of binaries. The compiler - will not rearrange clauses that match binaries. Placing the - clause that matches against the empty binary <em>last</em> will usually - be slightly faster than placing it <em>first</em>.</p> + does not rearrange clauses that match binaries. Placing the + clause that matches against the empty binary <em>last</em> is usually + slightly faster than placing it <em>first</em>.</p> - <p>Here is a rather contrived example to show another exception:</p> + <p>The following is a rather unnatural example to show another + exception:</p> <p><em>DO NOT</em></p> <code type="erl"> @@ -53,27 +53,30 @@ atom_map1(five) -> 5; atom_map1(six) -> 6.</code> <p>The problem is the clause with the variable <c>Int</c>. - Since a variable can match anything, including the atoms - <c>four</c>, <c>five</c>, and <c>six</c> that the following clauses - also will match, the compiler must generate sub-optimal code that will - execute as follows:</p> + As a variable can match anything, including the atoms + <c>four</c>, <c>five</c>, and <c>six</c>, which the following clauses + also match, the compiler must generate suboptimal code that + executes as follows:</p> - <p>First the input value is compared to <c>one</c>, <c>two</c>, and + <list type="bulleted"> + <item>First, the input value is compared to <c>one</c>, <c>two</c>, and <c>three</c> (using a single instruction that does a binary search; thus, quite efficient even if there are many values) to select which - one of the first three clauses to execute (if any).</p> + one of the first three clauses to execute (if any).</item> + + <item>>If none of the first three clauses match, the fourth clause + match as a variable always matches.</item> - <p>If none of the first three clauses matched, the fourth clause - will match since a variable always matches. If the guard test - <c>is_integer(Int)</c> succeeds, the fourth clause will be - executed.</p> + <item>If the guard test <c>is_integer(Int)</c> succeeds, the fourth + clause is executed.</item> - <p>If the guard test failed, the input value is compared to + <item>If the guard test fails, the input value is compared to <c>four</c>, <c>five</c>, and <c>six</c>, and the appropriate clause - is selected. (There will be a <c>function_clause</c> exception if - none of the values matched.)</p> + is selected. (There is a <c>function_clause</c> exception if none of + the values matched.)</item> + </list> - <p>Rewriting to either</p> + <p>Rewriting to either:</p> <p><em>DO</em></p> <code type="erl"><![CDATA[ @@ -85,7 +88,7 @@ atom_map2(five) -> 5; atom_map2(six) -> 6; atom_map2(Int) when is_integer(Int) -> Int.]]></code> - <p>or</p> + <p>or:</p> <p><em>DO</em></p> <code type="erl"><![CDATA[ @@ -97,9 +100,9 @@ atom_map3(four) -> 4; atom_map3(five) -> 5; atom_map3(six) -> 6.]]></code> - <p>will give slightly more efficient matching code.</p> + <p>gives slightly more efficient matching code.</p> - <p>Here is a less contrived example:</p> + <p>Another example:</p> <p><em>DO NOT</em></p> <code type="erl"><![CDATA[ @@ -116,7 +119,8 @@ map_pairs1(Map, [X|Xs], [Y|Ys]) -> match anything, the compiler is not allowed to rearrange the clauses, but must generate code that matches them in the order written.</p> - <p>If the function is rewritten like this</p> + <p>If the function is rewritten as follows, the compiler is free to + rearrange the clauses:</p> <p><em>DO</em></p> <code type="erl"><![CDATA[ @@ -127,8 +131,7 @@ map_pairs2(_Map, [_|_]=Xs, [] ) -> map_pairs2(Map, [X|Xs], [Y|Ys]) -> [Map(X, Y)|map_pairs2(Map, Xs, Ys)].]]></code> - <p>the compiler is free to rearrange the clauses. It will generate code - similar to this</p> + <p>The compiler will generate code similar to this:</p> <p><em>DO NOT (already done by the compiler)</em></p> <code type="erl"><![CDATA[ @@ -145,31 +148,35 @@ explicit_map_pairs(Map, Xs0, Ys0) -> Ys0 end.]]></code> - <p>which should be slightly faster for presumably the most common case + <p>This is slightly faster for probably the most common case that the input lists are not empty or very short. - (Another advantage is that Dialyzer is able to deduce a better type - for the variable <c>Xs</c>.)</p> + (Another advantage is that Dialyzer can deduce a better type + for the <c>Xs</c> variable.)</p> </section> <section> - <title>Function Calls </title> + <title>Function Calls</title> - <p>Here is an intentionally rough guide to the relative costs of - different kinds of calls. It is based on benchmark figures run on + <p>This is an intentionally rough guide to the relative costs of + different calls. It is based on benchmark figures run on Solaris/Sparc:</p> <list type="bulleted"> <item>Calls to local or external functions (<c>foo()</c>, <c>m:foo()</c>) - are the fastest kind of calls.</item> + are the fastest calls.</item> + <item>Calling or applying a fun (<c>Fun()</c>, <c>apply(Fun, [])</c>) - is about <em>three times</em> as expensive as calling a local function.</item> + is about <em>three times</em> as expensive as calling a local + function.</item> + <item>Applying an exported function (<c>Mod:Name()</c>, - <c>apply(Mod, Name, [])</c>) is about twice as expensive as calling a fun, - or about <em>six times</em> as expensive as calling a local function.</item> + <c>apply(Mod, Name, [])</c>) is about twice as expensive as calling + a fun or about <em>six times</em> as expensive as calling a local + function.</item> </list> <section> - <title>Notes and implementation details</title> + <title>Notes and Implementation Details</title> <p>Calling and applying a fun does not involve any hash-table lookup. A fun contains an (indirect) pointer to the function that implements @@ -178,42 +185,44 @@ explicit_map_pairs(Map, Xs0, Ys0) -> <warning><p><em>Tuples are not fun(s)</em>. A "tuple fun", <c>{Module,Function}</c>, is not a fun. The cost for calling a "tuple fun" is similar to that - of <c>apply/3</c> or worse. Using "tuple funs" is <em>strongly discouraged</em>, - as they may not be supported in a future release, - and because there exists a superior alternative since the R10B - release, namely the <c>fun Module:Function/Arity</c> syntax.</p></warning> + of <c>apply/3</c> or worse. + Using "tuple funs" is <em>strongly discouraged</em>, + as they might not be supported in a future Erlang/OTP release, + and because there exists a superior alternative from R10B, + namely the <c>fun Module:Function/Arity</c> syntax.</p></warning> <p><c>apply/3</c> must look up the code for the function to execute - in a hash table. Therefore, it will always be slower than a + in a hash table. It is therefore always slower than a direct call or a fun call.</p> <p>It no longer matters (from a performance point of view) - whether you write</p> + whether you write:</p> <code type="erl"> Module:Function(Arg1, Arg2)</code> - <p>or</p> + <p>or:</p> <code type="erl"> apply(Module, Function, [Arg1,Arg2])</code> - <p>(The compiler internally rewrites the latter code into the former.)</p> + <p>The compiler internally rewrites the latter code into the + former.</p> - <p>The following code</p> + <p>The following code is slightly slower because the shape of the + list of arguments is unknown at compile time.</p> <code type="erl"> apply(Module, Function, Arguments)</code> - <p>is slightly slower because the shape of the list of arguments - is not known at compile time.</p> </section> </section> <section> - <title>Memory usage in recursion</title> - <p>When writing recursive functions it is preferable to make them - tail-recursive so that they can execute in constant memory space.</p> + <title>Memory Usage in Recursion</title> + <p>When writing recursive functions, it is preferable to make them + tail-recursive so that they can execute in constant memory space:</p> + <p><em>DO</em></p> <code type="none"> list_length(List) -> @@ -224,13 +233,14 @@ list_length([], AccLen) -> list_length([_|Tail], AccLen) -> list_length(Tail, AccLen + 1). % Tail-recursive</code> + <p><em>DO NOT</em></p> + <code type="none"> list_length([]) -> 0. % Base case list_length([_ | Tail]) -> list_length(Tail) + 1. % Not tail-recursive</code> </section> - </chapter> diff --git a/system/doc/efficiency_guide/introduction.xml b/system/doc/efficiency_guide/introduction.xml index 9726d3ad11..a8360f1cdd 100644 --- a/system/doc/efficiency_guide/introduction.xml +++ b/system/doc/efficiency_guide/introduction.xml @@ -18,7 +18,6 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> <title>Introduction</title> @@ -32,38 +31,39 @@ <section> <title>Purpose</title> - <quote><p>Premature optimization is the root of all evil. -- D.E. Knuth</p></quote> + <quote><p>"Premature optimization is the root of all evil" + (D.E. Knuth)</p></quote> - <p>Efficient code can be well-structured and clean code, based on + <p>Efficient code can be well-structured and clean, based on a sound overall architecture and sound algorithms. Efficient code can be highly implementation-code that bypasses documented interfaces and takes advantage of obscure quirks in the current implementation.</p> - <p>Ideally, your code should only contain the first kind of efficient - code. If that turns out to be too slow, you should profile the application + <p>Ideally, your code only contains the first type of efficient + code. If that turns out to be too slow, profile the application to find out where the performance bottlenecks are and optimize only the - bottlenecks. Other code should stay as clean as possible.</p> + bottlenecks. Let other code stay as clean as possible.</p> - <p>Fortunately, compiler and run-time optimizations introduced in - R12B makes it easier to write code that is both clean and - efficient. For instance, the ugly workarounds needed in R11B and earlier + <p>Fortunately, compiler and runtime optimizations introduced in + Erlang/OTP R12B makes it easier to write code that is both clean and + efficient. For example, the ugly workarounds needed in R11B and earlier releases to get the most speed out of binary pattern matching are no longer necessary. In fact, the ugly code is slower than the clean code (because the clean code has become faster, not because the uglier code has become slower).</p> - <p>This Efficiency Guide cannot really learn you how to write efficient + <p>This Efficiency Guide cannot really teach you how to write efficient code. It can give you a few pointers about what to avoid and what to use, and some understanding of how certain language features are implemented. - We have generally not included general tips about optimization that will - work in any language, such as moving common calculations out of loops.</p> + This guide does not include general tips about optimization that + works in any language, such as moving common calculations out of loops.</p> </section> <section> <title>Prerequisites</title> - <p>It is assumed that the reader is familiar with the Erlang - programming language and concepts of OTP.</p> + <p>It is assumed that you are familiar with the Erlang programming + language and the OTP concepts.</p> </section> </chapter> diff --git a/system/doc/efficiency_guide/listhandling.xml b/system/doc/efficiency_guide/listhandling.xml index 9112738b18..b950f55ad1 100644 --- a/system/doc/efficiency_guide/listhandling.xml +++ b/system/doc/efficiency_guide/listhandling.xml @@ -18,10 +18,9 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> - <title>List handling</title> + <title>List Handling</title> <prepared>Bjorn Gustavsson</prepared> <docno></docno> <date>2007-11-16</date> @@ -30,19 +29,18 @@ </header> <section> - <title>Creating a list</title> + <title>Creating a List</title> - <p>Lists can only be built starting from the end and attaching - list elements at the beginning. If you use the <c>++</c> operator - like this</p> + <p>Lists can only be built starting from the end and attaching list + elements at the beginning. If you use the "<c>++</c>" operator as + follows, a new list is created that is a copy of the elements in + <c>List1</c>, followed by <c>List2</c>:</p> <code type="erl"> List1 ++ List2</code> - <p>you will create a new list which is copy of the elements in <c>List1</c>, - followed by <c>List2</c>. Looking at how <c>lists:append/1</c> or <c>++</c> would be - implemented in plain Erlang, it can be seen clearly that the first list - is copied:</p> + <p>Looking at how <c>lists:append/1</c> or <c>++</c> would be + implemented in plain Erlang, clearly the first list is copied:</p> <code type="erl"> append([H|T], Tail) -> @@ -50,12 +48,12 @@ append([H|T], Tail) -> append([], Tail) -> Tail.</code> - <p>So the important thing when recursing and building a list is to - make sure that you attach the new elements to the beginning of the list, - so that you build <em>a</em> list, and not hundreds or thousands of - copies of the growing result list.</p> + <p>When recursing and building a list, it is important to ensure + that you attach the new elements to the beginning of the list. In + this way, you will build <em>one</em> list, not hundreds or thousands + of copies of the growing result list.</p> - <p>Let us first look at how it should not be done:</p> + <p>Let us first see how it is not to be done:</p> <p><em>DO NOT</em></p> <code type="erl"><![CDATA[ @@ -67,11 +65,11 @@ bad_fib(0, _Current, _Next, Fibs) -> bad_fib(N, Current, Next, Fibs) -> bad_fib(N - 1, Next, Current + Next, Fibs ++ [Current]).]]></code> - <p>Here we are not a building a list; in each iteration step we - create a new list that is one element longer than the new previous list.</p> + <p>Here more than one list is built. In each iteration step a new list + is created that is one element longer than the new previous list.</p> - <p>To avoid copying the result in each iteration, we must build the list in - reverse order and reverse the list when we are done:</p> + <p>To avoid copying the result in each iteration, build the list in + reverse order and reverse the list when you are done:</p> <p><em>DO</em></p> <code type="erl"><![CDATA[ @@ -86,49 +84,45 @@ tail_recursive_fib(N, Current, Next, Fibs) -> </section> <section> - <title>List comprehensions</title> + <title>List Comprehensions</title> <p>Lists comprehensions still have a reputation for being slow. They used to be implemented using funs, which used to be slow.</p> - <p>In recent Erlang/OTP releases (including R12B), a list comprehension</p> + <p>In recent Erlang/OTP releases (including R12B), a list comprehension:</p> <code type="erl"><![CDATA[ [Expr(E) || E <- List]]]></code> - <p>is basically translated to a local function</p> + <p>is basically translated to a local function:</p> <code type="erl"> 'lc^0'([E|Tail], Expr) -> [Expr(E)|'lc^0'(Tail, Expr)]; 'lc^0'([], _Expr) -> [].</code> - <p>In R12B, if the result of the list comprehension will <em>obviously</em> not be used, - a list will not be constructed. For instance, in this code</p> + <p>In R12B, if the result of the list comprehension will <em>obviously</em> + not be used, a list will not be constructed. For example, in this code:</p> <code type="erl"><![CDATA[ [io:put_chars(E) || E <- List], ok.]]></code> - <p>or in this code</p> + <p>or in this code:</p> <code type="erl"><![CDATA[ -. -. -. +... case Var of ... -> [io:put_chars(E) || E <- List]; ... -> end, some_function(...), -. -. -.]]></code> +...]]></code> - <p>the value is neither assigned to a variable, nor passed to another function, - nor returned, so there is no need to construct a list and the compiler will simplify - the code for the list comprehension to</p> + <p>the value is not assigned to a variable, not passed to another function, + and not returned. This means that there is no need to construct a list and + the compiler will simplify the code for the list comprehension to:</p> <code type="erl"> 'lc^0'([E|Tail], Expr) -> @@ -139,14 +133,15 @@ some_function(...), </section> <section> - <title>Deep and flat lists</title> + <title>Deep and Flat Lists</title> <p><seealso marker="stdlib:lists#flatten/1">lists:flatten/1</seealso> - builds an entirely new list. Therefore, it is expensive, and even - <em>more</em> expensive than the <c>++</c> (which copies its left argument, - but not its right argument).</p> + builds an entirely new list. It is therefore expensive, and even + <em>more</em> expensive than the <c>++</c> operator (which copies its + left argument, but not its right argument).</p> - <p>In the following situations, you can easily avoid calling <c>lists:flatten/1</c>:</p> + <p>In the following situations, you can easily avoid calling + <c>lists:flatten/1</c>:</p> <list type="bulleted"> <item>When sending data to a port. Ports understand deep lists @@ -155,16 +150,19 @@ some_function(...), <item>When calling BIFs that accept deep lists, such as <seealso marker="erts:erlang#list_to_binary/1">list_to_binary/1</seealso> or <seealso marker="erts:erlang#iolist_to_binary/1">iolist_to_binary/1</seealso>.</item> - <item>When you know that your list is only one level deep, you can can use + <item>When you know that your list is only one level deep, you can use <seealso marker="stdlib:lists#append/1">lists:append/1</seealso>.</item> </list> - <p><em>Port example</em></p> + <section> + <title>Port Example</title> + <p><em>DO</em></p> <pre> ... port_command(Port, DeepList) ...</pre> + <p><em>DO NOT</em></p> <pre> ... @@ -180,7 +178,7 @@ some_function(...), port_command(Port, TerminatedStr) ...</pre> - <p>Instead do like this:</p> + <p>Instead:</p> <p><em>DO</em></p> <pre> @@ -188,47 +186,53 @@ some_function(...), TerminatedStr = [String, 0], % String="foo" => [[$f, $o, $o], 0] port_command(Port, TerminatedStr) ...</pre> + </section> + + <section> + <title>Append Example</title> - <p><em>Append example</em></p> <p><em>DO</em></p> <pre> > lists:append([[1], [2], [3]]). [1,2,3] ></pre> + <p><em>DO NOT</em></p> <pre> > lists:flatten([[1], [2], [3]]). [1,2,3] ></pre> + </section> </section> <section> - <title>Why you should not worry about recursive lists functions</title> + <title>Recursive List Functions</title> - <p>In the performance myth chapter, the following myth was exposed: - <seealso marker="myths#tail_recursive">Tail-recursive functions - are MUCH faster than recursive functions</seealso>.</p> + <p>In Section 7.2, the following myth was exposed: + <seealso marker="myths#tail_recursive">Tail-Recursive Functions + are Much Faster Than Recursive Functions</seealso>.</p> <p>To summarize, in R12B there is usually not much difference between a body-recursive list function and tail-recursive function that reverses the list at the end. Therefore, concentrate on writing beautiful code - and forget about the performance of your list functions. In the time-critical - parts of your code (and only there), <em>measure</em> before rewriting - your code.</p> - - <p><em>Important note</em>: This section talks about lists functions that - <em>construct</em> lists. A tail-recursive function that does not construct - a list runs in constant space, while the corresponding body-recursive - function uses stack space proportional to the length of the list. - For instance, a function that sums a list of integers, should <em>not</em> be - written like this</p> + and forget about the performance of your list functions. In the + time-critical parts of your code (and only there), <em>measure</em> + before rewriting your code.</p> + + <note><p>This section is about list functions that <em>construct</em> + lists. A tail-recursive function that does not construct a list runs + in constant space, while the corresponding body-recursive function + uses stack space proportional to the length of the list.</p></note> + + <p>For example, a function that sums a list of integers, is + <em>not</em> to be written as follows:</p> <p><em>DO NOT</em></p> <code type="erl"> recursive_sum([H|T]) -> H+recursive_sum(T); recursive_sum([]) -> 0.</code> - <p>but like this</p> + <p>Instead:</p> <p><em>DO</em></p> <code type="erl"> diff --git a/system/doc/efficiency_guide/myths.xml b/system/doc/efficiency_guide/myths.xml index b1108dbab2..70d2dae88e 100644 --- a/system/doc/efficiency_guide/myths.xml +++ b/system/doc/efficiency_guide/myths.xml @@ -31,47 +31,48 @@ <file>myths.xml</file> </header> + <marker id="myths"></marker> <p>Some truths seem to live on well beyond their best-before date, - perhaps because "information" spreads more rapidly from person-to-person - faster than a single release note that notes, for instance, that funs + perhaps because "information" spreads faster from person-to-person + than a single release note that says, for example, that funs have become faster.</p> - <p>Here we try to kill the old truths (or semi-truths) that have + <p>This section tries to kill the old truths (or semi-truths) that have become myths.</p> <section> - <title>Myth: Funs are slow</title> - <p>Yes, funs used to be slow. Very slow. Slower than <c>apply/3</c>. + <title>Myth: Funs are Slow</title> + <p>Funs used to be very slow, slower than <c>apply/3</c>. Originally, funs were implemented using nothing more than compiler trickery, ordinary tuples, <c>apply/3</c>, and a great deal of ingenuity.</p> - <p>But that is ancient history. Funs was given its own data type - in the R6B release and was further optimized in the R7B release. - Now the cost for a fun call falls roughly between the cost for a call to - local function and <c>apply/3</c>.</p> + <p>But that is history. Funs was given its own data type + in R6B and was further optimized in R7B. + Now the cost for a fun call falls roughly between the cost for a call + to a local function and <c>apply/3</c>.</p> </section> <section> - <title>Myth: List comprehensions are slow</title> + <title>Myth: List Comprehensions are Slow</title> <p>List comprehensions used to be implemented using funs, and in the - bad old days funs were really slow.</p> + old days funs were indeed slow.</p> - <p>Nowadays the compiler rewrites list comprehensions into an ordinary - recursive function. Of course, using a tail-recursive function with + <p>Nowadays, the compiler rewrites list comprehensions into an ordinary + recursive function. Using a tail-recursive function with a reverse at the end would be still faster. Or would it? That leads us to the next myth.</p> </section> <section> - <title>Myth: Tail-recursive functions are MUCH faster - than recursive functions</title> + <title>Myth: Tail-Recursive Functions are Much Faster + Than Recursive Functions</title> <p><marker id="tail_recursive"></marker>According to the myth, recursive functions leave references - to dead terms on the stack and the garbage collector will have to - copy all those dead terms, while tail-recursive functions immediately + to dead terms on the stack and the garbage collector has to copy + all those dead terms, while tail-recursive functions immediately discard those terms.</p> <p>That used to be true before R7B. In R7B, the compiler started @@ -79,48 +80,47 @@ be used with an empty list, so that the garbage collector would not keep dead values any longer than necessary.</p> - <p>Even after that optimization, a tail-recursive function would - still most of the time be faster than a body-recursive function. Why?</p> + <p>Even after that optimization, a tail-recursive function is + still most of the times faster than a body-recursive function. Why?</p> <p>It has to do with how many words of stack that are used in each - recursive call. In most cases, a recursive function would use more words + recursive call. In most cases, a recursive function uses more words on the stack for each recursion than the number of words a tail-recursive - would allocate on the heap. Since more memory is used, the garbage - collector will be invoked more frequently, and it will have more work traversing + would allocate on the heap. As more memory is used, the garbage + collector is invoked more frequently, and it has more work traversing the stack.</p> - <p>In R12B and later releases, there is an optimization that will + <p>In R12B and later releases, there is an optimization that in many cases reduces the number of words used on the stack in - body-recursive calls, so that a body-recursive list function and + body-recursive calls. A body-recursive list function and a tail-recursive function that calls <seealso - marker="stdlib:lists#reverse/1">lists:reverse/1</seealso> at the - end will use exactly the same amount of memory. + marker="stdlib:lists#reverse/1">lists:reverse/1</seealso> at + the end will use the same amount of memory. <c>lists:map/2</c>, <c>lists:filter/2</c>, list comprehensions, and many other recursive functions now use the same amount of space as their tail-recursive equivalents.</p> - <p>So which is faster?</p> + <p>So, which is faster? + It depends. On Solaris/Sparc, the body-recursive function seems to + be slightly faster, even for lists with a lot of elements. On the x86 + architecture, tail-recursion was up to about 30% faster.</p> - <p>It depends. On Solaris/Sparc, the body-recursive function seems to - be slightly faster, even for lists with very many elements. On the x86 - architecture, tail-recursion was up to about 30 percent faster.</p> - - <p>So the choice is now mostly a matter of taste. If you really do need + <p>So, the choice is now mostly a matter of taste. If you really do need the utmost speed, you must <em>measure</em>. You can no longer be - absolutely sure that the tail-recursive list function will be the fastest - in all circumstances.</p> + sure that the tail-recursive list function always is the fastest.</p> - <p>Note: A tail-recursive function that does not need to reverse the - list at the end is, of course, faster than a body-recursive function, + <note><p>A tail-recursive function that does not need to reverse the + list at the end is faster than a body-recursive function, as are tail-recursive functions that do not construct any terms at all - (for instance, a function that sums all integers in a list).</p> + (for example, a function that sums all integers in a list).</p></note> </section> <section> - <title>Myth: '++' is always bad</title> + <title>Myth: Operator "++" is Always Bad</title> - <p>The <c>++</c> operator has, somewhat undeservedly, got a very bad reputation. - It probably has something to do with code like</p> + <p>The <c>++</c> operator has, somewhat undeservedly, got a bad reputation. + It probably has something to do with code like the following, + which is the most inefficient way there is to reverse a list:</p> <p><em>DO NOT</em></p> <code type="erl"> @@ -129,12 +129,10 @@ naive_reverse([H|T]) -> naive_reverse([]) -> [].</code> - <p>which is the most inefficient way there is to reverse a list. - Since the <c>++</c> operator copies its left operand, the result - will be copied again and again and again... leading to quadratic - complexity.</p> + <p>As the <c>++</c> operator copies its left operand, the result + is copied repeatedly, leading to quadratic complexity.</p> - <p>On the other hand, using <c>++</c> like this</p> + <p>But using <c>++</c> as follows is not bad:</p> <p><em>OK</em></p> <code type="erl"> @@ -143,11 +141,11 @@ naive_but_ok_reverse([H|T], Acc) -> naive_but_ok_reverse([], Acc) -> Acc.</code> - <p>is not bad. Each list element will only be copied once. + <p>Each list element is copied only once. The growing result <c>Acc</c> is the right operand - for the <c>++</c> operator, and it will <em>not</em> be copied.</p> + for the <c>++</c> operator, and it is <em>not</em> copied.</p> - <p>Of course, experienced Erlang programmers would actually write</p> + <p>Experienced Erlang programmers would write as follows:</p> <p><em>DO</em></p> <code type="erl"> @@ -156,32 +154,34 @@ vanilla_reverse([H|T], Acc) -> vanilla_reverse([], Acc) -> Acc.</code> - <p>which is slightly more efficient because you don't build a - list element only to directly copy it. (Or it would be more efficient - if the the compiler did not automatically rewrite <c>[H]++Acc</c> + <p>This is slightly more efficient because here you do not build a + list element only to copy it directly. (Or it would be more efficient + if the compiler did not automatically rewrite <c>[H]++Acc</c> to <c>[H|Acc]</c>.)</p> </section> <section> - <title>Myth: Strings are slow</title> - - <p>Actually, string handling could be slow if done improperly. - In Erlang, you'll have to think a little more about how the strings - are used and choose an appropriate representation and use - the <seealso marker="stdlib:re">re</seealso> module instead of the obsolete - <c>regexp</c> module if you are going to use regular expressions.</p> + <title>Myth: Strings are Slow</title> + + <p>String handling can be slow if done improperly. + In Erlang, you need to think a little more about how the strings + are used and choose an appropriate representation. If you + use regular expressions, use the + <seealso marker="stdlib:re">re</seealso> module in STDLIB + instead of the obsolete <c>regexp</c> module.</p> </section> <section> - <title>Myth: Repairing a Dets file is very slow</title> + <title>Myth: Repairing a Dets File is Very Slow</title> <p>The repair time is still proportional to the number of records - in the file, but Dets repairs used to be much, much slower in the past. + in the file, but Dets repairs used to be much slower in the past. Dets has been massively rewritten and improved.</p> </section> <section> - <title>Myth: BEAM is a stack-based byte-code virtual machine (and therefore slow)</title> + <title>Myth: BEAM is a Stack-Based Byte-Code Virtual Machine + (and Therefore Slow)</title> <p>BEAM is a register-based virtual machine. It has 1024 virtual registers that are used for holding temporary values and for passing arguments when @@ -193,11 +193,11 @@ vanilla_reverse([], Acc) -> </section> <section> - <title>Myth: Use '_' to speed up your program when a variable is not used</title> + <title>Myth: Use "_" to Speed Up Your Program When a Variable + is Not Used</title> - <p>That was once true, but since R6B the BEAM compiler is quite capable of seeing itself + <p>That was once true, but from R6B the BEAM compiler can see that a variable is not used.</p> </section> - </chapter> diff --git a/system/doc/efficiency_guide/processes.xml b/system/doc/efficiency_guide/processes.xml index 86951e2dcc..3bdc314235 100644 --- a/system/doc/efficiency_guide/processes.xml +++ b/system/doc/efficiency_guide/processes.xml @@ -30,15 +30,15 @@ </header> <section> - <title>Creation of an Erlang process</title> + <title>Creating an Erlang Process</title> - <p>An Erlang process is lightweight compared to operating - systems threads and processes.</p> + <p>An Erlang process is lightweight compared to threads and + processes in operating systems.</p> <p>A newly spawned Erlang process uses 309 words of memory in the non-SMP emulator without HiPE support. (SMP support - and HiPE support will both add to this size.) The size can - be found out like this:</p> + and HiPE support both add to this size.) The size can + be found as follows:</p> <pre> Erlang (BEAM) emulator version 5.6 [async-threads:0] [kernel-poll:false] @@ -51,11 +51,11 @@ Eshell V5.6 (abort with ^G) 3> <input>Bytes div erlang:system_info(wordsize).</input> 309</pre> - <p>The size includes 233 words for the heap area (which includes the stack). - The garbage collector will increase the heap as needed.</p> + <p>The size includes 233 words for the heap area (which includes the + stack). The garbage collector increases the heap as needed.</p> <p>The main (outer) loop for a process <em>must</em> be tail-recursive. - If not, the stack will grow until the process terminates.</p> + Otherwise, the stack grows until the process terminates.</p> <p><em>DO NOT</em></p> <code type="erl"> @@ -74,7 +74,7 @@ loop() -> <p>The call to <c>io:format/2</c> will never be executed, but a return address will still be pushed to the stack each time <c>loop/0</c> is called recursively. The correct tail-recursive - version of the function looks like this:</p> + version of the function looks as follows:</p> <p><em>DO</em></p> <code type="erl"> @@ -90,92 +90,98 @@ loop() -> end.</code> <section> - <title>Initial heap size</title> + <title>Initial Heap Size</title> <p>The default initial heap size of 233 words is quite conservative - in order to support Erlang systems with hundreds of thousands or - even millions of processes. The garbage collector will grow and - shrink the heap as needed.</p> + to support Erlang systems with hundreds of thousands or + even millions of processes. The garbage collector grows and + shrinks the heap as needed.</p> <p>In a system that use comparatively few processes, performance - <em>might</em> be improved by increasing the minimum heap size using either - the <c>+h</c> option for + <em>might</em> be improved by increasing the minimum heap size + using either the <c>+h</c> option for <seealso marker="erts:erl">erl</seealso> or on a process-per-process basis using the <c>min_heap_size</c> option for <seealso marker="erts:erlang#spawn_opt/4">spawn_opt/4</seealso>.</p> - <p>The gain is twofold: Firstly, although the garbage collector will - grow the heap, it will grow it step by step, which will be more - costly than directly establishing a larger heap when the process - is spawned. Secondly, the garbage collector may also shrink the - heap if it is much larger than the amount of data stored on it; - setting the minimum heap size will prevent that.</p> - - <warning><p>The emulator will probably use more memory, and because garbage - collections occur less frequently, huge binaries could be + <p>The gain is twofold:</p> + <list type="bulleted"> + <item>Although the garbage collector grows the heap, it grows it + step-by-step, which is more costly than directly establishing a + larger heap when the process is spawned.</item> + <item>The garbage collector can also shrink the heap if it is + much larger than the amount of data stored on it; + setting the minimum heap size prevents that.</item> + </list> + + <warning><p>The emulator probably uses more memory, and because garbage + collections occur less frequently, huge binaries can be kept much longer.</p></warning> <p>In systems with many processes, computation tasks that run - for a short time could be spawned off into a new process with - a higher minimum heap size. When the process is done, it will - send the result of the computation to another process and terminate. - If the minimum heap size is calculated properly, the process may not - have to do any garbage collections at all. - <em>This optimization should not be attempted + for a short time can be spawned off into a new process with + a higher minimum heap size. When the process is done, it sends + the result of the computation to another process and terminates. + If the minimum heap size is calculated properly, the process might + not have to do any garbage collections at all. + <em>This optimization is not to be attempted without proper measurements.</em></p> </section> - </section> <section> - <title>Process messages</title> + <title>Process Messages</title> - <p>All data in messages between Erlang processes is copied, with - the exception of + <p>All data in messages between Erlang processes is copied, + except for <seealso marker="binaryhandling#refc_binary">refc binaries</seealso> on the same Erlang node.</p> <p>When a message is sent to a process on another Erlang node, - it will first be encoded to the Erlang External Format before - being sent via an TCP/IP socket. The receiving Erlang node decodes - the message and distributes it to the right process.</p> + it is first encoded to the Erlang External Format before + being sent through a TCP/IP socket. The receiving Erlang node decodes + the message and distributes it to the correct process.</p> <section> - <title>The constant pool</title> + <title>Constant Pool</title> <p>Constant Erlang terms (also called <em>literals</em>) are now kept in constant pools; each loaded module has its own pool. - The following function</p> + The following function does no longer build the tuple every time + it is called (only to have it discarded the next time the garbage + collector was run), but the tuple is located in the module's + constant pool:</p> <p><em>DO</em> (in R12B and later)</p> <code type="erl"> days_in_month(M) -> - element(M, {31,28,31,30,31,30,31,31,30,31,30,31}).</code> - - <p>will no longer build the tuple every time it is called (only - to have it discarded the next time the garbage collector was run), but - the tuple will be located in the module's constant pool.</p> + element(M, {31,28,31,30,31,30,31,31,30,31,30,31}).</code> <p>But if a constant is sent to another process (or stored in - an ETS table), it will be <em>copied</em>. - The reason is that the run-time system must be able - to keep track of all references to constants in order to properly - unload code containing constants. (When the code is unloaded, - the constants will be copied to the heap of the processes that refer + an Ets table), it is <em>copied</em>. + The reason is that the runtime system must be able + to keep track of all references to constants to unload code + containing constants properly. (When the code is unloaded, + the constants are copied to the heap of the processes that refer to them.) The copying of constants might be eliminated in a future - release.</p> + Erlang/OTP release.</p> </section> <section> - <title>Loss of sharing</title> + <title>Loss of Sharing</title> - <p>Shared sub-terms are <em>not</em> preserved when a term is sent - to another process, passed as the initial process arguments in - the <c>spawn</c> call, or stored in an ETS table. - That is an optimization. Most applications do not send messages - with shared sub-terms.</p> + <p>Shared subterms are <em>not</em> preserved in the following + cases:</p> + <list type="bulleted"> + <item>When a term is sent to another process</item> + <item>When a term is passed as the initial process arguments in + the <c>spawn</c> call</item> + <item>When a term is stored in an Ets table</item> + </list> + <p>That is an optimization. Most applications do not send messages + with shared subterms.</p> - <p>Here is an example of how a shared sub-term can be created:</p> + <p>The following example shows how a shared subterm can be created:</p> <code type="erl"> kilo_byte() -> @@ -186,32 +192,32 @@ kilo_byte(0, Acc) -> kilo_byte(N, Acc) -> kilo_byte(N-1, [Acc|Acc]).</code> - <p><c>kilo_byte/0</c> creates a deep list. If we call - <c>list_to_binary/1</c>, we can convert the deep list to a binary - of 1024 bytes:</p> + <p><c>kilo_byte/1</c> creates a deep list. + If <c>list_to_binary/1</c> is called, the deep list can be + converted to a binary of 1024 bytes:</p> <pre> 1> <input>byte_size(list_to_binary(efficiency_guide:kilo_byte())).</input> 1024</pre> - <p>Using the <c>erts_debug:size/1</c> BIF we can see that the + <p>Using the <c>erts_debug:size/1</c> BIF, it can be seen that the deep list only requires 22 words of heap space:</p> <pre> 2> <input>erts_debug:size(efficiency_guide:kilo_byte()).</input> 22</pre> - <p>Using the <c>erts_debug:flat_size/1</c> BIF, we can calculate - the size of the deep list if sharing is ignored. It will be + <p>Using the <c>erts_debug:flat_size/1</c> BIF, the size of the + deep list can be calculated if sharing is ignored. It becomes the size of the list when it has been sent to another process - or stored in an ETS table:</p> + or stored in an Ets table:</p> <pre> 3> <input>erts_debug:flat_size(efficiency_guide:kilo_byte()).</input> 4094</pre> - <p>We can verify that sharing will be lost if we insert the - data into an ETS table:</p> + <p>It can be verified that sharing will be lost if the data is + inserted into an Ets table:</p> <pre> 4> <input>T = ets:new(tab, []).</input> @@ -223,21 +229,21 @@ true 7> <input>erts_debug:flat_size(element(2, hd(ets:lookup(T, key)))).</input> 4094</pre> - <p>When the data has passed through an ETS table, + <p>When the data has passed through an Ets table, <c>erts_debug:size/1</c> and <c>erts_debug:flat_size/1</c> return the same value. Sharing has been lost.</p> - <p>In a future release of Erlang/OTP, we might implement a - way to (optionally) preserve sharing. We have no plans to make - preserving of sharing the default behaviour, since that would + <p>In a future Erlang/OTP release, it might be implemented a + way to (optionally) preserve sharing. There are no plans to make + preserving of sharing the default behaviour, as that would penalize the vast majority of Erlang applications.</p> </section> </section> <section> - <title>The SMP emulator</title> + <title>SMP Emulator</title> - <p>The SMP emulator (introduced in R11B) will take advantage of a + <p>The SMP emulator (introduced in R11B) takes advantage of a multi-core or multi-CPU computer by running several Erlang scheduler threads (typically, the same as the number of cores). Each scheduler thread schedules Erlang processes in the same way as the Erlang scheduler @@ -247,11 +253,11 @@ true <em>must have more than one runnable Erlang process</em> most of the time. Otherwise, the Erlang emulator can still only run one Erlang process at the time, but you must still pay the overhead for locking. Although - we try to reduce the locking overhead as much as possible, it will never - become exactly zero.</p> + Erlang/OTP tries to reduce the locking overhead as much as possible, + it will never become exactly zero.</p> - <p>Benchmarks that may seem to be concurrent are often sequential. - The estone benchmark, for instance, is entirely sequential. So is also + <p>Benchmarks that appear to be concurrent are often sequential. + The estone benchmark, for example, is entirely sequential. So is the most common implementation of the "ring benchmark"; usually one process is active, while the others wait in a <c>receive</c> statement.</p> @@ -259,6 +265,5 @@ true can be used to profile your application to see how much potential (or lack thereof) it has for concurrency.</p> </section> - </chapter> diff --git a/system/doc/efficiency_guide/profiling.xml b/system/doc/efficiency_guide/profiling.xml index b93c884270..5df12eefe0 100644 --- a/system/doc/efficiency_guide/profiling.xml +++ b/system/doc/efficiency_guide/profiling.xml @@ -30,190 +30,197 @@ </header> <section> - <title>Do not guess about performance - profile</title> + <title>Do Not Guess About Performance - Profile</title> <p>Even experienced software developers often guess wrong about where - the performance bottlenecks are in their programs.</p> - - <p>Therefore, profile your program to see where the performance + the performance bottlenecks are in their programs. Therefore, profile + your program to see where the performance bottlenecks are and concentrate on optimizing them.</p> - <p>Erlang/OTP contains several tools to help finding bottlenecks.</p> + <p>Erlang/OTP contains several tools to help finding bottlenecks:</p> + + <list type="bulleted"> + <item><c>fprof</c> provides the most detailed information about + where the program time is spent, but it significantly slows down the + program it profiles.</item> - <p><c>fprof</c> provide the most detailed information - about where the time is spent, but it significantly slows down the - program it profiles.</p> + <item><p><c>eprof</c> provides time information of each function + used in the program. No call graph is produced, but <c>eprof</c> has + considerable less impact on the program it profiles.</p> + <p>If the program is too large to be profiled by <c>fprof</c> or + <c>eprof</c>, the <c>cover</c> and <c>cprof</c> tools can be used + to locate code parts that are to be more thoroughly profiled using + <c>fprof</c> or <c>eprof</c>.</p></item> - <p><c>eprof</c> provides time information of each function used - in the program. No callgraph is produced but <c>eprof</c> has - considerable less impact on the program profiled.</p> + <item><c>cover</c> provides execution counts per line per + process, with less overhead than <c>fprof</c>. Execution counts + can, with some caution, be used to locate potential performance + bottlenecks.</item> - <p>If the program is too big to be profiled by <c>fprof</c> or <c>eprof</c>, - <c>cover</c> and <c>cprof</c> could be used to locate parts of the - code that should be more thoroughly profiled using <c>fprof</c> or - <c>eprof</c>.</p> + <item><c>cprof</c> is the most lightweight tool, but it only + provides execution counts on a function basis (for all processes, + not per process).</item> + </list> - <p><c>cover</c> provides execution counts per line per process, - with less overhead than <c>fprof</c>. Execution counts can - with some caution be used to locate potential performance bottlenecks. - The most lightweight tool is <c>cprof</c>, but it only provides execution - counts on a function basis (for all processes, not per process).</p> + <p>The tools are further described in + <seealso marker="#profiling_tools">Tools</seealso>.</p> </section> <section> - <title>Big systems</title> - <p>If you have a big system it might be interesting to run profiling + <title>Large Systems</title> + <p>For a large system, it can be interesting to run profiling on a simulated and limited scenario to start with. But bottlenecks - have a tendency to only appear or cause problems when - there are many things going on at the same time, and when there - are many nodes involved. Therefore it is desirable to also run + have a tendency to appear or cause problems only when + many things are going on at the same time, and when + many nodes are involved. Therefore, it is also desirable to run profiling in a system test plant on a real target system.</p> - <p>When your system is big you do not want to run the profiling - tools on the whole system. You want to concentrate on processes - and modules that you know are central and stand for a big part of the - execution.</p> + + <p>For a large system, you do not want to run the profiling + tools on the whole system. Instead you want to concentrate on + central processes and modules, which contribute for a big part + of the execution.</p> </section> <section> - <title>What to look for</title> - <p>When analyzing the result file from the profiling activity - you should look for functions that are called many + <title>What to Look For</title> + <p>When analyzing the result file from the profiling activity, + look for functions that are called many times and have a long "own" execution time (time excluding calls - to other functions). Functions that just are called very - many times can also be interesting, as even small things can add - up to quite a bit if they are repeated often. Then you need to - ask yourself what can I do to reduce this time. Appropriate - types of questions to ask yourself are: </p> + to other functions). Functions that are called a lot of + times can also be interesting, as even small things can add + up to quite a bit if repeated often. Also + ask yourself what you can do to reduce this time. The following + are appropriate types of questions to ask yourself:</p> + <list type="bulleted"> - <item>Can I reduce the number of times the function is called?</item> - <item>Are there tests that can be run less often if I change - the order of tests?</item> - <item>Are there redundant tests that can be removed? </item> - <item>Is there some expression calculated giving the same result - each time? </item> - <item>Are there other ways of doing this that are equivalent and + <item>Is it possible to reduce the number of times the function + is called?</item> + <item>Can any test be run less often if the order of tests is + changed?</item> + <item>Can any redundant tests be removed?</item> + <item>Does any calculated expression give the same result + each time?</item> + <item>Are there other ways to do this that are equivalent and more efficient?</item> - <item>Can I use another internal data representation to make - things more efficient? </item> + <item>Can another internal data representation be used to make + things more efficient?</item> </list> - <p>These questions are not always trivial to answer. You might - need to do some benchmarks to back up your theory, to avoid - making things slower if your theory is wrong. See <seealso marker="#benchmark">benchmarking</seealso>.</p> + + <p>These questions are not always trivial to answer. Some + benchmarks might be needed to back up your theory and to avoid + making things slower if your theory is wrong. For details, see + <seealso marker="#benchmark">Benchmarking</seealso>.</p> </section> <section> <title>Tools</title> - + <marker id="profiling_tools"></marker> <section> <title>fprof</title> - <p> - <c>fprof</c> measures the execution time for each function, - both own time i.e how much time a function has used for its - own execution, and accumulated time i.e. including called - functions. The values are displayed per process. You also get - to know how many times each function has been - called. <c>fprof</c> is based on trace to file in order to - minimize runtime performance impact. Using fprof is just a - matter of calling a few library functions, see - <seealso marker="tools:fprof">fprof</seealso> - manual page under the application tools.<c> fprof</c> was introduced in - version R8 of Erlang/OTP. - </p> + <p><c>fprof</c> measures the execution time for each function, + both own time, that is, how much time a function has used for its + own execution, and accumulated time, that is, including called + functions. The values are displayed per process. You also get + to know how many times each function has been called.</p> + + <p><c>fprof</c> is based on trace to file to minimize runtime + 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> </section> - <section> - <title>eprof</title> - <p> - <c>eprof</c> is based on the Erlang trace_info BIFs. Eprof shows how much time has been used by - each process, and in which function calls this time has been - spent. Time is shown as percentage of total time and absolute time. - See <seealso marker="tools:eprof">eprof</seealso> for - additional information. - </p> - </section> + <section> + <title>eprof</title> + <p><c>eprof</c> is based on the Erlang <c>trace_info</c> BIFs. + <c>eprof</c> shows how much time has been used by each process, + 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> + </section> <section> <title>cover</title> - <p> - <c>cover</c>'s primary use is coverage analysis to verify - test cases, making sure all relevant code is covered. - <c>cover</c> counts how many times each executable line of - code is executed when a program is run. This is done on a per - module basis. Of course this information can be used to - determine what code is run very frequently and could therefore - be subject for optimization. Using cover is just a matter of - calling a few library functions, see - <seealso marker="tools:cover">cover</seealso> - manual page under the application tools.</p> + <p>The primary use of <c>cover</c> is coverage analysis to verify + test cases, making sure that all relevant code is covered. + <c>cover</c> counts how many times each executable line of code + is executed when a program is run, on a per module basis.</p> + <p>Clearly, this information can be used to determine what + code is run very frequently and can therefore be subject for + 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> </section> <section> <title>cprof</title> <p><c>cprof</c> is something in between <c>fprof</c> and - <c>cover</c> regarding features. It counts how many times each - function is called when the program is run, on a per module - basis. <c>cprof</c> has a low performance degradation effect (versus - <c>fprof</c>) and does not need to recompile - any modules to profile (versus <c>cover</c>). - See <seealso marker="tools:cprof">cprof</seealso> manual page for additional - information. - </p> + <c>cover</c> regarding features. It counts how many times each + function is called when the program is run, on a per module + basis. <c>cprof</c> has a low performance degradation effect + (compared with <c>fprof</c>) and does not need to recompile + 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> </section> <section> - <title>Tool summarization</title> + <title>Tool Summary</title> <table> <row> - <cell align="center" valign="middle">Tool</cell> - <cell align="center" valign="middle">Results</cell> - <cell align="center" valign="middle">Size of result</cell> - <cell align="center" valign="middle">Effects on program execution time</cell> - <cell align="center" valign="middle">Records number of calls</cell> - <cell align="center" valign="middle">Records Execution time</cell> - <cell align="center" valign="middle">Records called by</cell> - <cell align="center" valign="middle">Records garbage collection</cell> + <cell><em>Tool</em></cell> + <cell><em>Results</em></cell> + <cell><em>Size of Result</em></cell> + <cell><em>Effects on Program Execution Time</em></cell> + <cell><em>Records Number of Calls</em></cell> + <cell><em>Records Execution Time</em></cell> + <cell><em>Records Called by</em></cell> + <cell><em>Records Garbage Collection</em></cell> </row> <row> - <cell align="left" valign="middle"><c>fprof </c></cell> - <cell align="left" valign="middle">per process to screen/file </cell> - <cell align="left" valign="middle">large </cell> - <cell align="left" valign="middle">significant slowdown </cell> - <cell align="left" valign="middle">yes </cell> - <cell align="left" valign="middle">total and own</cell> - <cell align="left" valign="middle">yes </cell> - <cell align="left" valign="middle">yes </cell> + <cell><c>fprof</c></cell> + <cell>Per process to screen/file</cell> + <cell>Large</cell> + <cell>Significant slowdown</cell> + <cell>Yes</cell> + <cell>Total and own</cell> + <cell>Yes</cell> + <cell>Yes</cell> </row> <row> - <cell align="left" valign="middle"><c>eprof </c></cell> - <cell align="left" valign="middle">per process/function to screen/file </cell> - <cell align="left" valign="middle">medium </cell> - <cell align="left" valign="middle">small slowdown </cell> - <cell align="left" valign="middle">yes </cell> - <cell align="left" valign="middle">only total </cell> - <cell align="left" valign="middle">no </cell> - <cell align="left" valign="middle">no </cell> + <cell><c>eprof</c></cell> + <cell>Per process/function to screen/file</cell> + <cell>Medium</cell> + <cell>Small slowdown</cell> + <cell>Yes</cell> + <cell>Only total</cell> + <cell>No</cell> + <cell>No</cell> </row> <row> - <cell align="left" valign="middle"><c>cover </c></cell> - <cell align="left" valign="middle">per module to screen/file</cell> - <cell align="left" valign="middle">small </cell> - <cell align="left" valign="middle">moderate slowdown</cell> - <cell align="left" valign="middle">yes, per line </cell> - <cell align="left" valign="middle">no </cell> - <cell align="left" valign="middle">no </cell> - <cell align="left" valign="middle">no </cell> + <cell><c>cover</c></cell> + <cell>Per module to screen/file</cell> + <cell>Small</cell> + <cell>Moderate slowdown</cell> + <cell>Yes, per line</cell> + <cell>No</cell> + <cell>No</cell> + <cell>No</cell> </row> <row> - <cell align="left" valign="middle"><c>cprof </c></cell> - <cell align="left" valign="middle">per module to caller</cell> - <cell align="left" valign="middle">small </cell> - <cell align="left" valign="middle">small slowdown </cell> - <cell align="left" valign="middle">yes </cell> - <cell align="left" valign="middle">no </cell> - <cell align="left" valign="middle">no </cell> - <cell align="left" valign="middle">no </cell> + <cell><c>cprof</c></cell> + <cell>Per module to caller</cell> + <cell>Small</cell> + <cell>Small slowdown</cell> + <cell>Yes</cell> + <cell>No</cell> + <cell>No</cell> + <cell>No</cell> </row> - <tcaption></tcaption> + <tcaption>Tool Summary</tcaption> </table> </section> </section> @@ -226,49 +233,51 @@ implementation of a given algorithm or function is the fastest. Benchmarking is far from an exact science. Today's operating systems generally run background tasks that are difficult to turn off. - Caches and multiple CPU cores doesn't make it any easier. - It would be best to run Unix-computers in single-user mode when + Caches and multiple CPU cores does not facilitate benchmarking. + It would be best to run UNIX computers in single-user mode when benchmarking, but that is inconvenient to say the least for casual testing.</p> <p>Benchmarks can measure wall-clock time or CPU time.</p> - <p><seealso marker="stdlib:timer#tc/3">timer:tc/3</seealso> measures + <list type="bulleted"> + <item><seealso marker="stdlib:timer#tc/3">timer:tc/3</seealso> measures wall-clock time. The advantage with wall-clock time is that I/O, - swapping, and other activities in the operating-system kernel are + swapping, and other activities in the operating system kernel are included in the measurements. The disadvantage is that the - the measurements will vary wildly. Usually it is best to run the - benchmark several times and note the shortest time - that time should + measurements vary a lot. Usually it is best to run the + benchmark several times and note the shortest time, which is to be the minimum time that is possible to achieve under the best of - circumstances.</p> + circumstances.</item> - <p><seealso marker="erts:erlang#statistics/1">statistics/1</seealso> - with the argument <c>runtime</c> measures CPU time spent in the Erlang - virtual machine. The advantage is that the results are more + <item><seealso marker="erts:erlang#statistics/1">statistics/1</seealso> + with argument <c>runtime</c> measures CPU time spent in the Erlang + virtual machine. The advantage with CPU time is that the results are more consistent from run to run. The disadvantage is that the time spent in the operating system kernel (such as swapping and I/O) - are not included. Therefore, measuring CPU time is misleading if - any I/O (file or socket) is involved.</p> + is not included. Therefore, measuring CPU time is misleading if + any I/O (file or socket) is involved.</item> + </list> <p>It is probably a good idea to do both wall-clock measurements and CPU time measurements.</p> - <p>Some additional advice:</p> + <p>Some final advice:</p> <list type="bulleted"> - <item>The granularity of both types of measurement could be quite - high so you should make sure that each individual measurement + <item>The granularity of both measurement types can be high. + Therefore, ensure that each individual measurement lasts for at least several seconds.</item> - <item>To make the test fair, each new test run should run in its own, + <item>To make the test fair, each new test run is to run in its own, newly created Erlang process. Otherwise, if all tests run in the - same process, the later tests would start out with larger heap sizes - and therefore probably do less garbage collections. You could - also consider restarting the Erlang emulator between each test.</item> + same process, the later tests start out with larger heap sizes + and therefore probably do fewer garbage collections. + Also consider restarting the Erlang emulator between each test.</item> <item>Do not assume that the fastest implementation of a given algorithm - on computer architecture X also is the fastest on computer architecture Y.</item> - + on computer architecture X is also the fastest on computer architecture + Y.</item> </list> </section> </chapter> diff --git a/system/doc/efficiency_guide/tablesDatabases.xml b/system/doc/efficiency_guide/tablesDatabases.xml index 94c921fa1c..215c2afa1f 100644 --- a/system/doc/efficiency_guide/tablesDatabases.xml +++ b/system/doc/efficiency_guide/tablesDatabases.xml @@ -18,10 +18,9 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - </legalnotice> - <title>Tables and databases</title> + <title>Tables and Databases</title> <prepared>Ingela Anderton</prepared> <docno></docno> <date>2001-08-07</date> @@ -30,46 +29,45 @@ </header> <section> - <title>Ets, Dets and Mnesia</title> + <title>Ets, Dets, and Mnesia</title> <p>Every example using Ets has a corresponding example in - Mnesia. In general all Ets examples also apply to Dets tables.</p> + Mnesia. In general, all Ets examples also apply to Dets tables.</p> <section> - <title>Select/Match operations</title> - <p>Select/Match operations on Ets and Mnesia tables can become + <title>Select/Match Operations</title> + <p>Select/match operations on Ets and Mnesia tables can become very expensive operations. They usually need to scan the complete - table. You should try to structure your - data so that you minimize the need for select/match - operations. However, if you really need a select/match operation, - it will still be more efficient than using <c>tab2list</c>. - Examples of this and also of ways to avoid select/match will be provided in - some of the following sections. The functions - <c>ets:select/2</c> and <c>mnesia:select/3</c> should be preferred over - <c>ets:match/2</c>,<c>ets:match_object/2</c>, and <c>mnesia:match_object/3</c>.</p> - <note> - <p>There are exceptions when the complete table is not - scanned, for instance if part of the key is bound when searching an - <c>ordered_set</c> table, or if it is a Mnesia - table and there is a secondary index on the field that is - selected/matched. If the key is fully bound there will, of course, be - no point in doing a select/match, unless you have a bag table and - you are only interested in a sub-set of the elements with - the specific key.</p> - </note> - <p>When creating a record to be used in a select/match operation you - want most of the fields to have the value '_'. The easiest and fastest way - to do that is as follows:</p> + table. Try to structure the data to minimize the need for select/match + operations. However, if you require a select/match operation, + it is still more efficient than using <c>tab2list</c>. + Examples of this and of how to avoid select/match are provided in + the following sections. The functions + <c>ets:select/2</c> and <c>mnesia:select/3</c> are to be preferred + over <c>ets:match/2</c>, <c>ets:match_object/2</c>, and + <c>mnesia:match_object/3</c>.</p> + <p>In some circumstances, the select/match operations do not need + to scan the complete table. + For example, if part of the key is bound when searching an + <c>ordered_set</c> table, or if it is a Mnesia + table and there is a secondary index on the field that is + selected/matched. If the key is fully bound, there is + no point in doing a select/match, unless you have a bag table + and are only interested in a subset of the elements with + the specific key.</p> + <p>When creating a record to be used in a select/match operation, you + want most of the fields to have the value "_". The easiest and + fastest way to do that is as follows:</p> <pre> #person{age = 42, _ = '_'}. </pre> </section> <section> - <title>Deleting an element</title> - <p>The delete operation is considered + <title>Deleting an Element</title> + <p>The <c>delete</c> operation is considered successful if the element was not present in the table. Hence all attempts to check that the element is present in the Ets/Mnesia table before deletion are unnecessary. Here follows - an example for Ets tables.</p> + an example for Ets tables:</p> <p><em>DO</em></p> <pre> ... @@ -88,14 +86,16 @@ end, </section> <section> - <title>Data fetching</title> - <p>Do not fetch data that you already have! Consider that you - have a module that handles the abstract data type Person. You - export the interface function <c>print_person/1</c> that uses the internal functions - <c>print_name/1</c>, <c>print_age/1</c>, <c>print_occupation/1</c>.</p> + <title>Fetching Data</title> + <p>Do not fetch data that you already have.</p> + <p>Consider that you have a module that handles the abstract data + type <c>Person</c>. You export the interface function + <c>print_person/1</c>, which uses the internal functions + <c>print_name/1</c>, <c>print_age/1</c>, and + <c>print_occupation/1</c>.</p> <note> - <p>If the functions <c>print_name/1</c> and so on, had been interface - functions the matter comes in to a whole new light, as you + <p>If the function <c>print_name/1</c>, and so on, had been interface + functions, the situation would have been different, as you do not want the user of the interface to know about the internal data representation. </p> </note> @@ -136,7 +136,7 @@ print_person(PersonId) -> io:format("No person with ID = ~p~n", [PersonID]) end. -%%% Internal functions +%%% Internal functionss print_name(PersonID) -> [Person] = ets:lookup(person, PersonId), io:format("No person ~p~n", [Person#person.name]). @@ -151,31 +151,31 @@ print_occupation(PersonID) -> </section> <section> - <title>Non-persistent data storage </title> + <title>Non-Persistent Database Storage</title> <p>For non-persistent database storage, prefer Ets tables over - Mnesia local_content tables. Even the Mnesia <c>dirty_write</c> + Mnesia <c>local_content</c> tables. Even the Mnesia <c>dirty_write</c> operations carry a fixed overhead compared to Ets writes. Mnesia must check if the table is replicated or has indices, this involves at least one Ets lookup for each - <c>dirty_write</c>. Thus, Ets writes will always be faster than + <c>dirty_write</c>. Thus, Ets writes is always faster than Mnesia writes.</p> </section> <section> <title>tab2list</title> - <p>Assume we have an Ets-table, which uses <c>idno</c> as key, - and contains:</p> + <p>Assuming an Ets table that uses <c>idno</c> as key + and contains the following:</p> <pre> [#person{idno = 1, name = "Adam", age = 31, occupation = "mailman"}, #person{idno = 2, name = "Bryan", age = 31, occupation = "cashier"}, #person{idno = 3, name = "Bryan", age = 35, occupation = "banker"}, #person{idno = 4, name = "Carl", age = 25, occupation = "mailman"}]</pre> - <p>If we <em>must</em> return all data stored in the Ets-table we - can use <c>ets:tab2list/1</c>. However, usually we are only + <p>If you <em>must</em> return all data stored in the Ets table, you + can use <c>ets:tab2list/1</c>. However, usually you are only interested in a subset of the information in which case - <c>ets:tab2list/1</c> is expensive. If we only want to extract - one field from each record, e.g., the age of every person, we - should use:</p> + <c>ets:tab2list/1</c> is expensive. If you only want to extract + one field from each record, for example, the age of every person, + then:</p> <p><em>DO</em></p> <pre> ... @@ -192,8 +192,8 @@ ets:select(Tab,[{ #person{idno='_', TabList = ets:tab2list(Tab), lists:map(fun(X) -> X#person.age end, TabList), ...</pre> - <p>If we are only interested in the age of all persons named - Bryan, we should:</p> + <p>If you are only interested in the age of all persons named + "Bryan", then:</p> <p><em>DO</em></p> <pre> ... @@ -224,8 +224,8 @@ BryanList = lists:filter(fun(X) -> X#person.name == "Bryan" end, TabList), lists:map(fun(X) -> X#person.age end, BryanList), ...</pre> - <p>If we need all information stored in the Ets table about - persons named Bryan we should:</p> + <p>If you need all information stored in the Ets table about + persons named "Bryan", then:</p> <p><em>DO</em></p> <pre> ... @@ -243,60 +243,60 @@ lists:filter(fun(X) -> X#person.name == "Bryan" end, TabList), </section> <section> - <title>Ordered_set tables</title> - <p>If the data in the table should be accessed so that the order + <title>Ordered_set Tables</title> + <p>If the data in the table is to be accessed so that the order of the keys in the table is significant, the table type - <c>ordered_set</c> could be used instead of the more usual + <c>ordered_set</c> can be used instead of the more usual <c>set</c> table type. An <c>ordered_set</c> is always - traversed in Erlang term order with regard to the key field - so that return values from functions such as <c>select</c>, + traversed in Erlang term order regarding the key field + so that the return values from functions such as <c>select</c>, <c>match_object</c>, and <c>foldl</c> are ordered by the key values. Traversing an <c>ordered_set</c> with the <c>first</c> and <c>next</c> operations also returns the keys ordered.</p> <note> <p>An <c>ordered_set</c> only guarantees that - objects are processed in <em>key</em> order. Results from functions as - <c>ets:select/2</c> appear in the <em>key</em> order even if + objects are processed in <em>key</em> order. + Results from functions such as + <c>ets:select/2</c> appear in <em>key</em> order even if the key is not included in the result.</p> </note> </section> </section> <section> - <title>Ets specific</title> + <title>Ets-Specific</title> <section> - <title>Utilizing the keys of the Ets table</title> - <p>An Ets table is a single key table (either a hash table or a - tree ordered by the key) and should be used as one. In other + <title>Using Keys of Ets Table</title> + <p>An Ets table is a single-key table (either a hash table or a + tree ordered by the key) and is to be used as one. In other words, use the key to look up things whenever possible. A - lookup by a known key in a set Ets table is constant and for a - ordered_set Ets table it is O(logN). A key lookup is always + lookup by a known key in a <c>set</c> Ets table is constant and for + an <c>ordered_set</c> Ets table it is O(logN). A key lookup is always preferable to a call where the whole table has to be - scanned. In the examples above, the field <c>idno</c> is the + scanned. In the previous examples, the field <c>idno</c> is the key of the table and all lookups where only the name is known - will result in a complete scan of the (possibly large) table + result in a complete scan of the (possibly large) table for a matching result.</p> <p>A simple solution would be to use the <c>name</c> field as the key instead of the <c>idno</c> field, but that would cause - problems if the names were not unique. A more general solution - would be to create a second table with <c>name</c> as key and - <c>idno</c> as data, i.e. to index (invert) the table with regards - to the <c>name</c> field. The second table would of course have to be - kept consistent with the master table. Mnesia could do this - for you, but a home brew index table could be very efficient + problems if the names were not unique. A more general solution would + be to create a second table with <c>name</c> as key and + <c>idno</c> as data, that is, to index (invert) the table regarding + the <c>name</c> field. Clearly, the second table would have to be + kept consistent with the master table. Mnesia can do this + for you, but a home brew index table can be very efficient compared to the overhead involved in using Mnesia.</p> <p>An index table for the table in the previous examples would - have to be a bag (as keys would appear more than once) and could + have to be a bag (as keys would appear more than once) and can have the following contents:</p> <pre> - [#index_entry{name="Adam", idno=1}, #index_entry{name="Bryan", idno=2}, #index_entry{name="Bryan", idno=3}, #index_entry{name="Carl", idno=4}]</pre> - <p>Given this index table a lookup of the <c>age</c> fields for - all persons named "Bryan" could be done like this:</p> + <p>Given this index table, a lookup of the <c>age</c> fields for + all persons named "Bryan" can be done as follows:</p> <pre> ... MatchingIDs = ets:lookup(IndexTable,"Bryan"), @@ -306,30 +306,31 @@ lists:map(fun(#index_entry{idno = ID}) -> end, MatchingIDs), ...</pre> - <p>Note that the code above never uses <c>ets:match/2</c> but - instead utilizes the <c>ets:lookup/2</c> call. The + <p>Notice that this code never uses <c>ets:match/2</c> but + instead uses the <c>ets:lookup/2</c> call. The <c>lists:map/2</c> call is only used to traverse the <c>idno</c>s - matching the name "Bryan" in the table; therefore the number of lookups + matching the name "Bryan" in the table; thus the number of lookups in the master table is minimized.</p> <p>Keeping an index table introduces some overhead when - inserting records in the table, therefore the number of operations - gained from the table has to be weighted against the number of - operations inserting objects in the table. However, note that the gain when - the key can be used to lookup elements is significant.</p> + inserting records in the table. The number of operations gained + from the table must therefore be compared against the number of + operations inserting objects in the table. However, notice that the + gain is significant when the key can be used to lookup elements.</p> </section> </section> <section> - <title>Mnesia specific</title> + <title>Mnesia-Specific</title> <section> - <title>Secondary index</title> + <title>Secondary Index</title> <p>If you frequently do a lookup on a field that is not the - key of the table, you will lose performance using - "mnesia:select/match_object" as this function will traverse the - whole table. You may create a secondary index instead and + key of the table, you lose performance using + "mnesia:select/match_object" as this function traverses the + whole table. You can create a secondary index instead and use "mnesia:index_read" to get faster access, however this - will require more memory. Example:</p> + requires more memory.</p> + <p><em>Example</em></p> <pre> -record(person, {idno, name, age, occupation}). ... @@ -347,14 +348,15 @@ PersonsAge42 = <section> <title>Transactions </title> - <p>Transactions is a way to guarantee that the distributed + <p>Using transactions is a way to guarantee that the distributed Mnesia database remains consistent, even when many different - processes update it in parallel. However if you have - real time requirements it is recommended to use dirty - operations instead of transactions. When using the dirty - operations you lose the consistency guarantee, this is usually + processes update it in parallel. However, if you have + real-time requirements it is recommended to use <c>dirty</c> + operations instead of transactions. When using <c>dirty</c> + operations, you lose the consistency guarantee; this is usually solved by only letting one process update the table. Other - processes have to send update requests to that process.</p> + processes must send update requests to that process.</p> + <p><em>Example</em></p> <pre> ... % Using transaction diff --git a/system/doc/embedded/embedded_nt.xml b/system/doc/embedded/embedded_nt.xml index 530e3663e4..2e3b32eb84 100644 --- a/system/doc/embedded/embedded_nt.xml +++ b/system/doc/embedded/embedded_nt.xml @@ -31,54 +31,47 @@ <rev>PA2</rev> <file>embedded_nt.xml</file> </header> - <p>This chapter describes the OS specific parts of OTP which relate - to Windows NT. - </p> + <marker id="windows nt"></marker> + <p>This section describes the operating system-specific parts of OTP + that relate to Windows NT.</p> + <p>A normal installation of Windows NT 4.0, with Service Pack 4 or + later, is required for an embedded Windows NT running OTP.</p> <section> - <title>Introduction</title> - <p>A normal installation of NT 4.0, with service pack 4 or later, - is required for an embedded Windows NT running OTP.</p> + <title>Memory Use</title> + <p>RAM memory of 96 MB is recommended to run OTP on Windows NT. + A system with less than 64 MB of RAM is not recommended.</p> </section> <section> - <title>Memory Usage</title> - <p>RAM memory of 96 MBytes is recommended to run OTP on NT. - A system with less than 64 Mbytes of RAM is not recommended.</p> + <title>Disk Space Use</title> + <p>A minimum Windows NT installation with networking needs 250 MB, + and an extra 130 MB for the swap file.</p> </section> <section> - <title>Disk Space Usage</title> - <p>A minimum NT installation with networking needs 250 MB, and - an additional 130 MB for the swap file. </p> - </section> - - <section> - <title>Installation</title> - <p>Normal NT installation is performed. No additional application - programs are needed, such as Internet explorer or web server. Networking - with TCP/IP is required. <br></br> - - Service pack 4 or later must be installed.</p> + <title>Installing an Embedded System</title> + <p>Normal Windows NT installation is performed. No additional + application programs are needed, such as Internet Explorer or + web server. Networking with TCP/IP is required.</p> + <p>Service Pack 4 or later must be installed.</p> <section> <title>Hardware Watchdog</title> - <p>For Windows NT running on standard PCs with ISA and/or PCI bus - there is a possibility to install an extension card with a hardware - watchdog. - </p> - <p>See also the <c>heart(3)</c> reference manual page in - <em>Kernel</em>. - </p> + <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> </section> </section> <section> <title>Starting Erlang</title> - <p>On an embedded system, the <c>erlsrv</c> module should be used, - to install the erlang process as a Windows system service. - This service can start - after NT has booted. See documentation for <c>erlsrv</c>.</p> + <p>On an embedded system, the <c>erlsrv</c> module is to be used + 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> </section> </chapter> diff --git a/system/doc/embedded/embedded_solaris.xml b/system/doc/embedded/embedded_solaris.xml index cab3437725..1861436a8e 100644 --- a/system/doc/embedded/embedded_solaris.xml +++ b/system/doc/embedded/embedded_solaris.xml @@ -31,125 +31,97 @@ <rev>B</rev> <file>embedded_solaris.xml</file> </header> - <p>This chapter describes the OS specific parts of OTP which relate - to Solaris. - </p> + <marker id="embedded solaris"></marker> + + <p>This section describes the operating system-specific parts + of OTP that relate to Solaris.</p> <section> - <title>Memory Usage</title> - <p>Solaris takes about 17 Mbyte of RAM on a system with 64 Mbyte of - total RAM. This leaves about 47 Mbyte for the applications. If - the system utilizes swapping, these figures cannot be improved + <title>Memory Use</title> + <p>Solaris takes about 17 MB of RAM on a system with 64 MB of + total RAM. This leaves about 47 MB for the applications. If + the system uses swapping, these figures cannot be improved because unnecessary daemon processes are swapped out. However, if swapping is disabled, or if the swap space is of limited resource in the system, it becomes necessary to kill off - unnecessary daemon processes. - </p> + unnecessary daemon processes.</p> </section> <section> - <title>Disk Space Usage</title> + <title>Disk Space Use</title> <p>The disk space required by Solaris can be minimized by using the - Core User support installation. It requires about 80 Mbyte of + Core User support installation. It requires about 80 MB of disk space. This installs only the minimum software required to - boot and run Solaris. The disk space can be further reduced by + boot and run Solaris. The disk space can be further reduced by deleting unnecessary individual files. However, unless disk space is a critical resource the effort required and the risks - involved may not be justified.</p> + involved cannot be justified.</p> </section> <section> - <title>Installation</title> + <title>Installing an Embedded System</title> <p>This section is about installing an embedded system. - The following topics are considered, - </p> + The following topics are considered: + </p> <list type="bulleted"> - <item> - <p>Creation of user and installation directory,</p> - </item> - <item> - <p>Installation of embedded system,</p> - </item> - <item> - <p>Configuration for automatic start at reboot,</p> - </item> - <item> - <p>Making a hardware watchdog available,</p> - </item> - <item> - <p>Changing permission for reboot,</p> - </item> - <item> - <p>Patches,</p> - </item> - <item> - <p>Configuration of the OS_Mon application.</p> - </item> + <item>Creating user and installation directory</item> + <item>Installing an embedded system</item> + <item>Configuring automatic start at boot</item> + <item>Making a hardware watchdog available</item> + <item>Changing permission for reboot</item> + <item>Setting TERM environment variable</item> + <item>Adding patches</item> + <item>Installing module os_sup in application os_mon</item> </list> - <p>Several of the procedures described below require expert - knowledge of the Solaris 2 operating system. For most of them - super user privilege is needed. - </p> + <p>Several of the procedures in this section require expert + knowledge of the Solaris operating system. For most of them + super user privilege is needed.</p> <section> - <title>Creation of User and Installation Directory</title> - <p>It is recommended that the Embedded Environment is run by an - ordinary user, i.e. a user who does not have super user - privileges. - </p> - <p>Throughout this section we assume that the user name is - <c>otpuser</c>, and that the home directory of that user is, - </p> + <title>Creating User and Installation Directory</title> + <p>It is recommended that the embedded environment is run by an + ordinary user, that is, a user who does not have super user + privileges.</p> + <p>In this section, it is assumed that the username is + <c>otpuser</c> and that the home directory of that user is:</p> <pre> /export/home/otpuser</pre> - <p>Furthermore, we assume that in the home directory of + <p>It is also assumed that in the home directory of <c>otpuser</c>, there is a directory named <c>otp</c>, the - full path of which is, - </p> + full path of which is:</p> <pre> /export/home/otpuser/otp</pre> <p>This directory is the <em>installation directory</em> of the - Embedded Environment. - </p> + embedded environment.</p> </section> <section> - <title>Installation of an Embedded System</title> - <p>The procedure for installation of an embedded system does - not differ from that of an ordinary system (see the - <em>Installation Guide</em>), - except for the following: - </p> + <title>Installing an Embedded System</title> + <p>The procedure for installing an embedded system + is the same as for an ordinary system (see + Installation Guide), except for the following:</p> <list type="bulleted"> - <item> - <p>the (compressed) tape archive file should be - extracted in the installation directory as defined above, - and,</p> - </item> - <item> - <p>there is no need to link the start script to a - standard directory like <c>/usr/local/bin</c>.</p> - </item> + <item>The (compressed) tape archive file is to be extracted in + the installation directory defined above.</item> + <item>It is not needed to link the start script to a standard + directory like <c>/usr/local/bin</c>.</item> </list> </section> <section> - <title>Configuration for Automatic Start at Boot</title> - <p>A true embedded system has to start when the system - boots. This section accounts for the necessary configurations - needed to achieve that. - </p> - <p>The embedded system and all the applications will start - automatically if the script file shown below is added to the - <c>/etc/rc3.d</c> directory. The file must be owned and - readable by <c>root</c>, and its name cannot be arbitrarily - assigned. The following name is recommended, - </p> + <title>Configuring Automatic Start at Boot</title> + <p>A true embedded system must start when the system boots. + This section accounts for the necessary configurations + needed to achieve that.</p> + <p>The embedded system and all the applications start + automatically if the script file shown below is added to + directory <c>/etc/rc3.d</c>. The file must be owned and + readable by <c>root</c>. Its name cannot be arbitrarily + assigned; the following name is recommended:</p> <pre> S75otp.system</pre> - <p>For further details on initialization (and termination) - scripts, and naming thereof, see the Solaris documentation. - </p> + <p>For more details on initialization (and termination) + scripts, and naming thereof, see the Solaris documentation.</p> <pre> #!/bin/sh # @@ -187,386 +159,333 @@ case "$1" in echo "Usage: $0 { start | stop }" ;; esac</pre> - <p>The file <c>/export/home/otpuser/otp/bin/start</c> referred to - in the above script, is precisely the script <c>start</c> - described in the section <em>Starting Erlang</em> below. The + <p>File <c>/export/home/otpuser/otp/bin/start</c> referred to + in the above script is precisely the <c>start</c> script + described in <em>Starting Erlang</em>. The script variable <c>OTP_ROOT</c> in that <c>start</c> script - corresponds to the example path - </p> + corresponds to the following example path used in this + section:</p> <pre> /export/home/otpuser/otp</pre> - <p>used in this section. The <c>start</c> script should be edited - accordingly. - </p> - <p>Use of the <c>killproc</c> procedure in the above script could - be combined with a call to <c>erl_call</c>, e.g. - </p> + <p>The <c>start</c> script is to be edited accordingly.</p> + <p>Use of the <c>killproc</c> procedure in the above script can + be combined with a call to <c>erl_call</c>, for example:</p> <pre> $SOME_PATH/erl_call -n Node init stop</pre> - <p>In order to take Erlang down gracefully see the - <c>erl_call(1)</c> reference manual page for further details - on the use of <c>erl_call</c>. That however requires that - Erlang runs as a distributed node which is not always the - case. - </p> - <p>The <c>killproc</c> procedure should not be removed: the + <p>To take Erlang down gracefully, see the <c>erl_call(1)</c> + manual page in <c>erl_interface</c> for details on the use + of <c>erl_call</c>. However, + that requires that Erlang runs as a distributed node, which is + not always the case.</p> + <p>The <c>killproc</c> procedure is not to be removed. The purpose is here to move from run level 3 (multi-user mode with networking resources) to run level 2 (multi-user mode without - such resources), in which Erlang should not run. - </p> + such resources), in which Erlang is not to run.</p> </section> <section> - <title>Hardware Watchdog</title> + <title>Making Hardware Watchdog Available</title> <p>For Solaris running on VME boards from Force Computers, - there is a possibility to activate the onboard hardware - watchdog, provided a VME bus driver is added to the operating - system (see also <em>Installation Problems</em> below). - </p> - <p>See also the <c>heart(3)</c> reference manual page in - <em>Kernel</em>. - </p> + 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> </section> <section> <title>Changing Permissions for Reboot</title> <p>If the <c>HEART_COMMAND</c> environment variable is to be set - in the <c>start</c> script in the section, <em>Starting Erlang</em>, and if the value shall be set to the - path of the Solaris <c>reboot</c> command, i.e. - </p> + in the <c>start</c> script in + <em>Starting Erlang</em>, and if the value is to be set to the + path of the Solaris <c>reboot</c> command, that is:</p> <pre> HEART_COMMAND=/usr/sbin/reboot</pre> - <p>the ownership and file permissions for <c>/usr/sbin/reboot</c> - must be changed as follows, - </p> + <p>then the ownership and file permissions for + <c>/usr/sbin/reboot</c> must be changed as follows:</p> <pre> chown 0 /usr/sbin/reboot chmod 4755 /usr/sbin/reboot</pre> - <p>See also the <c>heart(3)</c> reference manual page in - <em>Kernel</em>. - </p> + <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p> </section> <section> - <title>The TERM Environment Variable</title> - <p>When the Erlang runtime system is automatically started from the - <c>S75otp.system</c> script the <c>TERM</c> environment - variable has to be set. The following is a minimal setting, - </p> + <title>Setting TERM Environment Variable</title> + <p>When the Erlang runtime system is automatically started from + the <c>S75otp.system</c> script, the <c>TERM</c> environment + variable must be set. The following is a minimal setting:</p> <pre> TERM=sun</pre> - <p>which should be added to the <c>start</c> script described in - the section. - </p> + <p>This is to be added to the <c>start</c> script.</p> </section> <section> - <title>Patches</title> + <title>Adding Patches</title> <p>For proper functioning of flushing file system data to disk on - Solaris 2.5.1, the version specific patch with number - 103640-02 must be added to the operating system. There may be - other patches needed, see the release README file - <c><![CDATA[<ERL_INSTALL_DIR>/README]]></c>. - </p> + Solaris 2.5.1, the version-specific patch with number + 103640-02 must be added to the operating system. Other + patches might be needed, see the release README file + <c><![CDATA[<ERL_INSTALL_DIR>/README]]></c>.</p> </section> <section> - <title>Installation of Module os_sup in Application OS_Mon</title> + <title>Installing Module os_sup in Application os_mon</title> <p>The following four installation procedures require super user - privilege. - </p> - - <section> - <title>Installation</title> - <list type="ordered"> - <item> - <p><em>Make a copy the Solaris standard configuration file for syslogd.</em></p> - <list type="bulleted"> - <item> - <p>Make a copy the Solaris standard configuration - file for syslogd. This file is usually named - <c>syslog.conf</c> and found in the <c>/etc</c> - directory.</p> - </item> - <item> - <p>The file name of the copy must be - <c>syslog.conf.ORIG</c> but the directory location - is optional. Usually it is <c>/etc</c>. - </p> - <p>A simple way to do this is to issue the command</p> - <code type="none"> + privilege:</p> + + <section> + <title>Installation</title> + <list type="bulleted"> + <item><em>Make a copy of the Solaris standard configuration + file for <c>syslogd</c>:</em> + <list type="bulleted"> + <item>Make a copy of the Solaris standard configuration + file for <c>syslogd</c>. This file is usually named + <c>syslog.conf</c> and found in directory <c>/etc</c>.</item> + <item>The filename of the copy must be <c>syslog.conf.ORIG</c>. + The directory location is optional; usually it is <c>/etc</c>. + A simple way to do this is to issue the following command: + <code type="none"> cp /etc/syslog.conf /etc/syslog.conf.ORIG</code> </item> - </list> - </item> - <item> - <p><em>Make an Erlang specific configuration file for syslogd.</em></p> - <list type="bulleted"> - <item> - <p>Make an edited copy of the back-up copy previously - made.</p> - </item> - <item> - <p>The file name must be <c>syslog.conf.OTP</c> and the - path must be the same as the back-up copy.</p> - </item> - <item> - <p>The format of the configuration file is found in the - man page for <c>syslog.conf(5)</c>, by issuing the - command <c>man syslog.conf</c>.</p> - </item> - <item> - <p>Usually a line is added which should state:</p> - <list type="bulleted"> - <item> - <p>which types of information that will be - supervised by Erlang,</p> - </item> - <item> - <p>the name of the file (actually a named pipe) - that should receive the information.</p> - </item> - </list> - </item> - <item> - <p>If e.g. only information originating from the - unix-kernel should be supervised, the line should - begin with <c>kern.LEVEL</c> (for the possible - values of <c>LEVEL</c> see <c>syslog.conf(5)</c>).</p> - </item> - <item> - <p>After at least one tab-character, the line added - should contain the full name of the named pipe where - syslogd writes its information. The path must be the - same as for the <c>syslog.conf.ORIG</c> and - <c>syslog.conf.OTP</c> files. The file name must be - <c>syslog.otp</c>.</p> - </item> - <item> - <p>If the directory for the <c>syslog.conf.ORIG</c> and - <c>syslog.conf.OTP</c> files is <c>/etc</c> the line - in <c>syslog.conf.OTP</c> will look like:</p> - <code type="none"> + </list> + </item> + <item><em>Make an Erlang-specific configuration file for + <c>syslogd</c>:</em> + <list type="bulleted"> + <item>Make an edited copy of the backup copy previously + made.</item> + <item>The filename must be <c>syslog.conf.OTP</c>. The + path must be the same as the backup copy.</item> + <item>The format of the configuration file is found in the + <c>syslog.conf(5)</c> manual page, by issuing the command + <c>man syslog.conf</c>.</item> + <item>Usually a line is added that is to state: + <list type="bulleted"> + <item>Which types of information that is to be + supervised by Erlang</item> + <item>The name of the file (actually a named pipe) that + is to receive the information</item> + </list> + </item> + <item>If, for example, only information originating from + the UNIX kernel is to be supervised, the line is to + begin with <c>kern.LEVEL</c>. For the possible + values of <c>LEVEL</c>, see <c>syslog.conf(5)</c>.</item> + <item>After at least one tab-character, the line added is to + contain the full name of the named pipe where <c>syslogd</c> + writes its information. The path must be the same as for the + files <c>syslog.conf.ORIG</c> and <c>syslog.conf.OTP</c>. + The filename must be <c>syslog.otp</c>.</item> + <item>If the directory for the files <c>syslog.conf.ORIG</c> + and <c>syslog.conf.OTP</c> is <c>/etc</c>, the line in + <c>syslog.conf.OTP</c> is as follows: + <code type="none"> kern.LEVEL /etc/syslog.otp</code> - </item> - </list> - </item> - <item> - <p><em>Check the file privileges of the configuration files.</em></p> - <list type="bulleted"> - <item> - <p>The configuration files should have <c>rw-r--r--</c> - file privileges and be owned by root.</p> - </item> - <item> - <p>A simple way to do this is to issue the commands</p> - <code type="none"> + </item> + </list> + </item> + <item><em>Check the file privileges of the configuration + files:</em> + <list type="bulleted"> + <item>The configuration files is to have <c>rw-r--r--</c> + file privileges and be owned by root.</item> + <item>A simple way to do this is to issue these commands: + <code type="none"> chmod 644 /etc/syslog.conf chmod 644 /etc/syslog.conf.ORIG chmod 644 /etc/syslog.conf.OTP</code> - </item> - <item> - <p><em>Note:</em> If the <c>syslog.conf.ORIG</c> and - <c>syslog.conf.OTP</c> files are not in the - <c>/etc</c> directory, the file path in the second - and third command must be modified.</p> - </item> - </list> - </item> - <item> - <p><em>Modify file privileges and ownership of the mod_syslog utility.</em></p> - <list type="bulleted"> - <item> - <p>The file privileges and ownership of the - <c>mod_syslog</c> utility must be modified.</p> - </item> - <item> - <p>The full name of the binary executable file is - derived from the position of the <c>os_mon</c> - application if the file system by adding - <c>/priv/bin/mod_syslog</c>. The generic full name - of the binary executable file is thus</p> - <code type="none"><![CDATA[ + </item> + <item>Notice that if the files <c>syslog.conf.ORIG</c> and + <c>syslog.conf.OTP</c> are not in directory <c>/etc</c>, + the file path in the second and third command must be + modified.</item> + </list> + </item> + <item><em>Modify file privileges and ownership of the + <c>mod_syslog</c> utility:</em> + <list type="bulleted"> + <item>The file privileges and ownership of the + <c>mod_syslog</c> utility must be modified.</item> + <item><p>The full name of the binary executable file is + derived from the position of application <c>os_mon</c> + in the file system by adding + <c>/priv/bin/mod_syslog</c>. The generic full name + of the binary executable file is thus:</p> + <code type="none"><![CDATA[ <OTP_ROOT>/lib/os_mon-<REV>/priv/bin/mod_syslog]]></code> - <p><em>Example:</em> If the path to the otp-root is - <c>/usr/otp</c>, thus the path to the <c>os_mon</c> - application is <c>/usr/otp/lib/os_mon-1.0</c> - (assuming revision 1.0) and the full name of the - binary executable file is - <c>/usr/otp/lib/os_mon-1.0/priv/bin/mod_syslog</c>.</p> - </item> - <item> - <p>The binary executable file must be owned by root, - have <c>rwsr-xr-x</c> file privileges, in particular - the setuid bit of user must be set. - </p> - </item> - <item> - <p>A simple way to do this is to issue the commands</p> - <code type="none"><![CDATA[ + <p><em>Example:</em> If the path to <c>otp-root</c> is + <c>/usr/otp</c>, then the path to the <c>os_mon</c> + application is <c>/usr/otp/lib/os_mon-1.0</c> + (assuming revision 1.0) and the full name of the + binary executable file is + <c>/usr/otp/lib/os_mon-1.0/priv/bin/mod_syslog</c>.</p> + </item> + <item>The binary executable file must be owned by root, + have <c>rwsr-xr-x</c> file privileges, in particular + the <c>setuid</c> bit of the user must be set.</item> + <item><p>A simple way to do this is to issue the following + commands:</p> + <code type="none"><![CDATA[ cd <OTP_ROOT>/lib/os_mon-<REV>/priv/bin/mod_syslog chmod 4755 mod_syslog chown root mod_syslog]]></code> - </item> - </list> - </item> - </list> - </section> - - <section> - <title>Testing the Application Configuration File</title> - <p>The following procedure does not require root privilege. - </p> - <list type="bulleted"> - <item> - <p>Ensure that the configuration parameters for the - <c>os_sup</c> module in the <c>os_mon</c> application - are correct.</p> - </item> - <item> - <p>Browse the application configuration file (do - <em>not</em> edit it). The full name of the application - configuration file is derived from the position of the - OS_Mon application if the file system by adding - <c>/ebin/os_mon.app</c>. - </p> - <p>The generic full name of the file is thus</p> - <code type="none"><![CDATA[ + </item> + </list> + </item> + </list> + </section> + + <section> + <title>Testing the Application Configuration File</title> + <p>The following procedure does not require root privilege:</p> + <list type="bulleted"> + <item>Ensure that the configuration parameters for the + <c>os_sup</c> module in the <c>os_mon</c> application + are correct.</item> + <item><p>Browse the application configuration file (do + <em>not</em> edit it). The full name of the application + configuration file is derived from the position of the + <c>os_mon</c> application in the file system by adding + <c>/ebin/os_mon.app</c>.</p> + <p>The generic full name of the file is thus:</p> + <code type="none"><![CDATA[ <OTP_ROOT>/lib/os_mon-<REV>/ebin/os_mon.app.]]></code> - <p><em>Example:</em> If the path to the otp-root is - <c>/usr/otp</c>, thus the path to the <c>os_mon</c> - application is <c>/usr/otp/lib/os_mon-1.0 </c> (assuming - revision 1.0) and the full name of the binary executable - file is <c>/usr/otp/lib/os_mon-1.0/ebin/os_mon.app</c>.</p> - </item> - <item> - <p>Ensure that the following configuration parameters are - bound to the correct values.</p> - </item> + <p><em>Example:</em> If the path to <c>otp-root</c> is + <c>/usr/otp</c>, then the path to the <c>os_mon</c> application + is <c>/usr/otp/lib/os_mon-1.0 </c> (assuming revision 1.0) and + the full name of the binary executable file is + <c>/usr/otp/lib/os_mon-1.0/ebin/os_mon.app</c>.</p> + </item> + <item>Ensure that the following configuration parameters have + correct values:</item> </list> - <table> + + <table> <row> <cell align="left" valign="top"><em>Parameter</em></cell> <cell align="left" valign="top"><em>Function</em></cell> <cell align="left" valign="top"><em>Standard value</em></cell> </row> <row> - <cell align="left" valign="middle">start_os_sup</cell> - <cell align="left" valign="middle">Specifies if os_sup will be started or not.</cell> - <cell align="left" valign="middle"><c>true</c>for the first instance on the hardware; <c>false</c>for the other instances.</cell> + <cell align="left" valign="middle"><c>start_os_sup</c></cell> + <cell align="left" valign="middle">Specifies if <c>os_sup</c> + is to be started or not.</cell> + <cell align="left" valign="middle"><c>true</c> for the + first instance on the hardware; <c>false</c> for the + other instances</cell> </row> <row> - <cell align="left" valign="middle">os_sup_own</cell> - <cell align="left" valign="middle">The directory for (1)the back-up copy, (2) the Erlang specific configuration file for syslogd.</cell> + <cell align="left" valign="middle"><c>os_sup_own</c></cell> + <cell align="left" valign="middle">The directory for + (1) back-up copy and (2) Erlang-specific configuration + file for <c>syslogd</c></cell> <cell align="left" valign="middle"><c>"/etc"</c></cell> </row> <row> - <cell align="left" valign="middle">os_sup_syslogconf</cell> - <cell align="left" valign="middle">The full name for the Solaris standard configuration file for syslogd </cell> + <cell align="left" valign="middle"><c>os_sup_syslogconf</c></cell> + <cell align="left" valign="middle">The full name for the + Solaris standard configuration file for <c>syslogd</c></cell> <cell align="left" valign="middle"><c>"/etc/syslog.conf"</c></cell> </row> <row> - <cell align="left" valign="middle">error_tag</cell> - <cell align="left" valign="middle">The tag for the messages that are sent to the error logger in the Erlang runtime system.</cell> + <cell align="left" valign="middle"><c>error_tag</c></cell> + <cell align="left" valign="middle">The tag for the + messages that are sent to the error logger in the Erlang + runtime system</cell> <cell align="left" valign="middle"><c>std_error</c></cell> </row> <tcaption>Configuration Parameters</tcaption> </table> - <p>If the values listed in the <c>os_mon.app</c> do not suit - your needs, you should <c>not</c> edit that file. Instead - you should <em>override</em> values in a <em>system configuration file</em>, the full pathname of which is given - on the command line to <c>erl</c>. - </p> - <p><em>Example:</em> The following is an example of the - contents of an application configuration file.</p> - <p></p> - <pre> + + <p>If the values listed in <c>os_mon.app</c> do not suit + your needs, do <em>not</em> edit that file. Instead + <em>override</em> the values in a <em>system configuration + file</em>, the full pathname of which is given + on the command line to <c>erl</c>.</p> + <p><em>Example:</em> Contents of an application configuration + file:</p> + <pre> [{os_mon, [{start_os_sup, true}, {os_sup_own, "/etc"}, {os_sup_syslogconf, "/etc/syslog.conf"}, {os_sup_errortag, std_error}]}].</pre> - </section> - - <section> - <title>Related Documents</title> - <p>See also the <c>os_mon(3)</c>, <c>application(3)</c> and - <c>erl(1)</c> reference manual pages.</p> - </section> </section> <section> - <title>Installation Problems</title> - <p>The hardware watchdog timer which is controlled by the - <c>heart</c> port program requires the <c>FORCEvme</c> - package, which contains the VME bus driver, to be - installed. This driver, however, may clash with the Sun - <c>mcp</c> driver and cause the system to completely refuse to - boot. To cure this problem, the following lines should be - added to <c>/etc/system</c>: - </p> + <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> + </section> + </section> + + <section> + <title>Installation Problems</title> + <p>The hardware watchdog timer, which is controlled by the + <c>heart</c> port program, requires package <c>FORCEvme</c>, + which contains the VME bus driver, to be + installed. However, this driver can clash with the Sun + <c>mcp</c> driver and cause the system to refuse to + boot. To cure this problem, the following lines are + to be added to <c>/etc/system</c>:</p> <list type="bulleted"> <item><c>exclude: drv/mcp</c></item> <item><c>exclude: drv/mcpzsa</c></item> <item><c>exclude: drv/mcpp</c></item> </list> <warning> - <p>It is recommended that these lines be added to avoid the - clash described, which may make it completely impossible to - boot the system.</p> + <p>It is recommended to add these lines to avoid a clash. + The clash can make it impossible to boot the system.</p> </warning> </section> </section> <section> <title>Starting Erlang</title> - <p>This section describes how an embedded system is started. There - are four programs involved, and they all normally reside in the - directory <c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. The only exception is - the program <c>start</c>, which may be located anywhere, and - also is the only program that must be modified by the user. - </p> - <p>In an embedded system there usually is no interactive shell. - However, it is possible for an operator to attach to the Erlang - system by giving the command <c>to_erl</c>. He is then - connected to the Erlang shell, and may give ordinary Erlang - commands. All interaction with the system through this shell is - logged in a special directory. - </p> - <p>Basically, the procedure is as follows. The program - <c>start</c> is called when the machine is started. It calls - <c>run_erl</c>, which sets things up so the operator can attach - to the system. It calls <c>start_erl</c> which calls the - correct version of <c>erlexec</c> (which is located in - <c><![CDATA[<ERL_INSTALL_DIR>/erts-EVsn/bin]]></c>) with the correct - <c>boot</c> and <c>config</c> files. - </p> + <p>This section describes how an embedded system is started. Four + programs are involved and they normally reside in the directory + <c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. The only exception is + the <c>start</c> program, which can be located anywhere, and + is also the only program that must be modified by the user.</p> + <p>In an embedded system, there is usually no interactive shell. + However, an operator can attach to the Erlang + system by command <c>to_erl</c>. The operator is then + connected to the Erlang shell and can give ordinary Erlang + commands. All interaction with the system through this shell is + logged in a special directory.</p> + <p>Basically, the procedure is as follows:</p> + <list type="bulleted"> + <item>The <c>start</c> program is called when the machine + is started.</item> + <item>It calls <c>run_erl</c>, which sets up things so the + operator can attach to the system.</item> + <item>It calls <c>start_erl</c>, which calls the correct + version of <c>erlexec</c> (which is located in + <c><![CDATA[<ERL_INSTALL_DIR>/erts-EVsn/bin]]></c>) with the + correct <c>boot</c> and <c>config</c> files.</item> + </list> </section> <section> <title>Programs</title> - <section> <title>start</title> - <p>This program is called when the machine is started. It may - be modified or re-written to suit a special system. By + <p>This program is called when the machine is started. It can + be modified or rewritten to suit a special system. By 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> + <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> <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> - <p>This program should set static parameters and environment + It must also take an optional parameter, which defaults to + <c><![CDATA[<ERL_INSTALL_DIR>/releases/start_erl.data]]></c>.</p> + <p>This program is to set static parameters and environment variables such as <c>-sname Name</c> and <c>HEART_COMMAND</c> - to reboot the machine. - </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. - </p> + to reboot the machine.</p> + <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> <p>The following script illustrates the default behaviour of the - program. - </p> + program:</p> <code type="none"><![CDATA[ #!/bin/sh # Usage: start [DataFile] @@ -583,10 +502,9 @@ START_ERL_DATA=${1:-$RELDIR/start_erl.data} $ROOTDIR/bin/run_erl /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl \ $ROOTDIR $RELDIR $START_ERL_DATA" > /dev/null 2>&1 &]]></code> <p>The following script illustrates a modification where the node - is given the name <c>cp1</c>, and the environment variables + is given the name <c>cp1</c>, and where the environment variables <c>HEART_COMMAND</c> and <c>TERM</c> have been added to the - above script. - </p> + previous script:</p> <code type="none"><![CDATA[ #!/bin/sh # Usage: start [DataFile] @@ -606,11 +524,10 @@ START_ERL_DATA=${1:-$RELDIR/start_erl.data} $ROOTDIR/bin/run_erl /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl \ $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname cp1" > /dev/null 2>&1 &]]></code> - <p>If a diskless and/or read-only client node is about to start the - <c>start_erl.data</c> file is located in the client directory at - the master node. Thus, the <c>START_ERL_DATA</c> line should look - like: - </p> + <p>If a diskless and/or read-only client node is about to start, + file <c>start_erl.data</c> is located in the client directory at + the master node. Thus, the <c>START_ERL_DATA</c> line is to look + like:</p> <code type="none"> CLIENTDIR=$ROOTDIR/clients/clientname START_ERL_DATA=${1:-$CLIENTDIR/bin/start_erl.data}</code> @@ -620,22 +537,24 @@ START_ERL_DATA=${1:-$CLIENTDIR/bin/start_erl.data}</code> <title>run_erl</title> <p>This program is used to start the emulator, but you will not be connected to the shell. <c>to_erl</c> is used to connect to - the Erlang shell. - </p> + the Erlang shell.</p> <code type="none"> Usage: run_erl pipe_dir/ log_dir "exec command [parameters ...]"</code> - <p>Where <c>pipe_dir/</c> should be <c>/tmp/</c> (<c>to_erl</c> - uses this name by default) and <c>log_dir</c> is where the log - files are written. <c>command [parameters]</c> is executed, - and everything written to stdin and stdout is logged in the - <c>log_dir</c>. - </p> - <p>In the <c>log_dir</c>, log files are written. Each logfile - has a name of the form: <c>erlang.log.N</c> where N is a - generation number, ranging from 1 to 5. Each logfile holds up - to 100kB text. As time goes by the following logfiles will be - found in the logfile directory</p> - <code type="none"> +<p>Here:</p> + <list type="bulleted"> + <item><c>pipe_dir/</c> is to be <c>/tmp/</c> (<c>to_erl</c> + uses this name by default).</item> + <item><c>log_dir</c> is where the log files are written.</item> + <item><c>command [parameters]</c> is executed.</item> + <item>Everything written to <c>stdin</c> and <c>stdout</c> + is logged in <c>log_dir</c>.</item> + </list> + <p>Log files are written in <c>log_dir</c>. Each log file + has a name of the form <c>erlang.log.N</c>, where N is a + generation number, ranging from 1 to 5. Each log file holds up + to 100 kB text. As time goes by, the following log files are + found in the log file directory:</p> + <code type="none"> erlang.log.1 erlang.log.1, erlang.log.2 erlang.log.1, erlang.log.2, erlang.log.3 @@ -643,48 +562,40 @@ erlang.log.1, erlang.log.2, erlang.log.3, erlang.log.4 erlang.log.2, erlang.log.3, erlang.log.4, erlang.log.5 erlang.log.3, erlang.log.4, erlang.log.5, erlang.log.1 ...</code> - <p>with the most recent logfile being the right most in each row - of the above list. That is, the most recent file is the one - with the highest number, or if there are already four files, - the one before the skip. - </p> - <p>When a logfile is opened (for appending or created) a time - stamp is written to the file. If nothing has been written to + <p>The most recent log file is the rightmost in each row. That + is, the most recent file is the one with the highest number, or + if there are already four files, the one before the skip.</p> + <p>When a log file is opened (for appending or created), a time + stamp is written to the file. If nothing has been written to the log files for 15 minutes, a record is inserted that says - that we're still alive. - </p> + that we are still alive.</p> </section> <section> <title>to_erl</title> <p>This program is used to attach to a running Erlang runtime - system, started with <c>run_erl</c>. - </p> + system, started with <c>run_erl</c>.</p> <code type="none"> Usage: to_erl [pipe_name | pipe_dir]</code> - <p>Where <c>pipe_name</c> defaults to <c>/tmp/erlang.pipe.N</c>. - </p> + <p>Here <c>pipe_name</c> defaults to <c>/tmp/erlang.pipe.N</c>.</p> <p>To disconnect from the shell without exiting the Erlang - system, type <c>Ctrl-D</c>. - </p> + system, type <c>Ctrl-D</c>.</p> </section> <section> <title>start_erl</title> <p>This program starts the Erlang emulator with parameters - <c>-boot</c> and <c>-config</c> set. It reads data about - where these files are located from a file called - <c>start_erl.data</c> which is located in the <c><![CDATA[<RELDIR>]]></c>. - Each new release introduces a new data file. This file is - automatically generated by the release handler in Erlang. - </p> - <p>The following script illustrates the behaviour of the - program. - </p> + <c>-boot</c> and <c>-config</c> set. It reads data about + where these files are located from a file named + <c>start_erl.data</c>, which is located in + <c><![CDATA[<RELDIR>]]></c>. + Each new release introduces a new data file. This file is + automatically generated by the release handler in Erlang.</p> + <p>The following script illustrates the behaviour of the program:</p> <code type="none"> #!/bin/sh # -# This program is called by run_erl. It starts +# This program is called by run_erl. It starts # the Erlang emulator and sets -boot and -config parameters. # It should only be used at an embedded target system. # @@ -710,22 +621,23 @@ 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> 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 cannot - read a new <c>start_erl.data</c> file (the file is not - possible to change dynamically) the boot and config files are + 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 + read a new <c>start_erl.data</c> file (the file cannot + be changed dynamically). The boot and config files are always fetched from the same place (but with new contents if - a new release has been installed). The <c>release_handler</c> - copies this files to the <c>bin</c> directory in the client + a new release has been installed).</p> + <p>The <c>release_handler</c> + copies these files to the <c>bin</c> directory in the client directory at the master nodes whenever a new release is made - permanent. - </p> - <p>Assuming the same <c>CLIENTDIR</c> as above the last line - should look like: - </p> - <code type="none"> + permanent.</p> + <p>Assuming the same <c>CLIENTDIR</c> as above, the last line + is to look like:</p> + <code type="none"> exec $BINDIR/erlexec -boot $CLIENTDIR/bin/start \ -config $CLIENTDIR/bin/sys $*</code> </section> diff --git a/system/doc/embedded/part.xml b/system/doc/embedded/part.xml index e4366bd2c2..f3b44bf494 100644 --- a/system/doc/embedded/part.xml +++ b/system/doc/embedded/part.xml @@ -31,17 +31,17 @@ <rev>C</rev> <file></file> </header> + <description> - <p>This manual describes the issues that are specific + <marker id="embedded systems user guide"></marker> + <p>This section describes the issues that are specific for running Erlang on an embedded system. It describes the differences in installing and starting - Erlang compared to how it is done for a non-embedded system. - </p> - <p>Note that this is a supplementary document. You still need to - read the Installation Guide. - </p> - <p>There is also target architecture specific information in - the top level README file of the Erlang distribution.</p> + Erlang compared to how it is done for a non-embedded system.</p> + <note><p>This is a supplementary section. You also need to + read Section 1 Installation Guide.</p></note> + <p>There is also target architecture-specific information in + the top-level README file of the Erlang distribution.</p> </description> <xi:include href="embedded_solaris.xml"/> <xi:include href="embedded_nt.xml"/> diff --git a/system/doc/getting_started/conc_prog.xml b/system/doc/getting_started/conc_prog.xml index 2b64826a93..0dd9efb363 100644 --- a/system/doc/getting_started/conc_prog.xml +++ b/system/doc/getting_started/conc_prog.xml @@ -29,25 +29,26 @@ <file>conc_prog.xml</file> </header> + <marker id="Distributed Programming"></marker> <section> <title>Processes</title> <p>One of the main reasons for using Erlang instead of other functional languages is Erlang's ability to handle concurrency - and distributed programming. By concurrency we mean programs - which can handle several threads of execution at the same time. - For example, modern operating systems would allow you to use a - word processor, a spreadsheet, a mail client and a print job all - running at the same time. Of course each processor (CPU) in + and distributed programming. By concurrency is meant programs + that can handle several threads of execution at the same time. + For example, modern operating systems allow you to use a + word processor, a spreadsheet, a mail client, and a print job all + running at the same time. Each processor (CPU) in the system is probably only handling one thread (or job) at a - time, but it swaps between the jobs a such a rate that it gives + time, but it swaps between the jobs at such a rate that it gives the illusion of running them all at the same time. It is easy to - create parallel threads of execution in an Erlang program and it - is easy to allow these threads to communicate with each other. In - Erlang we call each thread of execution a <em>process</em>.</p> + create parallel threads of execution in an Erlang program and + to allow these threads to communicate with each other. In + Erlang, each thread of execution is called a <em>process</em>.</p> <p>(Aside: the term "process" is usually used when the threads of execution share no data with each other and the term "thread" when they share data in some way. Threads of execution in Erlang - share no data, that's why we call them processes).</p> + share no data, that is why they are called processes).</p> <p>The Erlang BIF <c>spawn</c> is used to create a new process: <c>spawn(Module, Exported_Function, List of Arguments)</c>. Consider the following module:</p> @@ -73,14 +74,14 @@ hello hello hello done</pre> - <p>We can see that function <c>say_something</c> writes its first - argument the number of times specified by second argument. Now - look at the function <c>start</c>. It starts two Erlang processes, - one which writes "hello" three times and one which writes - "goodbye" three times. Both of these processes use the function - <c>say_something</c>. Note that a function used in this way by - <c>spawn</c> to start a process must be exported from the module - (i.e. in the <c>-export</c> at the start of the module).</p> + <p>As shown, the function <c>say_something</c> writes its first + argument the number of times specified by second argument. + The function <c>start</c> starts two Erlang processes, + one that writes "hello" three times and one that writes + "goodbye" three times. Both processes use the function + <c>say_something</c>. Notice that a function used in this way by + <c>spawn</c>, to start a process, must be exported from the module + (that is, in the <c>-export</c> at the start of the module).</p> <pre> 9> <input>tut14:start().</input> hello @@ -90,19 +91,19 @@ hello goodbye hello goodbye</pre> - <p>Notice that it didn't write "hello" three times and then - "goodbye" three times, but the first process wrote a "hello", + <p>Notice that it did not write "hello" three times and then + "goodbye" three times. Instead, the first process wrote a "hello", the second a "goodbye", the first another "hello" and so forth. But where did the <0.63.0> come from? The return value of a - function is of course the return value of the last "thing" in - the function. The last thing in the function <c>start</c> is:</p> + function is the return value of the last "thing" in + the function. The last thing in the function <c>start</c> is</p> <code type="none"> spawn(tut14, say_something, [goodbye, 3]).</code> <p><c>spawn</c> returns a <em>process identifier</em>, or <em>pid</em>, which uniquely identifies the process. So <0.63.0> - is the pid of the <c>spawn</c> function call above. We will see - how to use pids in the next example.</p> - <p>Note as well that we have used ~p instead of ~w in + is the pid of the <c>spawn</c> function call above. + The next example shows how to use pids.</p> + <p>Notice also that ~p is used instead of ~w in <c>io:format</c>. To quote the manual: "~p Writes the data with standard syntax in the same way as ~w, but breaks terms whose printed representation is longer than one line into many lines @@ -112,8 +113,8 @@ spawn(tut14, say_something, [goodbye, 3]).</code> <section> <title>Message Passing</title> - <p>In the following example we create two processes which send - messages to each other a number of times.</p> + <p>In the following example two processes are created and + they send messages to each other a number of times.</p> <code type="none"> -module(tut15). @@ -157,13 +158,13 @@ Pong received ping Ping received pong ping finished Pong finished</pre> - <p>The function <c>start</c> first creates a process, let's call it - "pong":</p> + <p>The function <c>start</c> first creates a process, + let us call it "pong":</p> <code type="none"> Pong_PID = spawn(tut15, pong, [])</code> <p>This process executes <c>tut15:pong()</c>. <c>Pong_PID</c> is the process identity of the "pong" process. The function - <c>start</c> now creates another process "ping".</p> + <c>start</c> now creates another process "ping":</p> <code type="none"> spawn(tut15, ping, [3, Pong_PID]),</code> <p>This process executes:</p> @@ -181,7 +182,7 @@ receive pong() end.</code> <p>The <c>receive</c> construct is used to allow processes to wait - for messages from other processes. It has the format:</p> + for messages from other processes. It has the following format:</p> <code type="none"> receive pattern1 -> @@ -192,35 +193,37 @@ receive patternN actionsN end.</code> - <p>Note: no ";" before the <c>end</c>.</p> + <p>Notice there is no ";" before the <c>end</c>.</p> <p>Messages between Erlang processes are simply valid Erlang terms. - I.e. they can be lists, tuples, integers, atoms, pids etc.</p> + That is, they can be lists, tuples, integers, atoms, pids, + and so on.</p> <p>Each process has its own input queue for messages it receives. New messages received are put at the end of the queue. When a process executes a <c>receive</c>, the first message in the queue - is matched against the first pattern in the <c>receive</c>, if + is matched against the first pattern in the <c>receive</c>. If this matches, the message is removed from the queue and - the actions corresponding to the the pattern are executed.</p> + the actions corresponding to the pattern are executed.</p> <p>However, if the first pattern does not match, the second pattern - is tested, if this matches the message is removed from the queue + is tested. If this matches, the message is removed from the queue and the actions corresponding to the second pattern are executed. - If the second pattern does not match the third is tried and so on - until there are no more pattern to test. If there are no more - patterns to test, the first message is kept in the queue and we - try the second message instead. If this matches any pattern, + If the second pattern does not match, the third is tried and so on + until there are no more patterns to test. If there are no more + patterns to test, the first message is kept in the queue and + the second message is tried instead. If this matches any pattern, the appropriate actions are executed and the second message is removed from the queue (keeping the first message and any other - messages in the queue). If the second message does not match we - try the third message and so on until we reach the end of - the queue. If we reach the end of the queue, the process blocks + messages in the queue). If the second message does not match, + the third message is tried, and so on, until the end of + the queue is reached. If the end of the queue is reached, + the process blocks (stops execution) and waits until a new message is received and this procedure is repeated.</p> - <p>Of course the Erlang implementation is "clever" and minimizes + <p>The Erlang implementation is "clever" and minimizes the number of times each message is tested against the patterns in each <c>receive</c>.</p> <p>Now back to the ping pong example.</p> <p>"Pong" is waiting for messages. If the atom <c>finished</c> is - received, "pong" writes "Pong finished" to the output and as it + received, "pong" writes "Pong finished" to the output and, as it has nothing more to do, terminates. If it receives a message with the format:</p> <code type="none"> @@ -229,20 +232,20 @@ end.</code> <c>pong</c> to the process "ping":</p> <code type="none"> Ping_PID ! pong</code> - <p>Note how the operator "!" is used to send messages. The syntax + <p>Notice how the operator "!" is used to send messages. The syntax of "!" is:</p> <code type="none"> Pid ! Message</code> - <p>I.e. <c>Message</c> (any Erlang term) is sent to the process + <p>That is, <c>Message</c> (any Erlang term) is sent to the process with identity <c>Pid</c>.</p> <p>After sending the message <c>pong</c> to the process "ping", "pong" calls the <c>pong</c> function again, which causes it to - get back to the <c>receive</c> again and wait for another message. - Now let's look at the process "ping". Recall that it was started + get back to the <c>receive</c> again and wait for another message.</p> + <p>Now let us look at the process "ping". Recall that it was started by executing:</p> <code type="none"> tut15:ping(3, Pong_PID)</code> - <p>Looking at the function <c>ping/2</c> we see that the second + <p>Looking at the function <c>ping/2</c>, the second clause of <c>ping/2</c> is executed since the value of the first argument is 3 (not 0) (first clause head is <c>ping(0,Pong_PID)</c>, second clause head is @@ -250,9 +253,9 @@ tut15:ping(3, Pong_PID)</code> <p>The second clause sends a message to "pong":</p> <code type="none"> Pong_PID ! {ping, self()},</code> - <p><c>self()</c> returns the pid of the process which executes + <p><c>self()</c> returns the pid of the process that executes <c>self()</c>, in this case the pid of "ping". (Recall the code - for "pong", this will land up in the variable <c>Ping_PID</c> in + for "pong", this lands up in the variable <c>Ping_PID</c> in the <c>receive</c> previously explained.)</p> <p>"Ping" now waits for a reply from "pong":</p> <code type="none"> @@ -260,37 +263,37 @@ receive pong -> io:format("Ping received pong~n", []) end,</code> - <p>and writes "Ping received pong" when this reply arrives, after + <p>It writes "Ping received pong" when this reply arrives, after which "ping" calls the <c>ping</c> function again.</p> <code type="none"> ping(N - 1, Pong_PID)</code> <p><c>N-1</c> causes the first argument to be decremented until it becomes 0. When this occurs, the first clause of <c>ping/2</c> - will be executed:</p> + is executed:</p> <code type="none"> ping(0, Pong_PID) -> Pong_PID ! finished, io:format("ping finished~n", []);</code> <p>The atom <c>finished</c> is sent to "pong" (causing it to terminate as described above) and "ping finished" is written to - the output. "Ping" then itself terminates as it has nothing left + the output. "Ping" then terminates as it has nothing left to do.</p> </section> <section> <title>Registered Process Names</title> - <p>In the above example, we first created "pong" so as to be able - to give the identity of "pong" when we started "ping". I.e. in - some way "ping" must be able to know the identity of "pong" in - order to be able to send a message to it. Sometimes processes - which need to know each others identities are started completely + <p>In the above example, "pong" was first created to be able + to give the identity of "pong" when "ping" was started. That is, in + some way "ping" must be able to know the identity of "pong" to be + able to send a message to it. Sometimes processes + which need to know each other's identities are started independently of each other. Erlang thus provides a mechanism for processes to be given names so that these names can be used as identities instead of pids. This is done by using the <c>register</c> BIF:</p> <code type="none"> register(some_atom, Pid)</code> - <p>We will now re-write the ping pong example using this and giving + <p>Let us now rewrite the ping pong example using this and give the name <c>pong</c> to the "pong" process:</p> <code type="none"> -module(tut16). @@ -335,52 +338,57 @@ Pong received ping Ping received pong ping finished Pong finished</pre> - <p>In the <c>start/0</c> function,</p> + <p>Here the <c>start/0</c> function,</p> <code type="none"> register(pong, spawn(tut16, pong, [])),</code> <p>both spawns the "pong" process and gives it the name <c>pong</c>. - In the "ping" process we can now send messages to <c>pong</c> by:</p> + In the "ping" process, messages can be sent to <c>pong</c> by:</p> <code type="none"> pong ! {ping, self()},</code> - <p>so that <c>ping/2</c> now becomes <c>ping/1</c> as we don't have - to use the argument <c>Pong_PID</c>.</p> + <p><c>ping/2</c> now becomes <c>ping/1</c> as + the argument <c>Pong_PID</c> is not needed.</p> </section> <section> <title>Distributed Programming</title> - <p>Now let's re-write the ping pong program with "ping" and "pong" - on different computers. Before we do this, there are a few things - we need to set up to get this to work. The distributed Erlang + <p>Let us rewrite the ping pong program with "ping" and "pong" + on different computers. First a few things + are needed to set up to get this to work. The distributed Erlang implementation provides a basic security mechanism to prevent unauthorized access to an Erlang system on another computer. Erlang systems which talk to each other must have the same <em>magic cookie</em>. The easiest way to achieve this is by having a file called <c>.erlang.cookie</c> in your home - directory on all machines which on which you are going to run - Erlang systems communicating with each other (on Windows systems - the home directory is the directory where pointed to by the $HOME - environment variable - you may need to set this. On Linux or Unix - you can safely ignore this and simply create a file called - <c>.erlang.cookie</c> in the directory you get to after executing - the command <c>cd</c> without any argument). - The <c>.erlang.cookie</c> file should contain one line with - the same atom. For example, on Linux or Unix in the OS shell:</p> + directory on all machines on which you are going to run + Erlang systems communicating with each other: + </p> + <list type="bulleted"> + <item>On Windows systems the home directory is the directory + pointed out by the environment variable $HOME - you may need + to set this.</item> + <item> On Linux or UNIX + you can safely ignore this and simply create a file called + <c>.erlang.cookie</c> in the directory you get to after executing + the command <c>cd</c> without any argument.</item> + </list> + <p>The <c>.erlang.cookie</c> file is to contain a line with + the same atom. For example, on Linux or UNIX, in the OS shell:</p> <pre> $ <input>cd</input> $ <input>cat > .erlang.cookie</input> this_is_very_secret $ <input>chmod 400 .erlang.cookie</input></pre> - <p>The <c>chmod</c> above make the <c>.erlang.cookie</c> file + <p>The <c>chmod</c> above makes the <c>.erlang.cookie</c> file accessible only by the owner of the file. This is a requirement.</p> - <p>When you start an Erlang system which is going to talk to other - Erlang systems, you must give it a name, e.g.: </p> + <p>When you start an Erlang system that is going to talk to other + Erlang systems, you must give it a name, for example:</p> <pre> $ <input>erl -sname my_name</input></pre> <p>We will see more details of this later. If you want to experiment with distributed Erlang, but you only have one computer to work on, you can start two separate Erlang systems on the same computer but give them different names. Each Erlang - system running on a computer is called an Erlang node.</p> + system running on a computer is called an <em>Erlang node</em>.</p> <p>(Note: <c>erl -sname</c> assumes that all nodes are in the same IP domain and we can use only the first component of the IP address, if we want to use nodes in different domains we use @@ -420,10 +428,10 @@ start_pong() -> start_ping(Pong_Node) -> spawn(tut17, ping, [3, Pong_Node]).</code> - <p>Let us assume we have two computers called gollum and kosken. We - will start a node on kosken called ping and then a node on gollum + <p>Let us assume there are two computers called gollum and kosken. + First a node is started on kosken, called ping, and then a node on gollum, called pong.</p> - <p>On kosken (on a Linux/Unix system):</p> + <p>On kosken (on a Linux/UNIX system):</p> <pre> kosken> <input>erl -sname ping</input> Erlang (BEAM) emulator version 5.2.3.7 [hipe] [threads:0] @@ -437,12 +445,12 @@ Erlang (BEAM) emulator version 5.2.3.7 [hipe] [threads:0] Eshell V5.2.3.7 (abort with ^G) (pong@gollum)1></pre> - <p>Now we start the "pong" process on gollum:</p> + <p>Now the "pong" process on gollum is started:</p> <pre> (pong@gollum)1> <input>tut17:start_pong().</input> true</pre> - <p>and start the "ping" process on kosken (from the code above you - will see that a parameter of the <c>start_ping</c> function is + <p>And the "ping" process on kosken is started (from the code above you + can see that a parameter of the <c>start_ping</c> function is the node name of the Erlang system where "pong" is running):</p> <pre> (ping@kosken)1> <input>tut17:start_ping(pong@gollum).</input> @@ -451,8 +459,7 @@ Ping received pong Ping received pong Ping received pong ping finished</pre> - <p>Here we see that the ping pong program has run, on the "pong" - side we see:</p> + <p>As shown, the ping pong program has run. On the "pong" side:</p> <pre> (pong@gollum)2> Pong received ping @@ -460,28 +467,28 @@ Pong received ping Pong received ping Pong finished (pong@gollum)2></pre> - <p>Looking at the <c>tut17</c> code we see that the <c>pong</c> - function itself is unchanged, the lines:</p> + <p>Looking at the <c>tut17</c> code, you see that the <c>pong</c> + function itself is unchanged, the following lines work in the same way + irrespective of on which node the "ping" process is executes:</p> <code type="none"> {ping, Ping_PID} -> io:format("Pong received ping~n", []), Ping_PID ! pong,</code> - <p>work in the same way irrespective of on which node the "ping" - process is executing. Thus Erlang pids contain information about - where the process executes so if you know the pid of a process, - the "!" operator can be used to send it a message if the process - is on the same node or on a different node.</p> - <p>A difference is how we send messages to a registered process on + <p>Thus, Erlang pids contain information about + where the process executes. So if you know the pid of a process, + the "!" operator can be used to send it a message disregarding + if the process is on the same node or on a different node.</p> + <p>A difference is how messages are sent to a registered process on another node:</p> <code type="none"> {pong, Pong_Node} ! {ping, self()},</code> - <p>We use a tuple <c>{registered_name,node_name}</c> instead of + <p>A tuple <c>{registered_name,node_name}</c> is used instead of just the <c>registered_name</c>.</p> - <p>In the previous example, we started "ping" and "pong" from + <p>In the previous example, "ping" and "pong" were started from the shells of two separate Erlang nodes. <c>spawn</c> can also be - used to start processes in other nodes. The next example is - the ping pong program, yet again, but this time we will start - "ping" in another node:</p> + used to start processes in other nodes.</p> + <p>The next example is the ping pong program, yet again, + but this time "ping" is started in another node:</p> <code type="none"> -module(tut18). @@ -513,7 +520,7 @@ start(Ping_Node) -> register(pong, spawn(tut18, pong, [])), spawn(Ping_Node, tut18, ping, [3, node()]).</code> <p>Assuming an Erlang system called ping (but not the "ping" - process) has already been started on kosken, then on gollum we do:</p> + process) has already been started on kosken, then on gollum this is done:</p> <pre> (pong@gollum)1> <input>tut18:start(ping@kosken).</input> <3934.39.0> @@ -525,39 +532,40 @@ Pong received ping Ping received pong Pong finished ping finished</pre> - <p>Notice we get all the output on gollum. This is because the io + <p>Notice that all the output is received on gollum. This is because + the I/O system finds out where the process is spawned from and sends all output there.</p> </section> <section> <title>A Larger Example</title> - <p>Now for a larger example. We will make an extremely simple - "messenger". The messenger is a program which allows users to log + <p>Now for a larger example with a simple + "messenger". The messenger is a program that allows users to log in on different nodes and send simple messages to each other.</p> - <p>Before we start, let's note the following:</p> + <p>Before starting, notice the following:</p> <list type="bulleted"> <item> - <p>This example will just show the message passing logic - no - attempt at all has been made to provide a nice graphical user - interface. This can, of course, also be done in Erlang - but - that's another tutorial.</p> + <p>This example only shows the message passing logic - no + attempt has been made to provide a nice graphical user + interface, although this can also be done in Erlang.</p> </item> <item> - <p>This sort of problem can be solved more easily if you use - the facilities in OTP, which will also provide methods for - updating code on the fly etc. But again, that's another - tutorial.</p> + <p>This sort of problem can be solved easier by use of + the facilities in OTP, which also provide methods for + updating code on the fly and so on (see + <seealso marker="doc/design_principles:des_princ#otp design principles"> + OTP Design Principles</seealso>).</p> </item> <item> - <p>The first program we write will contain some inadequacies - regarding the handling of nodes which disappear. We will correct - these in a later version of the program.</p> + <p>The first program contains some inadequacies + regarding handling of nodes which disappear. + These are corrected in a later version of the program.</p> </item> </list> - <p>We will set up the messenger by allowing "clients" to connect to - a central server and say who and where they are. I.e. a user - won't need to know the name of the Erlang node where another user + <p>The messenger is set up by allowing "clients" to connect to + a central server and say who and where they are. That is, a user + does not need to know the name of the Erlang node where another user is located to send a message.</p> <p>File <c>messenger.erl</c>:</p> <marker id="ex"></marker> @@ -728,19 +736,19 @@ await_result() -> {messenger, What} -> % Normal response io:format("~p~n", [What]) end.</code> - <p>To use this program you need to:</p> + <p>To use this program, you need to:</p> <list type="bulleted"> - <item>configure the <c>server_node()</c> function</item> - <item>copy the compiled code (<c>messenger.beam</c>) to - the directory on each computer where you start Erlang.</item> + <item>Configure the <c>server_node()</c> function.</item> + <item>Copy the compiled code (<c>messenger.beam</c>) to + the directory on each computer where you start Erlang.</item> </list> - <p>In the following example of use of this program I have started - nodes on four different computers, but if you don't have that - many machines available on your network you could start up + <p>In the following example using this program, + nodes are started on four different computers. If you do not have that + many machines available on your network, you can start several nodes on the same machine.</p> - <p>We start up four Erlang nodes: messenger@super, c1@bilbo, + <p>Four Erlang nodes are started up: messenger@super, c1@bilbo, c2@kosken, c3@gollum.</p> - <p>First we start up a the server at messenger@super:</p> + <p>First the server at messenger@super is started up:</p> <pre> (messenger@super)1> <input>messenger:start_server().</input> true</pre> @@ -754,7 +762,7 @@ logged_on</pre> (c2@kosken)1> <input>messenger:logon(james).</input> true logged_on</pre> - <p>and Fred logs on at c3@gollum:</p> + <p>And Fred logs on at c3@gollum:</p> <pre> (c3@gollum)1> <input>messenger:logon(fred).</input> true @@ -764,7 +772,7 @@ logged_on</pre> (c1@bilbo)2> <input>messenger:message(fred, "hello").</input> ok sent</pre> - <p>And Fred receives the message and sends a message to Peter and + <p>Fred receives the message and sends a message to Peter and logs off:</p> <pre> Message from peter: "hello" @@ -779,27 +787,28 @@ logoff</pre> ok receiver_not_found</pre> <p>But this fails as Fred has already logged off.</p> - <p>First let's look at some of the new concepts we have introduced.</p> + <p>First let us look at some of the new concepts that have + been introduced.</p> <p>There are two versions of the <c>server_transfer</c> function: one with four arguments (<c>server_transfer/4</c>) and one with five (<c>server_transfer/5</c>). These are regarded by Erlang as two separate functions.</p> - <p>Note how we write the <c>server</c> function so that it calls - itself, via <c>server(User_List)</c>, and thus creates a loop. + <p>Notice how to write the <c>server</c> function so that it calls + itself, through <c>server(User_List)</c>, and thus creates a loop. The Erlang compiler is "clever" and optimizes the code so that this really is a sort of loop and not a proper function call. But - this only works if there is no code after the call, otherwise - the compiler will expect the call to return and make a proper + this only works if there is no code after the call. Otherwise, + the compiler expects the call to return and make a proper function call. This would result in the process getting bigger and bigger for every loop.</p> - <p>We use functions from the <c>lists</c> module. This is a very + <p>Functions in the <c>lists</c> module are used. This is a very useful module and a study of the manual page is recommended (<c>erl -man lists</c>). <c>lists:keymember(Key,Position,Lists)</c> looks through a list of tuples and looks at <c>Position</c> in each tuple to see if it is the same as <c>Key</c>. The first element is position 1. If it finds a tuple where the element at <c>Position</c> is the same as - Key, it returns <c>true</c>, otherwise <c>false</c>.</p> + <c>Key</c>, it returns <c>true</c>, otherwise <c>false</c>.</p> <pre> 3> <input>lists:keymember(a, 2, [{x,y,z},{b,b,b},{b,a,c},{q,r,s}]).</input> true @@ -812,82 +821,83 @@ false</pre> [{x,y,z},{b,b,b},{q,r,s}]</pre> <p><c>lists:keysearch</c> is like <c>lists:keymember</c>, but it returns <c>{value,Tuple_Found}</c> or the atom <c>false</c>.</p> - <p>There are a lot more very useful functions in the <c>lists</c> + <p>There are many very useful functions in the <c>lists</c> module.</p> - <p>An Erlang process will (conceptually) run until it does a + <p>An Erlang process (conceptually) runs until it does a <c>receive</c> and there is no message which it wants to receive - in the message queue. I say "conceptually" because the Erlang + in the message queue. "conceptually" is used here because the Erlang system shares the CPU time between the active processes in the system.</p> <p>A process terminates when there is nothing more for it to do, - i.e. the last function it calls simply returns and doesn't call + that is, the last function it calls simply returns and does not call another function. Another way for a process to terminate is for it to call <c>exit/1</c>. The argument to <c>exit/1</c> has a - special meaning which we will look at later. In this example we - will do <c>exit(normal)</c> which has the same effect as a + special meaning, which is discussed later. In this example, + <c>exit(normal)</c> is done, which has the same effect as a process running out of functions to call.</p> <p>The BIF <c>whereis(RegisteredName)</c> checks if a registered - process of name <c>RegisteredName</c> exists and return the pid - of the process if it does exist or the atom <c>undefined</c> if - it does not.</p> - <p>You should by now be able to understand most of the code above - so I'll just go through one case: a message is sent from one user - to another.</p> + process of name <c>RegisteredName</c> exists. If it exists, the pid of + that process is returned. If it does not exist, the atom + <c>undefined</c> is returned.</p> + <p>You should by now be able to understand most of the code in the + messenger-module. Let us study one case in detail: a message is + sent from one user to another.</p> <p>The first user "sends" the message in the example above by:</p> <code type="none"> messenger:message(fred, "hello")</code> <p>After testing that the client process exists:</p> <code type="none"> whereis(mess_client) </code> - <p>and a message is sent to <c>mess_client</c>:</p> + <p>And a message is sent to <c>mess_client</c>:</p> <code type="none"> mess_client ! {message_to, fred, "hello"}</code> <p>The client sends the message to the server by:</p> <code type="none"> {messenger, messenger@super} ! {self(), message_to, fred, "hello"},</code> - <p>and waits for a reply from the server.</p> + <p>And waits for a reply from the server.</p> <p>The server receives this message and calls:</p> <code type="none"> server_transfer(From, fred, "hello", User_List),</code> - <p>which checks that the pid <c>From</c> is in the <c>User_List</c>:</p> + <p>This checks that the pid <c>From</c> is in the <c>User_List</c>:</p> <code type="none"> lists:keysearch(From, 1, User_List) </code> - <p>If <c>keysearch</c> returns the atom <c>false</c>, some sort of + <p>If <c>keysearch</c> returns the atom <c>false</c>, some error has occurred and the server sends back the message:</p> <code type="none"> From ! {messenger, stop, you_are_not_logged_on}</code> - <p>which is received by the client which in turn does + <p>This is received by the client, which in turn does <c>exit(normal)</c> and terminates. If <c>keysearch</c> returns - <c>{value,{From,Name}}</c> we know that the user is logged on and - is his name (peter) is in variable <c>Name</c>. We now call:</p> + <c>{value,{From,Name}}</c> it is certain that the user is logged on and + that his name (peter) is in variable <c>Name</c>.</p> + <p>Let us now call:</p> <code type="none"> server_transfer(From, peter, fred, "hello", User_List)</code> - <p>Note that as this is <c>server_transfer/5</c> it is not the same - as the previous function <c>server_transfer/4</c>. We do another - <c>keysearch</c> on <c>User_List</c> to find the pid of the client - corresponding to fred:</p> + <p>Notice that as this is <c>server_transfer/5</c>, it is not the same + as the previous function <c>server_transfer/4</c>. Another + <c>keysearch</c> is done on <c>User_List</c> to find the pid of + the client corresponding to fred:</p> <code type="none"> lists:keysearch(fred, 2, User_List)</code> - <p>This time we use argument 2 which is the second element in - the tuple. If this returns the atom <c>false</c> we know that - fred is not logged on and we send the message:</p> + <p>This time argument 2 is used, which is the second element in + the tuple. If this returns the atom <c>false</c>, + fred is not logged on and the following message is sent:</p> <code type="none"> From ! {messenger, receiver_not_found};</code> - <p>which is received by the client, if <c>keysearch</c> returns:</p> + <p>This is received by the client.</p> + <p> If <c>keysearch</c> returns:</p> <code type="none"> {value, {ToPid, fred}}</code> - <p>we send the message:</p> + <p>The following message is sent to fred's client:</p> <code type="none"> ToPid ! {message_from, peter, "hello"}, </code> - <p>to fred's client and the message:</p> + <p>The following message is sent to peter's client:</p> <code type="none"> From ! {messenger, sent} </code> - <p>to peter's client.</p> <p>Fred's client receives the message and prints it:</p> <code type="none"> {message_from, peter, "hello"} -> io:format("Message from ~p: ~p~n", [peter, "hello"])</code> - <p>and peter's client receives the message in + <p>Peter's client receives the message in the <c>await_result</c> function.</p> </section> </chapter> diff --git a/system/doc/getting_started/intro.xml b/system/doc/getting_started/intro.xml index e8d568bcaf..f9a56e4322 100644 --- a/system/doc/getting_started/intro.xml +++ b/system/doc/getting_started/intro.xml @@ -18,7 +18,7 @@ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> <title>Introduction</title> @@ -28,38 +28,47 @@ <rev></rev> <file>intro.xml</file> </header> + <marker id="getting started"></marker> + + <p>This section is a quick start tutorial to get you started with Erlang. + Everything in this section is true, but only part of the truth. For example, + only the simplest form of the syntax is shown, not all esoteric forms. + Also, parts that are greatly simplified are indicated with *manual*. + This means that a lot more information on the subject is to be found in + the Erlang book or in + <seealso marker="doc/reference_manual:introduction#erlang ref manual"> + Erlang Reference Manual</seealso>.</p> <section> - <title>Introduction</title> - <p>This is a "kick start" tutorial to get you started with Erlang. - Everything here is true, but only part of the truth. For example, - I'll only tell you the simplest form of the syntax, not all - esoteric forms. Where I've greatly oversimplified things I'll - write *manual* which means there is lots more information to be - found in the Erlang book or in the <em>Erlang Reference Manual</em>.</p> - <p>I also assume that this isn't the first time you have touched a - computer and you have a basic idea about how they are programmed. - Don't worry, I won't assume you're a wizard programmer.</p> + <title>Prerequisites</title> + + <p>The reader of this section is assumed to be familiar with the following:</p> + <list type="bulleted"> + <item>Computers in general</item> + <item>Basics on how computers are programmed</item> + </list> + </section> <section> - <title>Things Left Out</title> - <p>In particular the following has been omitted:</p> + <title>Omitted Topics</title> + + <p>The following topics are not treated in this section:</p> <list type="bulleted"> - <item>References</item> - <item>Local error handling (catch/throw)</item> - <item>Single direction links (monitor)</item> - <item>Handling of binary data (binaries / bit syntax)</item> - <item>List comprehensions</item> - <item>How to communicate with the outside world and/or software - written in other languages (ports). There is however a separate - tutorial for this, <em>Interoperability Tutorial</em></item> - <item>Very few of the Erlang libraries have been touched on (for - example file handling)</item> - <item>OTP has been totally skipped and in consequence the Mnesia - database has been skipped.</item> - <item>Hash tables for Erlang terms (ETS)</item> - <item>Changing code in running systems</item> + <item>References.</item> + <item>Local error handling (catch/throw).</item> + <item>Single direction links (monitor).</item> + <item>Handling of binary data (binaries / bit syntax).</item> + <item>List comprehensions.</item> + <item>How to communicate with the outside world and software + written in other languages (ports); + this is described in + <seealso marker="doc/tutorial:introduction#interoperability tutorial"> + Interoperability Tutorial</seealso>.</item> + <item>Erlang libraries (for example, file handling).</item> + <item>OTP and (in consequence) the Mnesia database.</item> + <item>Hash tables for Erlang terms (ETS).</item> + <item>Changing code in running systems.</item> </list> </section> </chapter> diff --git a/system/doc/getting_started/records_macros.xml b/system/doc/getting_started/records_macros.xml index 73c8ce5c8d..bec751fea2 100644 --- a/system/doc/getting_started/records_macros.xml +++ b/system/doc/getting_started/records_macros.xml @@ -29,27 +29,32 @@ <file>record_macros.xml</file> </header> <p>Larger programs are usually written as a collection of files with - a well defined interface between the various parts.</p> + a well-defined interface between the various parts.</p> <section> <title>The Larger Example Divided into Several Files</title> - <p>To illustrate this, we will divide the messenger example from - the previous chapter into five files.</p> - <taglist> - <tag><c>mess_config.hrl</c></tag> - <item>header file for configuration data</item> - <tag><c>mess_interface.hrl</c></tag> - <item>interface definitions between the client and the messenger</item> - <tag><c>user_interface.erl</c></tag> - <item>functions for the user interface</item> - <tag><c>mess_client.erl</c></tag> - <item>functions for the client side of the messenger</item> - <tag><c>mess_server.erl</c></tag> - <item>functions for the server side of the messenger</item> - </taglist> - <p>While doing this we will also clean up the message passing - interface between the shell, the client and the server and define - it using <em>records</em>, we will also introduce <em>macros</em>.</p> + <p>To illustrate this, the messenger example from + the previous section is divided into the following five files:</p> + <list type="bulleted"> + <item> + <p><c>mess_config.hrl</c></p> + <p>Header file for configuration data</p></item> + <item> + <p><c>mess_interface.hrl</c></p> + <p>Interface definitions between the client and the messenger</p></item> + <item> + <p><c>user_interface.erl</c></p> + <p>Functions for the user interface</p></item> + <item> + <p><c>mess_client.erl</c></p> + <p>Functions for the client side of the messenger</p></item> + <item> + <p><c>mess_server.erl</c></p> + <p>Functions for the server side of the messenger</p></item> + </list> + <p>While doing this, the message passing interface between the shell, + the client, and the server is cleaned up and is defined + using <em>records</em>. Also, <em>macros</em> are introduced:</p> <code type="none"> %%%----FILE mess_config.hrl---- @@ -244,14 +249,14 @@ server_transfer(From, Name, To, Message, User_List) -> <section> <title>Header Files</title> - <p>You will see some files above with extension <c>.hrl</c>. These - are header files which are included in the <c>.erl</c> files by:</p> + <p>As shown above, some files have extension <c>.hrl</c>. These + are header files that are included in the <c>.erl</c> files by:</p> <code type="none"> -include("File_Name").</code> <p>for example:</p> <code type="none"> -include("mess_interface.hrl").</code> - <p>In our case above the file is fetched from the same directory as + <p>In the case above the file is fetched from the same directory as all the other files in the messenger example. (*manual*).</p> <p>.hrl files can contain any valid Erlang code but are most often used for record and macro definitions.</p> @@ -265,64 +270,63 @@ server_transfer(From, Name, To, Message, User_List) -> <p>For example:</p> <code type="none"> -record(message_to,{to_name, message}).</code> - <p>This is exactly equivalent to:</p> + <p>This is equivalent to:</p> <code type="none"> {message_to, To_Name, Message}</code> - <p>Creating record, is best illustrated by an example:</p> + <p>Creating a record is best illustrated by an example:</p> <code type="none"> #message_to{message="hello", to_name=fred)</code> - <p>This will create:</p> + <p>This creates:</p> <code type="none"> {message_to, fred, "hello"}</code> - <p>Note that you don't have to worry about the order you assign + <p>Notice that you do not have to worry about the order you assign values to the various parts of the records when you create it. The advantage of using records is that by placing their definitions in header files you can conveniently define - interfaces which are easy to change. For example, if you want to - add a new field to the record, you will only have to change + interfaces that are easy to change. For example, if you want to + add a new field to the record, you only have to change the code where the new field is used and not at every place the record is referred to. If you leave out a field when creating - a record, it will get the value of the atom undefined. (*manual*)</p> + a record, it gets the value of the atom <c>undefined</c>. (*manual*)</p> <p>Pattern matching with records is very similar to creating records. For example, inside a <c>case</c> or <c>receive</c>:</p> <code type="none"> #message_to{to_name=ToName, message=Message} -></code> - <p>is the same as:</p> + <p>This is the same as:</p> <code type="none"> {message_to, ToName, Message}</code> </section> <section> <title>Macros</title> - <p>The other thing we have added to the messenger is a macro. + <p>Another thing that has been added to the messenger is a macro. The file <c>mess_config.hrl</c> contains the definition:</p> <code type="none"> %%% Configure the location of the server node, -define(server_node, messenger@super).</code> - <p>We include this file in mess_server.erl:</p> + <p>This file is included in <c>mess_server.erl</c>:</p> <code type="none"> -include("mess_config.hrl").</code> <p>Every occurrence of <c>?server_node</c> in <c>mess_server.erl</c> - will now be replaced by <c>messenger@super</c>.</p> - <p>The other place a macro is used is when we spawn the server - process:</p> + is now replaced by <c>messenger@super</c>.</p> + <p>A macro is also used when spawning the server process:</p> <code type="none"> spawn(?MODULE, server, [])</code> - <p>This is a standard macro (i.e. defined by the system, not - the user). <c>?MODULE</c> is always replaced by the name of - current module (i.e. the <c>-module</c> definition near the start + <p>This is a standard macro (that is, defined by the system, not by + the user). <c>?MODULE</c> is always replaced by the name of the + current module (that is, the <c>-module</c> definition near the start of the file). There are more advanced ways of using macros with, - for example parameters (*manual*).</p> + for example, parameters (*manual*).</p> <p>The three Erlang (<c>.erl</c>) files in the messenger example are individually compiled into object code file (<c>.beam</c>). The Erlang system loads and links these files into the system - when they are referred to during execution of the code. In our - case we simply have put them in the same directory which is our - current working directory (i.e. the place we have done "cd" to). + when they are referred to during execution of the code. In this + case, they are simply put in our current working directory + (that is, the place you have done "cd" to). There are ways of putting the <c>.beam</c> files in other directories.</p> <p>In the messenger example, no assumptions have been made about - what the message being sent is. It could be any valid Erlang term.</p> + what the message being sent is. It can be any valid Erlang term.</p> </section> </chapter> diff --git a/system/doc/getting_started/robustness.xml b/system/doc/getting_started/robustness.xml index e8fb81d5e8..82fe0cbc4f 100644 --- a/system/doc/getting_started/robustness.xml +++ b/system/doc/getting_started/robustness.xml @@ -28,27 +28,27 @@ <rev></rev> <file>robustness.xml</file> </header> - <p>There are several things which are wrong with - the <seealso marker="conc_prog#ex">messenger example</seealso> from - the previous chapter. For example, if a node where a user is logged - on goes down without doing a log off, the user will remain in - the server's <c>User_List</c> but the client will disappear thus - making it impossible for the user to log on again as the server - thinks the user already logged on.</p> + <p>Several things are wrong with the messenger example in + <seealso marker="conc_prog#ex">A Larger Example</seealso>. + For example, if a node where a user is logged + on goes down without doing a logoff, the user remains in + the server's <c>User_List</c>, but the client disappears. This + makes it impossible for the user to log on again as the server + thinks the user already is logged on.</p> <p>Or what happens if the server goes down in the middle of sending a - message leaving the sending client hanging for ever in + message, leaving the sending client hanging forever in the <c>await_result</c> function?</p> <section> - <title>Timeouts</title> - <p>Before improving the messenger program we will look into some + <title>Time-outs</title> + <p>Before improving the messenger program, let us look at some general principles, using the ping pong program as an example. Recall that when "ping" finishes, it tells "pong" that it has done so by sending the atom <c>finished</c> as a message to "pong" - so that "pong" could also finish. Another way to let "pong" - finish, is to make "pong" exit if it does not receive a message - from ping within a certain time, this can be done by adding a - <em>timeout</em> to <c>pong</c> as shown in the following example:</p> + so that "pong" can also finish. Another way to let "pong" + finish is to make "pong" exit if it does not receive a message + from ping within a certain time. This can be done by adding a + <em>time-out</em> to <c>pong</c> as shown in the following example:</p> <code type="none"> -module(tut19). @@ -80,9 +80,9 @@ start_pong() -> start_ping(Pong_Node) -> spawn(tut19, ping, [3, Pong_Node]).</code> - <p>After we have compiled this and copied the <c>tut19.beam</c> - file to the necessary directories:</p> - <p>On (pong@kosken):</p> + <p>After this is compiled and the file <c>tut19.beam</c> + is copied to the necessary directories, the following is seen + on (pong@kosken): </p> <pre> (pong@kosken)1> <input>tut19:start_pong().</input> true @@ -90,7 +90,7 @@ Pong received ping Pong received ping Pong received ping Pong timed out</pre> - <p>On (ping@gollum):</p> + <p>And the following is seen on (ping@gollum):</p> <pre> (ping@gollum)1> <input>tut19:start_ping(pong@kosken).</input> <0.36.0> @@ -98,7 +98,7 @@ Ping received pong Ping received pong Ping received pong ping finished </pre> - <p>(The timeout is set in:</p> + <p>The time-out is set in:</p> <code type="none"> pong() -> receive @@ -109,35 +109,36 @@ pong() -> after 5000 -> io:format("Pong timed out~n", []) end.</code> - <p>We start the timeout (<c>after 5000</c>) when we enter - <c>receive</c>. The timeout is canceled if <c>{ping,Ping_PID}</c> + <p>The time-out (<c>after 5000</c>) is started when + <c>receive</c> is entered. + The time-out is canceled if <c>{ping,Ping_PID}</c> is received. If <c>{ping,Ping_PID}</c> is not received, - the actions following the timeout will be done after 5000 + the actions following the time-out are done after 5000 milliseconds. <c>after</c> must be last in the <c>receive</c>, - i.e. preceded by all other message reception specifications in - the <c>receive</c>. Of course we could also call a function which - returned an integer for the timeout:</p> + that is, preceded by all other message reception specifications in + the <c>receive</c>. It is also possible to call a function that + returned an integer for the time-out:</p> <code type="none"> after pong_timeout() -></code> - <p>In general, there are better ways than using timeouts to - supervise parts of a distributed Erlang system. Timeouts are - usually appropriate to supervise external events, for example if + <p>In general, there are better ways than using time-outs to + supervise parts of a distributed Erlang system. Time-outs are + usually appropriate to supervise external events, for example, if you have expected a message from some external system within a - specified time. For example, we could use a timeout to log a user - out of the messenger system if they have not accessed it, for - example, in ten minutes.</p> + specified time. For example, a time-out can be used to log a user + out of the messenger system if they have not accessed it for, + say, ten minutes.</p> </section> <section> <title>Error Handling</title> - <p>Before we go into details of the supervision and error handling - in an Erlang system, we need see how Erlang processes terminate, + <p>Before going into details of the supervision and error handling + in an Erlang system, let us see how Erlang processes terminate, or in Erlang terminology, <em>exit</em>.</p> <p>A process which executes <c>exit(normal)</c> or simply runs out of things to do has a <em>normal</em> exit.</p> - <p>A process which encounters a runtime error (e.g. divide by zero, - bad match, trying to call a function which doesn't exist etc) - exits with an error, i.e. has an <em>abnormal</em> exit. A + <p>A process which encounters a runtime error (for example, divide by zero, + bad match, trying to call a function that does not exist and so on) + exits with an error, that is, has an <em>abnormal</em> exit. A process which executes <seealso marker="erts:erlang#exit/1">exit(Reason)</seealso> where <c>Reason</c> is any Erlang term except the atom @@ -151,18 +152,23 @@ after pong_timeout() -></code> links to.</p> <p>The signal carries information about the pid it was sent from and the exit reason.</p> - <p>The default behaviour of a process which receives a normal exit + <p>The default behaviour of a process that receives a normal exit is to ignore the signal.</p> - <p>The default behaviour in the two other cases (i.e. abnormal exit) - above is to bypass all messages to the receiving process and to - kill it and to propagate the same error signal to the killed - process' links. In this way you can connect all processes in a - transaction together using links and if one of the processes - exits abnormally, all the processes in the transaction will be - killed. As we often want to create a process and link to it at + <p>The default behaviour in the two other cases (that is, abnormal exit) + above is to:</p> + <list type="bulleted"> + <item>Bypass all messages to the receiving process.</item> + <item>Kill the receiving process.</item> + <item>Propagate the same error signal to the links of the + killed process.</item> + </list> + <p>In this way you can connect all processes in a + transaction together using links. If one of the processes + exits abnormally, all the processes in the transaction are + killed. As it is often wanted to create a process and link to it at the same time, there is a special BIF, <seealso marker="erts:erlang#spawn_link/1">spawn_link</seealso> - which does the same as <c>spawn</c>, but also creates a link to + that does the same as <c>spawn</c>, but also creates a link to the spawned process.</p> <p>Now an example of the ping pong example using links to terminate "pong":</p> @@ -208,13 +214,13 @@ Pong received ping Ping received pong</pre> <p>This is a slight modification of the ping pong program where both processes are spawned from the same <c>start/1</c> function, - where the "ping" process can be spawned on a separate node. Note + and the "ping" process can be spawned on a separate node. Notice the use of the <c>link</c> BIF. "Ping" calls - <c>exit(ping)</c> when it finishes and this will cause an exit - signal to be sent to "pong" which will also terminate.</p> + <c>exit(ping)</c> when it finishes and this causes an exit + signal to be sent to "pong", which also terminates.</p> <p>It is possible to modify the default behaviour of a process so that it does not get killed when it receives abnormal exit - signals, but all signals will be turned into normal messages with + signals. Instead, all signals are turned into normal messages on the format <c>{'EXIT',FromPID,Reason}</c> and added to the end of the receiving process' message queue. This behaviour is set by:</p> <code type="none"> @@ -223,8 +229,8 @@ process_flag(trap_exit, true)</code> <seealso marker="erts:erlang#process_flag/2">erlang(3)</seealso>. Changing the default behaviour of a process in this way is usually not done in standard user programs, but is left to - the supervisory programs in OTP (but that's another tutorial). - However we will modify the ping pong program to illustrate exit + the supervisory programs in OTP. + However, the ping pong program is modified to illustrate exit trapping.</p> <code type="none"> -module(tut21). @@ -277,7 +283,7 @@ pong exiting, got {'EXIT',<3820.39.0>,ping}</pre> <section> <title>The Larger Example with Robustness Added</title> - <p>Now we return to the messenger program and add changes which + <p>Let us return to the messenger program and add changes to make it more robust:</p> <code type="none"> %%% Message passing utility. @@ -449,35 +455,34 @@ await_result() -> io:format("No response from server~n", []), exit(timeout) end.</code> - <p>We have added the following changes:</p> + <p>The following changes are added:</p> <p>The messenger server traps exits. If it receives an exit signal, - <c>{'EXIT',From,Reason}</c> this means that a client process has - terminated or is unreachable because:</p> + <c>{'EXIT',From,Reason}</c>, this means that a client process has + terminated or is unreachable for one of the following reasons:</p> <list type="bulleted"> - <item>the user has logged off (we have removed the "logoff" - message),</item> - <item>the network connection to the client is broken,</item> - <item>the node on which the client process resides has gone down, - or</item> - <item>the client processes has done some illegal operation.</item> + <item>The user has logged off (the "logoff" + message is removed).</item> + <item>The network connection to the client is broken.</item> + <item>The node on which the client process resides has gone down.</item> + <item>The client processes has done some illegal operation.</item> </list> - <p>If we receive an exit signal as above, we delete the tuple, - <c>{From,Name}</c> from the servers <c>User_List</c> using + <p>If an exit signal is received as above, the tuple + <c>{From,Name}</c> is deleted from the servers <c>User_List</c> using the <c>server_logoff</c> function. If the node on which the server runs goes down, an exit signal (automatically generated by - the system), will be sent to all of the client processes: + the system) is sent to all of the client processes: <c>{'EXIT',MessengerPID,noconnection}</c> causing all the client processes to terminate.</p> - <p>We have also introduced a timeout of five seconds in - the <c>await_result</c> function. I.e. if the server does not + <p>Also, a time-out of five seconds has been introduced in + the <c>await_result</c> function. That is, if the server does not reply within five seconds (5000 ms), the client terminates. This - is really only needed in the logon sequence before the client and + is only needed in the logon sequence before the client and the server are linked.</p> - <p>An interesting case is if the client was to terminate before + <p>An interesting case is if the client terminates before the server links to it. This is taken care of because linking to a non-existent process causes an exit signal, - <c>{'EXIT',From,noproc}</c>, to be automatically generated as if - the process terminated immediately after the link operation.</p> + <c>{'EXIT',From,noproc}</c>, to be automatically generated. This is + as if the process terminated immediately after the link operation.</p> </section> </chapter> diff --git a/system/doc/getting_started/seq_prog.xml b/system/doc/getting_started/seq_prog.xml index 699b9487ed..5d96aed8d4 100644 --- a/system/doc/getting_started/seq_prog.xml +++ b/system/doc/getting_started/seq_prog.xml @@ -31,11 +31,15 @@ <section> <title>The Erlang Shell</title> - <p>Most operating systems have a command interpreter or shell- Unix - and Linux have many, while Windows has the Command Prompt. Erlang has - its own shell where you can directly write bits of Erlang code - and evaluate (run) them to see what happens (see - <seealso marker="stdlib:shell">shell(3)</seealso>). Start + <p> + Most operating systems have a command interpreter or shell, UNIX + and Linux have many, Windows has the command prompt. Erlang has + its own shell where bits of Erlang code can be written directly, + and be evaluated to see what happens + (see the <seealso marker="stdlib:shell">shell(3)</seealso> + manual page in STDLIB). + </p> + <p>Start the Erlang shell (in Linux or UNIX) by starting a shell or command interpreter in your operating system and typing <c>erl</c>. You will see something like this.</p> @@ -45,41 +49,39 @@ Erlang R15B (erts-5.9.1) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe] [ker Eshell V5.9.1 (abort with ^G) 1></pre> - <p>Now type in "2 + 5." as shown below.</p> + <p>Type "2 + 5." in the shell and then press Enter (carriage return). + Notice that you tell the shell you are done entering code by finishing + with a full stop "." and a carriage return.</p> <pre> 1> <input>2 + 5.</input> 7 2></pre> - <p>In Windows, the shell is started by double-clicking on the Erlang - shell icon.</p> - <p>You'll notice that the Erlang shell has numbered the lines that - can be entered, (as 1> 2>) and that it has correctly told you - that 2 + 5 is 7! Also notice that you have to tell it you are - done entering code by finishing with a full stop "." and a - carriage return. If you make mistakes writing things in the shell, - you can delete things by using the backspace key as in most - shells. There are many more editing commands in the shell - (See the chapter <seealso marker="erts:tty">"tty - A command line interface"</seealso> in ERTS User's Guide).</p> - <p>(Note: you will find a lot of line numbers given by the shell - out of sequence in this tutorial as it was written and the code - tested in several sessions.)</p> - <p>Now let's try a more complex calculation.</p> + <p>As shown, the Erlang shell numbers the lines that + can be entered, (as 1> 2>) and that it correctly says + that 2 + 5 is 7. If you make writing mistakes in the shell, + you can delete with the backspace key, as in most shells. + There are many more editing commands in the shell + (see <seealso marker="erts:tty">tty - A command line interface</seealso> in ERTS User's Guide).</p> + <p>(Notice that many line numbers given by the shell in the + following examples are out of sequence. This is because this + tutorial was written and code-tested in separate sessions).</p> + <p>Here is a bit more complex calculation:</p> <pre> 2> <input>(42 + 77) * 66 / 3.</input> 2618.0</pre> - <p>Here you can see the use of brackets and the multiplication - operator "*" and division operator "/", just as in normal - arithmetic (see the chapter - <seealso marker="doc/reference_manual:expressions">"Arithmetic Expressions"</seealso> in the Erlang Reference Manual).</p> - <p>To shutdown the Erlang system and the Erlang shell type - Control-C. You will see the following output:</p> + <p>Notice the use of brackets, the multiplication operator "*", + and the division operator "/", as in normal arithmetic (see + <seealso marker="doc/reference_manual:expressions">Expressions</seealso>).</p> + <p>Press Control-C to shut down the Erlang system and the Erlang + shell.</p> + <p>The following output is shown:</p> <pre> BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution <input>a</input> %</pre> <p>Type "a" to leave the Erlang system.</p> - <p>Another way to shutdown the Erlang system is by entering + <p>Another way to shut down the Erlang system is by entering <c>halt()</c>:</p> <pre> 3> <input>halt().</input> @@ -88,67 +90,70 @@ BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded <section> <title>Modules and Functions</title> - <p>A programming language isn't much use if you can just run code + <p>A programming language is not much use if you only can run code from the shell. So here is a small Erlang program. Enter it into - a file called <c>tut.erl</c> (the file name <c>tut.erl</c> is - important, also make sure that it is in the same directory as - the one where you started <c>erl</c>) using a suitable - text editor. If you are lucky your editor will have an Erlang - mode which will make it easier for you to enter and format your - code nicely (see the chapter - <seealso marker="tools:erlang_mode_chapter">"The Erlang mode for Emacs"</seealso> in Tools User's Guide), but you can manage - perfectly well without. Here's the code to enter:</p> + a file named <c>tut.erl</c> using a suitable + text editor. The file name <c>tut.erl</c> is important, and also + that it is in the same directory as the one where you started + <c>erl</c>). If you are lucky your editor has an Erlang mode + that makes it easier for you to enter and format your code + nicely (see <seealso + marker="tools:erlang_mode_chapter">The Erlang mode for + Emacs</seealso> in Tools User's Guide), but you can manage + perfectly well without. Here is the code to enter:</p> <code type="none"> -module(tut). -export([double/1]). double(X) -> 2 * X.</code> - <p>It's not hard to guess that this "program" doubles the value of - numbers. I'll get back to the first two lines later. Let's compile - the program. This can be done in your Erlang shell as shown below:</p> + <p>It is not hard to guess that this program doubles the value of + numbers. The first two lines of the code are described later. + Let us compile the program. This can be done in an Erlang shell + as follows, where <c>c</c> means compile:</p> <pre> 3> <input>c(tut).</input> {ok,tut}</pre> - <p>The <c>{ok,tut}</c> tells you that the compilation was OK. If it - said "error" instead, you have made some mistake in the text you - entered and there will also be error messages to give you some - idea as to what has gone wrong so you can change what you have - written and try again.</p> - <p>Now let's run the program.</p> + <p>The <c>{ok,tut}</c> means that the compilation is OK. If it + says "error" it means that there is some mistake in the text + that you entered. Additional error messages gives an idea to + what is wrong so you can modify the text and then try to compile + the program again.</p> + <p>Now run the program:</p> <pre> 4> <input>tut:double(10).</input> 20</pre> - <p>As expected double of 10 is 20.</p> - <p>Now let's get back to the first two lines. Erlang programs are - written in files. Each file contains what we call an Erlang - <em>module</em>. The first line of code in the module tells us - the name of the module (see the chapter - <seealso marker="doc/reference_manual:modules">"Modules"</seealso> - in the Erlang Reference Manual).</p> + <p>As expected, double of 10 is 20.</p> + <p>Now let us get back to the first two lines of the code. Erlang + programs are + written in files. Each file contains an Erlang + <em>module</em>. The first line of code in the module is + the module name (see + <seealso marker="doc/reference_manual:modules">Modules</seealso>):</p> <code type="none"> -module(tut).</code> - <p>This tells us that the module is called <em>tut</em>. Note - the "." at the end of the line. The files which are used to store + <p>Thus, the module is called <em>tut</em>. Notice + the full stop "." at the end of the line. The files which are + used to store the module must have the same name as the module but with - the extension ".erl". In our case the file name is <c>tut.erl</c>. - When we use a function in another module, we use the syntax, - <c>module_name:function_name(arguments)</c>. So</p> + the extension ".erl". In this case the file name is <c>tut.erl</c>. + When using a function in another module, the syntax + <c>module_name:function_name(arguments)</c> is used. So the + following means call function <c>double</c> in module <c>tut</c> + with argument "10".</p> <pre> 4> <input>tut:double(10).</input></pre> - <p>means call function <c>double</c> in module <c>tut</c> with - argument "10".</p> - <p>The second line:</p> + <p>The second line says that the module <c>tut</c> contains a + function called <c>double</c>, which takes one argument + (<c>X</c> in our example):</p> <code type="none"> -export([double/1]).</code> - <p>says that the module <c>tut</c> contains a function called - <c>double</c> which takes one argument (<c>X</c> in our example) - and that this function can be called from outside the module - <c>tut</c>. More about this later. Again note the "." at the end - of the line.</p> - <p>Now for a more complicated example, the factorial of a number - (e.g. factorial of 4 is 4 * 3 * 2 * 1). Enter the following code - in a file called <c>tut1.erl</c>.</p> + <p>The second line also says that this function can be called from + outside the module <c>tut</c>. More about this later. Again, + notice the "." at the end of the line.</p> + <p>Now for a more complicated example, the factorial of a number. + For example, the factorial of 4 is 4 * 3 * 2 * 1, which equals 24.</p> + <p>Enter the following code in a file named <c>tut1.erl</c>:</p> <code type="none"> -module(tut1). -export([fac/1]). @@ -157,30 +162,34 @@ fac(1) -> 1; fac(N) -> N * fac(N - 1).</code> - <p>Compile the file</p> - <pre> -5> <input>c(tut1).</input> -{ok,tut1}</pre> - <p>And now calculate the factorial of 4.</p> - <pre> -6> <input>tut1:fac(4).</input> -24</pre> - <p>The first part:</p> + <p>So this is a module, called <c>tut1</c> that contains a + function called <c>fac></c>, which takes one argument, + <c>N</c>.</p> + <p>The first part says that the factorial of 1 is 1.:</p> <code type="none"> fac(1) -> 1;</code> - <p>says that the factorial of 1 is 1. Note that we end this part - with a ";" which indicates that there is more of this function to - come. The second part:</p> + <p>Notice that this part ends with a semicolon ";" that indicates + that there is more of the function <c>fac></c> to come.</p> + <p>The second part says that the factorial of N is N multiplied + by the factorial of N - 1:</p> <code type="none"> fac(N) -> N * fac(N - 1).</code> - <p>says that the factorial of N is N multiplied by the factorial of - N - 1. Note that this part ends with a "." saying that there are + <p>Notice that this part ends with a "." saying that there are no more parts of this function.</p> - <p>A function can have many arguments. Let's expand the module - <c>tut1</c> with the rather stupid function to multiply two - numbers:</p> + <p>Compile the file:</p> + <pre> +5> <input>c(tut1).</input> +{ok,tut1}</pre> + <p>And now calculate the factorial of 4.</p> + <pre> +6> <input>tut1:fac(4).</input> +24</pre> + <p>Here the function <c>fac></c> in module <c>tut1</c> is called + with argument <c>4</c>.</p> + <p>A function can have many arguments. Let us expand the module + <c>tut1</c> with the function to multiply two numbers:</p> <code type="none"> -module(tut1). -export([fac/1, mult/2]). @@ -192,36 +201,36 @@ fac(N) -> mult(X, Y) -> X * Y.</code> - <p>Note that we have also had to expand the <c>-export</c> line + <p>Notice that it is also required to expand the <c>-export</c> line with the information that there is another function <c>mult</c> with two arguments.</p> <p>Compile:</p> <pre> 7> <input>c(tut1).</input> {ok,tut1}</pre> - <p>and try it out:</p> + <p>Try out the new function <c>mult</c>:</p> <pre> 8> <input>tut1:mult(3,4).</input> 12</pre> - <p>In the example above the numbers are integers and the arguments - in the functions in the code, <c>N</c>, <c>X</c>, <c>Y</c> are + <p>In this example the numbers are integers and the arguments + in the functions in the code <c>N</c>, <c>X</c>, and <c>Y</c> are called variables. Variables must start with a capital letter - (see the chapter - <seealso marker="doc/reference_manual:expressions">"Variables"</seealso> - in the Erlang Reference Manual). Examples of variables could be - <c>Number</c>, <c>ShoeSize</c>, <c>Age</c> etc.</p> + (see + <seealso marker="doc/reference_manual:expressions">Variables</seealso>). + Examples of variables are + <c>Number</c>, <c>ShoeSize</c>, and <c>Age</c>.</p> </section> <section> <title>Atoms</title> - <p>Atoms are another data type in Erlang. Atoms start with a small - letter ((see the chapter - <seealso marker="doc/reference_manual:data_types">"Atom"</seealso> - in the Erlang Reference Manual)), for example: <c>charles</c>, - <c>centimeter</c>, <c>inch</c>. Atoms are simply names, nothing - else. They are not like variables which can have a value.</p> - <p>Enter the next program (file: <c>tut2.erl</c>) which could be - useful for converting from inches to centimeters and vice versa:</p> + <p>Atom is another data type in Erlang. Atoms start with a small + letter (see + <seealso marker="doc/reference_manual:data_types">Atom</seealso>), + for example, <c>charles</c>, + <c>centimeter</c>, and <c>inch</c>. Atoms are simply names, nothing + else. They are not like variables, which can have a value.</p> + <p>Enter the next program in a file named <c>tut2.erl</c>). It can be + useful for converting from inches to centimeters and conversely:</p> <code type="none"> -module(tut2). -export([convert/2]). @@ -231,27 +240,30 @@ convert(M, inch) -> convert(N, centimeter) -> N * 2.54.</code> - <p>Compile and test:</p> + <p>Compile:</p> <pre> 9> <input>c(tut2).</input> {ok,tut2} +</pre> + <p>Test:</p> + <pre> 10> <input>tut2:convert(3, inch).</input> 1.1811023622047243 11> <input>tut2:convert(7, centimeter).</input> 17.78</pre> - <p>Notice that I have introduced decimals (floating point numbers) - without any explanation, but I guess you can cope with that.</p> - <p>See what happens if I enter something other than centimeter or - inch in the convert function:</p> + <p>Notice the introduction of decimals (floating point numbers) + without any explanation. Hopefully you can cope with that.</p> + <p>Let us see what happens if something other than <c>centimeter</c> or + <c>inch</c> is entered in the <c>convert</c> function:</p> <pre> 12> <input>tut2:convert(3, miles).</input> ** exception error: no function clause matching tut2:convert(3,miles) (tut2.erl, line 4)</pre> <p>The two parts of the <c>convert</c> function are called its - clauses. Here we see that "miles" is not part of either of - the clauses. The Erlang system can't <em>match</em> either of - the clauses so we get an error message <c>function_clause</c>. - The shell formats the error message nicely, but the error tuple - is saved in the shell's history list and can be output by the shell + clauses. As shown, <c>miles</c> is not part of either of + the clauses. The Erlang system cannot <em>match</em> either of + the clauses so an error message <c>function_clause</c> is returned. + The shell formats the error message nicely, but the error tuple + is saved in the shell's history list and can be output by the shell command <c>v/1</c>:</p> <pre> 13> <input>v(12).</input> @@ -271,14 +283,15 @@ convert(N, centimeter) -> Consider:</p> <code type="none"> tut2:convert(3, inch).</code> - <p>Does this mean that 3 is in inches? Or that 3 is in centimeters - and we want to convert it to inches? So Erlang has a way to group - things together to make things more understandable. We call these - <em>tuples</em>. Tuples are surrounded by "{" and "}".</p> - <p>So we can write <c>{inch,3}</c> to denote 3 inches and - <c>{centimeter,5}</c> to denote 5 centimeters. Now let's write a - new program which converts centimeters to inches and vice versa. - (file <c>tut3.erl</c>).</p> + <p>Does this mean that 3 is in inches? Or does it mean that 3 is + in centimeters + and is to be converted to inches? Erlang has a way to group + things together to make things more understandable. These are called + <em>tuples</em> and are surrounded by curly brackets, "{" and "}".</p> + <p>So, <c>{inch,3}</c> denotes 3 inches and + <c>{centimeter,5}</c> denotes 5 centimeters. Now let us write a + new program that converts centimeters to inches and conversely. + Enter the following code in a file called <c>tut3.erl</c>):</p> <code type="none"> -module(tut3). -export([convert_length/1]). @@ -295,47 +308,48 @@ convert_length({inch, Y}) -> {centimeter,12.7} 16> <input>tut3:convert_length(tut3:convert_length({inch, 5})).</input> {inch,5.0}</pre> - <p>Note on line 16 we convert 5 inches to centimeters and back - again and reassuringly get back to the original value. I.e + <p>Notice on line 16 that 5 inches is converted to centimeters and back + again and reassuringly get back to the original value. That is, the argument to a function can be the result of another function. - Pause for a moment and consider how line 16 (above) works. - The argument we have given the function <c>{inch,5}</c> is first - matched against the first head clause of <c>convert_length</c> - i.e. <c>convert_length({centimeter,X})</c> where it can be seen + Consider how line 16 (above) works. + The argument given to the function <c>{inch,5}</c> is first + matched against the first head clause of <c>convert_length</c>, + that is, <c>convert_length({centimeter,X})</c>. It can be seen that <c>{centimeter,X}</c> does not match <c>{inch,5}</c> - (the head is the bit before the "->"). This having failed, we try - the head of the next clause i.e. <c>convert_length({inch,Y})</c>, - this matches and <c>Y</c> get the value 5.</p> - <p>We have shown tuples with two parts above, but tuples can have - as many parts as we want and contain any valid Erlang + (the head is the bit before the "->"). This having failed, + let us try + the head of the next clause that is, <c>convert_length({inch,Y})</c>. + This matches, and <c>Y</c> gets the value 5.</p> + <p>Tuples can have more than two parts, in fact + as many parts as you want, and contain any valid Erlang <em>term</em>. For example, to represent the temperature of - various cities of the world we could write:</p> + various cities of the world:</p> <code type="none"> {moscow, {c, -10}} {cape_town, {f, 70}} {paris, {f, 28}}</code> - <p>Tuples have a fixed number of things in them. We call each thing - in a tuple an element. So in the tuple <c>{moscow,{c,-10}}</c>, - element 1 is <c>moscow</c> and element 2 is <c>{c,-10}</c>. I - have chosen <c>c</c> meaning Centigrade (or Celsius) and <c>f</c> - meaning Fahrenheit.</p> + <p>Tuples have a fixed number of items in them. Each item in a + tuple is called an <em>element</em>. In the tuple + <c>{moscow,{c,-10}}</c>, element 1 is <c>moscow</c> and element + 2 is <c>{c,-10}</c>. Here <c>c</c> represents Celsius and + <c>f</c> Fahrenheit.</p> </section> <section> <title>Lists</title> - <p>Whereas tuples group things together, we also want to be able to - represent lists of things. Lists in Erlang are surrounded by "[" - and "]". For example, a list of the temperatures of various cities - in the world could be:</p> + <p>Whereas tuples group things together, it is also needed to + represent lists of things. Lists in Erlang are surrounded by + square brackets, "[" and "]". For example, a list of the + temperatures of various cities in the world can be:</p> <code type="none"> [{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]</code> - <p>Note that this list was so long that it didn't fit on one line. - This doesn't matter, Erlang allows line breaks at all "sensible - places" but not, for example, in the middle of atoms, integers - etc.</p> - <p>A very useful way of looking at parts of lists, is by using "|". - This is best explained by an example using the shell.</p> + <p>Notice that this list was so long that it did not fit on one line. + This does not matter, Erlang allows line breaks at all "sensible + places" but not, for example, in the middle of atoms, integers, + and others.</p> + <p>A useful way of looking at parts of lists, is by using "|". + This is best explained by an example using the shell:</p> <pre> 17> <input>[First |TheRest] = [1,2,3,4,5].</input> [1,2,3,4,5] @@ -343,9 +357,9 @@ convert_length({inch, Y}) -> 1 19> <input>TheRest.</input> [2,3,4,5]</pre> - <p>We use | to separate the first elements of the list from - the rest of the list. (<c>First</c> has got value 1 and - <c>TheRest</c> value [2,3,4,5].)</p> + <p>To separate the first elements of the list from the rest of the + list, <c>|</c> is used. <c>First</c> has got value 1 and + <c>TheRest</c> has got the value [2,3,4,5].</p> <p>Another example:</p> <pre> 20> <input>[E1, E2 | R] = [1,2,3,4,5,6,7].</input> @@ -356,10 +370,10 @@ convert_length({inch, Y}) -> 2 23> <input>R.</input> [3,4,5,6,7]</pre> - <p>Here we see the use of | to get the first two elements from - the list. Of course if we try to get more elements from the list - than there are elements in the list we will get an error. Note - also the special case of the list with no elements [].</p> + <p>Here you see the use of <c>|</c> to get the first two elements from + the list. If you try to get more elements from the list + than there are elements in the list, an error is returned. Notice + also the special case of the list with no elements, []:</p> <pre> 24> <input>[A, B | C] = [1, 2].</input> [1,2] @@ -369,13 +383,13 @@ convert_length({inch, Y}) -> 2 27> <input>C.</input> []</pre> - <p>In all the examples above, I have been using new variable names, - not reusing the old ones: <c>First</c>, <c>TheRest</c>, <c>E1</c>, - <c>E2</c>, <c>R</c>, <c>A</c>, <c>B</c>, <c>C</c>. The reason + <p>In the previous examples, new variable names are used, instead of + reusing the old ones: <c>First</c>, <c>TheRest</c>, <c>E1</c>, + <c>E2</c>, <c>R</c>, <c>A</c>, <c>B</c>, and <c>C</c>. The reason for this is that a variable can only be given a value once in its - context (scope). I'll get back to this later, it isn't so - peculiar as it sounds!</p> - <p>The following example shows how we find the length of a list:</p> + context (scope). More about this later.</p> + <p>The following example shows how to find the length of a list. + Enter the following code in a file named <c>tut4.erl</c>):</p> <code type="none"> -module(tut4). @@ -385,7 +399,7 @@ list_length([]) -> 0; list_length([First | Rest]) -> 1 + list_length(Rest).</code> - <p>Compile (file <c>tut4.erl</c>) and test:</p> + <p>Compile and test:</p> <pre> 28> <input>c(tut4).</input> {ok,tut4} @@ -404,14 +418,14 @@ list_length([First | Rest]) -> <c>Rest</c>.</p> <p>(Advanced readers only: This is not tail recursive, there is a better way to write this function.)</p> - <p>In general we can say we use tuples where we would use "records" - or "structs" in other languages and we use lists when we want to - represent things which have varying sizes, (i.e. where we would - use linked lists in other languages).</p> - <p>Erlang does not have a string data type, instead strings can be - represented by lists of ASCII characters. So the list - <c>[97,98,99]</c> is equivalent to "abc". The Erlang shell is - "clever" and guesses the what sort of list we mean and outputs it + <p>In general, tuples are used where "records" + or "structs" are used in other languages. Also, lists are used when + representing things with varying sizes, that is, where + linked lists are used in other languages.</p> + <p>Erlang does not have a string data type. Instead, strings can be + represented by lists of Unicode characters. This implies for example that + the list <c>[97,98,99]</c> is equivalent to "abc". The Erlang shell is + "clever" and guesses what list you mean and outputs it in what it thinks is the most appropriate form, for example:</p> <pre> 30> <input>[97,98,99].</input> @@ -420,16 +434,17 @@ list_length([First | Rest]) -> <section> <title>Maps</title> - <p>Maps are a set of key to value associations. These associations - are encapsulated with "#{" and "}". To create an association from - "key" to value 42, we write:</p> + <p>Maps are a set of key to value associations. These associations + are encapsulated with "#{" and "}". To create an association + from "key" to value 42:</p> <code type="none"> > #{ "key" => 42 }. #{"key" => 42}</code> - <p>We will jump straight into the deep end with an example using some - interesting features.</p> - <p>The following example shows how we calculate alpha blending using - maps to reference color and alpha channels:</p> + <p>Let us jump straight into the deep end with an example using some + interesting features.</p> + <p>The following example shows how to calculate alpha blending + using maps to reference color and alpha channels. Enter the code + in a file named <c>color.erl</c>):</p> <code type="none"> -module(color). @@ -468,7 +483,7 @@ green(#{green := SV, alpha := SA}, #{green := DV, alpha := DA}) -> SV*SA + DV*DA*(1.0 - SA). blue(#{blue := SV, alpha := SA}, #{blue := DV, alpha := DA}) -> SV*SA + DV*DA*(1.0 - SA).</code> - <p>Compile (file <c>color.erl</c>) and test:</p> + <p>Compile and test:</p> <pre> > <input>c(color).</input> {ok,color} @@ -484,50 +499,48 @@ blue(#{blue := SV, alpha := SA}, #{blue := DV, alpha := DA}) -> <p>This example warrants some explanation:</p> <code type="none"> -define(is_channel(V), (is_float(V) andalso V >= 0.0 andalso V =< 1.0)).</code> - <p> - First we define a macro <c>is_channel</c> to help with our guard tests. - This is only here for convenience and to reduce syntax cluttering. - - You can read more about <seealso marker="doc/reference_manual:macros">Macros</seealso> - in the Erlang Reference Manual. - </p> + <p>First a macro <c>is_channel</c> is defined to help with the + guard tests. This is only here for convenience and to reduce + syntax cluttering. For more information about macros, see + <seealso marker="doc/reference_manual:macros"> + The Preprocessor</seealso>. + </p> <code type="none"> new(R,G,B,A) when ?is_channel(R), ?is_channel(G), ?is_channel(B), ?is_channel(A) -> #{red => R, green => G, blue => B, alpha => A}.</code> - <p> - The function <c>new/4</c> creates a new map term with and lets the keys - <c>red</c>, <c>green</c>, <c>blue</c> and <c>alpha</c> be associated - with an initial value. In this case we only allow for float - values between and including 0.0 and 1.0 as ensured by the <c>?is_channel/1</c> macro - for each argument. Only the <c>=></c> operator is allowed when creating a new map. + <p>The function <c>new/4</c> creates a new map term and lets the keys + <c>red</c>, <c>green</c>, <c>blue</c>, and <c>alpha</c> be + associated with an initial value. In this case, only float + values between and including 0.0 and 1.0 are allowed, as ensured + by the <c>?is_channel/1</c> macro for each argument. Only the + <c>=></c> operator is allowed when creating a new map. + </p> + <p>By calling <c>blend/2</c> on any color term created by + <c>new/4</c>, the resulting color can be calculated as + determined by the two map terms. + </p> + <p>The first thing <c>blend/2</c> does is to calculate the + resulting alpha channel: </p> - <p> - By calling <c>blend/2</c> on any color term created by <c>new/4</c> we can calculate - the resulting color as determined by the two maps terms. - </p> - <p> - The first thing <c>blend/2</c> does is to calculate the resulting alpha channel. - </p> <code type="none"> alpha(#{alpha := SA}, #{alpha := DA}) -> SA + DA*(1.0 - SA).</code> - <p> - We fetch the value associated with key <c>alpha</c> for both arguments using - the <c>:=</c> operator. Any other keys - in the map are ignored, only the key <c>alpha</c> is required and checked for. - </p> - <p>This is also the case for functions <c>red/2</c>, <c>blue/2</c> and <c>green/2</c>.</p> + <p>The value associated with key <c>alpha</c> is fetched for both + arguments using the <c>:=</c> operator. The other keys in the + map are ignored, only the key <c>alpha</c> is required and + checked for. + </p> + <p>This is also the case for functions <c>red/2</c>, + <c>blue/2</c>, and <c>green/2</c>.</p> <code type="none"> red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) -> SV*SA + DV*DA*(1.0 - SA).</code> - <p> - The difference here is that we check for two keys in each map argument. The other keys - are ignored. - </p> - <p> - Finally we return the resulting color in <c>blend/3</c>. - </p> + <p>The difference here is that a check is made for two keys in + each map argument. The other keys are ignored. + </p> + <p>Finally, let us return the resulting color in <c>blend/3</c>: + </p> <code type="none"> blend(Src,Dst,Alpha) when Alpha > 0.0 -> Dst#{ @@ -536,20 +549,20 @@ blend(Src,Dst,Alpha) when Alpha > 0.0 -> blue := blue(Src,Dst) / Alpha, alpha := Alpha };</code> - <p> - We update the <c>Dst</c> map with new channel values. The syntax for updating an existing key with a new value is done with <c>:=</c> operator. - </p> + <p>The <c>Dst</c> map is updated with new channel values. The + syntax for updating an existing key with a new value is with the + <c>:=</c> operator. + </p> </section> <section> <title>Standard Modules and Manual Pages</title> - <p>Erlang has a lot of standard modules to help you do things. For - example, the module <c>io</c> contains a lot of functions to help - you do formatted input/output. To look up information about - standard modules, the command <c>erl -man</c> can be used at - the operating shell or command prompt (i.e. at the same place as - that where you started <c>erl</c>). Try the operating system - shell command:</p> + <p>Erlang has many standard modules to help you do things. For + example, the module <c>io</c> contains many functions that help + in doing formatted input/output. To look up information about + standard modules, the command <c>erl -man</c> can be used at the + operating shell or command prompt (the same place as you started + <c>erl</c>). Try the operating system shell command:</p> <pre> % <input>erl -man io</input> ERLANG MODULE DEFINITION io(3) @@ -561,21 +574,21 @@ DESCRIPTION This module provides an interface to standard Erlang IO servers. The output functions all return ok if they are suc- ...</pre> - <p>If this doesn't work on your system, the documentation is - included as HTML in the Erlang/OTP release, or you can read + <p>If this does not work on your system, the documentation is + included as HTML in the Erlang/OTP release. You can also read the documentation as HTML or download it as PDF from either of the sites www.erlang.se (commercial Erlang) or www.erlang.org - (open source), for example for release R9B:</p> + (open source). For example, for Erlang/OTP release R9B:</p> <code type="none"> http://www.erlang.org/doc/r9b/doc/index.html</code> </section> <section> <title>Writing Output to a Terminal</title> - <p>It's nice to be able to do formatted output in these example, so + <p>It is nice to be able to do formatted output in examples, so the next example shows a simple way to use the <c>io:format</c> - function. Of course, just like all other exported functions, you - can test the <c>io:format</c> function in the shell:</p> + function. Like all other exported functions, you can test the + <c>io:format</c> function in the shell:</p> <pre> 31> <input>io:format("hello world~n", []).</input> hello world @@ -589,28 +602,28 @@ ok 34> <input>io:format("this outputs two Erlang terms: ~w ~w~n", [hello, world]).</input> this outputs two Erlang terms: hello world ok</pre> - <p>The function <c>format/2</c> (i.e. <c>format</c> with two + <p>The function <c>format/2</c> (that is, <c>format</c> with two arguments) takes two lists. The first one is nearly always a list - written between " ". This list is printed out as it stands, + written between " ". This list is printed out as it is, except that each ~w is replaced by a term taken in order from the second list. Each ~n is replaced by a new line. The <c>io:format/2</c> function itself returns the atom <c>ok</c> if everything goes as planned. Like other functions in Erlang, it crashes if an error occurs. This is not a fault in Erlang, it is a deliberate policy. Erlang has sophisticated mechanisms to - handle errors which we will show later. As an exercise, try to - make <c>io:format</c> crash, it shouldn't be difficult. But + handle errors which are shown later. As an exercise, try to + make <c>io:format</c> crash, it should not be difficult. But notice that although <c>io:format</c> crashes, the Erlang shell itself does not crash.</p> </section> <section> <title>A Larger Example</title> - <p>Now for a larger example to consolidate what we have learnt so - far. Assume we have a list of temperature readings from a number - of cities in the world. Some of them are in Celsius (Centigrade) - and some in Fahrenheit (as in the previous list). First let's - convert them all to Celsius, then let's print out the data neatly.</p> + <p>Now for a larger example to consolidate what you have learnt so + far. Assume that you have a list of temperature readings from a number + of cities in the world. Some of them are in Celsius + and some in Fahrenheit (as in the previous list). First let us + convert them all to Celsius, then let us print the data neatly.</p> <code type="none"> %% This module is in file tut5.erl @@ -642,50 +655,50 @@ stockholm -4 c paris -2.2222222222222223 c london 2.2222222222222223 c ok</pre> - <p>Before we look at how this program works, notice that we have - added a few comments to the code. A comment starts with a % - character and goes on to the end of the line. Note as well that + <p>Before looking at how this program works, notice that + a few comments are added to the code. A comment starts with a + %-character and goes on to the end of the line. Notice also that the <c>-export([format_temps/1]).</c> line only includes - the function <c>format_temps/1</c>, the other functions are - <em>local</em> functions, i.e. they are not visible from outside + the function <c>format_temps/1</c>. The other functions are + <em>local</em> functions, that is, they are not visible from outside the module <c>tut5</c>.</p> - <p>Note as well that when testing the program from the shell, I had - to spread the input over two lines as the line was too long.</p> - <p>When we call <c>format_temps</c> the first time, <c>City</c> + <p>Notice also that when testing the program from the shell, + the input is spread over two lines as the line was too long.</p> + <p>When <c>format_temps</c> is called the first time, <c>City</c> gets the value <c>{moscow,{c,-10}}</c> and <c>Rest</c> is - the rest of the list. So we call the function - <c>print_temp(convert_to_celsius({moscow,{c,-10}}))</c>.</p> - <p>Here we see a function call as + the rest of the list. So the function + <c>print_temp(convert_to_celsius({moscow,{c,-10}}))</c> is called.</p> + <p>Here is a function call as <c>convert_to_celsius({moscow,{c,-10}})</c> as the argument to - the function <c>print_temp</c>. When we <em>nest</em> function - calls like this we execute (evaluate) them from the inside out. - I.e. we first evaluate <c>convert_to_celsius({moscow,{c,-10}})</c> + the function <c>print_temp</c>. When function calls are <em>nested</em> + like this, they execute (evaluate) from the inside out. + That is, first <c>convert_to_celsius({moscow,{c,-10}})</c> is evaluated, which gives the value <c>{moscow,{c,-10}}</c> as the temperature - is already in Celsius and then we evaluate - <c>print_temp({moscow,{c,-10}})</c>. The function - <c>convert_to_celsius</c> works in a similar way to + is already in Celsius. Then <c>print_temp({moscow,{c,-10}})</c> + is evaluated. + The function <c>convert_to_celsius</c> works in a similar way to the <c>convert_length</c> function in the previous example.</p> <p><c>print_temp</c> simply calls <c>io:format</c> in a similar way - to what has been described above. Note that ~-15w says to print + to what has been described above. Notice that ~-15w says to print the "term" with a field length (width) of 15 and left justify it. - (<seealso marker="stdlib:io#fwrite/1">io(3)</seealso>).</p> - <p>Now we call <c>format_temps(Rest)</c> with the rest of the list + (see the <seealso marker="stdlib:io#fwrite/1">io(3)</seealso>) manual page in STDLIB.</p> + <p>Now <c>format_temps(Rest)</c> is called with the rest of the list as an argument. This way of doing things is similar to the loop - constructs in other languages. (Yes, this is recursion, but don't + constructs in other languages. (Yes, this is recursion, but do not let that worry you.) So the same <c>format_temps</c> function is called again, this time <c>City</c> gets the value - <c>{cape_town,{f,70}}</c> and we repeat the same procedure as - before. We go on doing this until the list becomes empty, i.e. [], + <c>{cape_town,{f,70}}</c> and the same procedure is repeated as + before. This is done until the list becomes empty, that is [], which causes the first clause <c>format_temps([])</c> to match. This simply returns (results in) the atom <c>ok</c>, so the program ends.</p> </section> <section> - <title>Matching, Guards and Scope of Variables</title> - <p>It could be useful to find the maximum and minimum temperature + <title>Matching, Guards, and Scope of Variables</title> + <p>It can be useful to find the maximum and minimum temperature in lists like this. Before extending the program to do this, - let's look at functions for finding the maximum value of + let us look at functions for finding the maximum value of the elements in a list:</p> <code type="none"> -module(tut6). @@ -705,53 +718,57 @@ list_max([Head|Rest], Result_so_far) -> {ok,tut6} 38> <input>tut6:list_max([1,2,3,4,5,7,4,3,2,1]).</input> 7</pre> - <p>First note that we have two functions here with the same name - <c>list_max</c>. However each of these takes a different number + <p>First notice that two functions have the same name, + <c>list_max</c>. However, each of these takes a different number of arguments (parameters). In Erlang these are regarded as - completely different functions. Where we need to distinguish - between these functions we write <c>name/arity</c>, where - <c>name</c> is the name of the function and <c>arity</c> is + completely different functions. Where you need to distinguish + between these functions, you write Name/Arity, where + Name is the function name and Arity is the number of arguments, in this case <c>list_max/1</c> and <c>list_max/2</c>.</p> - <p>This is an example where we walk through a list "carrying" a - value with us, in this case <c>Result_so_far</c>. + <p>In this example you walk through a list "carrying" a + value, in this case <c>Result_so_far</c>. <c>list_max/1</c> simply assumes that the max value of the list is the head of the list and calls <c>list_max/2</c> with the rest - of the list and the value of the head of the list, in the above - this would be <c>list_max([2,3,4,5,7,4,3,2,1],1)</c>. If we tried + of the list and the value of the head of the list. In the above + this would be <c>list_max([2,3,4,5,7,4,3,2,1],1)</c>. If you tried to use <c>list_max/1</c> with an empty list or tried to use it - with something which isn't a list at all, we would cause an error. - Note that the Erlang philosophy is not to handle errors of this + with something that is not a list at all, you would cause an error. + Notice that the Erlang philosophy is not to handle errors of this type in the function they occur, but to do so elsewhere. More about this later.</p> - <p>In <c>list_max/2</c> we walk down the list and use <c>Head</c> + <p>In <c>list_max/2</c>, you walk down the list and use <c>Head</c> instead of <c>Result_so_far</c> when <c>Head</c> > - <c>Result_so_far</c>. <c>when</c> is a special word we use before - the -> in the function to say that we should only use this part - of the function if the test which follows is true. We call tests - of this type a <em>guard</em>. If the guard isn't true (we say - the guard fails), we try the next part of the function. In this - case if <c>Head</c> isn't greater than <c>Result_so_far</c> then - it must be smaller or equal to is, so we don't need a guard on - the next part of the function.</p> - <p>Some useful operators in guards are, < less than, > - greater than, == equal, >= greater or equal, =< less or - equal, /= not equal. (See the chapter - <seealso marker="doc/reference_manual:expressions">"Guard Sequences"</seealso> in the Erlang Reference Manual.)</p> - <p>To change the above program to one which works out the minimum - value of the element in a list, all we would need to do is to + <c>Result_so_far</c>. <c>when</c> is a special word used before + the -> in the function to say that you only use this part + of the function if the test that follows is true. A test + of this type is called <em>guard</em>. If the guard is false (that is, + the guard fails), the next part of the function is tried. In this + case, if <c>Head</c> is not greater than <c>Result_so_far</c>, then + it must be smaller or equal to it. This means that a guard on + the next part of the function is not needed.</p> + <p>Some useful operators in guards are: + </p><list type="bulleted"><item>< less than</item> + <item>> greater than</item> + <item>== equal</item> + <item>>= greater or equal</item> + <item>=< less or equal</item> + <item>/= not equal</item></list> + <p>(see <seealso marker="doc/reference_manual:expressions">Guard Sequences</seealso>).</p> + <p>To change the above program to one that works out the minimum + value of the element in a list, you only need to write < instead of >. (But it would be wise to change - the name of the function to <c>list_min</c> :-).)</p> - <p>Remember that I mentioned earlier that a variable could only be - given a value once in its scope? In the above we see, for example, - that <c>Result_so_far</c> has been given several values. This is - OK since every time we call <c>list_max/2</c> we create a new - scope and one can regard the <c>Result_so_far</c> as a completely + the name of the function to <c>list_min</c>.)</p> + <p>Earlier it was mentioned that a variable can only be + given a value once in its scope. In the above you see + that <c>Result_so_far</c> is given several values. This is + OK since every time you call <c>list_max/2</c> you create a new + scope and one can regard <c>Result_so_far</c> as a different variable in each scope.</p> <p>Another way of creating and giving a variable a value is by using - the match operator = . So if I write <c>M = 5</c>, a variable - called <c>M</c> will be created and given the value 5. If, in - the same scope I then write <c>M = 6</c>, I'll get an error. Try + the match operator = . So if you write <c>M = 5</c>, a variable + called <c>M</c> is created with the value 5. If, in + the same scope, you then write <c>M = 6</c>, an error is returned. Try this out in the shell:</p> <pre> 39> <input>M = 5.</input> @@ -771,21 +788,21 @@ list_max([Head|Rest], Result_so_far) -> paris 45> <input>Y.</input> {f,28}</pre> - <p>Here we see that <c>X</c> gets the value <c>paris</c> and + <p>Here <c>X</c> gets the value <c>paris</c> and <c>Y</c><c>{f,28}</c>.</p> - <p>Of course if we try to do the same again with another city, we - get an error:</p> + <p>If you try to do the same again with another city, + an error is returned:</p> <pre> 46> <input>{X, Y} = {london, {f, 36}}.</input> ** exception error: no match of right hand side value {london,{f,36}}</pre> <p>Variables can also be used to improve the readability of - programs, for example, in the <c>list_max/2</c> function above, - we could write:</p> + programs. For example, in function <c>list_max/2</c> above, + you can write:</p> <code type="none"> list_max([Head|Rest], Result_so_far) when Head > Result_so_far -> New_result_far = Head, list_max(Rest, New_result_far);</code> - <p>which is possibly a little clearer.</p> + <p>This is possibly a little clearer.</p> </section> <section> @@ -824,9 +841,9 @@ reverse([], Reversed_List) -> {ok,tut8} 53> <input>tut8:reverse([1,2,3]).</input> [3,2,1]</pre> - <p>Consider how <c>Reversed_List</c> is built. It starts as [], we - then successively take off the heads of the list to be reversed - and add them to the the <c>Reversed_List</c>, as shown in + <p>Consider how <c>Reversed_List</c> is built. It starts as [], + then successively the heads are taken off of the list to be reversed + and added to the the <c>Reversed_List</c>, as shown in the following:</p> <code type="none"> reverse([1|2,3], []) => @@ -840,14 +857,15 @@ reverse([3|[]], [2,1]) => reverse([], [3,2,1]) => [3,2,1]</code> - <p>The module <c>lists</c> contains a lot of functions for - manipulating lists, for example for reversing them, so before you - write a list manipulating function it is a good idea to check - that one isn't already written for you. (see - <seealso marker="stdlib:lists">lists(3)</seealso>).</p> - <p>Now let's get back to the cities and temperatures, but take a more - structured approach this time. First let's convert the whole list - to Celsius as follows and test the function:</p> + <p>The module <c>lists</c> contains many functions for + manipulating lists, for example, for reversing them. So before + writing a list-manipulating function it is a good idea to check + if one not already is written for you + (see the <seealso marker="stdlib:lists">lists(3)</seealso> + manual page in STDLIB).</p> + <p>Now let us get back to the cities and temperatures, but take a more + structured approach this time. First let us convert the whole list + to Celsius as follows:</p> <code type="none"> -module(tut7). -export([format_temps/1]). @@ -864,6 +882,7 @@ convert_list_to_c([City | Rest]) -> convert_list_to_c([]) -> [].</code> + <p>Test the function:</p> <pre> 54> <input>c(tut7).</input> {ok, tut7}. @@ -874,26 +893,26 @@ convert_list_to_c([]) -> {stockholm,{c,-4}}, {paris,{c,-2.2222222222222223}}, {london,{c,2.2222222222222223}}]</pre> - <p>Looking at this bit by bit:</p> + <p>Explanation:</p> <code type="none"> format_temps(List_of_cities) -> convert_list_to_c(List_of_cities).</code> - <p>Here we see that <c>format_temps/1</c> calls + <p>Here <c>format_temps/1</c> calls <c>convert_list_to_c/1</c>. <c>convert_list_to_c/1</c> takes off the head of the <c>List_of_cities</c>, converts it to Celsius if needed. The | operator is used to add the (maybe) converted to the converted rest of the list:</p> <code type="none"> [Converted_City | convert_list_to_c(Rest)];</code> - <p>or</p> + <p>or:</p> <code type="none"> [City | convert_list_to_c(Rest)];</code> - <p>We go on doing this until we get to the end of the list (i.e. - the list is empty):</p> + <p>This is done until the end of the list is reached, that is, + the list is empty:</p> <code type="none"> convert_list_to_c([]) -> [].</code> - <p>Now we have converted the list, we add a function to print it:</p> + <p>Now when the list is converted, a function to print it is added:</p> <code type="none"> -module(tut7). -export([format_temps/1]). @@ -928,12 +947,12 @@ stockholm -4 c paris -2.2222222222222223 c london 2.2222222222222223 c ok</pre> - <p>We now have to add a function to find the cities with - the maximum and minimum temperatures. The program below isn't - the most efficient way of doing this as we walk through the list + <p>Now a function has to be added to find the cities with + the maximum and minimum temperatures. The following program is not + the most efficient way of doing this as you walk through the list of cities four times. But it is better to first strive for clarity and correctness and to make programs efficient only if - really needed.</p> + needed.</p> <code type="none"><![CDATA[ -module(tut7). -export([format_temps/1]). @@ -1003,8 +1022,8 @@ ok</pre> <section> <title>If and Case</title> <p>The function <c>find_max_and_min</c> works out the maximum and - minimum temperature. We have introduced a new construct here - <c>if</c>. If works as follows:</p> + minimum temperature. A new construct, <c>if</c>, is introduced here. + If works as follows:</p> <code type="none"> if Condition 1 -> @@ -1016,14 +1035,15 @@ if Condition 4 -> Action 4 end</code> - <p>Note there is no ";" before <c>end</c>! Conditions are the same - as guards, tests which succeed or fail. Erlang starts at the top - until it finds a condition which succeeds and then it evaluates + <p>Notice that there is no ";" before <c>end</c>. Conditions do + the same as guards, that is, tests that succeed or fail. Erlang + starts at the top + and tests until it finds a condition that succeeds. Then it evaluates (performs) the action following the condition and ignores all - other conditions and action before the <c>end</c>. If no - condition matches, there will be a run-time failure. A condition - which always is succeeds is the atom, <c>true</c> and this is - often used last in an <c>if</c> meaning do the action following + other conditions and actions before the <c>end</c>. If no + condition matches, a run-time failure occurs. A condition + that always succeeds is the atom <c>true</c>. This is + often used last in an <c>if</c>, meaning, do the action following the <c>true</c> if all other conditions have failed.</p> <p>The following is a short program to show the workings of <c>if</c>.</p> @@ -1039,10 +1059,10 @@ test_if(A, B) -> B == 6 -> io:format("B == 6~n", []), b_equals_6; - A == 2, B == 3 -> %i.e. A equals 2 and B equals 3 + A == 2, B == 3 -> %That is A equals 2 and B equals 3 io:format("A == 2, B == 3~n", []), a_equals_2_b_equals_3; - A == 1 ; B == 7 -> %i.e. A equals 1 or B equals 7 + A == 1 ; B == 7 -> %That is A equals 1 or B equals 7 io:format("A == 1 ; B == 7~n", []), a_equals_1_or_b_equals_7 end.</code> @@ -1068,19 +1088,19 @@ a_equals_1_or_b_equals_7 66> <input>tut9:test_if(33, 33).</input> ** exception error: no true branch found when evaluating an if expression in function tut9:test_if/2 (tut9.erl, line 5)</pre> - <p>Notice that <c>tut9:test_if(33,33)</c> did not cause any - condition to succeed so we got the run time error - <c>if_clause</c>, here nicely formatted by the shell. See the chapter - <seealso marker="doc/reference_manual:expressions">"Guard Sequences"</seealso> in the Erlang Reference Manual for details - of the many guard tests available. <c>case</c> is another - construct in Erlang. Recall that we wrote the - <c>convert_length</c> function as:</p> + <p>Notice that <c>tut9:test_if(33,33)</c> does not cause any + condition to succeed. This leads to the run time error + <c>if_clause</c>, here nicely formatted by the shell. See + <seealso marker="doc/reference_manual:expressions">Guard Sequences</seealso> + for details of the many guard tests available.</p> + <p><c>case</c> is another construct in Erlang. Recall that the + <c>convert_length</c> function was written as:</p> <code type="none"> convert_length({centimeter, X}) -> {inch, X / 2.54}; convert_length({inch, Y}) -> {centimeter, Y * 2.54}.</code> - <p>We could also write the same program as:</p> + <p>The same program can also be written as:</p> <code type="none"> -module(tut10). -export([convert_length/1]). @@ -1099,12 +1119,13 @@ convert_length(Length) -> {centimeter,15.24} 69> <input>tut10:convert_length({centimeter, 2.5}).</input> {inch,0.984251968503937}</pre> - <p>Notice that both <c>case</c> and <c>if</c> have <em>return values</em>, i.e. in the above example <c>case</c> returned + <p>Both <c>case</c> and <c>if</c> have <em>return values</em>, that is, + in the above example <c>case</c> returned either <c>{inch,X/2.54}</c> or <c>{centimeter,Y*2.54}</c>. The behaviour of <c>case</c> can also be modified by using guards. - An example should hopefully clarify this. The following example - tells us the length of a month, given the year. We need to know - the year of course, since February has 29 days in a leap year.</p> + The following example clarifies this. It + tells us the length of a month, given the year. + The year must be known, since February has 29 days in a leap year.</p> <code type="none"> -module(tut11). -export([month_length/2]). @@ -1150,57 +1171,58 @@ month_length(Year, Month) -> </section> <section> - <title>Built In Functions (BIFs)</title> - <p>Built in functions (BIFs) are functions which for some reason are - built in to the Erlang virtual machine. BIFs often implement - functionality that is impossible to implement in Erlang or is too - inefficient to implement in Erlang. Some BIFs can be called - by use of the function name only, but they by default belong - to the erlang module. So for example, the call to the BIF <c>trunc</c> + <title>Built-In Functions (BIFs)</title> + <p>BIFs are functions that for some reason are + built-in to the Erlang virtual machine. BIFs often implement + functionality that is impossible or is too + inefficient to implement in Erlang. Some BIFs can be called + using the function name only but they are by default belonging + to the <c>erlang</c> module. For example, the call to the + BIF <c>trunc</c> below is equivalent to a call to <c>erlang:trunc</c>.</p> - <p>As you can see, we first find out if a year is leap or not. If a - year is divisible by 400, it is a leap year. To find this out we - first divide the year by 400 and use the built in function - <c>trunc</c> (more later) to cut off any decimals. We then - multiply by 400 again and see if we get back the same value. For - example, year 2004:</p> + <p>As shown, first it is checked if a year is leap. If a + year is divisible by 400, it is a leap year. To determine this, + first divide the year by 400 and use the BIF + <c>trunc</c> (more about this later) to cut off any decimals. Then + multiply by 400 again and see if the same value is returned again. + For example, year 2004:</p> <code type="none"> 2004 / 400 = 5.01 trunc(5.01) = 5 5 * 400 = 2000</code> - <p>and we can see that we got back 2000 which is not the same as - 2004, so 2004 isn't divisible by 400. Year 2000:</p> + <p>2000 is not the same as 2004, so 2004 is not divisible by 400. + Year 2000:</p> <code type="none"> 2000 / 400 = 5.0 trunc(5.0) = 5 5 * 400 = 2000</code> - <p>so we have a leap year. The next two tests, which check if the year is - divisible by 100 or 4, are done in the same way. The first - <c>if</c> returns <c>leap</c> or <c>not_leap</c> which ends up - in the variable <c>Leap</c>. We use this variable in the guard - for <c>feb</c> in the following <c>case</c> which tells us how + <p>That is, a leap year. The next two <c>trunc</c>-tests evaluate + if the year is divisible by 100 or 4 in the same way. The first + <c>if</c> returns <c>leap</c> or <c>not_leap</c>, which lands up + in the variable <c>Leap</c>. This variable is used in the guard + for <c>feb</c> in the following <c>case</c> that tells us how long the month is.</p> - <p>This example showed the use of <c>trunc</c>. An easier way would - be to use the Erlang operator <c>rem</c>, which gives the remainder - after division. For example:</p> + <p>This example showed the use of <c>trunc</c>. It is easier + to use the Erlang operator <c>rem</c> that gives the remainder + after division, for example:</p> <pre> 74> <input>2004 rem 400.</input> 4</pre> - <p>so instead of writing:</p> + <p>So instead of writing:</p> <code type="none"> trunc(Year / 400) * 400 == Year -> leap;</code> - <p>we could write:</p> + <p>it can be written:</p> <code type="none"> Year rem 400 == 0 -> leap;</code> - <p>There are many other built in functions (BIF) such as - <c>trunc</c>. Only a few built in functions can be used in guards, + <p>There are many other BIFs such as + <c>trunc</c>. Only a few BIFs can be used in guards, and you cannot use functions you have defined yourself in guards. - (see the chapter - <seealso marker="doc/reference_manual:expressions">"Guard Sequences"</seealso> in the Erlang Reference Manual) (Aside for - advanced readers: This is to ensure that guards don't have side - effects.) Let's play with a few of these functions in the shell:</p> + (see + <seealso marker="doc/reference_manual:expressions">Guard Sequences</seealso>) + (For advanced readers: This is to ensure that guards do not have side + effects.) Let us play with a few of these functions in the shell:</p> <pre> 75> <input>trunc(5.6).</input> 5 @@ -1218,7 +1240,7 @@ false true 82> <input>is_tuple([paris, {c, 30}]).</input> false</pre> - <p>All the above can be used in guards. Now for some which can't be + <p>All of these can be used in guards. Now for some BIFs that cannot be used in guards:</p> <pre> 83> <input>atom_to_list(hello).</input> @@ -1227,22 +1249,22 @@ false</pre> goodbye 85> <input>integer_to_list(22).</input> "22"</pre> - <p>The 3 BIFs above do conversions which would be difficult (or + <p>These three BIFs do conversions that would be difficult (or impossible) to do in Erlang.</p> </section> <section> - <title>Higher Order Functions (Funs)</title> + <title>Higher-Order Functions (Funs)</title> <p>Erlang, like most modern functional programming languages, has - higher order functions. We start with an example using the shell:</p> + higher-order functions. Here is an example using the shell:</p> <pre> 86> <input>Xf = fun(X) -> X * 2 end.</input> #Fun<erl_eval.5.123085357> 87> <input>Xf(5).</input> 10</pre> - <p>What we have done here is to define a function which doubles - the value of number and assign this function to a variable. Thus - <c>Xf(5)</c> returned the value 10. Two useful functions when + <p>Here is defined a function that doubles + the value of a number and assigned this function to a variable. Thus + <c>Xf(5)</c> returns value 10. Two useful functions when working with lists are <c>foreach</c> and <c>map</c>, which are defined as follows:</p> <code type="none"> @@ -1258,17 +1280,16 @@ map(Fun, []) -> [].</code> <p>These two functions are provided in the standard module <c>lists</c>. <c>foreach</c> takes a list and applies a fun to - every element in the list, <c>map</c> creates a new list by + every element in the list. <c>map</c> creates a new list by applying a fun to every element in a list. Going back to - the shell, we start by using <c>map</c> and a fun to add 3 to + the shell, <c>map</c> is used and a fun to add 3 to every element of a list:</p> <pre> 88> <input>Add_3 = fun(X) -> X + 3 end.</input> #Fun<erl_eval.5.123085357> 89> <input>lists:map(Add_3, [1,2,3]).</input> [4,5,6]</pre> - <p>Now let's print out the temperatures in a list of cities (yet - again):</p> + <p>Let us (again) print the temperatures in a list of cities:</p> <pre> 90> <input>Print_City = fun({City, {X, Temp}}) -> io:format("~-15w ~w ~w~n",</input> <input>[City, X, Temp]) end.</input> @@ -1281,7 +1302,7 @@ stockholm c -4 paris f 28 london f 36 ok</pre> - <p>We will now define a fun which can be used to go through a list + <p>Let us now define a fun that can be used to go through a list of cities and temperatures and transform them all to Celsius.</p> <code type="none"> -module(tut13). @@ -1303,21 +1324,21 @@ convert_list_to_c(List) -> {stockholm,{c,-4}}, {paris,{c,-2}}, {london,{c,2}}]</pre> - <p>The <c>convert_to_c</c> function is the same as before, but we - use it as a fun:</p> + <p>The <c>convert_to_c</c> function is the same as before, but here + it is used as a fun:</p> <code type="none"> lists:map(fun convert_to_c/1, List)</code> - <p>When we use a function defined elsewhere as a fun we can refer - to it as <c>Function/Arity</c> (remember that <c>Arity</c> = - number of arguments). So in the <c>map</c> call we write - <c>lists:map(fun convert_to_c/1, List)</c>. As you can see + <p>When a function defined elsewhere is used as a fun, it can be referred + to as <c>Function/Arity</c> (remember that <c>Arity</c> = + number of arguments). So in the <c>map</c>-call + <c>lists:map(fun convert_to_c/1, List)</c> is written. As shown, <c>convert_list_to_c</c> becomes much shorter and easier to understand.</p> <p>The standard module <c>lists</c> also contains a function <c>sort(Fun, List)</c> where <c>Fun</c> is a fun with two - arguments. This fun should return <c>true</c> if the the first + arguments. This fun returns <c>true</c> if the first argument is less than the second argument, or else <c>false</c>. - We add sorting to the <c>convert_list_to_c</c>:</p> + Sorting is added to the <c>convert_list_to_c</c>:</p> <code type="none"><![CDATA[ -module(tut13). @@ -1342,13 +1363,13 @@ convert_list_to_c(List) -> {paris,{c,-2}}, {london,{c,2}}, {cape_town,{c,21}}]</pre> - <p>In <c>sort</c> we use the fun:</p> + <p>In <c>sort</c> the fun is used:</p> <code type="none"><![CDATA[ fun({_, {c, Temp1}}, {_, {c, Temp2}}) -> Temp1 < Temp2 end,]]></code> - <p>Here we introduce the concept of an <em>anonymous variable</em> - "_". This is simply shorthand for a variable which is going to - get a value, but we will ignore the value. This can be used - anywhere suitable, not just in fun's. <c><![CDATA[Temp1 < Temp2]]></c> + <p>Here the concept of an <em>anonymous variable</em> + "_" is introduced. This is simply shorthand for a variable that + gets a value, but the value is ignored. This can be used + anywhere suitable, not just in funs. <c><![CDATA[Temp1 < Temp2]]></c> returns <c>true</c> if <c>Temp1</c> is less than <c>Temp2</c>.</p> </section> </chapter> diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile index 83210bd21f..a4ef6c9d7c 100644 --- a/system/doc/installation_guide/Makefile +++ b/system/doc/installation_guide/Makefile @@ -58,7 +58,8 @@ XML_FILES = \ GENERATED_XML_FILES = \ INSTALL.xml \ INSTALL-CROSS.xml \ - INSTALL-WIN32.xml + INSTALL-WIN32.xml \ + OTP-PATCH-APPLY.xml # ---------------------------------------------------- @@ -73,7 +74,8 @@ REDIRECT_HTML_DIR = $(HTMLDIR)/source REDIRECT_HTML_FILES = \ $(REDIRECT_HTML_DIR)/INSTALL.html \ $(REDIRECT_HTML_DIR)/INSTALL-CROSS.html \ - $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html + $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html \ + $(REDIRECT_HTML_DIR)/OTP-PATCH-APPLY.html # ---------------------------------------------------- # FLAGS diff --git a/system/doc/installation_guide/install-binary.xml b/system/doc/installation_guide/install-binary.xml index af7dab6e44..ead4b19323 100644 --- a/system/doc/installation_guide/install-binary.xml +++ b/system/doc/installation_guide/install-binary.xml @@ -33,25 +33,23 @@ </header> <section> <title>Windows</title> + <p>The system is delivered as a Windows Installer executable. + Get it from http://www.erlang.org/download.html</p> <section> - <title>Introduction</title> - <p>The system is delivered as a Windows Installer executable. - Get it from our <url href="http://www.erlang.org/download.html">download page</url>.</p> - </section> - - <section> - <title>Installation</title> - <p>The installation procedure is is automated. Double-click the + <title>Installing</title> + <p>The installation procedure is automated. Double-click the <c>.exe</c> file icon and follow the instructions.</p> </section> + <section> - <title>Verification</title> + <title>Verifying</title> <list type="bulleted"> <item> <p>Start Erlang/OTP by double-clicking on the Erlang shortcut icon on the desktop.</p> - <p>Expect a command line window to pop up with an output looking something like this:</p> + <p>Expect a command-line window to pop up with an output looking + something like this:</p> <pre> Erlang/OTP 17 [erts-6.0] [64-bit] [smp:2:2] @@ -59,12 +57,12 @@ 1></pre> </item> <item> - <p>Exit by entering the command <c>halt()</c>,</p> + <p>Exit by entering the command <c>halt()</c>.</p> <pre> 2> <input>halt().</input></pre> - <p>which will close the Erlang/OTP shell. </p> + <p>This closes the Erlang/OTP shell.</p> </item> </list> - </section> + </section> </section> </chapter> diff --git a/system/doc/installation_guide/part.xml b/system/doc/installation_guide/part.xml index 02bf98db7c..5b1b3833cd 100644 --- a/system/doc/installation_guide/part.xml +++ b/system/doc/installation_guide/part.xml @@ -29,10 +29,12 @@ <file>part.xml</file> </header> <description> - <p>How to install Erlang/OTP on UNIX or Windows.</p> + <marker id="installation guide"></marker> + <p>This section describes how to install Erlang/OTP on UNIX and Windows.</p> </description> <xi:include href="install-binary.xml"/> <xi:include href="INSTALL.xml"/> <xi:include href="INSTALL-CROSS.xml"/> <xi:include href="INSTALL-WIN32.xml"/> -</part>
\ No newline at end of file + <xi:include href="OTP-PATCH-APPLY.xml"/> +</part> diff --git a/system/doc/installation_guide/xmlfiles.mk b/system/doc/installation_guide/xmlfiles.mk index c443334cd7..a18c82bc25 100644 --- a/system/doc/installation_guide/xmlfiles.mk +++ b/system/doc/installation_guide/xmlfiles.mk @@ -20,4 +20,5 @@ INST_GUIDE_CHAPTER_FILES = \ install-binary.xml \ INSTALL.xml \ INSTALL-CROSS.xml \ - INSTALL-WIN32.xml + INSTALL-WIN32.xml \ + OTP-PATCH-APPLY.xml diff --git a/system/doc/oam/oam_intro.xml b/system/doc/oam/oam_intro.xml index f4f990393e..de4867ca16 100644 --- a/system/doc/oam/oam_intro.xml +++ b/system/doc/oam/oam_intro.xml @@ -28,241 +28,224 @@ <rev>A</rev> <file>oam_intro.xml</file> </header> - <p>The operation and maintenance support in OTP consists of a - generic model for management subsystems in OTP, and some - components to be used in these subsystems. This document - describes the model. - </p> - <p>The main idea in the model is that it is management protocol - independent. Thus, it is not tied to any specific management - protocol. An API is defined which can be used to write - adaptations for specific management protocols. - </p> - <p>Each OAM component in OTP is implemented as one sub application, - which can be included in a management application for the system. - Note that such a complete management application is not in the - scope of this generic functionality. Examples illustrating how such an - application can be built are included however. - </p> + <marker id="oam principles"></marker> + <p>The Operation and Maintenance (OAM) support in OTP consists of a + generic model for management subsystems in OTP, and some components + to be used in these subsystems. This section describes the model.</p> + + <p>The main idea in the model is that it is not tied to any specific + management protocol. An Application Programming Interface (API) is + defined, which can be used to write adaptations for specific + management protocols.</p> + + <p>Each OAM component in OTP is implemented as one sub-application, which + can be included in a management application for the system. Notice that + such a complete management application is not in the scope of this + generic functionality. However, this section includes examples + illustrating how such an application can be built.</p> <section> <title>Terminology</title> - <p>The protocol independent architectural model on the network - level is the well-known <term id="Manager-Agent model"><termdef>Client-Server model for management operations</termdef></term>. This model is based on the client-server - principle, where the manager (client) sends <term id="requests"><termdef>A request is sent from a manager to an agent when it accesses management information.</termdef></term>to the - agent (server), the agent sends <term id="replies"><termdef>A reply is sent from the agent as a response to a request from a manager.</termdef></term>back to the manager. There are two main - differences to the normal client-server model. First, there are - usually a few managers that communicate with many agents; and - second, the agent may spontaneously send <term id="notifications"><termdef>A notification is sent spontaneously from an agent to a manager, e.g. an alarm.</termdef></term>to the - manager. The picture below illustrates the idea.</p> + <p>The protocol-independent architectural model on the network level + is the well-known client-server model for management operations. This + model is based on the client-server principle, where the manager + (client) sends a request from a manager to an agent (server) when it + accesses management information. The agent sends a reply back to the + manager. There are two main differences to the normal + client-server model:</p> + <list type="bulleted"> + <item><p>Usually a few managers communicate with many agents.</p></item> + <item><p>The agent can spontaneously send a notification, for example, + an alarm, to the manager.</p></item> + </list> + <p>The following picture illustrates the idea:</p> + <image file="../oam/terminology.gif"> <icaption>Terminology</icaption> </image> - <p>The manager is often referred to as the <term id="NMS"></term>, to - emphasize that it usually is realized as a program that presents - data to an operator. - </p> - <p>The agent is an entity that executes within a <term id="NE"></term>. - In OTP, the network element may be a distributed system, meaning - that the distributed system is managed as one entity. Of - course, the agent may be configured to be able to run on one of - several nodes, making it a distributed OTP application. - </p> - <p>The management information is defined in an <term id="MIB"></term>. - It is a formal definition of which information the agent makes - available to the manager. The manager accesses the MIB through - a management protocol, such as SNMP, CMIP, HTTP or CORBA. Each - of these protocols have their own MIB definition language. In - SNMP, it is a subset of ASN.1, in CMIP it is GDMO, in HTTP it is - implicit, and using CORBA, it is IDL. Usually, the entities - defined in the MIB are called <term id="MO"></term>, although these - objects do not have to be objects in the OO way,for example, a simple - scalar variable defined in an MIB is called a Managed Object. - The Managed Objects are logical objects, not necessarily with a - one-to-one mapping to the resources. - </p> + + <p>The manager is often referred to as the <em>Network Management + System (NMS)</em>, to emphasize that it usually is realized as a + program that presents data to an operator.</p> + + <p>The agent is an entity that executes within a <em>Network + Element (NE)</em>. In OTP, the NE can be a distributed system, + meaning that the distributed system is managed as one entity. + Of course, the agent can be configured to be able to run on one + of several nodes, making it a distributed OTP application.</p> + + <p>The management information is defined in a <em>Management + Information Base (MIB)</em>. It is a formal definition of which + information the agent makes available to the manager. The + manager accesses the MIB through a management protocol, such + as SNMP, CMIP, HTTP, or CORBA. Each protocol has its own MIB + definition language. In SNMP, it is a subset of ASN.1, in CMIP + it is GDMO, in HTTP it is implicit, and using CORBA, it is IDL.</p> + + <p>Usually, the entities defined in the MIB are + called <em>Managed Objects (MOs)</em>, although they do not + have to be objects in the object-oriented way. For example, + a simple scalar variable defined in a MIB is called an MO. The + MOs are logical objects, not necessarily with a one-to-one + mapping to the resources.</p> </section> <section> <title>Model</title> - <p>In this section, the generic protocol independent model for use - within an OTP based network element is presented. This model is - used by all operation and maintenance components, and may be - used by the applications. The advantage of the model is that it - clearly separates the resources from the management protocol. - The resources do not need to be aware of which management - protocol is used to manage the system. This makes it possible - to manage the same resources with different protocols. - </p> - <p>The different entities involved in this model are the <term id="agent"></term>which terminates the management protocol, and the - <term id="resources"></term>which is to be managed, i.e. the actual - application entities. The resources should in general have no - knowledge of the management protocol used, and the agent should - have no knowledge of the managed resources. This implies that - some sort of translation mechanism must be used, to translate - the management operations to operations on the resources. This - translation mechanism is usually called - <em>instrumentation</em>, and the function that implements it is - called <term id="instrumentation function"></term>. The - instrumentation functions are written for each combination of - management protocol and resource to be managed. For example, if - an application is to be managed by SNMP and HTTP, two sets of - instrumentation functions are defined; one that maps SNMP - requests to the resources, and one that e.g. generates an HTML - page for some resources. - </p> - <p>When a manager makes a request to the agent, we have the - following picture:</p> + <p>This section presents the generic protocol-independent model + for use within an OTP-based NE. This model is used by + all OAM components and can be used by the applications. The + advantage of the model is that it clearly separates the + resources from the management protocol. The resources do not + need to be aware of which management protocol is used to manage + the system. The same resources can therefore be managed with + different protocols.</p> + + <p>The entities involved in this model are the agent, which + terminates the management protocol, and the resources, which + is to be managed, that is, the actual application entities. + The resources should in general have no knowledge of the + management protocol used, and the agent should have no + knowledge of the managed resources. This implies that a + translation mechanism is needed, to translate the management + operations to operations on the resources. This translation + mechanism is usually called <em>instrumentation</em> and the + function that implements it is called <em>instrumentation + function</em>. The instrumentation functions are written for + each combination of management protocol and resource to be + managed. For example, if an application is to be managed by + SNMP and HTTP, two sets of instrumentation functions are + defined; one that maps SNMP requests to the resources, and + one that, for example, generates an HTML page for some + resources.</p> + + <p>When a manager makes a request to the agent, the following + illustrates the situation:</p> + <image file="../oam/snmp_model_1.gif"> - <icaption>Request to an agent by a manager</icaption> + <icaption>Request to An Agent by a Manager</icaption> </image> - <p>Note that the mapping between instrumentation function and - resource is not necessarily 1-1. It is also possible to write - one instrumentation function for each resource, and use that - function from different protocols. - </p> - <p>The agent receives a request and maps this request to calls to - one or several instrumentation functions. The instrumentation - functions perform operations on the resources to implement the - semantics associated with the managed object. - </p> - <p>For example, a system that is managed with SNMP and HTTP may be - structured in the following way:</p> + + <p>The mapping between an instrumentation function and a + resource is not necessarily 1-1. It is also possible to write + one instrumentation function for each resource, and use that + function from different protocols.</p> + + <p>The agent receives a request and maps it to calls to one or + more instrumentation functions. These functions perform + operations on the resources to implement the semantics + associated with the MO.</p> + + <p>For example, a system that is managed with SNMP and HTTP + can be structured as follows:</p> + <image file="../oam/snmp_model_2.gif"> - <icaption>Structure of a system managed with SNMP and HTTP</icaption> + <icaption>Structure of a System Managed with SNMP and HTTP</icaption> </image> - <p>The resources may send notifications to the manager as well. - Examples of notifications are events and alarms. There is a - need for the resource to generate protocol independent - notifications. The following picture illustrates how this is - achieved:</p> + + <p>The resources can send notifications to the manager as well. + Examples of notifications are events and alarms. The resource + needs to generate protocol-independent notifications. + The following picture illustrates how this is achieved:</p> + <image file="../oam/snmp_model_3.gif"> - <icaption>Notification handling</icaption> + <icaption>Notification Handling</icaption> </image> - <p>The main idea is that the resource sends the notfications as - Erlang terms to a dedicated <c>gen_event</c> process. Into this - process, handlers for the different management protocols are - installed. When an event is received by this process, it is - forwarded to each installed handler. The handlers are - responsible for translating the event into a notification to be - sent over the management protocol. For example, a handler for - SNMP would translate each event into an SNMP trap. - </p> + + <p>The main idea is that the resource sends the notifications as + Erlang terms to a dedicated <c>gen_event</c> process. Into this + process, handlers for the different management protocols are + installed. When an event is received by this process, it is + forwarded to each installed handler. The handlers are + responsible for translating the event into a notification to be + sent over the management protocol. For example, a handler for + SNMP translates each event into an SNMP trap.</p> </section> <section> - <title>SNMP based OAM</title> - <p>For all OAM components, SNMP adaptations are provided. Other - adaptations may be defined in the future. - </p> + <title>SNMP-Based OAM</title> + <p>For all OAM components, SNMP adaptations are provided. Other + adaptations might be defined in the future.</p> + <p>The OAM components, and some other OTP applications, define - SNMP MIBs. All these MIBs are written in SNMPv2 SMI syntax, as - defined in RFC1902. For convenience we also deliver the SNMPv1 - SMI equivalent. All MIBs are designed to be v1/v2 compatible, - i.e. the v2 MIBs do not use any construct not available in v1. - </p> + SNMP MIBs. These MIBs are written in SNMPv2 SMI syntax, as + defined in RFC 1902. For convenience we also deliver the SNMPv1 + SMI equivalent. All MIBs are designed to be v1/v2 compatible, + that is, the v2 MIBs do not use any construct not available in + v1.</p> <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 - import some objects from this MIB. - </p> - <p>Each MIB is contained in one application. The MIB text files - are stored under <c><![CDATA[mibs/<MIB>.mib]]></c> in the application - directory. The generated <c>.hrl</c> files with constant - declarations are stored under <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> + <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 + import some objects from this MIB.</p> + + <p>Each MIB is contained in one application. The MIB text + files are stored under <c><![CDATA[mibs/<MIB>.mib]]></c> in + the application directory. The generated <c>.hrl</c> files + with constant declarations are stored under + <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> + <code type="none"> sasl-1.3/mibs/OTP-MIB.mib - include/OTP-MIB.hrl - priv/mibs/OTP-MIB.bin</code> - <p>An application that needs to IMPORT this mib into another - MIB, should use the <c>il</c> option to the snmp mib compiler: - </p> +include/OTP-MIB.hrl +priv/mibs/OTP-MIB.bin</code> + + <p>An application that needs to import this MIB into another + MIB is to use the <c>il</c> option to the SNMP MIB compiler:</p> + <code type="none"> snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code> + <p>If the application needs to include the generated - <c>.hrl</c> file, it should use the <c>-include_lib</c> - directive to the Erlang compiler. - </p> + <c>.hrl</c> file, it is to use the <c>-include_lib</c> + directive to the Erlang compiler:</p> + <code type="none"> -module(my_mib). - -include_lib("sasl/include/OTP-MIB.hrl").</code> - <p>The following MIBs are defined in the OTP system: - </p> - <taglist> - <tag>OTP-REG (sasl)</tag> - <item> - <p>This MIB contains the top-level OTP registration - objects, used by all other MIBs. - </p> - </item> - <tag>OTP-TC (sasl)</tag> - <item> - <p>This MIB contains the general Textual Conventions, - which can be used by any other MIB. - </p> - </item> - <tag>OTP-MIB (sasl)</tag> - <item> - <p>This MIB contains objects for instrumentation of the - Erlang nodes, the Erlang machines and the applications in - the system. - </p> - </item> - <tag>OTP-OS-MON-MIB (os_mon)</tag> - <item> - <p>This MIB contains objects for instrumentation of disk, - memory and cpu usage of the nodes in the system. - </p> - </item> - <tag>OTP-SNMPEA-MIB (snmp)</tag> - <item> - <p>This MIB contains objects for instrumentation and - control of the extensible snmp agent itself. Note that - the agent also implements the standard SNMPv2-MIB (or v1 - part of MIB-II, if SNMPv1 is used). - </p> - </item> - <tag>OTP-EVA-MIB (eva)</tag> - <item> - <p>This MIB contains objects for instrumentation and - control of the events and alarms in the system. - </p> - </item> - <tag>OTP-LOG-MIB (eva)</tag> - <item> - <p>This MIB contains objects for instrumentation and - control of the logs and FTP transfer of logs. - </p> - </item> - <tag>OTP-EVA-LOG-MIB (eva)</tag> - <item> - <p>This MIB contains objects for instrumentation and - control of the events and alarm logs in the system. - </p> - </item> - <tag>OTP-SNMPEA-LOG-MIB (eva)</tag> - <item> - <p>This MIB contains objects for instrumentation and - control of the snmp audit trail log in the system. - </p> - </item> - </taglist> + + <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 + OTP registration objects, used by all other MIBs.</p></item> + <item><p><c>OTP-TC</c> (in <c>sasl</c>) 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 + 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 + objects for instrumentation of disk, memory, and CPU use + of the nodes in the system.</p></item> + <item><p><c>OTP-SNMPEA-MIB</c> (in <c>snmp</c>) + contains objects for instrumentation and control of the extensible + SNMP agent itself. The agent also implements the standard SNMPv2-MIB + (or v1 part of MIB-II, if SNMPv1 is used).</p></item> + <item><p><c>OTP-EVA-MIB</c> (in <c>eva</c>) contains objects + for instrumentation and control of the events and alarms in + the system.</p></item> + <item><p><c>OTP-LOG-MIB</c> (in <c>eva</c>) contains objects + for instrumentation and control of the logs and FTP transfer of + logs.</p></item> + <item><p><c>OTP-EVA-LOG-MIB</c> (in <c>eva</c>) contains objects + for instrumentation and control of the events and alarm logs + in the system.</p></item> + <item><p><c>OTP-SNMPEA-LOG-MIB</c> (in <c>eva</c>) contains + objects for instrumentation and control of the SNMP audit + trail log in the system.</p></item> + </list> + <p>The different applications use different strategies for - loading the MIBs into the agent. Some MIB implementations are - code-only, while others need a server. One way, used by the - code-only mib implementations, is for the user to call a - function such as <c>otp_mib:init(Agent)</c> to load the MIB, - and <c>otp_mib:stop(Agent)</c> to unload the MIB. See the - application manual page for each application for a description - of how to load each MIB. - </p> + loading the MIBs into the agent. Some MIB implementations are + code-only, while others need a server. One way, used by the + code-only MIB implementations, is for the user to call a + function such as <c>otp_mib:init(Agent)</c> to load the MIB, + and <c>otp_mib:stop(Agent)</c> to unload the MIB. See the + manual page for each application for a description of how + to load each MIB.</p> </section> </section> </chapter> diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml index fb321c1ba9..f97f171a69 100644 --- a/system/doc/programming_examples/bit_syntax.xml +++ b/system/doc/programming_examples/bit_syntax.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,62 +31,64 @@ <section> <title>Introduction</title> - <p>In Erlang a Bin is used for constructing binaries and matching + <p>In Erlang, a Bin is used for constructing binaries and matching binary patterns. A Bin is written with the following syntax:</p> <code type="none"><![CDATA[ <<E1, E2, ... En>>]]></code> - <p>A Bin is a low-level sequence of bits or bytes. The purpose of a Bin is - to be able to, from a high level, construct a binary,</p> + <p>A Bin is a low-level sequence of bits or bytes. + The purpose of a Bin is to enable construction of binaries:</p> <code type="none"><![CDATA[ Bin = <<E1, E2, ... En>>]]></code> - <p>in which case all elements must be bound, or to match a binary,</p> + <p>All elements must be bound. Or match a binary:</p> <code type="none"><![CDATA[ <<E1, E2, ... En>> = Bin ]]></code> - <p>where <c>Bin</c> is bound, and where the elements are bound or + <p>Here, <c>Bin</c> is bound and the elements are bound or unbound, as in any match.</p> - <p>In R12B, a Bin need not consist of a whole number of bytes.</p> + <p>Since Erlang R12B, a Bin does not need to consist of a whole number of bytes.</p> <p>A <em>bitstring</em> is a sequence of zero or more bits, where - the number of bits doesn't need to be divisible by 8. If the number + the number of bits does not need to be divisible by 8. If the number of bits is divisible by 8, the bitstring is also a binary.</p> <p>Each element specifies a certain <em>segment</em> of the bitstring. A segment is a set of contiguous bits of the binary (not necessarily on a byte boundary). The first element specifies the initial segment, the second element specifies the following - segment etc.</p> - <p>The following examples illustrate how binaries are constructed + segment, and so on.</p> + <p>The following examples illustrate how binaries are constructed, or matched, and how elements and tails are specified.</p> <section> <title>Examples</title> - <p><em>Example 1: </em>A binary can be constructed from a set of + <p><em>Example 1:</em> A binary can be constructed from a set of constants or a string literal:</p> <code type="none"><![CDATA[ Bin11 = <<1, 17, 42>>, Bin12 = <<"abc">>]]></code> - <p>yields binaries of size 3; <c>binary_to_list(Bin11)</c> - evaluates to <c>[1, 17, 42]</c>, and - <c>binary_to_list(Bin12)</c> evaluates to <c>[97, 98, 99]</c>.</p> - <p><em>Example 2: </em>Similarly, a binary can be constructed + <p>This gives two binaries of size 3, with the following evaluations:</p> + <list type="bulleted"> + <item><c>binary_to_list(Bin11)</c> evaluates to <c>[1, 17, 42]</c>.</item> + <item><c>binary_to_list(Bin12)</c> evaluates to <c>[97, 98, 99]</c>.</item> + </list> + <p><em>Example 2:</em>Similarly, a binary can be constructed from a set of bound variables:</p> <code type="none"><![CDATA[ A = 1, B = 17, C = 42, Bin2 = <<A, B, C:16>>]]></code> - <p>yields a binary of size 4, and <c>binary_to_list(Bin2)</c> - evaluates to <c>[1, 17, 00, 42]</c> too. Here we used a - <em>size expression</em> for the variable <c>C</c> in order to + <p>This gives a binary of size 4. + Here, a <em>size expression</em> is used for the variable <c>C</c> to specify a 16-bits segment of <c>Bin2</c>.</p> - <p><em>Example 3: </em>A Bin can also be used for matching: if + <p><c>binary_to_list(Bin2)</c> evaluates to <c>[1, 17, 00, 42]</c>.</p> + <p><em>Example 3:</em> A Bin can also be used for matching. <c>D</c>, <c>E</c>, and <c>F</c> are unbound variables, and - <c>Bin2</c> is bound as in the former example,</p> + <c>Bin2</c> is bound, as in Example 2:</p> <code type="none"><![CDATA[ <<D:16, E, F/binary>> = Bin2]]></code> - <p>yields <c>D = 273</c>, <c>E = 00</c>, and F binds to a binary + <p>This gives <c>D = 273</c>, <c>E = 00</c>, and F binds to a binary of size 1: <c>binary_to_list(F) = [42]</c>.</p> <p><em>Example 4:</em> The following is a more elaborate example - of matching, where <c>Dgram</c> is bound to the consecutive - bytes of an IP datagram of IP protocol version 4, and where we - want to extract the header and the data of the datagram:</p> + of matching. Here, <c>Dgram</c> is bound to the consecutive + bytes of an IP datagram of IP protocol version 4. The ambition is + to extract the header and the data of the datagram:</p> <code type="none"><![CDATA[ -define(IP_VERSION, 4). -define(IP_MIN_HDR_LEN, 5). @@ -102,53 +104,59 @@ case Dgram of <<Opts:OptsLen/binary,Data/binary>> = RestDgram, ... end.]]></code> - <p>Here the segment corresponding to the <c>Opts</c> variable - has a <em>type modifier</em> specifying that <c>Opts</c> should + <p>Here, the segment corresponding to the <c>Opts</c> variable + has a <em>type modifier</em>, specifying that <c>Opts</c> is to bind to a binary. All other variables have the default type equal to unsigned integer.</p> - <p>An IP datagram header is of variable length, and its length - - measured in the number of 32-bit words - is given in - the segment corresponding to <c>HLen</c>, the minimum value of - which is 5. It is the segment corresponding to <c>Opts</c> - that is variable: if <c>HLen</c> is equal to 5, <c>Opts</c> - will be an empty binary.</p> + <p>An IP datagram header is of variable length. This length is + measured in the number of 32-bit words and is given in + the segment corresponding to <c>HLen</c>. The minimum value of + <c>HLen</c> is 5. It is the segment corresponding to <c>Opts</c> + that is variable, so if <c>HLen</c> is equal to 5, <c>Opts</c> + becomes an empty binary.</p> <p>The tail variables <c>RestDgram</c> and <c>Data</c> bind to - binaries, as all tail variables do. Both may bind to empty + binaries, as all tail variables do. Both can bind to empty binaries.</p> - <p>If the first 4-bits segment of <c>Dgram</c> is not equal to - 4, or if <c>HLen</c> is less than 5, or if the size of - <c>Dgram</c> is less than <c>4*HLen</c>, the match of - <c>Dgram</c> fails.</p> + <p>The match of <c>Dgram</c> fails if one of the following occurs:</p> + <list type="bulleted"> + <item>The first 4-bits segment of <c>Dgram</c> is not equal to 4.</item> + <item><c>HLen</c> is less than 5.</item> + <item>The size of <c>Dgram</c> is less than <c>4*HLen</c>.</item> + </list> </section> </section> <section> - <title>A Lexical Note</title> - <p>Note that "<c><![CDATA[B=<<1>>]]></c>" will be interpreted as + <title>Lexical Note</title> + <p>Notice that "<c><![CDATA[B=<<1>>]]></c>" will be interpreted as "<c><![CDATA[B =< <1>>]]></c>", which is a syntax error. - The correct way to write the expression is - "<c><![CDATA[B = <<1>>]]></c>".</p> + The correct way to write the expression is: + <c><![CDATA[B = <<1>>]]></c>.</p> </section> <section> <title>Segments</title> <p>Each segment has the following general syntax:</p> <p><c>Value:Size/TypeSpecifierList</c></p> - <p>Both the <c>Size</c> and the <c>TypeSpecifier</c> or both may be - omitted; thus the following variations are allowed:</p> - <p><c>Value</c></p> - <p><c>Value:Size</c></p> - <p><c>Value/TypeSpecifierList</c></p> - <p>Default values will be used for missing specifications. - The default values are described in the section + <p>The <c>Size</c> or the <c>TypeSpecifier</c>, or both, can be + omitted. Thus, the following variants are allowed:</p> + <list type="bulleted"> + <item><c>Value</c></item> + <item><c>Value:Size</c></item> + <item><c>Value/TypeSpecifierList</c></item> + </list> + <p>Default values are used when specifications are missing. + The default values are described in <seealso marker="#Defaults">Defaults</seealso>.</p> - <p>Used in binary construction, the <c>Value</c> part is any - expression. Used in binary matching, the <c>Value</c> part must - be a literal or variable. You can read more about - the <c>Value</c> part in the section about constructing - binaries and matching binaries.</p> + <p>The <c>Value</c> part is any expression, when used in binary construction. + Used in binary matching, the <c>Value</c> part must + be a literal or a variable. For more information about + the <c>Value</c> part, see + <seealso marker="#Constructing Binaries and Bitstrings">Constructing Binaries and Bitstrings</seealso> + and + <seealso marker="#Matching Binaries">Matching Binaries</seealso>.</p> <p>The <c>Size</c> part of the segment multiplied by the unit in - the <c>TypeSpecifierList</c> (described below) gives the number + <c>TypeSpecifierList</c> (described later) gives the number of bits for the segment. In construction, <c>Size</c> is any expression that evaluates to an integer. In matching, <c>Size</c> must be a constant expression or a variable.</p> @@ -160,22 +168,22 @@ end.]]></code> <c>binary</c>.</item> <tag>Signedness</tag> <item>The signedness specification can be either <c>signed</c> - or <c>unsigned</c>. Note that signedness only matters for + or <c>unsigned</c>. Notice that signedness only matters for matching.</item> <tag>Endianness</tag> <item>The endianness specification can be either <c>big</c>, <c>little</c>, or <c>native</c>. Native-endian means that - the endian will be resolved at load time to be either + the endian is resolved at load time, to be either big-endian or little-endian, depending on what is "native" for the CPU that the Erlang machine is run on.</item> <tag>Unit</tag> <item>The unit size is given as <c>unit:IntegerLiteral</c>. - The allowed range is 1-256. It will be multiplied by + The allowed range is 1-256. It is multiplied by the <c>Size</c> specifier to give the effective size of - the segment. In R12B, the unit size specifies the alignment - for binary segments without size (examples will follow).</item> + the segment. Since Erlang R12B, the unit size specifies the alignment + for binary segments without size.</item> </taglist> - <p>Example:</p> + <p><em>Example:</em></p> <code type="none"> X:4/little-signed-integer-unit:8</code> <p>This element has a total size of 4*8 = 32 bits, and it contains @@ -184,13 +192,14 @@ X:4/little-signed-integer-unit:8</code> <section> <title>Defaults</title> - <p><marker id="Defaults"></marker>The default type for a segment is integer. The default + <p><marker id="Defaults"></marker>The default type for + a segment is integer. The default type does not depend on the value, even if the value is a - literal. For instance, the default type in '<c><![CDATA[<<3.14>>]]></c>' is + literal. For example, the default type in <c><![CDATA[<<3.14>>]]></c> is integer, not float.</p> <p>The default <c>Size</c> depends on the type. For integer it is 8. For float it is 64. For binary it is all of the binary. In - matching, this default value is only valid for the very last + matching, this default value is only valid for the last element. All other binary elements in matching must have a size specification.</p> <p>The default unit depends on the the type. For <c>integer</c>, @@ -201,61 +210,60 @@ X:4/little-signed-integer-unit:8</code> <section> <title>Constructing Binaries and Bitstrings</title> + <marker id="Constructing Binaries and Bitstrings"></marker> <p>This section describes the rules for constructing binaries using the bit syntax. Unlike when constructing lists or tuples, the construction of a binary can fail with a <c>badarg</c> exception.</p> <p>There can be zero or more segments in a binary to be - constructed. The expression '<c><![CDATA[<<>>]]></c>' constructs a zero + constructed. The expression <c><![CDATA[<<>>]]></c> constructs a zero length binary.</p> <p>Each segment in a binary can consist of zero or more bits. There are no alignment rules for individual segments of type <c>integer</c> and <c>float</c>. For binaries and bitstrings without size, the unit specifies the alignment. Since the default alignment for the <c>binary</c> type is 8, the size of a binary - segment must be a multiple of 8 bits (i.e. only whole bytes). - Example:</p> + segment must be a multiple of 8 bits, that is, only whole bytes.</p> + <p><em>Example:</em></p> <code type="none"><![CDATA[ <<Bin/binary,Bitstring/bitstring>>]]></code> <p>The variable <c>Bin</c> must contain a whole number of bytes, because the <c>binary</c> type defaults to <c>unit:8</c>. - A <c>badarg</c> exception will be generated if <c>Bin</c> would - consist of (for instance) 17 bits.</p> + A <c>badarg</c> exception is generated if <c>Bin</c> + consist of, for example, 17 bits.</p> - <p>On the other hand, the variable <c>Bitstring</c> may consist of - any number of bits, for instance 0, 1, 8, 11, 17, 42, and so on, - because the default <c>unit</c> for bitstrings is 1.</p> + <p>The <c>Bitstring</c> variable can consist of + any number of bits, for example, 0, 1, 8, 11, 17, 42, and so on. + This is because the default <c>unit</c> for bitstrings is 1.</p> - <warning><p>For clarity, it is recommended not to change the unit - size for binaries, but to use <c>binary</c> when you need byte - alignment, and <c>bitstring</c> when you need bit alignment.</p></warning> + <p>For clarity, it is recommended not to change the unit + size for binaries. Instead, use <c>binary</c> when you need byte alignment + and <c>bitstring</c> when you need bit alignment.</p> - <p>The following example</p> + <p>The following example successfully constructs a bitstring of 7 bits, + provided that all of X and Y are integers:</p> <code type="none"><![CDATA[ <<X:1,Y:6>>]]></code> - <p>will successfully construct a bitstring of 7 bits. - (Provided that all of X and Y are integers.)</p> - <p>As noted earlier, segments have the following general syntax:</p> + <p>As mentioned earlier, segments have the following general syntax:</p> <p><c>Value:Size/TypeSpecifierList</c></p> <p>When constructing binaries, <c>Value</c> and <c>Size</c> can be any Erlang expression. However, for syntactical reasons, both <c>Value</c> and <c>Size</c> must be enclosed in parenthesis if the expression consists of anything more than a single literal - or variable. The following gives a compiler syntax error:</p> + or a variable. The following gives a compiler syntax error:</p> <code type="none"><![CDATA[ <<X+1:8>>]]></code> - <p>This expression must be rewritten to</p> + <p>This expression must be rewritten into the following, + to be accepted by the compiler:</p> <code type="none"><![CDATA[ <<(X+1):8>>]]></code> - <p>in order to be accepted by the compiler.</p> <section> <title>Including Literal Strings</title> - <p>As syntactic sugar, an literal string may be written instead - of a element.</p> + <p>A literal string can be written instead of an element:</p> <code type="none"><![CDATA[ <<"hello">>]]></code> - <p>which is syntactic sugar for</p> + <p>This is syntactic sugar for the following:</p> <code type="none"><![CDATA[ <<$h,$e,$l,$l,$o>>]]></code> </section> @@ -263,29 +271,30 @@ X:4/little-signed-integer-unit:8</code> <section> <title>Matching Binaries</title> - <p>This section describes the rules for matching binaries using + <marker id="Matching Binaries"></marker> + <p>This section describes the rules for matching binaries, using the bit syntax.</p> <p>There can be zero or more segments in a binary pattern. - A binary pattern can occur in every place patterns are allowed, - also inside other patterns. Binary patterns cannot be nested.</p> - <p>The pattern '<c><![CDATA[<<>>]]></c>' matches a zero length binary.</p> - <p>Each segment in a binary can consist of zero or more bits.</p> - <p>A segment of type <c>binary</c> must have a size evenly - divisible by 8 (or divisible by the unit size, if the unit size has been changed).</p> - <p>A segment of type <c>bitstring</c> has no restrictions on the size.</p> - <p>As noted earlier, segments have the following general syntax:</p> + A binary pattern can occur wherever patterns are allowed, + including inside other patterns. Binary patterns cannot be nested. + The pattern <c><![CDATA[<<>>]]></c> matches a zero length binary.</p> + <p>Each segment in a binary can consist of zero or more bits. + A segment of type <c>binary</c> must have a size evenly divisible by 8 + (or divisible by the unit size, if the unit size has been changed). + A segment of type <c>bitstring</c> has no restrictions on the size.</p> + <p>As mentioned earlier, segments have the following general syntax:</p> <p><c>Value:Size/TypeSpecifierList</c></p> - <p>When matching <c>Value</c> value must be either a variable or - an integer or floating point literal. Expressions are not + <p>When matching <c>Value</c>, value must be either a variable or + an integer, or a floating point literal. Expressions are not allowed.</p> <p><c>Size</c> must be an integer literal, or a previously bound - variable. Note that the following is not allowed:</p> + variable. The following is not allowed:</p> <code type="none"><![CDATA[ foo(N, <<X:N,T/binary>>) -> {X,T}.]]></code> <p>The two occurrences of <c>N</c> are not related. The compiler will complain that the <c>N</c> in the size field is unbound.</p> - <p>The correct way to write this example is like this:</p> + <p>The correct way to write this example is as follows:</p> <code type="none"><![CDATA[ foo(N, Bin) -> <<X:N,T/binary>> = Bin, @@ -303,14 +312,14 @@ foo(<<A:8,Rest/binary>>) ->]]></code> without size:</p> <code type="none"><![CDATA[ foo(<<A:8,Rest/bitstring>>) ->]]></code> - <p>There is no restriction on the number of bits in the tail.</p> + <p>There are no restrictions on the number of bits in the tail.</p> </section> </section> <section> <title>Appending to a Binary</title> - <p>In R12B, the following function for creating a binary out of - a list of triples of integers is now efficient:</p> + <p>Since Erlang R12B, the following function for creating a binary out of + a list of triples of integers is efficient:</p> <code type="none"><![CDATA[ triples_to_bin(T) -> triples_to_bin(T, <<>>). @@ -321,7 +330,9 @@ triples_to_bin([], Acc) -> Acc.]]></code> <p>In previous releases, this function was highly inefficient, because the binary constructed so far (<c>Acc</c>) was copied in each recursion step. - That is no longer the case. See the Efficiency Guide for more information.</p> + That is no longer the case. For more information, see + <seealso marker="doc/efficiency_guide:introduction"> + Efficiency Guide</seealso>.</p> </section> </chapter> diff --git a/system/doc/programming_examples/funs.xmlsrc b/system/doc/programming_examples/funs.xmlsrc index 7bfac9db8c..d4c32bc854 100644 --- a/system/doc/programming_examples/funs.xmlsrc +++ b/system/doc/programming_examples/funs.xmlsrc @@ -30,146 +30,138 @@ </header> <section> - <title>Example 1 - map</title> - <p>If we want to double every element in a list, we could write a - function named <c>double</c>:</p> + <title>map</title> + <p>The following function, <c>double</c>, doubles every element in a list:</p> <code type="none"> double([H|T]) -> [2*H|double(T)]; double([]) -> [].</code> - <p>This function obviously doubles the argument entered as input - as follows:</p> + <p>Hence, the argument entered as input is doubled as follows:</p> <pre> > <input>double([1,2,3,4]).</input> [2,4,6,8]</pre> - <p>We now add the function <c>add_one</c>, which adds one to every + <p>The following function, <c>add_one</c>, adds one to every element in a list:</p> <code type="none"> add_one([H|T]) -> [H+1|add_one(T)]; add_one([]) -> [].</code> - <p>These functions, <c>double</c> and <c>add_one</c>, have a very - similar structure. We can exploit this fact and write a function - <c>map</c> which expresses this similarity:</p> + <p>The functions <c>double</c> and <c>add_one</c> have a + similar structure. This can be used by writing a function + <c>map</c> that expresses this similarity:</p> <codeinclude file="funs1.erl" tag="%1" type="erl"></codeinclude> - <p>We can now express the functions <c>double</c> and - <c>add_one</c> in terms of <c>map</c> as follows:</p> + <p>The functions <c>double</c> and <c>add_one</c> can now be expressed + in terms of <c>map</c> as follows:</p> <code type="none"> double(L) -> map(fun(X) -> 2*X end, L). add_one(L) -> map(fun(X) -> 1 + X end, L).</code> - <p><c>map(F, List)</c> is a function which takes a function - <c>F</c> and a list <c>L</c> as arguments and returns the new - list which is obtained by applying <c>F</c> to each of + <p><c>map(F, List)</c> is a function that takes a function + <c>F</c> and a list <c>L</c> as arguments and returns a new + list, obtained by applying <c>F</c> to each of the elements in <c>L</c>.</p> <p>The process of abstracting out the common features of a number - of different programs is called procedural abstraction. - Procedural abstraction can be used in order to write several - different functions which have a similar structure, but differ - only in some minor detail. This is done as follows:</p> + of different programs is called <em>procedural abstraction</em>. + Procedural abstraction can be used to write several + different functions that have a similar structure, but differ + in some minor detail. This is done as follows:</p> <list type="ordered"> - <item>write one function which represents the common features of - these functions</item> - <item>parameterize the difference in terms of functions which + <item><em>Step 1.</em> Write one function that represents the common features of + these functions.</item> + <item><em>Step 2.</em> Parameterize the difference in terms of functions that are passed as arguments to the common function.</item> </list> </section> <section> - <title>Example 2 - foreach</title> - <p>This example illustrates procedural abstraction. Initially, we - show the following two examples written as conventional - functions:</p> - <list type="ordered"> - <item>all elements of a list are printed onto a stream</item> - <item>a message is broadcast to a list of processes.</item> - </list> + <title>foreach</title> + <p>This section illustrates procedural abstraction. Initially, + the following two examples are written as conventional + functions.</p> + <p>This function prints all elements of a list onto a stream:</p> <code type="none"> print_list(Stream, [H|T]) -> io:format(Stream, "~p~n", [H]), print_list(Stream, T); print_list(Stream, []) -> true.</code> + <p>This function broadcasts a message to a list of processes:</p> <code type="none"> broadcast(Msg, [Pid|Pids]) -> Pid ! Msg, broadcast(Msg, Pids); broadcast(_, []) -> true.</code> - <p>Both these functions have a very similar structure. They both - iterate over a list doing something to each element in the list. - The "something" has to be carried round as an extra argument to - the function which does this.</p> + <p>These two functions have a similar structure. They both + iterate over a list and do something to each element in the list. + The "something" is passed on as an extra argument to + the function that does this.</p> <p>The function <c>foreach</c> expresses this similarity:</p> <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> - <p>Using <c>foreach</c>, <c>print_list</c> becomes:</p> + <p>Using the function <c>foreach</c>, the function <c>print_list</c> becomes:</p> <code type="none"> foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L)</code> - <p><c>broadcast</c> becomes:</p> + <p>Using the function <c>foreach</c>, the function <c>broadcast</c> becomes:</p> <code type="none"> foreach(fun(Pid) -> Pid ! M end, L)</code> <p><c>foreach</c> is evaluated for its side-effect and not its value. <c>foreach(Fun ,L)</c> calls <c>Fun(X)</c> for each element <c>X</c> in <c>L</c> and the processing occurs in - the order in which the elements were defined in <c>L</c>. + the order that the elements were defined in <c>L</c>. <c>map</c> does not define the order in which its elements are processed.</p> </section> <section> - <title>The Syntax of Funs</title> - <p>Funs are written with the syntax:</p> + <title>Syntax of Funs</title> + <p>Funs are written with the following syntax:</p> <code type="none"> F = fun (Arg1, Arg2, ... ArgN) -> ... end</code> <p>This creates an anonymous function of <c>N</c> arguments and binds it to the variable <c>F</c>.</p> - <p>If we have already written a function in the same module and - wish to pass this function as an argument, we can use - the following syntax:</p> + <p>Another function, <c>FunctionName</c>, written in the same module, + can be passed as an argument, using the following syntax:</p> <code type="none"> F = fun FunctionName/Arity</code> - <p>With this form of function reference, the function which is + <p>With this form of function reference, the function that is referred to does not need to be exported from the module.</p> - <p>We can also refer to a function defined in a different module + <p>It is also possible to refer to a function defined in a different module, with the following syntax:</p> <code type="none"> -F = {Module, FunctionName}</code> +F = fun Module:FunctionName/Arity</code> <p>In this case, the function must be exported from the module in question.</p> - <p>The follow program illustrates the different ways of creating + <p>The following program illustrates the different ways of creating funs:</p> <codeinclude file="fun_test.erl" tag="%1" type="erl"></codeinclude> - <p>We can evaluate the fun <c>F</c> with the syntax:</p> + <p>The fun <c>F</c> can be evaluated with the following syntax:</p> <code type="none"> F(Arg1, Arg2, ..., Argn)</code> <p>To check whether a term is a fun, use the test - <c>is_function/1</c> in a guard. Example:</p> + <c>is_function/1</c> in a guard.</p> + <p><em>Example:</em></p> <code type="none"> f(F, Args) when is_function(F) -> apply(F, Args); f(N, _) when is_integer(N) -> N.</code> - <p>Funs are a distinct type. The BIFs erlang:fun_info/1,2 can + <p>Funs are a distinct type. The BIFs <c>erlang:fun_info/1,2</c> can be used to retrieve information about a fun, and the BIF - erlang:fun_to_list/1 returns a textual representation of a fun. - The check_process_code/2 BIF returns true if the process + <c>erlang:fun_to_list/1</c> returns a textual representation of a fun. + The <c>check_process_code/2</c> BIF returns <c>true</c> if the process contains funs that depend on the old version of a module.</p> - <note> - <p>In OTP R5 and earlier releases, funs were represented using - tuples.</p> - </note> </section> <section> <title>Variable Bindings Within a Fun</title> - <p>The scope rules for variables which occur in funs are as + <p>The scope rules for variables that occur in funs are as follows:</p> <list type="bulleted"> - <item>All variables which occur in the head of a fun are assumed + <item>All variables that occur in the head of a fun are assumed to be "fresh" variables.</item> - <item>Variables which are defined before the fun, and which + <item>Variables that are defined before the fun, and that occur in function calls or guard tests within the fun, have the values they had outside the fun.</item> - <item>No variables may be exported from a fun.</item> + <item>Variables cannot be exported from a fun.</item> </list> <p>The following examples illustrate these rules:</p> <code type="none"> @@ -177,12 +169,13 @@ print_list(File, List) -> {ok, Stream} = file:open(File, write), foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List), file:close(Stream).</code> - <p>In the above example, the variable <c>X</c> which is defined in - the head of the fun is a new variable. The value of the variable - <c>Stream</c> which is used within within the fun gets its value + <p>Here, the variable <c>X</c>, defined in + the head of the fun, is a new variable. The variable + <c>Stream</c>, which is used within the fun, gets its value from the <c>file:open</c> line.</p> - <p>Since any variable which occurs in the head of a fun is - considered a new variable it would be equally valid to write:</p> + <p>As any variable that occurs in the head of a fun is + considered a new variable, it is equally valid to write + as follows:</p> <code type="none"> print_list(File, List) -> {ok, Stream} = file:open(File, write), @@ -190,21 +183,21 @@ print_list(File, List) -> io:format(Stream,"~p~n",[File]) end, List), file:close(Stream).</code> - <p>In this example, <c>File</c> is used as the new variable - instead of <c>X</c>. This is rather silly since code in the body - of the fun cannot refer to the variable <c>File</c> which is - defined outside the fun. Compiling this example will yield - the diagnostic:</p> + <p>Here, <c>File</c> is used as the new variable + instead of <c>X</c>. This is not so wise because code in the fun + body cannot refer to the variable <c>File</c>, which is + defined outside of the fun. Compiling this example gives + the following diagnostic:</p> <code type="none"> ./FileName.erl:Line: Warning: variable 'File' - shadowed in 'lambda head'</code> - <p>This reminds us that the variable <c>File</c> which is defined - inside the fun collides with the variable <c>File</c> which is + shadowed in 'fun'</code> + <p>This indicates that the variable <c>File</c>, which is defined + inside the fun, collides with the variable <c>File</c>, which is defined outside the fun.</p> <p>The rules for importing variables into a fun has the consequence - that certain pattern matching operations have to be moved into + that certain pattern matching operations must be moved into guard expressions and cannot be written in the head of the fun. - For example, we might write the following code if we intend + For example, you might write the following code if you intend the first clause of <c>F</c> to be evaluated when the value of its argument is <c>Y</c>:</p> <code type="none"> @@ -216,7 +209,7 @@ f(...) -> ... end, ...) ...</code> - <p>instead of</p> + <p>instead of writng the following code:</p> <code type="none"> f(...) -> Y = ... @@ -229,35 +222,37 @@ f(...) -> </section> <section> - <title>Funs and the Module Lists</title> + <title>Funs and Module Lists</title> <p>The following examples show a dialogue with the Erlang shell. All the higher order functions discussed are exported from the module <c>lists</c>.</p> <section> <title>map</title> + <p><c>map</c> takes a function of one argument and a list of terms:</p> <codeinclude file="funs1.erl" tag="%1" type="erl"></codeinclude> - <p><c>map</c> takes a function of one argument and a list of - terms. It returns the list obtained by applying the function + <p>It returns the list obtained by applying the function to every argument in the list.</p> + <p>When a new fun is defined in the shell, the value of the fun + is printed as <c><![CDATA[Fun#<erl_eval>]]></c>:</p> <pre> > <input>Double = fun(X) -> 2 * X end.</input> #Fun<erl_eval.6.72228031> > <input>lists:map(Double, [1,2,3,4,5]).</input> [2,4,6,8,10]</pre> - <p>When a new fun is defined in the shell, the value of the Fun - is printed as <c><![CDATA[Fun#<erl_eval>]]></c>.</p> + </section> <section> <title>any</title> - <codeinclude file="funs1.erl" tag="%4" type="erl"></codeinclude> <p><c>any</c> takes a predicate <c>P</c> of one argument and a - list of terms. A predicate is a function which returns - <c>true</c> or <c>false</c>. <c>any</c> is true if there is a - term <c>X</c> in the list such that <c>P(X)</c> is <c>true</c>.</p> - <p>We define a predicate <c>Big(X)</c> which is <c>true</c> if - its argument is greater that 10.</p> + list of terms:</p> + <codeinclude file="funs1.erl" tag="%4" type="erl"></codeinclude> + <p>A predicate is a function that returns <c>true</c> or <c>false</c>. + <c>any</c> is <c>true</c> if there is a term <c>X</c> in the list such that + <c>P(X)</c> is <c>true</c>.</p> + <p>A predicate <c>Big(X)</c> is defined, which is <c>true</c> if + its argument is greater that 10:</p> <pre> > <input>Big = fun(X) -> if X > 10 -> true; true -> false end end.</input> #Fun<erl_eval.6.72228031> @@ -269,9 +264,10 @@ true</pre> <section> <title>all</title> + <p><c>all</c> has the same arguments as <c>any</c>:</p> <codeinclude file="funs1.erl" tag="%3" type="erl"></codeinclude> - <p><c>all</c> has the same arguments as <c>any</c>. It is true - if the predicate applied to all elements in the list is true.</p> + <p>It is <c>true</c> + if the predicate applied to all elements in the list is <c>true</c>.</p> <pre> > <input>lists:all(Big, [1,2,3,4,12,6]).</input> false @@ -281,11 +277,12 @@ true</pre> <section> <title>foreach</title> - <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> <p><c>foreach</c> takes a function of one argument and a list of - terms. The function is applied to each argument in the list. - <c>foreach</c> returns <c>ok</c>. It is used for its - side-effect only.</p> + terms:</p> + <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> + <p>The function is applied to each argument in the list. + <c>foreach</c> returns <c>ok</c>. It is only used for its + side-effect:</p> <pre> > <input>lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]).</input> 1 @@ -297,15 +294,16 @@ ok</pre> <section> <title>foldl</title> - <codeinclude file="funs1.erl" tag="%8" type="erl"></codeinclude> <p><c>foldl</c> takes a function of two arguments, an - accumulator and a list. The function is called with two + accumulator and a list:</p> + <codeinclude file="funs1.erl" tag="%8" type="erl"></codeinclude> + <p>The function is called with two arguments. The first argument is the successive elements in - the list, the second argument is the accumulator. The function - must return a new accumulator which is used the next time + the list. The second argument is the accumulator. The function + must return a new accumulator, which is used the next time the function is called.</p> - <p>If we have a list of lists <c>L = ["I","like","Erlang"]</c>, - then we can sum the lengths of all the strings in <c>L</c> as + <p>If you have a list of lists <c>L = ["I","like","Erlang"]</c>, + then you can sum the lengths of all the strings in <c>L</c> as follows:</p> <pre> > <input>L = ["I","like","Erlang"].</input> @@ -325,11 +323,11 @@ end</code> <section> <title>mapfoldl</title> + <p><c>mapfoldl</c> simultaneously maps and folds over a list:</p> <codeinclude file="funs1.erl" tag="%10" type="erl"></codeinclude> - <p><c>mapfoldl</c> simultaneously maps and folds over a list. - The following example shows how to change all letters in - <c>L</c> to upper case and count them.</p> - <p>First upcase:</p> + <p>The following example shows how to change all letters in + <c>L</c> to upper case and then count them.</p> + <p>First the change to upper case:</p> <pre> > <input>Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a;</input> <input>(X) -> X</input> @@ -344,7 +342,7 @@ end</code> "ERLANG" > <input>lists:map(Upcase_word, L).</input> ["I","LIKE","ERLANG"]</pre> - <p>Now we can do the fold and the map at the same time:</p> + <p>Now, the fold and the map can be done at the same time:</p> <pre> > <input>lists:mapfoldl(fun(Word, Sum) -></input> <input>{Upcase_word(Word), Sum + length(Word)}</input> @@ -354,23 +352,24 @@ end</code> <section> <title>filter</title> - <codeinclude file="funs1.erl" tag="%9" type="erl"></codeinclude> <p><c>filter</c> takes a predicate of one argument and a list - and returns all element in the list which satisfy - the predicate.</p> + and returns all elements in the list that satisfy + the predicate:</p> + <codeinclude file="funs1.erl" tag="%9" type="erl"></codeinclude> <pre> > <input>lists:filter(Big, [500,12,2,45,6,7]).</input> [500,12,45]</pre> - <p>When we combine maps and filters we can write very succinct - code. For example, suppose we want to define a set difference - function. We want to define <c>diff(L1, L2)</c> to be - the difference between the lists <c>L1</c> and <c>L2</c>. - This is the list of all elements in L1 which are not contained - in L2. This code can be written as follows:</p> + <p>Combining maps and filters enables writing of very succinct + code. For example, to define a set difference + function <c>diff(L1, L2)</c> to be + the difference between the lists <c>L1</c> and <c>L2</c>, + the code can be written as follows:</p> <code type="none"> diff(L1, L2) -> filter(fun(X) -> not member(X, L2) end, L1).</code> - <p>The AND intersection of the list <c>L1</c> and <c>L2</c> is + <p>This gives the list of all elements in L1 that are not contained + in L2.</p> + <p> The AND intersection of the list <c>L1</c> and <c>L2</c> is also easily defined:</p> <code type="none"> intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).</code> @@ -378,9 +377,9 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).</code> <section> <title>takewhile</title> - <codeinclude file="funs1.erl" tag="%5" type="erl"></codeinclude> <p><c>takewhile(P, L)</c> takes elements <c>X</c> from a list - <c>L</c> as long as the predicate <c>P(X)</c> is true.</p> + <c>L</c> as long as the predicate <c>P(X)</c> is true:</p> + <codeinclude file="funs1.erl" tag="%5" type="erl"></codeinclude> <pre> > <input>lists:takewhile(Big, [200,500,45,5,3,45,6]).</input> [200,500,45]</pre> @@ -388,8 +387,8 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).</code> <section> <title>dropwhile</title> + <p><c>dropwhile</c> is the complement of <c>takewhile</c>:</p> <codeinclude file="funs1.erl" tag="%6" type="erl"></codeinclude> - <p><c>dropwhile</c> is the complement of <c>takewhile</c>.</p> <pre> > <input>lists:dropwhile(Big, [200,500,45,5,3,45,6]).</input> [5,3,45,6]</pre> @@ -397,10 +396,10 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).</code> <section> <title>splitwith</title> - <codeinclude file="funs1.erl" tag="%7" type="erl"></codeinclude> <p><c>splitwith(P, L)</c> splits the list <c>L</c> into the two - sub-lists <c>{L1, L2}</c>, where <c>L = takewhile(P, L)</c> - and <c>L2 = dropwhile(P, L)</c>.</p> + sublists <c>{L1, L2}</c>, where <c>L = takewhile(P, L)</c> + and <c>L2 = dropwhile(P, L)</c>:</p> + <codeinclude file="funs1.erl" tag="%7" type="erl"></codeinclude> <pre> > <input>lists:splitwith(Big, [200,500,45,5,3,45,6]).</input> {[200,500,45],[5,3,45,6]}</pre> @@ -408,17 +407,17 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).</code> </section> <section> - <title>Funs Which Return Funs</title> - <p>So far, this section has only described functions which take - funs as arguments. It is also possible to write more powerful - functions which themselves return funs. The following examples - illustrate these type of functions.</p> + <title>Funs Returning Funs</title> + <p>So far, only functions that take + funs as arguments have been described. More powerful + functions, that themselves return funs, can also be written. The following + examples illustrate these type of functions.</p> <section> <title>Simple Higher Order Functions</title> - <p><c>Adder(X)</c> is a function which, given <c>X</c>, returns + <p><c>Adder(X)</c> is a function that given <c>X</c>, returns a new function <c>G</c> such that <c>G(K)</c> returns - <c>K + X</c>.</p> + <c>K + X</c>:</p> <pre> > <input>Adder = fun(X) -> fun(Y) -> X + Y end end.</input> #Fun<erl_eval.6.72228031> @@ -438,7 +437,7 @@ ints_from(N) -> fun() -> [N|ints_from(N+1)] end.</code> - <p>Then we can proceed as follows:</p> + <p>Then proceed as follows:</p> <pre> > <input>XX = lazy:ints_from(1).</input> #Fun<lazy.0.29874839> @@ -450,7 +449,7 @@ ints_from(N) -> #Fun<lazy.0.29874839> > <input>hd(Y()).</input> 2</pre> - <p>etc. - this is an example of "lazy embedding".</p> + <p>And so on. This is an example of "lazy embedding".</p> </section> <section> @@ -459,17 +458,21 @@ ints_from(N) -> <pre> Parser(Toks) -> {ok, Tree, Toks1} | fail</pre> <p><c>Toks</c> is the list of tokens to be parsed. A successful - parse returns <c>{ok, Tree, Toks1}</c>, where <c>Tree</c> is a - parse tree and <c>Toks1</c> is a tail of <c>Tree</c> which - contains symbols encountered after the structure which was - correctly parsed. Otherwise <c>fail</c> is returned.</p> - <p>The example which follows illustrates a simple, functional - parser which parses the grammar:</p> + parse returns <c>{ok, Tree, Toks1}</c>.</p> + <list type="bulleted"> + <item><c>Tree</c> is a parse tree.</item> + <item><c>Toks1</c> is a tail of <c>Tree</c> that + contains symbols encountered after the structure that was + correctly parsed.</item> + </list> + <p>An unsuccessful parse returns <c>fail</c>.</p> + <p>The following example illustrates a simple, functional + parser that parses the grammar:</p> <pre> (a | b) & (c | d)</pre> <p>The following code defines a function <c>pconst(X)</c> in - the module <c>funparse</c>, which returns a fun which parses a - list of tokens.</p> + the module <c>funparse</c>, which returns a fun that parses a + list of tokens:</p> <codeinclude file="funparse.erl" tag="%14" type="erl"></codeinclude> <p>This function can be used as follows:</p> <pre> @@ -479,17 +482,18 @@ Parser(Toks) -> {ok, Tree, Toks1} | fail</pre> {ok,{const,a},[b,c]} > <input>P1([x,y,z]).</input> fail</pre> - <p>Next, we define the two higher order functions <c>pand</c> - and <c>por</c> which combine primitive parsers to produce more - complex parsers. Firstly <c>pand</c>:</p> + <p>Next, the two higher order functions <c>pand</c> + and <c>por</c> are defined. They combine primitive parsers to produce more + complex parsers.</p> + <p>First <c>pand</c>:</p> <codeinclude file="funparse.erl" tag="%16" type="erl"></codeinclude> <p>Given a parser <c>P1</c> for grammar <c>G1</c>, and a parser <c>P2</c> for grammar <c>G2</c>, <c>pand(P1, P2)</c> returns a - parser for the grammar which consists of sequences of tokens - which satisfy <c>G1</c> followed by sequences of tokens which + parser for the grammar, which consists of sequences of tokens + that satisfy <c>G1</c>, followed by sequences of tokens that satisfy <c>G2</c>.</p> <p><c>por(P1, P2)</c> returns a parser for the language - described by the grammar <c>G1</c> or <c>G2</c>.</p> + described by the grammar <c>G1</c> or <c>G2</c>:</p> <codeinclude file="funparse.erl" tag="%15" type="erl"></codeinclude> <p>The original problem was to parse the grammar <c><![CDATA[(a | b) & (c | d)]]></c>. The following code addresses this @@ -497,7 +501,7 @@ fail</pre> <codeinclude file="funparse.erl" tag="%13" type="erl"></codeinclude> <p>The following code adds a parser interface to the grammar:</p> <codeinclude file="funparse.erl" tag="%12" type="erl"></codeinclude> - <p>We can test this parser as follows:</p> + <p>The parser can be tested as follows:</p> <pre> > <input>funparse:parse([a,c]).</input> {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} diff --git a/system/doc/programming_examples/list_comprehensions.xml b/system/doc/programming_examples/list_comprehensions.xml index d6c8a66e13..5b33b14dea 100644 --- a/system/doc/programming_examples/list_comprehensions.xml +++ b/system/doc/programming_examples/list_comprehensions.xml @@ -31,18 +31,15 @@ <section> <title>Simple Examples</title> - <p>We start with a simple example:</p> + <p>This section starts with a simple example, showing a generator and a filter:</p> <pre> > <input>[X || X <- [1,2,a,3,4,b,5,6], X > 3].</input> [a,4,b,5,6]</pre> - <p>This should be read as follows:</p> - <quote> - <p>The list of X such that X is taken from the list + <p>This is read as follows: The list of X such that X is taken from the list <c>[1,2,a,...]</c> and X is greater than 3.</p> - </quote> <p>The notation <c><![CDATA[X <- [1,2,a,...]]]></c> is a generator and the expression <c>X > 3</c> is a filter.</p> - <p>An additional filter can be added in order to restrict + <p>An additional filter, <c>integer(X)</c>, can be added to restrict the result to integers:</p> <pre> > <input>[X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3].</input> @@ -56,7 +53,7 @@ <section> <title>Quick Sort</title> - <p>The well known quick sort routine can be written as follows:</p> + <p>The well-known quick sort routine can be written as follows:</p> <code type="none"><![CDATA[ sort([Pivot|T]) -> sort([ X || X <- T, X < Pivot]) ++ @@ -64,15 +61,20 @@ sort([Pivot|T]) -> sort([ X || X <- T, X >= Pivot]); sort([]) -> [].]]></code> <p>The expression <c><![CDATA[[X || X <- T, X < Pivot]]]></c> is the list of - all elements in <c>T</c>, which are less than <c>Pivot</c>.</p> + all elements in <c>T</c> that are less than <c>Pivot</c>.</p> <p><c><![CDATA[[X || X <- T, X >= Pivot]]]></c> is the list of all elements in - <c>T</c>, which are greater or equal to <c>Pivot</c>.</p> - <p>To sort a list, we isolate the first element in the list and - split the list into two sub-lists. The first sub-list contains - all elements which are smaller than the first element in - the list, the second contains all elements which are greater - than or equal to the first element in the list. We then sort - the sub-lists and combine the results.</p> + <c>T</c> that are greater than or equal to <c>Pivot</c>.</p> + <p>A list sorted as follows:</p> + <list type="bulleted"> + <item>The first element in the list is isolated + and the list is split into two sublists.</item> + <item>The first sublist contains + all elements that are smaller than the first element in + the list.</item> + <item>The second sublist contains all elements that are greater + than, or equal to, the first element in the list.</item> + <item>Then the sublists are sorted and the results are combined.</item> + </list> </section> <section> @@ -82,10 +84,10 @@ sort([]) -> [].]]></code> <code type="none"><![CDATA[ perms([]) -> [[]]; perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].]]></code> - <p>We take take <c>H</c> from <c>L</c> in all possible ways. + <p>This takes <c>H</c> from <c>L</c> in all possible ways. The result is the set of all lists <c>[H|T]</c>, where <c>T</c> - is the set of all possible permutations of <c>L</c> with - <c>H</c> removed.</p> + is the set of all possible permutations of <c>L</c>, with + <c>H</c> removed:</p> <pre> > <input>perms([b,u,g]).</input> [[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]]</pre> @@ -97,7 +99,7 @@ perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].]]></code> that <c>A**2 + B**2 = C**2</c>.</p> <p>The function <c>pyth(N)</c> generates a list of all integers <c>{A,B,C}</c> such that <c>A**2 + B**2 = C**2</c> and where - the sum of the sides is equal to or less than <c>N</c>.</p> + the sum of the sides is equal to, or less than, <c>N</c>:</p> <code type="none"><![CDATA[ pyth(N) -> [ {A,B,C} || @@ -140,7 +142,7 @@ pyth1(N) -> </section> <section> - <title>Simplifications with List Comprehensions</title> + <title>Simplifications With List Comprehensions</title> <p>As an example, list comprehensions can be used to simplify some of the functions in <c>lists.erl</c>:</p> <code type="none"><![CDATA[ @@ -151,45 +153,47 @@ filter(Pred, L) -> [X || X <- L, Pred(X)].]]></code> <section> <title>Variable Bindings in List Comprehensions</title> - <p>The scope rules for variables which occur in list + <p>The scope rules for variables that occur in list comprehensions are as follows:</p> <list type="bulleted"> - <item>all variables which occur in a generator pattern are - assumed to be "fresh" variables</item> - <item>any variables which are defined before the list - comprehension and which are used in filters have the values - they had before the list comprehension</item> - <item>no variables may be exported from a list comprehension.</item> + <item>All variables that occur in a generator pattern are + assumed to be "fresh" variables.</item> + <item>Any variables that are defined before the list + comprehension, and that are used in filters, have the values + they had before the list comprehension.</item> + <item>Variables cannot be exported from a list comprehension.</item> </list> - <p>As an example of these rules, suppose we want to write + <p>As an example of these rules, suppose you want to write the function <c>select</c>, which selects certain elements from - a list of tuples. We might write + a list of tuples. Suppose you write <c><![CDATA[select(X, L) -> [Y || {X, Y} <- L].]]></c> with the intention - of extracting all tuples from <c>L</c> where the first item is + of extracting all tuples from <c>L</c>, where the first item is <c>X</c>.</p> - <p>Compiling this yields the following diagnostic:</p> + <p>Compiling this gives the following diagnostic:</p> <code type="none"> ./FileName.erl:Line: Warning: variable 'X' shadowed in generate</code> - <p>This diagnostic warns us that the variable <c>X</c> in - the pattern is not the same variable as the variable <c>X</c> - which occurs in the function head.</p> - <p>Evaluating <c>select</c> yields the following result:</p> + <p>This diagnostic warns that the variable <c>X</c> in + the pattern is not the same as the variable <c>X</c> + that occurs in the function head.</p> + <p>Evaluating <c>select</c> gives the following result:</p> <pre> > <input>select(b,[{a,1},{b,2},{c,3},{b,7}]).</input> [1,2,3,7]</pre> - <p>This result is not what we wanted. To achieve the desired - effect we must write <c>select</c> as follows:</p> + <p>This is not the wanted result. To achieve the desired + effect, <c>select</c> must be written as follows:</p> <code type="none"><![CDATA[ select(X, L) -> [Y || {X1, Y} <- L, X == X1].]]></code> <p>The generator now contains unbound variables and the test has - been moved into the filter. This now works as expected:</p> + been moved into the filter.</p> + <p>This now works as expected:</p> <pre> > <input>select(b,[{a,1},{b,2},{c,3},{b,7}]).</input> [2,7]</pre> - <p>One consequence of the rules for importing variables into a + <p>A consequence of the rules for importing variables into a list comprehensions is that certain pattern matching operations - have to be moved into the filters and cannot be written directly - in the generators. To illustrate this, do not write as follows:</p> + must be moved into the filters and cannot be written directly + in the generators.</p> + <p>To illustrate this, do <em>not</em> write as follows:</p> <code type="none"><![CDATA[ f(...) -> Y = ... diff --git a/system/doc/programming_examples/part.xml b/system/doc/programming_examples/part.xml index 0bec9b4cf5..9329717ce4 100644 --- a/system/doc/programming_examples/part.xml +++ b/system/doc/programming_examples/part.xml @@ -28,8 +28,9 @@ <rev></rev> </header> <description> - <p>This chapter contains examples on using records, funs, list - comprehensions and the bit syntax.</p> + <marker id="programming examples"></marker> + <p>This section contains examples on using records, funs, list + comprehensions, and the bit syntax.</p> </description> <xi:include href="records.xml"/> <xi:include href="funs.xml"/> diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml index 58cf136a0b..ffcc05e758 100644 --- a/system/doc/programming_examples/records.xml +++ b/system/doc/programming_examples/records.xml @@ -30,37 +30,39 @@ </header> <section> - <title>Records vs Tuples</title> - <p>The main advantage of using records instead of tuples is that + <title>Records and Tuples</title> + <p>The main advantage of using records rather than tuples is that fields in a record are accessed by name, whereas fields in a tuple are accessed by position. To illustrate these differences, - suppose that we want to represent a person with the tuple + suppose that you want to represent a person with the tuple <c>{Name, Address, Phone}</c>.</p> - <p>We must remember that the <c>Name</c> field is the first - element of the tuple, the <c>Address</c> field is the second - element, and so on, in order to write functions which manipulate - this data. For example, to extract data from a variable <c>P</c> - which contains such a tuple we might write the following code - and then use pattern matching to extract the relevant fields.</p> + <p>To write functions that manipulate this data, remember the following:</p> + <list type="bulleted"> + <item>The <c>Name</c> field is the first element of the tuple.</item> + <item>The <c>Address</c> field is the second element.</item> + <item>The <c>Phone</c> field is the third element.</item> + </list> + <p>For example, to extract data from a variable <c>P</c> + that contains such a tuple, you can write the following code + and then use pattern matching to extract the relevant fields:</p> <code type="none"> Name = element(1, P), Address = element(2, P), ...</code> - <p>Code like this is difficult to read and understand and errors - occur if we get the numbering of the elements in the tuple wrong. - If we change the data representation by re-ordering the fields, - or by adding or removing a field, then all references to - the person tuple, wherever they occur, must be checked and - possibly modified.</p> - <p>Records allow us to refer to the fields by name and not - position. We use a record instead of a tuple to store the data. - If we write a record definition of the type shown below, we can - then refer to the fields of the record by name.</p> + <p>Such code is difficult to read and understand, and errors + occur if the numbering of the elements in the tuple is wrong. + If the data representation of the fields is changed, by re-ordering, + adding, or removing fields, all references to + the person tuple must be checked and possibly modified.</p> + <p>Records allow references to the fields by name, instead of by + position. In the following example, a record instead of a tuple + is used to store the data:</p> <code type="none"> -record(person, {name, phone, address}).</code> - <p>For example, if <c>P</c> is now a variable whose value is a - <c>person</c> record, we can code as follows in order to access - the name and address fields of the records.</p> + <p>This enables references to the fields of the record by name. + For example, if <c>P</c> is a variable whose value is a + <c>person</c> record, the following code access + the name and address fields of the records:</p> <code type="none"> Name = P#person.name, Address = P#person.address, @@ -72,24 +74,25 @@ Address = P#person.address, <section> <title>Defining a Record</title> - <p>This definition of a person will be used in many of - the examples which follow. It contains three fields, <c>name</c>, - <c>phone</c> and <c>address</c>. The default values for + <p>This following definition of a <c>person</c> is used in several + examples in this section. Three fields are included, <c>name</c>, + <c>phone</c>, and <c>address</c>. The default values for <c>name</c> and <c>phone</c> is "" and [], respectively. The default value for <c>address</c> is the atom <c>undefined</c>, since no default value is supplied for this field:</p> <pre> -record(person, {name = "", phone = [], address}).</pre> - <p>We have to define the record in the shell in order to be able - use the record syntax in the examples:</p> + <p>The record must be defined in the shell to enable + use of the record syntax in the examples:</p> <pre> > <input>rd(person, {name = "", phone = [], address}).</input> person</pre> - <p>This is due to the fact that record definitions are available - at compile time only, not at runtime. See <c>shell(3)</c> for - details on records in the shell. - </p> + <p>This is because record definitions are only available + 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> </section> <section> @@ -98,12 +101,12 @@ person</pre> <pre> > <input>#person{phone=[0,8,2,3,4,3,1,2], name="Robert"}.</input> #person{name = "Robert",phone = [0,8,2,3,4,3,1,2],address = undefined}</pre> - <p>Since the <c>address</c> field was omitted, its default value + <p>As the <c>address</c> field was omitted, its default value is used.</p> - <p>There is a new feature introduced in Erlang 5.1/OTP R8B, - with which you can set a value to all fields in a record, - overriding the defaults in the record specification. The special - field <c>_</c>, means "all fields not explicitly specified".</p> + <p>From Erlang 5.1/OTP R8B, a value to all + fields in a record can be set with the special field <c>_</c>. + <c>_</c> means "all fields not explicitly specified".</p> + <p><em>Example:</em></p> <pre> > <input>#person{name = "Jakob", _ = '_'}.</input> #person{name = "Jakob",phone = '_',address = '_'}</pre> @@ -114,6 +117,7 @@ person</pre> <section> <title>Accessing a Record Field</title> + <p>The following example shows how to access a record field:</p> <pre> > <input>P = #person{name = "Joe", phone = [0,8,2,3,4,3,1,2]}.</input> #person{name = "Joe",phone = [0,8,2,3,4,3,1,2],address = undefined} @@ -123,6 +127,7 @@ person</pre> <section> <title>Updating a Record</title> + <p>The following example shows how to update a record:</p> <pre> > <input>P1 = #person{name="Joe", phone=[1,2,3], address="A street"}.</input> #person{name = "Joe",phone = [1,2,3],address = "A street"} @@ -133,7 +138,7 @@ person</pre> <section> <title>Type Testing</title> <p>The following example shows that the guard succeeds if - <c>P</c> is record of type <c>person</c>.</p> + <c>P</c> is record of type <c>person</c>:</p> <pre> foo(P) when is_record(P, person) -> a_person; foo(_) -> not_a_person.</pre> @@ -141,7 +146,7 @@ foo(_) -> not_a_person.</pre> <section> <title>Pattern Matching</title> - <p>Matching can be used in combination with records as shown in + <p>Matching can be used in combination with records, as shown in the following example:</p> <pre> > <input>P3 = #person{name="Joe", phone=[0,0,7], address="A street"}.</input> @@ -163,7 +168,7 @@ find_phone([], Name) -> <section> <title>Nested Records</title> - <p>The value of a field in a record might be an instance of a + <p>The value of a field in a record can be an instance of a record. Retrieval of nested data can be done stepwise, or in a single step, as shown in the following example:</p> <pre> @@ -173,11 +178,12 @@ find_phone([], Name) -> demo() -> P = #person{name= #name{first="Robert",last="Virding"}, phone=123}, First = (P#person.name)#name.first.</pre> - <p>In this example, <c>demo()</c> evaluates to <c>"Robert"</c>.</p> + <p>Here, <c>demo()</c> evaluates to <c>"Robert"</c>.</p> </section> <section> - <title>Example</title> + <title>A Longer Example</title> + <p>Comments are embedded in the following example:</p> <pre> %% File: person.hrl diff --git a/system/doc/reference_manual/character_set.xml b/system/doc/reference_manual/character_set.xml index b09b484582..d6989373bf 100644 --- a/system/doc/reference_manual/character_set.xml +++ b/system/doc/reference_manual/character_set.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2014</year><year>2014</year> + <year>2014</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,7 +31,7 @@ <section> <title>Character Set</title> - <p>In Erlang 4.8/OTP R5A the syntax of Erlang tokens was extended to + <p>Since Erlang 4.8/OTP R5A, the syntax of Erlang tokens is extended to allow the use of the full ISO-8859-1 (Latin-1) character set. This is noticeable in the following ways:</p> <list type="bulleted"> @@ -98,7 +98,7 @@ <cell align="center" valign="middle">ø - ÿ</cell> <cell align="left" valign="middle">Lowercase letters</cell> </row> - <tcaption>Character Classes.</tcaption> + <tcaption>Character Classes</tcaption> </table> <p>In Erlang/OTP R16B the syntax of Erlang tokens was extended to handle Unicode. The support is limited to @@ -111,13 +111,13 @@ </section> <section> <title>Source File Encoding</title> - <p>The Erlang source file <marker - id="encoding">encoding</marker> is selected by a + <marker id="encoding"></marker> + <p>The Erlang source file <c>encoding</c> is selected by a comment in one of the first two lines of the source file. The first string that matches the regular expression <c>coding\s*[:=]\s*([-a-zA-Z0-9])+</c> selects the encoding. If - the matching string is not a valid encoding it is ignored. The - valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the + the matching string is an invalid encoding, it is ignored. The + valid encodings are <c>Latin-1</c> and <c>UTF-8</c>, where the case of the characters can be chosen freely.</p> <p>The following example selects UTF-8 as default encoding:</p> <pre> @@ -127,7 +127,7 @@ %% For this file we have chosen encoding = Latin-1</pre> <pre> %% -*- coding: latin-1 -*-</pre> - <p>The default encoding for Erlang source files was changed from - Latin-1 to UTF-8 in Erlang OTP 17.0.</p> + <p>The default encoding for Erlang source files is changed from + Latin-1 to UTF-8 since Erlang/OTP 17.0.</p> </section> </chapter> diff --git a/system/doc/reference_manual/code_loading.xml b/system/doc/reference_manual/code_loading.xml index b5b5704df5..48ec32d6df 100644 --- a/system/doc/reference_manual/code_loading.xml +++ b/system/doc/reference_manual/code_loading.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2014</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -29,35 +29,39 @@ <file>code_loading.xml</file> </header> <p>How code is compiled and loaded is not a language issue, but - is system dependent. This chapter describes compilation and - code loading in Erlang/OTP with pointers to relevant parts of + is system-dependent. This section describes compilation and + code loading in Erlang/OTP with references to relevant parts of the documentation.</p> <section> <title>Compilation</title> <p>Erlang programs must be <em>compiled</em> to object code. - The compiler can generate a new file which contains the object - code. The current abstract machine which runs the object code is + The compiler can generate a new file that contains the object + code. The current abstract machine, which runs the object code, is called BEAM, therefore the object files get the suffix <c>.beam</c>. The compiler can also generate a binary which can be loaded directly.</p> - <p>The compiler is located in the Kernel module <c>compile</c>, see - <c>compile(3)</c>.</p> + <p>The compiler is located in the module <c>compile</c> (see the + <seealso marker="compiler:compile">compile(3)</seealso> manual page in + Compiler).</p> <pre> compile:file(Module) compile:file(Module, Options)</pre> <p>The Erlang shell understands the command <c>c(Module)</c> which both compiles and loads <c>Module</c>.</p> - <p>There is also a module <c>make</c> which provides a set of - functions similar to the UNIX type Make functions, see - <c>make(3)</c>.</p> - <p>The compiler can also be accessed from the OS prompt, see - <c>erl(1)</c>.</p> + <p>There is also a module <c>make</c>, which provides a set of + functions similar to the UNIX type Make functions, see the + <seealso marker="tools:make">make(3)</seealso> + manual page in Tools.</p> + <p>The compiler can also be accessed from the OS prompt, see the + <seealso marker="erts:erl">erl(1)</seealso> manual page in ERTS.</p> <pre> % erl -compile <input>Module1</input>...<input>ModuleN</input> % erl -make</pre> <p>The <c>erlc</c> program provides an even better way to compile - modules from the shell, see <c>erlc(1)</c>. It understands a + modules from the shell, see the + <seealso marker="erts:erlc">erlc(1)</seealso> manual page in ERTS. + It understands a number of flags that can be used to define macros, add search paths for include files, and more.</p> <pre> @@ -68,13 +72,17 @@ compile:file(Module, Options)</pre> <marker id="loading"></marker> <title>Code Loading</title> <p>The object code must be <em>loaded</em> into the Erlang runtime - system. This is handled by the <em>code server</em>, see - <c>code(3)</c>.</p> - <p>The code server loads code according to a code loading strategy + system. This is handled by the <em>code server</em>, see the + <seealso marker="kernel:code">code(3)</seealso> + manual page in Kernel.</p> + <p>The code server loads code according to a code loading strategy, which is either <em>interactive</em> (default) or - <em>embedded</em>. In interactive mode, code are searched for in + <em>embedded</em>. In interactive mode, code is searched for in a <em>code path</em> and loaded when first referenced. In - embedded mode, code is loaded at start-up according to a <em>boot script</em>. This is described in <em>System Principles</em>.</p> + embedded mode, code is loaded at start-up according to a + <em>boot script</em>. This is described in + <seealso marker="doc/system_principles:system_principles#code_loading"> + System Principles </seealso>.</p> </section> <section> @@ -86,16 +94,17 @@ compile:file(Module, Options)</pre> the system for the first time, the code becomes 'current'. If then a new instance of the module is loaded, the code of the previous instance becomes 'old' and the new instance becomes 'current'.</p> - <p>Both old and current code is valid, and may be evaluated + <p>Both old and current code is valid, and can be evaluated concurrently. Fully qualified function calls always refer to - current code. Old code may still be evaluated because of processes + current code. Old code can still be evaluated because of processes lingering in the old code.</p> - <p>If a third instance of the module is loaded, the code server will - remove (purge) the old code and any processes lingering in it will - be terminated. Then the third instance becomes 'current' and + <p>If a third instance of the module is loaded, the code server + removes (purges) the old code and any processes lingering in it is + terminated. Then the third instance becomes 'current' and the previously current code becomes 'old'.</p> <p>To change from old code to current code, a process must make a - fully qualified function call. Example:</p> + fully qualified function call.</p> + <p><em>Example:</em></p> <pre> -module(m). -export([loop/0]). @@ -109,60 +118,62 @@ loop() -> loop() end.</pre> <p>To make the process change code, send the message - <c>code_switch</c> to it. The process then will make a fully - qualified call to <c>m:loop()</c> and change to current code. - Note that <c>m:loop/0</c> must be exported.</p> - <p>For code replacement of funs to work, the syntax - <c>fun Module:FunctionName/Arity</c> should be used.</p> + <c>code_switch</c> to it. The process then makes a fully + qualified call to <c>m:loop()</c> and changes to current code. + Notice that <c>m:loop/0</c> must be exported.</p> + <p>For code replacement of funs to work, use the syntax + <c>fun Module:FunctionName/Arity</c>.</p> </section> <section> <marker id="on_load"></marker> - <title>Running a function when a module is loaded</title> + <title>Running a Function When a Module is Loaded</title> <warning> - <p>The <c>on_load</c> feature should be considered experimental - as there are a number of known weak points in current semantics - which therefore might also change in future releases:</p> + <p>The <c>on_load</c> feature is to be considered experimental + as there are a number of known weak points in current semantics, + which therefore might change in future Erlang/OTP releases:</p> <list> - <item><p>Doing external call in on_load to the module itself + <item><p>Doing external call in <c>on_load</c> to the module itself leads to deadlock.</p></item> <item><p>At module upgrade, other processes calling the module - get suspended waiting for on_load to finish. This can be very bad + get suspended waiting for <c>on_load</c> to finish. This can be very bad for applications with demands on realtime characteristics.</p></item> - <item><p>At module upgrade, no rollback is done if the on_load function fails. - The system will be left in a bad limbo state without any working + <item><p>At module upgrade, no rollback is done if the + <c>on_load</c> function fails. + The system is left in a bad limbo state without any working and reachable instance of the module.</p></item> </list> - <p>The problems with module upgrade described above could be fixed in future - releases by changing the behaviour to not make the module reachable until - after the on_load function has successfully returned.</p> + <p>The problems with module upgrade described above can be fixed in future + Erlang/OTP releases by changing the behaviour to not make the module reachable until + after the <c>on_load</c> function has successfully returned.</p> </warning> - <p>The <c>-on_load()</c> directive names a function that should - be run automatically when a module a loaded. Its syntax is:</p> + <p>The <c>-on_load()</c> directive names a function that is to + be run automatically when a module is loaded.</p> + <p>Its syntax is as follows:</p> <pre> -on_load(Name/0).</pre> - <p>It is not necessary to export the function. It will be called in a - freshly spawned process (which will be terminated as soon as the function + <p>It is not necessary to export the function. It is called in a + freshly spawned process (which terminates as soon as the function returns). The function must return <c>ok</c> if the module is to - be remained loaded and become callable, or any other value if the module - is to be unloaded. Generating an exception will also cause the + remain loaded and become callable, or any other value if the module + is to be unloaded. Generating an exception also causes the module to be unloaded. If the return value is not an atom, - a warning error report will be sent to the error logger.</p> + a warning error report is sent to the error logger.</p> <p>A process that calls any function in a module whose <c>on_load</c> - function has not yet returned will be suspended until the <c>on_load</c> + function has not yet returned, is suspended until the <c>on_load</c> function has returned.</p> - <p>In embedded mode, all modules will be loaded first and then - will all on_load functions be called. The system will be - terminated unless all of the on_load functions return + <p>In embedded mode, first all modules are loaded. + Then all <c>on_load</c> functions are called. The system is + terminated unless all of the <c>on_load</c> functions return <c>ok</c></p>. - <p>Example:</p> + <p><em>Example:</em></p> <pre> -module(m). @@ -174,7 +185,7 @@ load_my_nifs() -> erlang:load_nif(NifPath, Info).</pre> <p>If the call to <c>erlang:load_nif/2</c> fails, the module - will be unloaded and there will be warning report sent to + is unloaded and a warning report is sent to the error loader.</p> </section> diff --git a/system/doc/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml index ad92143179..6226fa2f31 100644 --- a/system/doc/reference_manual/data_types.xml +++ b/system/doc/reference_manual/data_types.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,12 +28,12 @@ <rev></rev> <file>data_types.xml</file> </header> + <p>Erlang provides a number of data types, which are listed in + this section.</p> <section> <title>Terms</title> - <p>Erlang provides a number of data types which are listed in this - chapter. A piece of data of any data type is called a - <em>term</em>.</p> + <p>A piece of data of any data type is called a <em>term</em>.</p> </section> <section> @@ -48,13 +48,13 @@ <em><c>char</c></em>.</item> <item><em><c>base</c></em><c>#</c><em><c>value</c></em> <br></br> - Integer with the base <em><c>base</c></em>, which must be an + Integer with the base <em><c>base</c></em>, that must be an integer in the range 2..36. <br></br> In Erlang 5.2/OTP R9B and earlier versions, the allowed range is 2..16.</item> </list> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>42.</input> 42 @@ -76,11 +76,11 @@ <section> <title>Atom</title> - <p>An atom is a literal, a constant with name. An atom should be + <p>An atom is a literal, a constant with name. An atom is to be enclosed in single quotes (') if it does not begin with a lower-case letter or if it contains other characters than alphanumeric characters, underscore (_), or @.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> hello phone_number @@ -91,11 +91,11 @@ phone_number <section> <title>Bit Strings and Binaries</title> <p>A bit string is used to store an area of untyped memory.</p> - <p>Bit Strings are expressed using the + <p>Bit strings are expressed using the <seealso marker="expressions#bit_syntax">bit syntax</seealso>.</p> - <p>Bit Strings which consists of a number of bits which is evenly - divisible by eight are called Binaries</p> - <p>Examples:</p> + <p>Bit strings that consist of a number of bits that are evenly + divisible by eight, are called <em>binaries</em></p> + <p><em>Examples:</em></p> <pre> 1> <input><<10,20>>.</input> <<10,20>> @@ -103,12 +103,14 @@ phone_number <<"ABC">> 1> <input><<1:1,0:1>>.</input> <<2:2>></pre> - <p>More examples can be found in Programming Examples.</p> + <p>For more examples, + see <seealso marker="doc/programming_examples:bit_syntax"> + Programming Examples</seealso>.</p> </section> <section> <title>Reference</title> - <p>A reference is a term which is unique in an Erlang runtime + <p>A reference is a term that is unique in an Erlang runtime system, created by calling <c>make_ref/0</c>.</p> </section> @@ -117,34 +119,42 @@ phone_number <p>A fun is a functional object. Funs make it possible to create an anonymous function and pass the function itself -- not its name -- as argument to other functions.</p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> 1> <input>Fun1 = fun (X) -> X+1 end.</input> #Fun<erl_eval.6.39074546> 2> <input>Fun1(2).</input> 3</pre> - <p>Read more about funs in <seealso marker="expressions#funs">Fun Expressions</seealso>. More examples can be found in Programming - Examples.</p> + <p>Read more about funs in <seealso marker="expressions#funs"> + Fun Expressions</seealso>. For more examples, see + <seealso marker="doc/programming_examples:funs"> + Programming Examples</seealso>.</p> </section> <section> <title>Port Identifier</title> - <p>A port identifier identifies an Erlang port. <c>open_port/2</c>, - which is used to create ports, will return a value of this type.</p> + <p>A port identifier identifies an Erlang port.</p> + <p><c>open_port/2</c>, which is used to create ports, returns + a value of this data type.</p> <p>Read more about ports in <seealso marker="ports">Ports and Port Drivers</seealso>.</p> </section> <section> <title>Pid</title> - <p>A process identifier, pid, identifies a process. - <c>spawn/1,2,3,4</c>, <c>spawn_link/1,2,3,4</c> and - <c>spawn_opt/4</c>, which are used to create processes, return - values of this type. Example:</p> + <p>A process identifier, pid, identifies a process.</p> + <p>The following BIFs, which are used to create processes, return + values of this data type:</p> + <list type="bulleted"> + <item><c>spawn/1,2,3,4</c></item> + <item><c>spawn_link/1,2,3,4</c></item> + <item><c>spawn_opt/4</c></item> + </list> + <p><em>Example:</em></p> <pre> 1> <input>spawn(m, f, []).</input> <0.51.0></pre> - <p>The BIF <c>self()</c> returns the pid of the calling process. - Example:</p> + <p>In the following example, the BIF <c>self()</c> returns + the pid of the calling process:</p> <pre> -module(m). -export([loop/0]). @@ -167,14 +177,14 @@ who_are_you</pre> <section> <title>Tuple</title> - <p>Compound data type with a fixed number of terms:</p> + <p>A tuple is a compound data type with a fixed number of terms:</p> <pre> {Term1,...,TermN}</pre> <p>Each term <c>Term</c> in the tuple is called an <em>element</em>. The number of elements is said to be the <em>size</em> of the tuple.</p> <p>There exists a number of BIFs to manipulate tuples.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>P = {adam,24,{july,29}}.</input> {adam,24,{july,29}} @@ -192,7 +202,8 @@ adam <section> <title>Map</title> - <p>Compound data type with a variable number of key-value associations:</p> + <p>A map is a compound data type with a variable number of + key-value associations:</p> <pre> #{Key1=>Value1,...,KeyN=>ValueN}</pre> <p>Each key-value association in the map is called an @@ -200,7 +211,7 @@ adam called <em>elements</em>. The number of association pairs is said to be the <em>size</em> of the map.</p> <p>There exists a number of BIFs to manipulate maps.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>M1 = #{name=>adam,age=>24,date=>{july,29}}.</input> #{age => 24,date => {july,29},name => adam} @@ -215,16 +226,18 @@ adam 6> <input>map_size(#{}).</input> 0</pre> <p>A collection of maps processing functions can be found in - the STDLIB module <seealso marker="stdlib:maps"><c>maps</c></seealso>.</p> - <p>Read more about <seealso marker="expressions#map_expressions">Maps</seealso>.</p> + <seealso marker="stdlib:maps"><c>maps</c></seealso> manual page + in STDLIB.</p> + <p>Read more about maps in <seealso marker="expressions#map_expressions"> + Map Expressions</seealso>.</p> <note> - <p>Maps are considered experimental during OTP 17.</p> + <p>Maps are considered to be experimental during Erlang/OTP R17.</p> </note> </section> <section> <title>List</title> - <p>Compound data type with a variable number of terms.</p> + <p>A list is a compound data type with a variable number of terms.</p> <pre> [Term1,...,TermN]</pre> <p>Each term <c>Term</c> in the list is called an @@ -232,20 +245,21 @@ adam the <em>length</em> of the list.</p> <p>Formally, a list is either the empty list <c>[]</c> or consists of a <em>head</em> (first element) and a <em>tail</em> - (remainder of the list) which is also a list. The latter can + (remainder of the list). + The <em>tail</em> is also a list. The latter can be expressed as <c>[H|T]</c>. The notation - <c>[Term1,...,TermN]</c> above is actually shorthand for + <c>[Term1,...,TermN]</c> above is equivalent with the list <c>[Term1|[...|[TermN|[]]]]</c>.</p> - <p>Example: <br></br> -<c>[]</c> is a list, thus <br></br> + <p><em>Example:</em></p> +<p><c>[]</c> is a list, thus <br></br> <c>[c|[]]</c> is a list, thus <br></br> <c>[b|[c|[]]]</c> is a list, thus <br></br> -<c>[a|[b|[c|[]]]]</c> is a list, or in short <c>[a,b,c]</c>.</p> - <p></p> +<c>[a|[b|[c|[]]]]</c> is a list, or in short <c>[a,b,c]</c></p> + <p>A list where the tail is a list is sometimes called a <em>proper list</em>. It is allowed to have a list where the tail is not a - list, for example <c>[a|b]</c>. However, this type of list is of + list, for example, <c>[a|b]</c>. However, this type of list is of little practical use.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>L1 = [a,2,{c,4}].</input> [a,2,{c,4}] @@ -262,18 +276,19 @@ a 7> <input>length([]).</input> 0</pre> <p>A collection of list processing functions can be found in - the STDLIB module <c>lists</c>.</p> + the <seealso marker="stdlib:lists">lists</seealso> manual + page in STDLIB.</p> </section> <section> <title>String</title> <p>Strings are enclosed in double quotes ("), but is not a - data type in Erlang. Instead a string <c>"hello"</c> is - shorthand for the list <c>[$h,$e,$l,$l,$o]</c>, that is + data type in Erlang. Instead, a string <c>"hello"</c> is + shorthand for the list <c>[$h,$e,$l,$l,$o]</c>, that is, <c>[104,101,108,108,111]</c>.</p> <p>Two adjacent string literals are concatenated into one. This is - done at compile-time and does not incur any runtime overhead. - Example:</p> + done in the compilation, thus, does not incur any runtime overhead.</p> + <p><em>Example:</em></p> <pre> "string" "42"</pre> <p>is equivalent to</p> @@ -285,12 +300,13 @@ a <title>Record</title> <p>A record is a data structure for storing a fixed number of elements. It has named fields and is similar to a struct in C. - However, record is not a true data type. Instead record + However, a record is not a true data type. Instead, record expressions are translated to tuple expressions during compilation. Therefore, record expressions are not understood by - the shell unless special actions are taken. See <c>shell(3)</c> - for details.</p> - <p>Examples:</p> + the shell unless special actions are taken. For details, see the + <seealso marker="stdlib:shell">shell(3)</seealso> manual + page in STDLIB).</p> + <p><em>Examples:</em></p> <pre> -module(person). -export([new/2]). @@ -304,14 +320,15 @@ new(Name, Age) -> {person,ernie,44}</pre> <p>Read more about records in <seealso marker="records">Records</seealso>. More examples can be - found in Programming Examples.</p> + found in <seealso marker="doc/programming_examples:records"> + Programming Examples</seealso>.</p> </section> <section> <title>Boolean</title> <p>There is no Boolean data type in Erlang. Instead the atoms <c>true</c> and <c>false</c> are used to denote Boolean values.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>2 =< 3</input>. true @@ -330,76 +347,80 @@ true</pre> </row> <row> <cell align="left" valign="middle">\b</cell> - <cell align="left" valign="middle">backspace</cell> + <cell align="left" valign="middle">Backspace</cell> </row> <row> <cell align="left" valign="middle">\d</cell> - <cell align="left" valign="middle">delete</cell> + <cell align="left" valign="middle">Delete</cell> </row> <row> <cell align="left" valign="middle">\e</cell> - <cell align="left" valign="middle">escape</cell> + <cell align="left" valign="middle">Escape</cell> </row> <row> <cell align="left" valign="middle">\f</cell> - <cell align="left" valign="middle">form feed</cell> + <cell align="left" valign="middle">Form feed</cell> </row> <row> <cell align="left" valign="middle">\n</cell> - <cell align="left" valign="middle">newline</cell> + <cell align="left" valign="middle">Newline</cell> </row> <row> <cell align="left" valign="middle">\r</cell> - <cell align="left" valign="middle">carriage return</cell> + <cell align="left" valign="middle">Carriage return</cell> </row> <row> <cell align="left" valign="middle">\s</cell> - <cell align="left" valign="middle">space</cell> + <cell align="left" valign="middle">Space</cell> </row> <row> <cell align="left" valign="middle">\t</cell> - <cell align="left" valign="middle">tab</cell> + <cell align="left" valign="middle">Tab</cell> </row> <row> <cell align="left" valign="middle">\v</cell> - <cell align="left" valign="middle">vertical tab</cell> + <cell align="left" valign="middle">Vertical tab</cell> </row> <row> <cell align="left" valign="middle">\XYZ, \YZ, \Z</cell> - <cell align="left" valign="middle">character with octal representation XYZ, YZ or Z</cell> + <cell align="left" valign="middle">Character with octal + representation XYZ, YZ or Z</cell> </row> <row> <cell align="left" valign="middle">\xXY</cell> - <cell align="left" valign="middle">character with hexadecimal representation XY</cell> + <cell align="left" valign="middle">Character with hexadecimal + representation XY</cell> </row> <row> <cell align="left" valign="middle">\x{X...}</cell> - <cell align="left" valign="middle">character with hexadecimal representation; X... is one or more hexadecimal characters</cell> + <cell align="left" valign="middle">Character with hexadecimal + representation; X... is one or more hexadecimal characters</cell> </row> <row> <cell align="left" valign="middle">\^a...\^z <br></br> \^A...\^Z</cell> - <cell align="left" valign="middle">control A to control Z</cell> + <cell align="left" valign="middle">Control A to control Z</cell> </row> <row> <cell align="left" valign="middle">\'</cell> - <cell align="left" valign="middle">single quote</cell> + <cell align="left" valign="middle">Single quote</cell> </row> <row> <cell align="left" valign="middle">\"</cell> - <cell align="left" valign="middle">double quote</cell> + <cell align="left" valign="middle">Double quote</cell> </row> <row> <cell align="left" valign="middle">\\</cell> - <cell align="left" valign="middle">backslash</cell> + <cell align="left" valign="middle">Backslash</cell> </row> - <tcaption>Recognized Escape Sequences.</tcaption> + <tcaption>Recognized Escape Sequences</tcaption> </table> </section> <section> <title>Type Conversions</title> - <p>There are a number of BIFs for type conversions. Examples:</p> + <p>There are a number of BIFs for type conversions.</p> + <p><em>Examples:</em></p> <pre> 1> <input>atom_to_list(hello).</input> "hello" diff --git a/system/doc/reference_manual/distributed.xml b/system/doc/reference_manual/distributed.xml index 88f98bc106..fb83e356f9 100644 --- a/system/doc/reference_manual/distributed.xml +++ b/system/doc/reference_manual/distributed.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,22 +36,24 @@ runtime system is called a <em>node</em>. Message passing between processes at different nodes, as well as links and monitors, are transparent when pids are used. Registered names, however, are - local to each node. This means the node must be specified as well - when sending messages etc. using registered names.</p> + local to each node. This means that the node must be specified as well + when sending messages, and so on, using registered names.</p> <p>The distribution mechanism is implemented using TCP/IP sockets. - How to implement an alternative carrier is described in <em>ERTS User's Guide</em>.</p> + How to implement an alternative carrier is described in the + <seealso marker="erts:alt_dist">ERTS User's Guide</seealso>.</p> </section> <section> <title>Nodes</title> - <p>A <em>node</em> is an executing Erlang runtime system which has - been given a name, using the command line flag <c>-name</c> + <p>A <em>node</em> is an executing Erlang runtime system that has + been given a name, using the command-line flag <c>-name</c> (long names) or <c>-sname</c> (short names).</p> - <p>The format of the node name is an atom <c>name@host</c> where - <c>name</c> is the name given by the user and <c>host</c> is + <p>The format of the node name is an atom <c>name@host</c>. + <c>name</c> is the name given by the user. <c>host</c> is the full host name if long names are used, or the first part of the host name if short names are used. <c>node()</c> returns - the name of the node. Example:</p> + the name of the node.</p> + <p><em>Example:</em></p> <pre> % <input>erl -name dilbert</input> ([email protected])1> <input>node().</input> @@ -69,16 +71,16 @@ dilbert@uab</pre> <section> <title>Node Connections</title> <p>The nodes in a distributed Erlang system are loosely connected. - The first time the name of another node is used, for example if + The first time the name of another node is used, for example, if <c>spawn(Node,M,F,A)</c> or <c>net_adm:ping(Node)</c> is called, - a connection attempt to that node will be made.</p> + a connection attempt to that node is made.</p> <p>Connections are by default transitive. If a node A connects to - node B, and node B has a connection to node C, then node A will - also try to connect to node C. This feature can be turned off by - using the command line flag <c>-connect_all false</c>, see - <c>erl(1)</c>.</p> + node B, and node B has a connection to node C, then node A + also tries to connect to node C. This feature can be turned off by + using the command-line flag <c>-connect_all false</c>, see the + <seealso marker="erts:erl">erl(1)</seealso> manual page in ERTS.</p> <p>If a node goes down, all connections to that node are removed. - Calling <c>erlang:disconnect_node(Node)</c> will force disconnection + Calling <c>erlang:disconnect_node(Node)</c> forces disconnection of a node.</p> <p>The list of (visible) nodes currently connected to is returned by <c>nodes()</c>.</p> @@ -89,23 +91,24 @@ dilbert@uab</pre> <p>The Erlang Port Mapper Daemon <em>epmd</em> is automatically started at every host where an Erlang node is started. It is responsible for mapping the symbolic node names to machine - addresses. See <c>epmd(1)</c>.</p> + addresses. See the + <seealso marker="erts:epmd">epmd(1)</seealso> manual page in ERTS.</p> </section> <section> <title>Hidden Nodes</title> <p>In a distributed Erlang system, it is sometimes useful to connect to a node without also connecting to all other nodes. - An example could be some kind of O&M functionality used to - inspect the status of a system without disturbing it. For this - purpose, a <em>hidden node</em> may be used.</p> - <p>A hidden node is a node started with the command line flag + An example is some kind of O&M functionality used to + inspect the status of a system, without disturbing it. For this + purpose, a <em>hidden node</em> can be used.</p> + <p>A hidden node is a node started with the command-line flag <c>-hidden</c>. Connections between hidden nodes and other nodes are not transitive, they must be set up explicitly. Also, hidden nodes does not show up in the list of nodes returned by <c>nodes()</c>. Instead, <c>nodes(hidden)</c> or <c>nodes(connected)</c> must be used. This means, for example, - that the hidden node will not be added to the set of nodes that + that the hidden node is not added to the set of nodes that <c>global</c> is keeping track of.</p> <p>This feature was added in Erlang 5.0/OTP R7.</p> </section> @@ -114,9 +117,11 @@ dilbert@uab</pre> <title>C Nodes</title> <p>A <em>C node</em> is a C program written to act as a hidden node in a distributed Erlang system. The library <em>Erl_Interface</em> - contains functions for this purpose. Refer to the documentation - for Erl_Interface and <em>Interoperability Tutorial</em> for more - information about C nodes.</p> + contains functions for this purpose. For more information about + C nodes, see the <seealso marker="erl_interface:ei_users_guide"> + Erl_Interface</seealso> application and + <seealso marker="doc/tutorial:introduction#interoperability tutorial"> + Interoperability Tutorial.</seealso>.</p> </section> <section> @@ -125,7 +130,7 @@ dilbert@uab</pre> with each other. In a network of different Erlang nodes, it is built into the system at the lowest possible level. Each node has its own <em>magic cookie</em>, which is an Erlang atom.</p> - <p>When a nodes tries to connect to another node, the magic cookies + <p>When a node tries to connect to another node, the magic cookies are compared. If they do not match, the connected node rejects the connection.</p> <p>At start-up, a node has a random atom assigned as its magic @@ -141,8 +146,8 @@ dilbert@uab</pre> the local node assume that all other nodes have the same cookie <c>Cookie</c>.</p> <p>Thus, groups of users with identical cookie files get Erlang - nodes which can communicate freely and without interference from - the magic cookie system. Users who want run nodes on separate + nodes that can communicate freely and without interference from + the magic cookie system. Users who want to run nodes on separate file systems must make certain that their cookie files are identical on the different file systems.</p> <p>For a node <c>Node1</c> with magic cookie <c>Cookie</c> to be @@ -154,19 +159,25 @@ dilbert@uab</pre> <p>The default when a connection is established between two nodes, is to immediately connect all other visible nodes as well. This way, there is always a fully connected network. If there are - nodes with different cookies, this method might be inappropriate - and the command line flag <c>-connect_all false</c> must be set, - see <seealso marker="erts:erl">erl(1)</seealso>.</p> + nodes with different cookies, this method can be inappropriate + and the command-line flag <c>-connect_all false</c> must be set, + see the <seealso marker="erts:erl">erl(1)</seealso> + manual page in ERTS.</p> <p>The magic cookie of the local node is retrieved by calling <c>erlang:get_cookie()</c>.</p> </section> <section> <title>Distribution BIFs</title> - <p>Some useful BIFs for distributed programming, see - <c>erlang(3)</c> for more information:</p> + <p>Some useful BIFs for distributed programming + (for more information, see the <seealso marker="erts:erlang"> + erlang(3)</seealso> manual page in ERTS:</p> <table> <row> + <cell align="left" valign="middle"><em>BIF</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>erlang:disconnect_node(Node)</c></cell> <cell align="left" valign="middle">Forces the disconnection of a node.</cell> </row> @@ -180,7 +191,9 @@ dilbert@uab</pre> </row> <row> <cell align="left" valign="middle"><c>monitor_node(Node, true|false)</c></cell> - <cell align="left" valign="middle">Monitor the status of <c>Node</c>. A message<c>{nodedown, Node}</c> is received if the connection to it is lost.</cell> + <cell align="left" valign="middle">Monitors the status of + <c>Node</c>. A message<c>{nodedown, Node}</c> is received + if the connection to it is lost.</cell> </row> <row> <cell align="left" valign="middle"><c>node()</c></cell> @@ -196,11 +209,16 @@ dilbert@uab</pre> </row> <row> <cell align="left" valign="middle"><c>nodes(Arg)</c></cell> - <cell align="left" valign="middle">Depending on <c>Arg</c>, this function can return a list not only of visible nodes, but also hidden nodes and previously known nodes, etc.</cell> + <cell align="left" valign="middle">Depending on <c>Arg</c>, + this function can return a list not only of visible nodes, + but also hidden nodes and previously known nodes, and so on.</cell> </row> <row> <cell align="left" valign="middle"><c>erlang:set_cookie(Node, Cookie)</c></cell> - <cell align="left" valign="middle">Sets the magic cookie used when connecting to <c>Node</c>. If <c>Node</c> is the current node, <c>Cookie</c> will be used when connecting to all new nodes.</cell> + <cell align="left" valign="middle">Sets the magic cookie used + when connecting to <c>Node</c>. If <c>Node</c> is the + current node, <c>Cookie</c> is used when connecting to + all new nodes.</cell> </row> <row> <cell align="left" valign="middle"><c>spawn[_link|_opt](Node, Fun)</c></cell> @@ -210,18 +228,24 @@ dilbert@uab</pre> <cell align="left" valign="middle"><c>spawn[_link|opt](Node, Module, FunctionName, Args)</c></cell> <cell align="left" valign="middle">Creates a process at a remote node.</cell> </row> - <tcaption>Distribution BIFs.</tcaption> + <tcaption>Distribution BIFs</tcaption> </table> </section> <section> - <title>Distribution Command Line Flags</title> - <p>Examples of command line flags used for distributed programming, - see <c>erl(1)</c> for more information:</p> + <title>Distribution Command-Line Flags</title> + <p>Examples of command-line flags used for distributed programming + (for more information, see the <seealso marker="erts:erl">erl(1) + </seealso> manual page in ERTS:</p> <table> <row> + <cell align="left" valign="middle"><em>Command-Line Flag</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>-connect_all false</c></cell> - <cell align="left" valign="middle">Only explicit connection set-ups will be used.</cell> + <cell align="left" valign="middle">Only explicit connection + set-ups are used.</cell> </row> <row> <cell align="left" valign="middle"><c>-hidden</c></cell> @@ -239,16 +263,20 @@ dilbert@uab</pre> <cell align="left" valign="middle"><c>-sname Name</c></cell> <cell align="left" valign="middle">Makes a runtime system into a node, using short node names.</cell> </row> - <tcaption>Distribution Command Line Flags.</tcaption> + <tcaption>Distribution Command-Line Flags</tcaption> </table> </section> <section> <title>Distribution Modules</title> <p>Examples of modules useful for distributed programming:</p> - <p>In Kernel:</p> + <p>In the Kernel application:</p> <table> <row> + <cell align="left" valign="middle"><em>Module</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>global</c></cell> <cell align="left" valign="middle">A global name registration facility.</cell> </row> @@ -266,9 +294,13 @@ dilbert@uab</pre> </row> <tcaption>Kernel Modules Useful For Distribution.</tcaption> </table> - <p>In STDLIB:</p> + <p>In the STDLIB application:</p> <table> <row> + <cell align="left" valign="middle"><em>Module</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>slave</c></cell> <cell align="left" valign="middle">Start and control of slave nodes.</cell> </row> diff --git a/system/doc/reference_manual/errors.xml b/system/doc/reference_manual/errors.xml index dde6e68f4a..66ecf6aa94 100644 --- a/system/doc/reference_manual/errors.xml +++ b/system/doc/reference_manual/errors.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,12 +38,12 @@ <item>Run-time errors</item> <item>Generated errors</item> </list> - <p>A compile-time error, for example a syntax error, should not + <p>A compile-time error, for example a syntax error, does not cause much trouble as it is caught by the compiler.</p> <p>A logical error is when a program does not behave as intended, - but does not crash. An example could be that nothing happens when + but does not crash. An example is that nothing happens when a button in a graphical user interface is clicked.</p> - <p>A run-time error is when a crash occurs. An example could be + <p>A run-time error is when a crash occurs. An example is when an operator is applied to arguments of the wrong type. The Erlang programming language has built-in features for handling of run-time errors.</p> @@ -54,23 +54,23 @@ of class <c>error</c>. </p> <p>A generated error is when the code itself calls - <c>exit/1</c> or <c>throw/1</c>. Note that emulated run-time + <c>exit/1</c> or <c>throw/1</c>. Notice that emulated run-time errors are not denoted as generated errors here. </p> <p>Generated errors are exceptions of classes <c>exit</c> and <c>throw</c>. </p> <p>When a run-time error or generated error occurs in Erlang, - execution for the process which evaluated + execution for the process that evaluated the erroneous expression is stopped. This is referred to as a <em>failure</em>, that execution or evaluation <em>fails</em>, or that the process <em>fails</em>, - <em>terminates</em> or <em>exits</em>. Note that a process may + <em>terminates</em>, or <em>exits</em>. Notice that a process can terminate/exit for other reasons than a failure.</p> - <p>A process that terminates will emit an <em>exit signal</em> with + <p>A process that terminates emits an <em>exit signal</em> with an <em>exit reason</em> that says something about which error - has occurred. Normally, some information about the error will - be printed to the terminal.</p> + has occurred. Normally, some information about the error is + printed to the terminal.</p> </section> <section> @@ -78,10 +78,12 @@ <p>Exceptions are run-time errors or generated errors and are of three different classes, with different origins. The <seealso marker="expressions#try">try</seealso> expression - (appeared in Erlang 5.4/OTP-R10B) + (new in Erlang 5.4/OTP R10B) can distinguish between the different classes, whereas the <seealso marker="expressions#catch">catch</seealso> - expression can not. They are described in the Expressions chapter.</p> + expression cannot. They are described in + <seealso marker="expressions">Expressions + </seealso>.</p> <table> <row> <cell align="left" valign="middle"><em>Class</em></cell> @@ -89,7 +91,9 @@ </row> <row> <cell align="left" valign="middle"><c>error</c></cell> - <cell align="left" valign="middle">Run-time error for example <c>1+a</c>, or the process called <c>erlang:error/1,2</c> (appeared in Erlang 5.4/OTP-R10B)</cell> + <cell align="left" valign="middle">Run-time error, + for example, <c>1+a</c>, or the process called + <c>erlang:error/1,2</c> (new in Erlang 5.4/OTP R10B)</cell> </row> <row> <cell align="left" valign="middle"><c>exit</c></cell> @@ -102,11 +106,11 @@ <tcaption>Exception Classes.</tcaption> </table> <p>An exception consists of its class, an exit reason - (the <seealso marker="#exit_reasons">Exit Reason</seealso>), - and a stack trace (that aids in finding the code location of + (see <seealso marker="#exit_reasons">Exit Reason</seealso>), + and a stack trace (which aids in finding the code location of the exception).</p> <p>The stack trace can be retrieved using - <c>erlang:get_stacktrace/0</c> (new in Erlang 5.4/OTP-R10B) + <c>erlang:get_stacktrace/0</c> (new in Erlang 5.4/OTP R10B) from within a <c>try</c> expression, and is returned for exceptions of class <c>error</c> from a <c>catch</c> expression.</p> <p>An exception of class <c>error</c> is also known as a run-time @@ -114,38 +118,38 @@ </section> <section> - <title>Handling of Run-Time Errors in Erlang</title> + <title>Handling of Run-time Errors in Erlang</title> <section> <title>Error Handling Within Processes</title> <p>It is possible to prevent run-time errors and other exceptions from causing the process to terminate by using <c>catch</c> or - <c>try</c>, see the Expressions chapter about - <seealso marker="expressions#catch">Catch</seealso> - and <seealso marker="expressions#try">Try</seealso>.</p> + <c>try</c>, see <seealso marker="expressions"> + Expressions</seealso> about + <seealso marker="expressions#catch">catch</seealso> + and <seealso marker="expressions#try">try</seealso>.</p> </section> <section> <title>Error Handling Between Processes</title> <p>Processes can monitor other processes and detect process terminations, see - the <seealso marker="processes#errors">Processes</seealso> - chapter.</p> + <seealso marker="processes#errors">Processes</seealso>.</p> </section> </section> <section> <marker id="exit_reasons"></marker> <title>Exit Reasons</title> - <p>When a run-time error occurs, - that is an exception of class <c>error</c>, - the exit reason is a tuple <c>{Reason,Stack}</c>. + <p>When a run-time error occurs, + that is an exception of class <c>error</c>. + The exit reason is a tuple <c>{Reason,Stack}</c>, where <c>Reason</c> is a term indicating the type of error:</p> <table> <row> <cell align="left" valign="middle"><em>Reason</em></cell> - <cell align="left" valign="middle"><em>Type of error</em></cell> + <cell align="left" valign="middle"><em>Type of Error</em></cell> </row> <row> <cell align="left" valign="middle"><c>badarg</c></cell> @@ -181,7 +185,7 @@ </row> <row> <cell align="left" valign="middle"><c>{badfun,F}</c></cell> - <cell align="left" valign="middle">There is something wrong with a fun <c>F</c>.</cell> + <cell align="left" valign="middle">Something is wrong with a fun <c>F</c>.</cell> </row> <row> <cell align="left" valign="middle"><c>{badarity,F}</c></cell> @@ -201,14 +205,17 @@ </row> <row> <cell align="left" valign="middle"><c>system_limit</c></cell> - <cell align="left" valign="middle">A system limit has been reached. See Efficiency Guide for information about system limits.</cell> + <cell align="left" valign="middle">A system limit has been reached. + See <seealso marker="doc/efficiency_guide:advanced"> + Efficiency Guide</seealso> for information about system limits. + </cell> </row> - <tcaption>Exit Reasons.</tcaption> + <tcaption>Exit Reasons</tcaption> </table> <p><c>Stack</c> is the stack of function calls being evaluated when the error occurred, given as a list of tuples <c>{Module,Name,Arity}</c> with the most recent function call - first. The most recent function call tuple may in some + first. The most recent function call tuple can in some cases be <c>{Module,Name,[Arg]}</c>.</p> </section> </chapter> diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml index 62a344ad58..fd3cfabd3d 100644 --- a/system/doc/reference_manual/expressions.xml +++ b/system/doc/reference_manual/expressions.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,13 +28,17 @@ <rev></rev> <file>expressions.xml</file> </header> - <p>In this chapter, all valid Erlang expressions are listed. + <p>In this section, all valid Erlang expressions are listed. When writing Erlang programs, it is also allowed to use macro- and record expressions. However, these expressions are expanded during compilation and are in that sense not true Erlang expressions. Macro- and record expressions are covered in - separate chapters: <seealso marker="macros">Macros</seealso> and - <seealso marker="records">Records</seealso>.</p> + separate sections: + </p> + <list type="bulleted"> + <item><p><seealso marker="macros">Preprocessor</seealso></p></item> + <item><p><seealso marker="records">Records</seealso></p></item> + </list> <section> <title>Expression Evaluation</title> @@ -48,15 +52,15 @@ Expr1 + Expr2</code> performed.</p> <p>Many of the operators can only be applied to arguments of a certain type. For example, arithmetic operators can only be - applied to numbers. An argument of the wrong type will cause - a <c>badarg</c> run-time error.</p> + applied to numbers. An argument of the wrong type causes + a <c>badarg</c> runtime error.</p> </section> <section> <marker id="term"></marker> <title>Terms</title> <p>The simplest form of expression is a term, that is an integer, - float, atom, string, list, map or tuple. + float, atom, string, list, map, or tuple. The return value is the term itself.</p> </section> @@ -65,9 +69,10 @@ Expr1 + Expr2</code> <p>A variable is an expression. If a variable is bound to a value, the return value is this value. Unbound variables are only allowed in patterns.</p> - <p>Variables start with an uppercase letter or underscore (_) - and may contain alphanumeric characters, underscore and @. - Examples:</p> + <p>Variables start with an uppercase letter or underscore (_). + Variables can contain alphanumeric characters, underscore and <c>@</c>. + </p> + <p><em>Examples:</em></p> <pre> X Name1 @@ -77,18 +82,20 @@ _ _Height</pre> <p>Variables are bound to values using <seealso marker="patterns">pattern matching</seealso>. Erlang - uses <em>single assignment</em>, a variable can only be bound + uses <em>single assignment</em>, that is, a variable can only be bound once.</p> <p>The <em>anonymous variable</em> is denoted by underscore (_) and can be used when a variable is required but its value can be - ignored. Example:</p> + ignored.</p> + <p><em>Example:</em></p> <pre> [H|_] = [1,2,3]</pre> - <p>Variables starting with underscore (_), for example + <p>Variables starting with underscore (_), for example, <c>_Height</c>, are normal variables, not anonymous. They are - however ignored by the compiler in the sense that they will not - generate any warnings for unused variables. Example: The following - code</p> + however ignored by the compiler in the sense that they do not + generate any warnings for unused variables.</p> + <p><em>Example:</em></p> + <p>The following code:</p> <pre> member(_, []) -> [].</pre> @@ -96,36 +103,37 @@ member(_, []) -> <pre> member(Elem, []) -> [].</pre> - <p>This will however cause a warning for an unused variable + <p>This causes a warning for an unused variable, <c>Elem</c>, if the code is compiled with the flag <c>warn_unused_vars</c> set. Instead, the code can be rewritten to:</p> <pre> member(_Elem, []) -> [].</pre> - <p>Note that since variables starting with an underscore are - not anonymous, this will match:</p> + <p>Notice that since variables starting with an underscore are + not anonymous, this matches:</p> <pre> {_,_} = {1,2}</pre> - <p>But this will fail:</p> + <p>But this fails:</p> <pre> {_N,_N} = {1,2}</pre> <p>The scope for a variable is its function clause. Variables bound in a branch of an <c>if</c>, <c>case</c>, or <c>receive</c> expression must be bound in all branches - to have a value outside the expression, otherwise they - will be regarded as 'unsafe' outside the expression.</p> + to have a value outside the expression. Otherwise they + are regarded as 'unsafe' outside the expression.</p> <p>For the <c>try</c> expression introduced in - Erlang 5.4/OTP-R10B, variable scoping is limited so that + Erlang 5.4/OTP R10B, variable scoping is limited so that variables bound in the expression are always 'unsafe' outside - the expression. This will be improved.</p> + the expression. This is to be improved.</p> </section> <section> <marker id="pattern"></marker> <title>Patterns</title> - <p>A pattern has the same structure as a term but may contain - unbound variables. Example:</p> + <p>A pattern has the same structure as a term but can contain + unbound variables.</p> + <p><em>Example:</em></p> <pre> Name1 [H|T] @@ -136,13 +144,13 @@ Name1 <section> <title>Match Operator = in Patterns</title> <p>If <c>Pattern1</c> and <c>Pattern2</c> are valid patterns, - then the following is also a valid pattern:</p> + the following is also a valid pattern:</p> <pre> Pattern1 = Pattern2</pre> <p>When matched against a term, both <c>Pattern1</c> and - <c>Pattern2</c> will be matched against the term. The idea - behind this feature is to avoid reconstruction of terms. - Example:</p> + <c>Pattern2</c> are matched against the term. The idea + behind this feature is to avoid reconstruction of terms.</p> + <p><em>Example:</em></p> <pre> f({connect,From,To,Number,Options}, To) -> Signal = {connect,From,To,Number,Options}, @@ -163,16 +171,20 @@ f(Signal, To) -> <pre> f("prefix" ++ Str) -> ...</pre> <p>This is syntactic sugar for the equivalent, but harder to - read</p> + read:</p> <pre> f([$p,$r,$e,$f,$i,$x | Str]) -> ...</pre> </section> <section> <title>Expressions in Patterns</title> - <p>An arithmetic expression can be used within a pattern, if - it uses only numeric or bitwise operators, and if its value - can be evaluated to a constant at compile-time. Example:</p> + <p>An arithmetic expression can be used within a pattern if + it meets both of the following two conditions:</p> + <list type="bulleted"> + <item>It uses only numeric or bitwise operators.</item> + <item>Its value can be evaluated to a constant when complied.</item> + </list> + <p><em>Example:</em></p> <pre> case {Value, Result} of {?THRESHOLD+1, ok} -> ...</pre> @@ -182,21 +194,21 @@ case {Value, Result} of <section> <title>Match</title> + <p>The following matches <c>Expr1</c>, a pattern, against + <c>Expr2</c>:</p> <pre> Expr1 = Expr2</pre> - <p>Matches <c>Expr1</c>, a pattern, against <c>Expr2</c>. - If the matching succeeds, any unbound variable in the pattern + <p>If the matching succeeds, any unbound variable in the pattern becomes bound and the value of <c>Expr2</c> is returned.</p> - <p>If the matching fails, a <c>badmatch</c> run-time error will - occur.</p> - <p>Examples:</p> + <p>If the matching fails, a <c>badmatch</c> run-time error occurs.</p> + <p><em>Examples:</em></p> <pre> 1> <input>{A, B} = {answer, 42}.</input> {answer,42} 2> <input>A.</input> answer 3> <input>{C, D} = [1, 2].</input> -** exception error: no match of right hand side value [1,2]</pre> +** exception error: no match of right-hand side value [1,2]</pre> </section> <section> @@ -210,27 +222,28 @@ ExprM:ExprF(Expr1,...,ExprN)</pre> <c>ExprF</c> must be an atom or an expression that evaluates to an atom. The function is said to be called by using the <em>fully qualified function name</em>. This is often referred - to as a <em>remote</em> or <em>external function call</em>. - Example:</p> + to as a <em>remote</em> or <em>external function call</em>.</p> + <p><em>Example:</em></p> <code type="none"> lists:keysearch(Name, 1, List)</code> <p>In the second form of function calls, <c>ExprF(Expr1,...,ExprN)</c>, <c>ExprF</c> must be an atom or evaluate to a fun.</p> - <p>If <c>ExprF</c> is an atom the function is said to be called by + <p>If <c>ExprF</c> is an atom, the function is said to be called by using the <em>implicitly qualified function name</em>. If the function <c>ExprF</c> is locally defined, it is called. - Alternatively if <c>ExprF</c> is explicitly imported from module - <c>M</c>, <c>M:ExprF(Expr1,...,ExprN)</c> is called. If + Alternatively, if <c>ExprF</c> is explicitly imported from the + <c>M</c> module, <c>M:ExprF(Expr1,...,ExprN)</c> is called. If <c>ExprF</c> is neither declared locally nor explicitly imported, <c>ExprF</c> must be the name of an automatically - imported BIF. Examples:</p> + imported BIF. </p> + <p><em>Examples:</em></p> <code type="none"> handle(Msg, State) spawn(m, init, [])</code> - <p>Examples where ExprF is a fun:</p> + <p><em>Examples</em> where <c>ExprF</c> is a fun:</p> <code type="none"> Fun1 = fun(X) -> X+1 end Fun1(3) @@ -239,16 +252,15 @@ Fun1(3) fun lists:append/2([1,2], [3,4]) => [1,2,3,4]</code> - <p>Note that when calling a local function, there is a difference - between using the implicitly or fully qualified function name, as - the latter always refers to the latest version of the module. See - <seealso marker="code_loading">Compilation and Code Loading</seealso>.</p> - - <p>See also the chapter about - <seealso marker="functions#eval">Function Evaluation</seealso>.</p> + <p>Notice that when calling a local function, there is a difference + between using the implicitly or fully qualified function name. + The latter always refers to the latest version of the module. + See <seealso marker="code_loading">Compilation and Code Loading + </seealso> and <seealso marker="functions#eval"> + Function Evaluation</seealso>.</p> <section> - <title>Local Function Names Clashing With Auto-imported BIFs</title> + <title>Local Function Names Clashing With Auto-Imported BIFs</title> <p>If a local function has the same name as an auto-imported BIF, the semantics is that implicitly qualified function calls are directed to the locally defined function, not to the BIF. To avoid @@ -260,9 +272,9 @@ fun lists:append/2([1,2], [3,4]) <warning><p>Before OTP R14A (ERTS version 5.8), an implicitly qualified function call to a function having the same name as an auto-imported BIF always resulted in the BIF being called. In - newer versions of the compiler the local function is instead - called. The change is there to avoid that future additions to the - set of auto-imported BIFs does not silently change the behavior + newer versions of the compiler, the local function is called instead. + This is to avoid that future additions to the + set of auto-imported BIFs do not silently change the behavior of old code.</p> <p>However, to avoid that old (pre R14) code changed its @@ -272,8 +284,8 @@ fun lists:append/2([1,2], [3,4]) 5.8) and have an implicitly qualified call to that function in your code, you either need to explicitly remove the auto-import using a compiler directive, or replace the call with a fully - qualified function call, otherwise you will get a compilation - error. See example below:</p> </warning> + qualified function call. Otherwise you get a compilation + error. See the following example:</p> </warning> <code type="none"> -export([length/1,f/1]). @@ -290,9 +302,10 @@ f(X) when erlang:length(X) > 3 -> %% Calls erlang:length/1, long.</code> <p>The same logic applies to explicitly imported functions from - other modules as to locally defined functions. To both import a + other modules, as to locally defined functions. + It is not allowed to both import a function from another module and have the function declared in the - module at the same time is not allowed.</p> + module at the same time:</p> <code type="none"> -export([f/1]). @@ -310,10 +323,10 @@ f(X) -> length(X). %% mod:length/1 is called</code> - <p>For auto-imported BIFs added to Erlang in release R14A and thereafter, + <p>For auto-imported BIFs added in Erlang/OTP R14A and thereafter, overriding the name with a local function or explicit import is always allowed. However, if the <c>-compile({no_auto_import,[F/A])</c> - directive is not used, the compiler will issue a warning whenever + directive is not used, the compiler issues a warning whenever the function is called in the module using the implicitly qualified function name.</p> </section> @@ -330,15 +343,16 @@ if BodyN end</pre> <p>The branches of an <c>if</c>-expression are scanned sequentially - until a guard sequence <c>GuardSeq</c> which evaluates to true is + until a guard sequence <c>GuardSeq</c> that evaluates to true is found. Then the corresponding <c>Body</c> (sequence of expressions separated by ',') is evaluated.</p> <p>The return value of <c>Body</c> is the return value of the <c>if</c> expression.</p> - <p>If no guard sequence is true, an <c>if_clause</c> run-time error - will occur. If necessary, the guard expression <c>true</c> can be + <p>If no guard sequence is evaluated as true, + an <c>if_clause</c> run-time error + occurs. If necessary, the guard expression <c>true</c> can be used in the last branch, as that guard sequence is always true.</p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> is_greater_than(X, Y) -> if @@ -367,8 +381,8 @@ end</pre> <p>The return value of <c>Body</c> is the return value of the <c>case</c> expression.</p> <p>If there is no matching pattern with a true guard sequence, - a <c>case_clause</c> run-time error will occur.</p> - <p>Example:</p> + a <c>case_clause</c> run-time error occurs.</p> + <p><em>Example:</em></p> <pre> is_valid_signal(Signal) -> case Signal of @@ -389,15 +403,15 @@ Expr1 ! Expr2</pre> <p>Sends the value of <c>Expr2</c> as a message to the process specified by <c>Expr1</c>. The value of <c>Expr2</c> is also the return value of the expression.</p> - <p><c>Expr1</c> must evaluate to a pid, a registered name (atom) or - a tuple <c>{Name,Node}</c>, where <c>Name</c> is an atom and - <c>Node</c> a node name, also an atom.</p> + <p><c>Expr1</c> must evaluate to a pid, a registered name (atom), or + a tuple <c>{Name,Node}</c>. <c>Name</c> is an atom and + <c>Node</c> is a node name, also an atom.</p> <list type="bulleted"> <item>If <c>Expr1</c> evaluates to a name, but this name is not - registered, a <c>badarg</c> run-time error will occur.</item> + registered, a <c>badarg</c> run-time error occurs.</item> <item>Sending a message to a pid never fails, even if the pid identifies a non-existing process.</item> - <item>Distributed message sending, that is if <c>Expr1</c> + <item>Distributed message sending, that is, if <c>Expr1</c> evaluates to a tuple <c>{Name,Node}</c> (or a pid located at another node), also never fails.</item> </list> @@ -420,14 +434,14 @@ end</pre> the second, and so on. If a match succeeds and the optional guard sequence <c>GuardSeq</c> is true, the corresponding <c>Body</c> is evaluated. The matching message is consumed, that - is removed from the mailbox, while any other messages in + is, removed from the mailbox, while any other messages in the mailbox remain unchanged.</p> <p>The return value of <c>Body</c> is the return value of the <c>receive</c> expression.</p> - <p><c>receive</c> never fails. Execution is suspended, possibly - indefinitely, until a message arrives that does match one of + <p><c>receive</c> never fails. The execution is suspended, possibly + indefinitely, until a message arrives that matches one of the patterns and with a true guard sequence. </p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> wait_for_onhook() -> receive @@ -438,7 +452,7 @@ wait_for_onhook() -> B ! {busy, self()}, wait_for_onhook() end.</pre> - <p>It is possible to augment the <c>receive</c> expression with a + <p>The <c>receive</c> expression can be augmented with a timeout:</p> <pre> receive @@ -451,14 +465,14 @@ after ExprT -> BodyT end</pre> - <p><c>ExprT</c> should evaluate to an integer. The highest allowed - value is 16#ffffffff, that is, the value must fit in 32 bits. + <p><c>ExprT</c> is to evaluate to an integer. The highest allowed + value is 16#FFFFFFFF, that is, the value must fit in 32 bits. <c>receive..after</c> works exactly as <c>receive</c>, except that if no matching message has arrived within <c>ExprT</c> - milliseconds, then <c>BodyT</c> is evaluated instead and its - return value becomes the return value of the <c>receive..after</c> - expression.</p> - <p>Example:</p> + milliseconds, then <c>BodyT</c> is evaluated instead. The + return value of <c>BodyT</c> then becomes the return value + of the <c>receive..after</c> expression.</p> + <p><em>Example:</em></p> <pre> wait_for_onhook() -> receive @@ -481,10 +495,10 @@ after ExprT -> BodyT end</pre> - <p>This construction will not consume any messages, only suspend - execution in the process for <c>ExprT</c> milliseconds and can be + <p>This construction does not consume any messages, only suspends + execution in the process for <c>ExprT</c> milliseconds. This can be used to implement simple timers.</p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> timer() -> spawn(m, timer, [self()]). @@ -498,12 +512,12 @@ timer(Pid) -> <p>There are two special cases for the timeout value <c>ExprT</c>:</p> <taglist> <tag><c>infinity</c></tag> - <item>The process should wait indefinitely for a matching message - -- this is the same as not using a timeout. Can be - useful for timeout values that are calculated at run-time.</item> + <item>The process is to wait indefinitely for a matching message; + this is the same as not using a timeout. This can be + useful for timeout values that are calculated at runtime.</item> <tag>0</tag> <item>If there is no matching message in the mailbox, the timeout - will occur immediately.</item> + occurs immediately.</item> </taglist> </section> @@ -518,39 +532,39 @@ Expr1 <input>op</input> Expr2</pre> </row> <row> <cell align="left" valign="middle">==</cell> - <cell align="left" valign="middle">equal to</cell> + <cell align="left" valign="middle">Equal to</cell> </row> <row> <cell align="left" valign="middle">/=</cell> - <cell align="left" valign="middle">not equal to</cell> + <cell align="left" valign="middle">Not equal to</cell> </row> <row> <cell align="left" valign="middle">=<</cell> - <cell align="left" valign="middle">less than or equal to</cell> + <cell align="left" valign="middle">Less than or equal to</cell> </row> <row> <cell align="left" valign="middle"><</cell> - <cell align="left" valign="middle">less than</cell> + <cell align="left" valign="middle">Less than</cell> </row> <row> <cell align="left" valign="middle">>=</cell> - <cell align="left" valign="middle">greater than or equal to</cell> + <cell align="left" valign="middle">Greater than or equal to</cell> </row> <row> <cell align="left" valign="middle">></cell> - <cell align="left" valign="middle">greater than</cell> + <cell align="left" valign="middle">Greater than</cell> </row> <row> <cell align="left" valign="middle">=:=</cell> - <cell align="left" valign="middle">exactly equal to</cell> + <cell align="left" valign="middle">Exactly equal to</cell> </row> <row> <cell align="left" valign="middle">=/=</cell> - <cell align="left" valign="middle">exactly not equal to</cell> + <cell align="left" valign="middle">Exactly not equal to</cell> </row> <tcaption>Term Comparison Operators.</tcaption> </table> - <p>The arguments may be of different data types. The following + <p>The arguments can be of different data types. The following order is defined:</p> <pre> number < atom < reference < fun < port < pid < tuple < list < bit string</pre> @@ -558,17 +572,18 @@ number < atom < reference < fun < port < pid < tuple < list size, two tuples with the same size are compared element by element.</p> <p>When comparing an integer to a float, the term with the lesser - precision will be converted into the other term's type, unless the - operator is one of =:= or =/=. A float is more precise than + precision is converted into the type of the other term, unless the + operator is one of <c>=:=</c> or <c>=/=</c>. A float is more precise than an integer until all significant figures of the float are to the left of the decimal point. This happens when the float is larger/smaller than +/-9007199254740992.0. The conversion strategy is changed depending on the size of the float because otherwise comparison of large floats and integers would lose their transitivity.</p> - <p>Returns the Boolean value of the expression, <c>true</c> or - <c>false</c>.</p> - <p>Examples:</p> + <p>Term comparison operators return the Boolean value of the + expression, <c>true</c> or <c>false</c>.</p> + + <p><em>Examples:</em></p> <pre> 1> <input>1==1.0.</input> true @@ -585,19 +600,19 @@ false</pre> Expr1 <input>op</input> Expr2</pre> <table> <row> - <cell align="left" valign="middle"><em>op</em></cell> + <cell align="left" valign="middle"><em>Operator</em></cell> <cell align="left" valign="middle"><em>Description</em></cell> - <cell align="left" valign="middle"><em>Argument type</em></cell> + <cell align="left" valign="middle"><em>Argument Type</em></cell> </row> <row> <cell align="left" valign="middle">+</cell> - <cell align="left" valign="middle">unary +</cell> - <cell align="left" valign="middle">number</cell> + <cell align="left" valign="middle">Unary +</cell> + <cell align="left" valign="middle">Number</cell> </row> <row> <cell align="left" valign="middle">-</cell> - <cell align="left" valign="middle">unary -</cell> - <cell align="left" valign="middle">number</cell> + <cell align="left" valign="middle">Unary -</cell> + <cell align="left" valign="middle">Number</cell> </row> <row> <cell align="left" valign="middle">+</cell> @@ -607,62 +622,62 @@ Expr1 <input>op</input> Expr2</pre> <row> <cell align="left" valign="middle">-</cell> <cell align="left" valign="middle"> </cell> - <cell align="left" valign="middle">number</cell> + <cell align="left" valign="middle">Number</cell> </row> <row> <cell align="left" valign="middle">*</cell> <cell align="left" valign="middle"> </cell> - <cell align="left" valign="middle">number</cell> + <cell align="left" valign="middle">Number</cell> </row> <row> <cell align="left" valign="middle">/</cell> - <cell align="left" valign="middle">floating point division</cell> - <cell align="left" valign="middle">number</cell> + <cell align="left" valign="middle">Floating point division</cell> + <cell align="left" valign="middle">Number</cell> </row> <row> <cell align="left" valign="middle">bnot</cell> - <cell align="left" valign="middle">unary bitwise not</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Unary bitwise NOT</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">div</cell> - <cell align="left" valign="middle">integer division</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Integer division</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">rem</cell> - <cell align="left" valign="middle">integer remainder of X/Y</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Integer remainder of X/Y</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">band</cell> - <cell align="left" valign="middle">bitwise and</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Bitwise AND</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">bor</cell> - <cell align="left" valign="middle">bitwise or</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Bitwise OR</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">bxor</cell> - <cell align="left" valign="middle">arithmetic bitwise xor</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Arithmetic bitwise XOR</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">bsl</cell> - <cell align="left" valign="middle">arithmetic bitshift left</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Arithmetic bitshift left</cell> + <cell align="left" valign="middle">Integer</cell> </row> <row> <cell align="left" valign="middle">bsr</cell> - <cell align="left" valign="middle">bitshift right</cell> - <cell align="left" valign="middle">integer</cell> + <cell align="left" valign="middle">Bitshift right</cell> + <cell align="left" valign="middle">Integer</cell> </row> <tcaption>Arithmetic Operators.</tcaption> </table> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>+1.</input> 1 @@ -697,28 +712,28 @@ Expr1 <input>op</input> Expr2</pre> Expr1 <input>op</input> Expr2</pre> <table> <row> - <cell align="left" valign="middle"><em>op</em></cell> + <cell align="left" valign="middle"><em>Operator</em></cell> <cell align="left" valign="middle"><em>Description</em></cell> </row> <row> <cell align="left" valign="middle">not</cell> - <cell align="left" valign="middle">unary logical not</cell> + <cell align="left" valign="middle">Unary logical NOT</cell> </row> <row> <cell align="left" valign="middle">and</cell> - <cell align="left" valign="middle">logical and</cell> + <cell align="left" valign="middle">Logical AND</cell> </row> <row> <cell align="left" valign="middle">or</cell> - <cell align="left" valign="middle">logical or</cell> + <cell align="left" valign="middle">Logical OR</cell> </row> <row> <cell align="left" valign="middle">xor</cell> - <cell align="left" valign="middle">logical xor</cell> + <cell align="left" valign="middle">Logical XOR</cell> </row> <tcaption>Logical Operators.</tcaption> </table> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>not true.</input> false @@ -737,28 +752,37 @@ true <pre> Expr1 orelse Expr2 Expr1 andalso Expr2</pre> - <p>Expressions where <c>Expr2</c> is evaluated only if - necessary. That is, <c>Expr2</c> is evaluated only if <c>Expr1</c> - evaluates to <c>false</c> in an <c>orelse</c> expression, or only - if <c>Expr1</c> evaluates to <c>true</c> in an <c>andalso</c> - expression. Returns either the value of <c>Expr1</c> (that is, + <p><c>Expr2</c> is evaluated only if + necessary. That is, <c>Expr2</c> is evaluated only if:</p> + <list type="bulleted"> + <item><p><c>Expr1</c> evaluates to <c>false</c> in an + <c>orelse</c> expression.</p> + </item> + </list> + <p>or</p> + <list type="bulleted"> + <item><p><c>Expr1</c> evaluates to <c>true</c> in an + <c>andalso</c> expression.</p> + </item> + </list> + <p>Returns either the value of <c>Expr1</c> (that is, <c>true</c> or <c>false</c>) or the value of <c>Expr2</c> - (if <c>Expr2</c> was evaluated).</p> + (if <c>Expr2</c> is evaluated).</p> - <p>Example 1:</p> + <p><em>Example 1:</em></p> <pre> case A >= -1.0 andalso math:sqrt(A+1) > B of</pre> - <p>This will work even if <c>A</c> is less than <c>-1.0</c>, + <p>This works even if <c>A</c> is less than <c>-1.0</c>, since in that case, <c>math:sqrt/1</c> is never evaluated.</p> - <p>Example 2:</p> + <p><em>Example 2:</em></p> <pre> OnlyOne = is_atom(L) orelse (is_list(L) andalso length(L) == 1),</pre> - <p>From R13A, <c>Expr2</c> is no longer required to evaluate to a - boolean value. As a consequence, <c>andalso</c> and <c>orelse</c> + <p>From Erlang/OTP R13A, <c>Expr2</c> is no longer required to evaluate to a + Boolean value. As a consequence, <c>andalso</c> and <c>orelse</c> are now tail-recursive. For instance, the following function is - tail-recursive in R13A and later:</p> + tail-recursive in Erlang/OTP R13A and later:</p> <pre> all(Pred, [Hd|Tail]) -> @@ -774,11 +798,11 @@ Expr1 ++ Expr2 Expr1 -- Expr2</pre> <p>The list concatenation operator <c>++</c> appends its second argument to its first and returns the resulting list.</p> - <p>The list subtraction operator <c>--</c> produces a list which - is a copy of the first argument, subjected to the following - procedure: for each element in the second argument, the first + <p>The list subtraction operator <c>--</c> produces a list that + is a copy of the first argument. The procedure is a follows: + for each element in the second argument, the first occurrence of this element (if any) is removed.</p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> 1> <input>[1,2,3]++[4,5].</input> [1,2,3,4,5] @@ -786,8 +810,8 @@ Expr1 -- Expr2</pre> [3,1,2]</pre> <warning><p>The complexity of <c>A -- B</c> is - proportional to <c>length(A)*length(B)</c>, meaning that it - will be very slow if both <c>A</c> and <c>B</c> are + proportional to <c>length(A)*length(B)</c>. That is, it + becomes very slow if both <c>A</c> and <c>B</c> are long lists.</p></warning> </section> @@ -802,7 +826,7 @@ Expr1 -- Expr2</pre> </p> <code>#{ K => V }</code> <p> - New maps may include multiple associations at construction by listing every + New maps can include multiple associations at construction by listing every association: </p> <code>#{ K1 => V1, .., Kn => Vn }</code> @@ -816,11 +840,11 @@ Expr1 -- Expr2</pre> </p> <p> Keys and values are separated by the <c>=></c> arrow and associations are - separated by <c>,</c>. + separated by a comma <c>,</c>. </p> <p> - Examples: + <em>Examples:</em> </p> <code> M0 = #{}, % empty map @@ -829,14 +853,14 @@ M2 = #{1 => 2, b => b}, % multiple associations with literals M3 = #{k => {A,B}}, % single association with variables M4 = #{{"w", 1} => f()}. % compound key associated with an evaluated expression</code> <p> - where, <c>A</c> and <c>B</c> are any expressions and <c>M0</c> through <c>M4</c> + Here, <c>A</c> and <c>B</c> are any expressions and <c>M0</c> through <c>M4</c> are the resulting map terms. </p> <p> - If two matching keys are declared, the latter key will take precedence. + If two matching keys are declared, the latter key takes precedence. </p> <p> - Example: + <em>Example:</em> </p> <pre> @@ -846,54 +870,57 @@ M4 = #{{"w", 1} => f()}. % compound key associated with an evaluated expression #{1 => b, 1.0 => a} </pre> <p> - The order in which the expressions constructing the keys and their - associated values are evaluated is not defined. The syntactic order of + The order in which the expressions constructing the keys (and their + associated values) are evaluated is not defined. The syntactic order of the key-value pairs in the construction is of no relevance, except in - the above mentioned case of two matching keys. + the recently mentioned case of two matching keys. </p> </section> <section> <title>Updating Maps</title> <p> - Updating a map has similar syntax as constructing it. + Updating a map has a similar syntax as constructing it. </p> <p> - An expression defining the map to be updated is put in front of the expression - defining the keys to be updated and their respective values. + An expression defining the map to be updated, is put in front of the expression + defining the keys to be updated and their respective values: </p> <code>M#{ K => V }</code> <p> - where <c>M</c> is a term of type map and <c>K</c> and <c>V</c> are any expression. + Here <c>M</c> is a term of type map and <c>K</c> and <c>V</c> are any expression. </p> <p> If key <c>K</c> does not match any existing key in the map, a new association - will be created from key <c>K</c> to value <c>V</c>. If key <c>K</c> matches - an existing key in map <c>M</c> its associated value will be replaced by the - new value <c>V</c>. In both cases the evaluated map expression will return a new map. + is created from key <c>K</c> to value <c>V</c>. + </p> + <p> If key <c>K</c> matches an existing key in map <c>M</c>, + its associated value + is replaced by the new value <c>V</c>. In both cases, the evaluated map expression + returns a new map. </p> <p> - If <c>M</c> is not of type map an exception of type <c>badmap</c> is thrown. + If <c>M</c> is not of type map, an exception of type <c>badmap</c> is thrown. </p> <p> - To only update an existing value, the following syntax is used, + To only update an existing value, the following syntax is used: </p> <code>M#{ K := V } </code> <p> - where <c>M</c> is an term of type map, <c>V</c> is an expression and <c>K</c> - is an expression which evaluates to an existing key in <c>M</c>. + Here <c>M</c> is a term of type map, <c>V</c> is an expression and <c>K</c> + is an expression that evaluates to an existing key in <c>M</c>. </p> <p> - If key <c>K</c> does not match any existing keys in map <c>M</c> an exception - of type <c>badarg</c> will be triggered at runtime. If a matching key <c>K</c> - is present in map <c>M</c> its associated value will be replaced by the new - value <c>V</c> and the evaluated map expression returns a new map. + If key <c>K</c> does not match any existing keys in map <c>M</c>, an exception + of type <c>badarg</c> is triggered at runtime. If a matching key <c>K</c> + is present in map <c>M</c>, its associated value is replaced by the new + value <c>V</c>, and the evaluated map expression returns a new map. </p> <p> - If <c>M</c> is not of type map an exception of type <c>badmap</c> is thrown. + If <c>M</c> is not of type map, an exception of type <c>badmap</c> is thrown. </p> <p> - Examples: + <em>Examples:</em> </p> <code> M0 = #{}, @@ -902,10 +929,10 @@ M2 = M1#{a => 1, b => 2}, M3 = M2#{"function" => fun() -> f() end}, M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code> <p> - where <c>M0</c> is any map. It follows that <c>M1 .. M4</c> are maps as well. + Here <c>M0</c> is any map. It follows that <c>M1 .. M4</c> are maps as well. </p> <p> - More Examples: + More <em>Examples:</em> </p> <pre> 1> <input>M = #{1 => a}.</input> @@ -921,83 +948,84 @@ M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code> As in construction, the order in which the key and value expressions are evaluated is not defined. The syntactic order of the key-value pairs in the update is of no - relevance, except in the case where two keys match, in which - case the latter value is used. + relevance, except in the case where two keys match. + In that case, the latter value is used. </p> </section> <section> <title>Maps in Patterns</title> <p> - Matching of key-value associations from maps is done in the following way: + Matching of key-value associations from maps is done as follows: </p> <code>#{ K := V } = M</code> <p> - where <c>M</c> is any map. The key <c>K</c> has to be an expression with bound - variables or a literals, and <c>V</c> can be any pattern with either bound or + Here <c>M</c> is any map. The key <c>K</c> must be an expression with bound + variables or literals. <c>V</c> can be any pattern with either bound or unbound variables. </p> <p> - If the variable <c>V</c> is unbound, it will be bound to the value associated - with the key <c>K</c>, which has to exist in the map <c>M</c>. If the variable - <c>V</c> is bound, it has to match the value associated with <c>K</c> in <c>M</c>. + If the variable <c>V</c> is unbound, it becomes bound to the value associated + with the key <c>K</c>, which must exist in the map <c>M</c>. If the variable + <c>V</c> is bound, it must match the value associated with <c>K</c> in <c>M</c>. </p> - <p> Example: </p> -<code> + <p><em>Example:</em></p> +<pre> 1> <input>M = #{"tuple" => {1,2}}.</input> #{"tuple" => {1,2}} 2> <input>#{"tuple" := {1,B}} = M.</input> #{"tuple" => {1,2}} 3> <input>B.</input> -2.</code> +2.</pre> <p> - This will bind variable <c>B</c> to integer <c>2</c>. + This binds variable <c>B</c> to integer <c>2</c>. </p> <p> - Similarly, multiple values from the map may be matched: + Similarly, multiple values from the map can be matched: </p> <code>#{ K1 := V1, .., Kn := Vn } = M</code> <p> - where keys <c>K1 .. Kn</c> are any expressions with literals or bound variables. If all - keys exist in map <c>M</c> all variables in <c>V1 .. Vn</c> will be matched to the + Here keys <c>K1 .. Kn</c> are any expressions with literals or bound variables. If all + keys exist in map <c>M</c>, all variables in <c>V1 .. Vn</c> is matched to the associated values of their respective keys. </p> <p> - If the matching conditions are not met, the match will fail, either with + If the matching conditions are not met, the match fails, either with: </p> <list> - <item> - a <c>badmatch</c> exception, if used in the context of the matching operator - as in the example, + <item><p>A <c>badmatch</c> exception.</p> + <p>This is if it is used in the context of the matching operator + as in the example.</p> </item> - <item> - or resulting in the next clause being tested in function heads and - case expressions. + <item><p>Or resulting in the next clause being tested in function heads and + case expressions.</p> </item> </list> <p> Matching in maps only allows for <c>:=</c> as delimiters of associations. + </p> + <p> The order in which keys are declared in matching has no relevance. </p> <p> - Duplicate keys are allowed in matching and will match each pattern associated - to the keys. + Duplicate keys are allowed in matching and match each pattern associated + to the keys: </p> <code>#{ K := V1, K := V2 } = M</code> <p> - Matching an expression against an empty map literal will match its type but - no variables will be bound: + Matching an expression against an empty map literal, matches its type but + no variables are bound: </p> <code>#{} = Expr</code> <p> - This expression will match if the expression <c>Expr</c> is of type map, otherwise - it will fail with an exception <c>badmatch</c>. + This expression matches if the expression <c>Expr</c> is of type map, otherwise + it fails with an exception <c>badmatch</c>. </p> <section> - <title>Matching syntax: Example with literals in function heads</title> + <title>Matching Syntax</title> <p> - Matching of literals as keys are allowed in function heads. + Matching of literals as keys are allowed in function heads: </p> <code> %% only start if not_started @@ -1014,17 +1042,19 @@ handle_call(change, From, #{ state := start } = S) -> <section> <title>Maps in Guards</title> <p> - Maps are allowed in guards as long as all sub-expressions are valid guard expressions. + Maps are allowed in guards as long as all subexpressions are valid guard expressions. </p> <p> - Two guard BIFs handles maps: + Two guard BIFs handle maps: </p> <list> <item> <seealso marker="erts:erlang#is_map/1">is_map/1</seealso> + in the <c>erlang</c> module </item> <item> <seealso marker="erts:erlang#map_size/1">map_size/1</seealso> + in the <c>erlang</c> module </item> </list> </section> @@ -1044,29 +1074,34 @@ Ei = Value | Value/TypeSpecifierList | Value:Size/TypeSpecifierList</pre> <p>Used in a bit string construction, <c>Value</c> is an expression - which should evaluate to an integer, float or bit string. If the - expression is something else than a single literal or variable, it - should be enclosed in parenthesis.</p> + that is to evaluate to an integer, float, or bit string. If the + expression is not a single literal or variable, it + is to be enclosed in parenthesis.</p> <p>Used in a bit string matching, <c>Value</c> must be a variable, - or an integer, float or string.</p> + or an integer, float, or string.</p> - <p>Note that, for example, using a string literal as in + <p>Notice that, for example, using a string literal as in <c><![CDATA[<<"abc">>]]></c> is syntactic sugar for <c><![CDATA[<<$a,$b,$c>>]]></c>.</p> <p>Used in a bit string construction, <c>Size</c> is an expression - which should evaluate to an integer.</p> + that is to evaluate to an integer.</p> - <p>Used in a bit string matching, <c>Size</c> must be an integer or a + <p>Used in a bit string matching, <c>Size</c> must be an integer, or a variable bound to an integer.</p> <p>The value of <c>Size</c> specifies the size of the segment in units (see below). The default value depends on the type (see - below). For <c>integer</c> it is 8, for - <c>float</c> it is 64, for <c>binary</c> and <c>bitstring</c> it is - the whole binary or bit string. In matching, this default value is only - valid for the very last element. All other bit string or binary + below):</p> + <list type="bulleted"> + <item>For <c>integer</c> it is 8.</item> + <item>For <c>float</c> it is 64.</item> + <item>For <c>binary</c> and <c>bitstring</c> it is + the whole binary or bit string.</item> + </list> + <p>In matching, this default value is only + valid for the last element. All other bit string or binary elements in the matching must have a size specification.</p> <p>For the <c>utf8</c>, <c>utf16</c>, and <c>utf32</c> types, @@ -1090,7 +1125,7 @@ Ei = Value | The default is <c>unsigned</c>.</item> <tag><c>Endianness</c>= <c>big</c> | <c>little</c> | <c>native</c></tag> - <item>Native-endian means that the endianness will be resolved at load + <item>Native-endian means that the endianness is resolved at load time to be either big-endian or little-endian, depending on what is native for the CPU that the Erlang machine is run on. Endianness only matters when the Type is either <c>integer</c>, @@ -1099,7 +1134,7 @@ Ei = Value | <tag><c>Unit</c>= <c>unit:IntegerLiteral</c></tag> <item>The allowed range is 1..256. Defaults to 1 for <c>integer</c>, - <c>float</c> and <c>bitstring</c>, and to 8 for <c>binary</c>. + <c>float</c>, and <c>bitstring</c>, and to 8 for <c>binary</c>. No unit specifier must be given for the types <c>utf8</c>, <c>utf16</c>, and <c>utf32</c>. </item> @@ -1110,8 +1145,8 @@ Ei = Value | <note><p>When constructing binaries, if the size <c>N</c> of an integer segment is too small to contain the given integer, the most significant - bits of the integer will be silently discarded and only the <c>N</c> least - significant bits will be put into the binary.</p></note> + bits of the integer are silently discarded and only the <c>N</c> least + significant bits are put into the binary.</p></note> <p>The types <c>utf8</c>, <c>utf16</c>, and <c>utf32</c> specifies encoding/decoding of the <em>Unicode Transformation Format</em>s UTF-8, UTF-16, @@ -1120,39 +1155,39 @@ Ei = Value | <p>When constructing a segment of a <c>utf</c> type, <c>Value</c> must be an integer in the range 0..16#D7FF or 16#E000....16#10FFFF. Construction - will fail with a <c>badarg</c> exception if <c>Value</c> is + fails with a <c>badarg</c> exception if <c>Value</c> is outside the allowed ranges. The size of the resulting binary - segment depends on the type and/or <c>Value</c>. For <c>utf8</c>, - <c>Value</c> will be encoded in 1 through 4 bytes. For - <c>utf16</c>, <c>Value</c> will be encoded in 2 or 4 - bytes. Finally, for <c>utf32</c>, <c>Value</c> will always be - encoded in 4 bytes.</p> + segment depends on the type or <c>Value</c>, or both:</p> + <list type="bulleted"> + <item>For <c>utf8</c>, <c>Value</c> is encoded in 1-4 bytes.</item> + <item>For <c>utf16</c>, <c>Value</c> is encoded in 2 or 4 bytes.</item> + <item>For <c>utf32</c>, <c>Value</c> is always be encoded in 4 bytes.</item> + </list> - <p>When constructing, a literal string may be given followed + <p>When constructing, a literal string can be given followed by one of the UTF types, for example: <c><![CDATA[<<"abc"/utf8>>]]></c> - which is syntatic sugar for + which is syntactic sugar for <c><![CDATA[<<$a/utf8,$b/utf8,$c/utf8>>]]></c>.</p> - <p>A successful match of a segment of a <c>utf</c> type results + <p>A successful match of a segment of a <c>utf</c> type, results in an integer in the range 0..16#D7FF or 16#E000..16#10FFFF. - The match will fail if returned value - would fall outside those ranges.</p> + The match fails if the returned value falls outside those ranges.</p> - <p>A segment of type <c>utf8</c> will match 1 to 4 bytes in the binary, + <p>A segment of type <c>utf8</c> matches 1-4 bytes in the binary, if the binary at the match position contains a valid UTF-8 sequence. (See RFC-3629 or the Unicode standard.)</p> - <p>A segment of type <c>utf16</c> may match 2 or 4 bytes in the binary. - The match will fail if the binary at the match position does not contain + <p>A segment of type <c>utf16</c> can match 2 or 4 bytes in the binary. + The match fails if the binary at the match position does not contain a legal UTF-16 encoding of a Unicode code point. (See RFC-2781 or the Unicode standard.)</p> - <p>A segment of type <c>utf32</c> may match 4 bytes in the binary in the - same way as an <c>integer</c> segment matching 32 bits. - The match will fail if the resulting integer is outside the legal ranges + <p>A segment of type <c>utf32</c> can match 4 bytes in the binary in the + same way as an <c>integer</c> segment matches 32 bits. + The match fails if the resulting integer is outside the legal ranges mentioned above.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>Bin1 = <<1,17,42>>.</input> <<1,17,42>> @@ -1181,11 +1216,13 @@ Ei = Value | 13> <input><<1024/utf8>>.</input> <<208,128>> </pre> - <p>Note that bit string patterns cannot be nested.</p> - <p>Note also that "<c><![CDATA[B=<<1>>]]></c>" is interpreted as + <p>Notice that bit string patterns cannot be nested.</p> + <p>Notice also that "<c><![CDATA[B=<<1>>]]></c>" is interpreted as "<c><![CDATA[B =<<1>>]]></c>" which is a syntax error. The correct way is to write a space after '=': "<c><![CDATA[B= <<1>>]]></c>.</p> - <p>More examples can be found in <em>Programming Examples</em>.</p> + <p>More examples are provided in + <seealso marker="doc/programming_examples:bit_syntax"> + Programming Examples</seealso>.</p> </section> <section> @@ -1200,16 +1237,16 @@ fun BodyK end</pre> <p>A fun expression begins with the keyword <c>fun</c> and ends - with the keyword <c>end</c>. Between them should be a function + with the keyword <c>end</c>. Between them is to be a function declaration, similar to a <seealso marker="functions#syntax">regular function declaration</seealso>, - except that the function name is optional and should be a variable if + except that the function name is optional and is to be a variable, if any.</p> <p>Variables in a fun head shadow the function name and both shadow - variables in the function clause surrounding the fun expression, and - variables bound in a fun body are local to the fun body.</p> + variables in the function clause surrounding the fun expression. + Variables bound in a fun body are local to the fun body.</p> <p>The return value of the expression is the resulting fun.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>Fun1 = fun (X) -> X+1 end.</input> #Fun<erl_eval.6.39074546> @@ -1232,15 +1269,17 @@ fun Module:Name/Arity</pre> syntactic sugar for:</p> <pre> fun (Arg1,...,ArgN) -> Name(Arg1,...,ArgN) end</pre> - <p>In <c>Module:Name/Arity</c>, <c>Module</c> and <c>Name</c> are atoms - and <c>Arity</c> is an integer. Starting from the R15 release, - <c>Module</c>, <c>Name</c>, and <c>Arity</c> may also be variables. - A fun defined in this way will refer to the function <c>Name</c> + <p>In <c>Module:Name/Arity</c>, <c>Module</c>, and <c>Name</c> are atoms + and <c>Arity</c> is an integer. Starting from Erlang/OTP R15, + <c>Module</c>, <c>Name</c>, and <c>Arity</c> can also be variables. + A fun defined in this way refers to the function <c>Name</c> with arity <c>Arity</c> in the <em>latest</em> version of module - <c>Module</c>. A fun defined in this way will not be dependent on - the code for module in which it is defined. + <c>Module</c>. A fun defined in this way is not dependent on + the code for the module in which it is defined. </p> - <p>More examples can be found in <em>Programming Examples</em>.</p> + <p>More examples are provided in + <seealso marker="doc/programming_examples:funs"> + Programming Examples</seealso>.</p> </section> <section> @@ -1250,23 +1289,26 @@ fun (Arg1,...,ArgN) -> Name(Arg1,...,ArgN) end</pre> catch Expr</code> <p>Returns the value of <c>Expr</c> unless an exception occurs during the evaluation. In that case, the exception is - caught. For exceptions of class <c>error</c>, - that is run-time errors: <c>{'EXIT',{Reason,Stack}}</c> - is returned. For exceptions of class <c>exit</c>, that is - the code called <c>exit(Term)</c>: <c>{'EXIT',Term}</c> is returned. - For exceptions of class <c>throw</c>, that is - the code called <c>throw(Term)</c>: <c>Term</c> is returned.</p> + caught.</p> + <p>For exceptions of class <c>error</c>, that is, + run-time errors, + <c>{'EXIT',{Reason,Stack}}</c> is returned.</p> + <p>For exceptions of class <c>exit</c>, that is, + the code called <c>exit(Term)</c>, + <c>{'EXIT',Term}</c> is returned.</p> + <p>For exceptions of class <c>throw</c>, that is + the code called <c>throw(Term)</c>, + <c>Term</c> is returned.</p> <p><c>Reason</c> depends on the type of error that occurred, and <c>Stack</c> is the stack of recent function calls, see - <seealso marker="errors#exit_reasons">Errors and Error Handling</seealso>.</p> - <p>Examples:</p> - <p></p> + <seealso marker="errors#exit_reasons">Exit Reasons</seealso>.</p> + <p><em>Examples:</em></p> <pre> 1> <input>catch 1+2.</input> 3 2> <input>catch 1+a.</input> {'EXIT',{badarith,[...]}}</pre> - <p>Note that <c>catch</c> has low precedence and catch + <p>Notice that <c>catch</c> has low precedence and catch subexpressions often needs to be enclosed in a block expression or in parenthesis:</p> <pre> @@ -1275,13 +1317,14 @@ catch Expr</code> 4> <input>A = (catch 1+2).</input> 3</pre> <p>The BIF <c>throw(Any)</c> can be used for non-local return from - a function. It must be evaluated within a <c>catch</c>, which will - return the value <c>Any</c>. Example:</p> + a function. It must be evaluated within a <c>catch</c>, which + returns the value <c>Any</c>.</p> + <p><em>Example:</em></p> <pre> 5> <input>catch throw(hello).</input> hello</pre> <p>If <c>throw/1</c> is not evaluated within a catch, a - <c>nocatch</c> run-time error will occur.</p> + <c>nocatch</c> run-time error occurs.</p> </section> <section> @@ -1297,14 +1340,17 @@ catch end</code> <p>This is an enhancement of <seealso marker="#catch">catch</seealso> that appeared in - Erlang 5.4/OTP-R10B. It gives the possibility do distinguish - between different exception classes, and to choose to handle only - the desired ones, passing the others on to an enclosing - <c>try</c> or <c>catch</c> or to default error handling.</p> - <p>Note that although the keyword <c>catch</c> is used in + Erlang 5.4/OTP R10B. It gives the possibility to:</p> + <list type="bulleted"> + <item>Distinguish between different exception classes.</item> + <item>Choose to handle only the desired ones.</item> + <item>Passing the others on to an enclosing + <c>try</c> or <c>catch</c>, or to default error handling.</item> + </list> + <p>Notice that although the keyword <c>catch</c> is used in the <c>try</c> expression, there is not a <c>catch</c> expression within the <c>try</c> expression.</p> - <p>Returns the value of <c>Exprs</c> (a sequence of expressions + <p>It returns the value of <c>Exprs</c> (a sequence of expressions <c>Expr1, ..., ExprN</c>) unless an exception occurs during the evaluation. In that case the exception is caught and the patterns <c>ExceptionPattern</c> with the right exception @@ -1318,7 +1364,7 @@ end</code> <c>Class</c> with a true guard sequence, the exception is passed on as if <c>Exprs</c> had not been enclosed in a <c>try</c> expression.</p> - <p>If an exception occurs during evaluation of <c>ExceptionBody</c> + <p>If an exception occurs during evaluation of <c>ExceptionBody</c>, it is not caught.</p> <p>The <c>try</c> expression can have an <c>of</c> section: @@ -1341,7 +1387,7 @@ end</code> the patterns <c>Pattern</c> are sequentially matched against the result in the same way as for a <seealso marker="#case">case</seealso> expression, except that if - the matching fails, a <c>try_clause</c> run-time error will occur.</p> + the matching fails, a <c>try_clause</c> run-time error occurs.</p> <p>An exception occurring during the evaluation of <c>Body</c> is not caught.</p> <p>The <c>try</c> expression can also be augmented with an @@ -1364,7 +1410,7 @@ after AfterBody end</code> <p><c>AfterBody</c> is evaluated after either <c>Body</c> or - <c>ExceptionBody</c> no matter which one. The evaluated value of + <c>ExceptionBody</c>, no matter which one. The evaluated value of <c>AfterBody</c> is lost; the return value of the <c>try</c> expression is the same with an <c>after</c> section as without.</p> <p>Even if an exception occurs during evaluation of <c>Body</c> or @@ -1373,13 +1419,13 @@ end</code> evaluated, so the exception from the <c>try</c> expression is the same with an <c>after</c> section as without.</p> <p>If an exception occurs during evaluation of <c>AfterBody</c> - itself it is not caught, so if <c>AfterBody</c> is evaluated after - an exception in <c>Exprs</c>, <c>Body</c> or <c>ExceptionBody</c>, + itself, it is not caught. So if <c>AfterBody</c> is evaluated after + an exception in <c>Exprs</c>, <c>Body</c>, or <c>ExceptionBody</c>, that exception is lost and masked by the exception in <c>AfterBody</c>.</p> - <p>The <c>of</c>, <c>catch</c> and <c>after</c> sections are all + <p>The <c>of</c>, <c>catch</c>, and <c>after</c> sections are all optional, as long as there is at least a <c>catch</c> or an - <c>after</c> section, so the following are valid <c>try</c> + <c>after</c> section. So the following are valid <c>try</c> expressions:</p> <code type="none"> try Exprs of @@ -1398,9 +1444,9 @@ after end try Exprs after AfterBody end</code> - <p>Example of using <c>after</c>, this code will close the file + <p>Next is an example of using <c>after</c>. This closes the file, even in the event of exceptions in <c>file:read/2</c> or in - <c>binary_to_term/1</c>, and exceptions will be the same as + <c>binary_to_term/1</c>. The exceptions are the same as without the <c>try</c>...<c>after</c>...<c>end</c> expression:</p> <code type="none"> termize_file(Name) -> @@ -1411,7 +1457,7 @@ termize_file(Name) -> after file:close(F) end.</code> - <p>Example: Using <c>try</c> to emulate <c>catch Expr</c>.</p> + <p>Next is an example of using <c>try</c> to emulate <c>catch Expr</c>:</p> <code type="none"> try Expr catch @@ -1427,7 +1473,7 @@ end</code> (Expr)</pre> <p>Parenthesized expressions are useful to override <seealso marker="#prec">operator precedences</seealso>, - for example in arithmetic expressions:</p> + for example, in arithmetic expressions:</p> <pre> 1> <input>1 + 2 * 3.</input> 7 @@ -1451,7 +1497,7 @@ end</pre> <section> <marker id="lcs"></marker> <title>List Comprehensions</title> - <p>List comprehensions are a feature of many modern functional + <p>List comprehensions is a feature of many modern functional programming languages. Subject to certain rules, they provide a succinct notation for generating elements in a list.</p> <p>List comprehensions are analogous to set comprehensions in @@ -1461,32 +1507,34 @@ end</pre> <p>List comprehensions are written with the following syntax:</p> <pre> [Expr || Qualifier1,...,QualifierN]</pre> - <p><c>Expr</c> is an arbitrary expression, and each + <p>Here, <c>Expr</c> is an arbitrary expression, and each <c>Qualifier</c> is either a generator or a filter.</p> <list type="bulleted"> <item>A <em>generator</em> is written as: <br></br> <c><![CDATA[Pattern <- ListExpr]]></c>. <br></br> -<c>ListExpr</c> must be an expression which evaluates to a +<c>ListExpr</c> must be an expression, which evaluates to a list of terms.</item> <item>A <em>bit string generator</em> is written as: <br></br> <c><![CDATA[BitstringPattern <= BitStringExpr]]></c>. <br></br> -<c>BitStringExpr</c> must be an expression which evaluates to a +<c>BitStringExpr</c> must be an expression, which evaluates to a bitstring.</item> - <item>A <em>filter</em> is an expression which evaluates to + <item>A <em>filter</em> is an expression, which evaluates to <c>true</c> or <c>false</c>.</item> </list> - <p>The variables in the generator patterns shadow variables in the function - clause surrounding the list comprehensions.</p> <p>A list comprehension + <p>The variables in the generator patterns, shadow variables in the function + clause, surrounding the list comprehensions.</p> <p>A list comprehension returns a list, where the elements are the result of evaluating <c>Expr</c> for each combination of generator list elements and bit string generator - elements for which all filters are true.</p> <p></p> <p>Example:</p> + elements, for which all filters are true.</p> + <p><em>Example:</em></p> <pre> 1> <input>[X*2 || X <- [1,2,3]].</input> [2,4,6]</pre> - <p>More examples can be found in <em>Programming Examples</em>.</p> - + <p>More examples are provoded in + <seealso marker="doc/programming_examples:list_comprehensions"> + Programming Examples.</seealso></p> </section> @@ -1500,34 +1548,35 @@ end</pre> the following syntax:</p> <pre> << BitString || Qualifier1,...,QualifierN >></pre> - <p><c>BitString</c> is a bit string expression, and each + <p>Here, <c>BitString</c> is a bit string expression and each <c>Qualifier</c> is either a generator, a bit string generator or a filter.</p> <list type="bulleted"> <item>A <em>generator</em> is written as: <br></br> <c><![CDATA[Pattern <- ListExpr]]></c>. <br></br> - <c>ListExpr</c> must be an expression which evaluates to a + <c>ListExpr</c> must be an expression that evaluates to a list of terms.</item> <item>A <em>bit string generator</em> is written as: <br></br> <c><![CDATA[BitstringPattern <= BitStringExpr]]></c>. <br></br> -<c>BitStringExpr</c> must be an expression which evaluates to a +<c>BitStringExpr</c> must be an expression that evaluates to a bitstring.</item> - <item>A <em>filter</em> is an expression which evaluates to + <item>A <em>filter</em> is an expression that evaluates to <c>true</c> or <c>false</c>.</item> </list> - <p>The variables in the generator patterns shadow variables in - the function clause surrounding the bit string comprehensions.</p> + <p>The variables in the generator patterns, shadow variables in + the function clause, surrounding the bit string comprehensions.</p> <p>A bit string comprehension returns a bit string, which is created by concatenating the results of evaluating <c>BitString</c> - for each combination of bit string generator elements for which all + for each combination of bit string generator elements, for which all filters are true.</p> - <p></p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> -1> <input><< << (X*2) >> || +1> <input><< << (X*2) >> || <<X>> <= << 1,2,3 >> >>.</input> <<2,4,6>></pre> - <p>More examples can be found in <em>Programming Examples</em>.</p> + <p>More examples are provided in + <seealso marker="doc/programming_examples:bit_syntax"> + Programming Examples.</seealso></p> </section> <section> @@ -1536,27 +1585,27 @@ end</pre> <p>A <em>guard sequence</em> is a sequence of guards, separated by semicolon (;). The guard sequence is true if at least one of - the guards is true. (The remaining guards, if any, will not be - evaluated.)<br></br> -<c>Guard1;...;GuardK</c></p> + the guards is true. (The remaining guards, if any, are not + evaluated.)</p> + <p><c>Guard1;...;GuardK</c></p> <p>A <em>guard</em> is a sequence of guard expressions, separated by comma (,). The guard is true if all guard expressions - evaluate to <c>true</c>.<br></br> -<c>GuardExpr1,...,GuardExprN</c></p> + evaluate to <c>true</c>.</p> + <p><c>GuardExpr1,...,GuardExprN</c></p> <p>The set of valid <em>guard expressions</em> (sometimes called guard tests) is a subset of the set of valid Erlang expressions. The reason for restricting the set of valid expressions is that evaluation of a guard expression must be guaranteed to be free - of side effects. Valid guard expressions are:</p> + of side effects. Valid guard expressions are the following:</p> <list type="bulleted"> - <item>the atom <c>true</c>,</item> - <item>other constants (terms and bound variables), all regarded - as false,</item> - <item>calls to the BIFs specified below,</item> - <item>term comparisons,</item> - <item>arithmetic expressions,</item> - <item>boolean expressions, and</item> - <item>short-circuit expressions (<c>andalso</c>/<c>orelse</c>).</item> + <item>The atom <c>true</c></item> + <item>Other constants (terms and bound variables), all regarded + as false</item> + <item>Calls to the BIFs specified in table <c>Type Test BIFs</c></item> + <item>Term comparisons</item> + <item>Arithmetic expressions</item> + <item>Boolean expressions</item> + <item>Short-circuit expressions (<c>andalso</c>/<c>orelse</c>)</item> </list> <table> <row> @@ -1610,13 +1659,13 @@ end</pre> <row> <cell align="left" valign="middle"><c>is_tuple/1</c></cell> </row> - <tcaption>Type Test BIFs.</tcaption> + <tcaption>Type Test BIFs</tcaption> </table> - <p>Note that most type test BIFs have older equivalents, without + <p>Notice that most type test BIFs have older equivalents, without the <c>is_</c> prefix. These old BIFs are retained for backwards - compatibility only and should not be used in new code. They are + compatibility only and are not to be used in new code. They are also only allowed at top level. For example, they are not allowed - in boolean expressions in guards.</p> + in Boolean expressions in guards.</p> <table> <row> <cell align="left" valign="middle"><c>abs(Number)</c></cell> @@ -1666,14 +1715,14 @@ end</pre> <row> <cell align="left" valign="middle"><c>tuple_size(Tuple)</c></cell> </row> - <tcaption>Other BIFs Allowed in Guard Expressions.</tcaption> + <tcaption>Other BIFs Allowed in Guard Expressions</tcaption> </table> - <p>If an arithmetic expression, a boolean expression, a + <p>If an arithmetic expression, a Boolean expression, a short-circuit expression, or a call to a guard BIF fails (because of invalid arguments), the entire guard fails. If the guard was part of a guard sequence, the next guard in the sequence (that is, - the guard following the next semicolon) will be evaluated.</p> + the guard following the next semicolon) is evaluated.</p> </section> @@ -1726,12 +1775,13 @@ end</pre> <cell align="left" valign="middle">catch</cell> <cell align="left" valign="middle"> </cell> </row> - <tcaption>Operator Precedence.</tcaption> + <tcaption>Operator Precedence</tcaption> </table> <p>When evaluating an expression, the operator with the highest priority is evaluated first. Operators with the same priority - are evaluated according to their associativity. Example: - The left associative arithmetic operators are evaluated left to + are evaluated according to their associativity.</p> + <p><em>Example:</em></p> + <p>The left associative arithmetic operators are evaluated left to right:</p> <pre> <input>6 + 5 * 4 - 3 / 2</input> evaluates to diff --git a/system/doc/reference_manual/functions.xml b/system/doc/reference_manual/functions.xml index 9498ef1402..8cf4da1b8b 100644 --- a/system/doc/reference_manual/functions.xml +++ b/system/doc/reference_manual/functions.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,7 +38,7 @@ clause body, separated by <c>-></c>.</p> <p>A clause <em>head</em> consists of the function name, an argument list, and an optional guard sequence - beginning with the keyword <c>when</c>.</p> + beginning with the keyword <c>when</c>:</p> <pre> Name(Pattern11,...,Pattern1N) [when GuardSeq1] -> Body1; @@ -48,9 +48,9 @@ Name(PatternK1,...,PatternKN) [when GuardSeqK] -> <p>The function name is an atom. Each argument is a pattern.</p> <p>The number of arguments <c>N</c> is the <em>arity</em> of the function. A function is uniquely defined by the module name, - function name and arity. That is, two functions with the same + function name, and arity. That is, two functions with the same name and in the same module, but with different arities are two - completely different functions.</p> + different functions.</p> <p>A function named <c>f</c> in the module <c>m</c> and with arity <c>N</c> is often denoted as <c>m:f/N</c>.</p> <p>A clause <em>body</em> consists of a sequence of expressions @@ -60,8 +60,8 @@ Expr1, ..., ExprN</pre> <p>Valid Erlang expressions and guard sequences are described in - <seealso marker="expressions">Erlang Expressions</seealso>.</p> - <p>Example:</p> + <seealso marker="expressions">Expressions</seealso>.</p> + <p><em>Example:</em></p> <pre> fact(N) when N>0 -> % first clause head N * fact(N-1); % first clause body @@ -75,23 +75,23 @@ fact(0) -> % second clause head <title>Function Evaluation</title> <p>When a function <c>m:f/N</c> is called, first the code for the function is located. If the function cannot be found, an - <c>undef</c> run-time error will occur. Note that the function + <c>undef</c> runtime error occurs. Notice that the function must be exported to be visible outside the module it is defined in.</p> <p>If the function is found, the function clauses are scanned - sequentially until a clause is found that fulfills the following - two conditions:</p> + sequentially until a clause is found that fulfills both of + the following two conditions:</p> <list type="ordered"> - <item>the patterns in the clause head can be successfully - matched against the given arguments, and</item> - <item>the guard sequence, if any, is true.</item> + <item>The patterns in the clause head can be successfully + matched against the given arguments.</item> + <item>The guard sequence, if any, is true.</item> </list> <p>If such a clause cannot be found, a <c>function_clause</c> - run-time error will occur.</p> + runtime error occurs.</p> <p>If such a clause is found, the corresponding clause body is evaluated. That is, the expressions in the body are evaluated sequentially and the value of the last expression is returned.</p> - <p>Example: Consider the function <c>fact</c>:</p> + <p>Consider the function <c>fact</c>:</p> <pre> -module(m). -export([fact/1]). @@ -100,17 +100,17 @@ fact(N) when N>0 -> N * fact(N-1); fact(0) -> 1.</pre> - <p>Assume we want to calculate factorial for 1:</p> + <p>Assume that you want to calculate the factorial for 1:</p> <pre> 1> <input>m:fact(1).</input></pre> <p>Evaluation starts at the first clause. The pattern <c>N</c> is - matched against the argument 1. The matching succeeds and - the guard (<c>N>0</c>) is true, thus <c>N</c> is bound to 1 and + matched against argument 1. The matching succeeds and + the guard (<c>N>0</c>) is true, thus <c>N</c> is bound to 1, and the corresponding body is evaluated:</p> <pre> <input>N * fact(N-1)</input> => (N is bound to 1) <input>1 * fact(0)</input></pre> - <p>Now <c>fact(0)</c> is called and the function clauses are + <p>Now, <c>fact(0)</c> is called, and the function clauses are scanned sequentially again. First, the pattern <c>N</c> is matched against 0. The matching succeeds, but the guard (<c>N>0</c>) is false. Second, the pattern 0 is matched against @@ -121,48 +121,51 @@ fact(0) -> <input>1</input></pre> <p>Evaluation has succeed and <c>m:fact(1)</c> returns 1.</p> <p>If <c>m:fact/1</c> is called with a negative number as - argument, no clause head will match. A <c>function_clause</c> - run-time error will occur.</p> + argument, no clause head matches. A <c>function_clause</c> + runtime error occurs.</p> </section> <section> <title>Tail recursion</title> <p>If the last expression of a function body is a function call, - a <em>tail recursive</em> call is done so that no system - resources for example call stack are consumed. This means - that an infinite loop can be done if it uses tail recursive + a <em>tail recursive</em> call is done. + This is to ensure that no system + resources, for example, call stack, are consumed. This means + that an infinite loop can be done if it uses tail-recursive calls.</p> - <p>Example:</p> + <p><em>Example:</em></p> <pre> loop(N) -> io:format("~w~n", [N]), loop(N+1).</pre> - <p>As a counter-example see the factorial example above - that is not tail recursive since a multiplication is done + <p>The earlier factorial example can act as a counter-example. + It is not tail-recursive, since a multiplication is done on the result of the recursive call to <c>fact(N-1)</c>.</p> </section> <section> - <title>Built-In Functions, BIFs</title> - <p><em>Built-in functions</em>, BIFs, are implemented in C code in - the runtime system and do things that are difficult or impossible - to implement in Erlang. Most of the built-in functions belong - to the module <c>erlang</c> but there are also built-in functions + <title>Built-In Functions (BIFs)</title> + <p>BIFs are implemented in C code in + the runtime system. BIFs do things that are difficult or impossible + to implement in Erlang. Most of the BIFs belong + to the module <c>erlang</c> but there are also BIFs belonging to a few other modules, for example <c>lists</c> and <c>ets</c>.</p> - <p>The most commonly used BIFs belonging to <c>erlang</c> are - <em>auto-imported</em>, they do not need to be prefixed with - the module name. Which BIFs are auto-imported is specified in - <c>erlang(3)</c>. For example, standard type conversion BIFs like + <p>The most commonly used BIFs belonging to <c>erlang(3)</c> are + <em>auto-imported</em>. They do not need to be prefixed with + the module name. Which BIFs that are auto-imported is specified in the + <seealso marker="erts:erlang">erlang(3)</seealso> module in ERTS. + For example, standard-type conversion BIFs like <c>atom_to_list</c> and BIFs allowed in guards can be called - without specifying the module name. Examples:</p> + without specifying the module name.</p> + <p><em>Examples:</em></p> <pre> 1> <input>tuple_size({a,b,c}).</input> 3 2> <input>atom_to_list('Erlang').</input> "Erlang"</pre> - <p>Note that normally it is the set of auto-imported built-in - functions that is referred to when talking about 'BIFs'.</p> + <p>Notice that it is normally the set of auto-imported BIFs + that are referred to when talking about 'BIFs'.</p> </section> </chapter> diff --git a/system/doc/reference_manual/introduction.xml b/system/doc/reference_manual/introduction.xml index 36bec17825..ee8b82e60f 100644 --- a/system/doc/reference_manual/introduction.xml +++ b/system/doc/reference_manual/introduction.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2014</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,20 +28,38 @@ <rev></rev> <file>introduction.xml</file> </header> + <marker id="erlang ref manual"></marker> + + <p>This section is the Erlang reference manual. It describes the + Erlang programming language. </p> <section> <title>Purpose</title> - <p>This reference manual describes the Erlang programming - language. The focus is on the language itself, not - the implementation. The language constructs are described in - text and with examples rather than formally specified, with - the intention to make the manual more readable. - The manual is not intended as a tutorial.</p> - <p>Information about this implementation of Erlang can be found, for - example, in <em>System Principles</em> (starting and stopping, - boot scripts, code loading, error logging, creating target - systems), <em>Efficiency Guide</em> (memory consumption, system - limits) and <em>ERTS User's Guide</em> (crash dumps, drivers).</p> + <p>The focus of the Erlang reference manual is on the language itself, + not the implementation of it. The language constructs are described in + text and with examples rather than formally specified. This is + to make the manual more readable. + The Erlang reference manual is not intended as a tutorial.</p> + <p>Information about implementation of Erlang can, for example, be found, + in the following:</p> + <list type="bulleted"> + <item><p><seealso marker="doc/system_principles:system_principles"> + System Principles</seealso></p> + <p>Starting and stopping, boot scripts, code loading, + <seealso marker="doc/system_principles:error_logging"> + error logging</seealso>, + <seealso marker="doc/system_principles:create_target"> + creating target systems</seealso></p> + </item> + <item><p><seealso marker="doc/efficiency_guide:advanced"> + Efficiency Guide</seealso></p> + <p>Memory consumption, system limits</p> + </item> + <item><p>ERTS User's Guide</p> + <p><seealso marker="erts:crash_dump">Crash dumps</seealso>, + <seealso marker="erts:driver">drivers</seealso></p> + </item> + </list> </section> <section> @@ -53,13 +71,13 @@ <section> <title>Document Conventions</title> - <p>In the document, the following terminology is used:</p> + <p>In this section, the following terminology is used:</p> <list type="bulleted"> <item>A <em>sequence</em> is one or more items. For example, a clause body consists of a sequence of expressions. This means that there must be at least one expression.</item> <item>A <em>list</em> is any number of items. For example, - an argument list can consist of zero, one or more arguments.</item> + an argument list can consist of zero, one, or more arguments.</item> </list> <p>If a feature has been added recently, in Erlang 5.0/OTP R7 or later, this is mentioned in the text.</p> @@ -68,15 +86,16 @@ <section> <title>Complete List of BIFs</title> <p>For a complete list of BIFs, their arguments and return values, - refer to <c>erlang(3)</c>.</p> + see <seealso marker="erts:erlang#process_flag/2">erlang(3)</seealso> + manual page in ERTS.</p> </section> <section> <title>Reserved Words</title> <p>The following are reserved words in Erlang:</p> - <p>after and andalso band begin bnot bor bsl bsr bxor case catch + <p><c>after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse receive rem try - when xor</p> + when xor</c></p> </section> </chapter> diff --git a/system/doc/reference_manual/macros.xml b/system/doc/reference_manual/macros.xml index 9fd0b0f287..01994aae5e 100644 --- a/system/doc/reference_manual/macros.xml +++ b/system/doc/reference_manual/macros.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,7 +21,7 @@ </legalnotice> - <title>The Preprocessor</title> + <title>Preprocessor</title> <prepared></prepared> <docno></docno> <date></date> @@ -31,17 +31,17 @@ <section> <title>File Inclusion</title> - <p>A file can be included in the following way:</p> + <p>A file can be included as follows:</p> <pre> -include(File). -include_lib(File).</pre> - <p><c>File</c>, a string, should point out a file. The contents of - this file are included as-is, at the position of the directive.</p> + <p><c>File</c>, a string, is to point out a file. The contents of + this file are included as is, at the position of the directive.</p> <p>Include files are typically used for record and macro definitions that are shared by several modules. It is - recommended that the file name extension <c>.hrl</c> be used - for include files.</p> - <p><c>File</c> may start with a path component <c>$VAR</c>, for + recommended to use the file name extension <c>.hrl</c> for + include files.</p> + <p><c>File</c> can start with a path component <c>$VAR</c>, for some string <c>VAR</c>. If that is the case, the value of the environment variable <c>VAR</c> as returned by <c>os:getenv(VAR)</c> is substituted for <c>$VAR</c>. If @@ -49,21 +49,29 @@ as is.</p> <p>If the filename <c>File</c> is absolute (possibly after variable substitution), the include file with that name is - included. Otherwise, the specified file is searched for in - the current working directory, in the same directory as - the module being compiled, and in the directories given by - the <c>include</c> option, in that order. - See <c>erlc(1)</c> and <c>compile(3)</c> for details.</p> - <p>Examples:</p> + included. Otherwise, the specified file is searched for + in the following directories, and in this order:</p> + <list type="ordered"> + <item>The current working directory</item> + <item>The directory where the module is being compiled</item> + <item>The directories given by the <c>include</c> option</item> + </list> + <p>For details, see the + <seealso marker="erts:erlc">erlc(1)</seealso> manual page + in ERTS and + <seealso marker="compiler:compile">compile(3)</seealso> + manual page in Compiler.</p> + <p><em>Examples:</em></p> <pre> -include("my_records.hrl"). -include("incdir/my_records.hrl"). -include("/home/user/proj/my_records.hrl"). -include("$PROJ_ROOT/my_records.hrl").</pre> - <p><c>include_lib</c> is similar to <c>include</c>, but should not + <p><c>include_lib</c> is similar to <c>include</c>, but is not to point out an absolute file. Instead, the first path component (possibly after variable substitution) is assumed to be - the name of an application. Example:</p> + the name of an application.</p> + <p><em>Example:</em></p> <pre> -include_lib("kernel/include/file.hrl").</pre> <p>The code server uses <c>code:lib_dir(kernel)</c> to find @@ -74,7 +82,7 @@ <section> <title>Defining and Using Macros</title> - <p>A macro is defined the following way:</p> + <p>A macro is defined as follows:</p> <code type="none"> -define(Const, Replacement). -define(Func(Var1,...,VarN), Replacement).</code> @@ -83,33 +91,34 @@ come before any usage of the macro.</p> <p>If a macro is used in several modules, it is recommended that the macro definition is placed in an include file.</p> - <p>A macro is used the following way:</p> + <p>A macro is used as follows:</p> <code type="none"> ?Const ?Func(Arg1,...,ArgN)</code> <p>Macros are expanded during compilation. A simple macro - <c>?Const</c> will be replaced with <c>Replacement</c>. - Example:</p> + <c>?Const</c> is replaced with <c>Replacement</c>.</p> + <p><em>Example:</em></p> <code type="none"> -define(TIMEOUT, 200). ... call(Request) -> server:call(refserver, Request, ?TIMEOUT).</code> - <p>This will be expanded to:</p> + <p>This is expanded to:</p> <code type="none"> call(Request) -> server:call(refserver, Request, 200).</code> - <p>A macro <c>?Func(Arg1,...,ArgN)</c> will be replaced with + <p>A macro <c>?Func(Arg1,...,ArgN)</c> is replaced with <c>Replacement</c>, where all occurrences of a variable <c>Var</c> from the macro definition are replaced with the corresponding - argument <c>Arg</c>. Example:</p> + argument <c>Arg</c>.</p> + <p><em>Example:</em></p> <code type="none"> -define(MACRO1(X, Y), {a, X, b, Y}). ... bar(X) -> ?MACRO1(a, b), ?MACRO1(X, 123)</code> - <p>This will be expanded to:</p> + <p>This is expanded to:</p> <code type="none"> bar(X) -> {a,a,b,b}, @@ -154,7 +163,7 @@ bar(X) -> -define(F0(), c). -define(F1(A), A). -define(C, m:f).</code> - <p>the following will not work:</p> + <p>the following does not work:</p> <code type="none"> f0() -> ?F0. % No, an empty list of arguments expected. @@ -165,7 +174,7 @@ f1(A) -> <code> f() -> ?C().</code> - <p>will expand to</p> + <p>is expanded to</p> <code> f() -> m:f().</code> @@ -185,7 +194,7 @@ f() -> defined.</item> <tag><c>-else.</c></tag> <item>Only allowed after an <c>ifdef</c> or <c>ifndef</c> - directive. If that condition was false, the lines following + directive. If that condition is false, the lines following <c>else</c> are evaluated instead.</item> <tag><c>-endif.</c></tag> <item>Specifies the end of an <c>ifdef</c> or <c>ifndef</c> @@ -194,7 +203,7 @@ f() -> <note> <p>The macro directives cannot be used inside functions.</p> </note> - <p>Example:</p> + <p><em>Example:</em></p> <code type="none"> -module(m). ... @@ -206,7 +215,7 @@ f() -> -endif. ...</code> - <p>When trace output is desired, <c>debug</c> should be defined + <p>When trace output is desired, <c>debug</c> is to be defined when the module <c>m</c> is compiled:</p> <pre> % <input>erlc -Ddebug m.erl</input> @@ -215,18 +224,18 @@ or 1> <input>c(m, {d, debug}).</input> {ok,m}</pre> - <p><c>?LOG(Arg)</c> will then expand to a call to <c>io:format/2</c> + <p><c>?LOG(Arg)</c> is then expanded to a call to <c>io:format/2</c> and provide the user with some simple trace output.</p> </section> <section> <title>Stringifying Macro Arguments</title> <p>The construction <c>??Arg</c>, where <c>Arg</c> is a macro - argument, will be expanded to a string containing the tokens of + argument, is expanded to a string containing the tokens of the argument. This is similar to the <c>#arg</c> stringifying construction in C.</p> <p>The feature was added in Erlang 5.0/OTP R7.</p> - <p>Example:</p> + <p><em>Example:</em></p> <code type="none"> -define(TESTCALL(Call), io:format("Call ~s: ~w~n", [??Call, Call])). @@ -236,7 +245,7 @@ or <code type="none"> io:format("Call ~s: ~w~n",["myfunction ( 1 , 2 )",myfunction(1,2)]), io:format("Call ~s: ~w~n",["you : function ( 2 , 1 )",you:function(2,1)]).</code> - <p>That is, a trace output with both the function called and + <p>That is, a trace output, with both the function called and the resulting value.</p> </section> </chapter> diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml index 5cb0c11371..39c739a146 100644 --- a/system/doc/reference_manual/modules.xml +++ b/system/doc/reference_manual/modules.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2014</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,7 +33,8 @@ <title>Module Syntax</title> <p>Erlang code is divided into <em>modules</em>. A module consists of a sequence of attributes and function declarations, each - terminated by period (.). Example:</p> + terminated by period (.).</p> + <p><em>Example:</em></p> <pre> -module(m). % module attribute -export([fact/1]). % module attribute @@ -42,50 +43,52 @@ fact(N) when N>0 -> % beginning of function declaration N * fact(N-1); % | fact(0) -> % | 1. % end of function declaration</pre> - <p>See the <seealso marker="functions">Functions</seealso> chapter - for a description of function declarations.</p> + <p>For a description of function declarations, see + <seealso marker="functions">Function Declaration Syntax</seealso>.</p> </section> <section> <title>Module Attributes</title> <p>A <em>module attribute</em> defines a certain property of a - module. A module attribute consists of a tag and a value.</p> + module.</p> + <p>A module attribute consists of a tag and a value:</p> <pre> -Tag(Value).</pre> <p><c>Tag</c> must be an atom, while <c>Value</c> must be a literal term. As a convenience in user-defined attributes, if the literal term <c>Value</c> has the syntax <c>Name/Arity</c> (where <c>Name</c> is an atom and <c>Arity</c> a positive integer), - the term <c>Name/Arity</c> will be translated to <c>{Name,Arity}</c>.</p> + the term <c>Name/Arity</c> is translated to <c>{Name,Arity}</c>.</p> <p>Any module attribute can be specified. The attributes are stored in the compiled code and can be retrieved by calling - <c>Module:module_info(attributes)</c> or by using - <seealso marker="stdlib:beam_lib#chunks/2">beam_lib(3)</seealso>.</p> + <c>Module:module_info(attributes)</c>, or by using the module + <seealso marker="stdlib:beam_lib#chunks/2">beam_lib(3)</seealso> + in STDLIB.</p> - <p>There are several module attributes with predefined meanings, - some of which have arity two, but user-defined module + <p>Several module attributes have predefined meanings. + Some of them have arity two, but user-defined module attributes must have arity one.</p> <section> <title>Pre-Defined Module Attributes</title> - <p>Pre-defined module attributes should be placed before any + <p>Pre-defined module attributes is to be placed before any function declaration.</p> <taglist> <tag><c>-module(Module).</c></tag> <item> <p>Module declaration, defining the name of the module. - The name <c>Module</c>, an atom, should be the same as - the file name minus the extension <c>erl</c>. Otherwise - <seealso marker="code_loading#loading">code loading</seealso> will + The name <c>Module</c>, an atom, is to be same as + the file name minus the extension <c>.erl</c>. Otherwise + <seealso marker="code_loading#loading">code loading</seealso> does not work as intended.</p> - <p>This attribute should be specified first and is the only - attribute which is mandatory.</p> + <p>This attribute is to be specified first and is the only + mandatory attribute.</p> </item> <tag><c>-export(Functions).</c></tag> <item> - <p>Exported functions. Specifies which of the functions - defined within the module that are visible outside + <p>Exported functions. Specifies which of the functions, + defined within the module, that are visible from outside the module.</p> <p><c>Functions</c> is a list <c>[Name1/Arity1, ..., NameN/ArityN]</c>, where each @@ -93,32 +96,37 @@ fact(0) -> % | </item> <tag><c>-import(Module,Functions).</c></tag> <item> - <p>Imported functions. Imported functions can be called - the same way as local functions, that is without any module + <p>Imported functions. Can be called + the same way as local functions, that is, without any module prefix.</p> <p><c>Module</c>, an atom, specifies which module to import functions from. <c>Functions</c> is a list similar as for - <c>export</c> above.</p> + <c>export</c>.</p> </item> <tag><c>-compile(Options).</c></tag> <item> - <p>Compiler options. <c>Options</c>, which is a single option - or a list of options, will be added to the option list when - compiling the module. See <c>compile(3)</c>.</p> + <p>Compiler options. <c>Options</c> is a single option + or a list of options. + This attribute is added to the option list when + compiling the module. See the <seealso marker="compiler:compile"> + compile(3)</seealso> manual page in Compiler.</p> </item> <tag><c>-vsn(Vsn).</c></tag> <item> <p>Module version. <c>Vsn</c> is any literal term and can be - retrieved using <c>beam_lib:version/1</c>, see - <seealso marker="stdlib:beam_lib#version/1">beam_lib(3)</seealso>.</p> + retrieved using <c>beam_lib:version/1</c>, see the + <seealso marker="stdlib:beam_lib#version/1">beam_lib(3)</seealso> + manual page in STDLIB.</p> <p>If this attribute is not specified, the version defaults to the MD5 checksum of the module.</p> </item> <tag><c>-on_load(Function).</c></tag> <item> - <p>Names a function that should be run automatically when a - module a loaded. See <seealso marker="code_loading#on_load"> - code loading</seealso> for more information.</p> + <p>This attribute names a function that is to be run + automatically when a + module is loaded. For more information, see + <seealso marker="code_loading#on_load"> + Running a Function When a Module is Loaded</seealso>.</p> </item> </taglist> </section> @@ -130,9 +138,14 @@ fact(0) -> % | <pre> -behaviour(Behaviour).</pre> <p>The atom <c>Behaviour</c> gives the name of the behaviour, - which can be a user defined behaviour or one of the OTP - standard behaviours <c>gen_server</c>, <c>gen_fsm</c>, - <c>gen_event</c> or <c>supervisor</c>.</p> + which can be a user-defined behaviour or one of the following OTP + standard behaviours:</p> + <list type="bulleted"> + <item><c>gen_server</c></item> + <item><c>gen_fsm</c></item> + <item><c>gen_event</c></item> + <item><c>supervisor</c></item> + </list> <p>The spelling <c>behavior</c> is also accepted.</p> <p>The callback functions of the module can be specified either directly by the exported function <c>behaviour_info/1</c>:</p> @@ -142,7 +155,7 @@ behaviour_info(callbacks) -> Callbacks.</pre> function:</p> <pre> -callback Name(Arguments) -> Result.</pre> - <p>where <c>Arguments</c> is a list of zero or more arguments. + <p>Here, <c>Arguments</c> is a list of zero or more arguments. The <c>-callback</c> attribute is to be preferred since the extra type information can be used by tools to produce documentation or find discrepancies.</p> @@ -153,7 +166,7 @@ behaviour_info(callbacks) -> Callbacks.</pre> <section> <title>Record Definitions</title> - <p>The same syntax as for module attributes is used by + <p>The same syntax as for module attributes is used for record definitions:</p> <pre> -record(Record,Fields).</pre> @@ -163,7 +176,7 @@ behaviour_info(callbacks) -> Callbacks.</pre> </section> <section> - <title>The Preprocessor</title> + <title>Preprocessor</title> <p>The same syntax as for module attributes is used by the preprocessor, which supports file inclusion, macros, and conditional compilation:</p> @@ -171,7 +184,7 @@ behaviour_info(callbacks) -> Callbacks.</pre> -include("SomeFile.hrl"). -define(Macro,Replacement).</pre> - <p>Read more in <seealso marker="macros">The Preprocessor</seealso>.</p> + <p>Read more in <seealso marker="macros">Preprocessor</seealso>.</p> </section> <section> @@ -180,17 +193,17 @@ behaviour_info(callbacks) -> Callbacks.</pre> changing the pre-defined macros <c>?FILE</c> and <c>?LINE</c>:</p> <pre> -file(File, Line).</pre> - <p>This attribute is used by tools such as Yecc to inform the - compiler that the source program was generated by another tool - and indicates the correspondence of source files to lines of - the original user-written file from which the source program - was produced.</p> + <p>This attribute is used by tools, such as Yecc, to inform the + compiler that the source program is generated by another tool. + It also indicates the correspondence of source files to lines of + the original user-written file, from which the source program + is produced.</p> </section> <section> <title>Types and function specifications</title> <p>A similar syntax as for module attributes is used for - specifying types and function specifications. + specifying types and function specifications: </p> <pre> -type my_type() :: atom() | integer(). @@ -200,32 +213,36 @@ behaviour_info(callbacks) -> Callbacks.</pre> <p> The description is based on <url href="http://www.erlang.org/eeps/eep-0008.html">EEP8 - - Types and function specifications</url> - which will not be further updated. + Types and function specifications</url>, + which is not to be further updated. </p> </section> </section> <section> <title>Comments</title> - <p>Comments may be placed anywhere in a module except within strings - and quoted atoms. The comment begins with the character "%", + <p>Comments can be placed anywhere in a module except within strings + and quoted atoms. A comment begins with the character "%", continues up to, but does not include the next end-of-line, and - has no effect. Note that the terminating end-of-line has + has no effect. Notice that the terminating end-of-line has the effect of white space.</p> </section> <section> - <title>The module_info/0 and module_info/1 functions</title> + <title>module_info/0 and module_info/1 functions</title> <p>The compiler automatically inserts the two special, exported - functions into each module: <c>Module:module_info/0</c> and - <c>Module:module_info/1</c>. These functions can be called to - retrieve information about the module.</p> + functions into each module:</p> + <list type="bulleted"> + <item><c>Module:module_info/0</c></item> + <item><c>Module:module_info/1</c></item> + </list> + <p>These functions can be called to retrieve information + about the module.</p> <section> <title>module_info/0</title> - <p>The <c>module_info/0</c> function in each module returns + <p>The <c>module_info/0</c> function in each module, returns a list of <c>{Key,Value}</c> tuples with information about the module. Currently, the list contain tuples with the following <c>Key</c>s: <c>module</c>, <c>attributes</c>, <c>compile</c>, @@ -235,7 +252,7 @@ behaviour_info(callbacks) -> Callbacks.</pre> <section> <title>module_info/1</title> - <p>The call <c>module_info(Key)</c>, where key is an atom, + <p>The call <c>module_info(Key)</c>, where <c>Key</c> is an atom, returns a single piece of information about the module.</p> <p>The following values are allowed for <c>Key</c>:</p> @@ -243,44 +260,46 @@ behaviour_info(callbacks) -> Callbacks.</pre> <taglist> <tag><c>module</c></tag> <item> - <p>Return an atom representing the module name.</p> + <p>Returns an atom representing the module name.</p> </item> <tag><c>attributes</c></tag> <item> - <p>Return a list of <c>{AttributeName,ValueList}</c> tuples, + <p>Returns a list of <c>{AttributeName,ValueList}</c> tuples, where <c>AttributeName</c> is the name of an attribute, - and <c>ValueList</c> is a list of values. Note: a given - attribute may occur more than once in the list with different + and <c>ValueList</c> is a list of values. Notice that a given + attribute can occur more than once in the list with different values if the attribute occurs more than once in the module.</p> - <p>The list of attributes will be empty if - the module has been stripped with - <seealso marker="stdlib:beam_lib#strip/1">beam_lib(3)</seealso>.</p> + <p>The list of attributes becomes empty if + the module is stripped with the + <seealso marker="stdlib:beam_lib#strip/1">beam_lib(3)</seealso> + module (in STDLIB).</p> </item> <tag><c>compile</c></tag> <item> - <p>Return a list of tuples containing information about - how the module was compiled. This list will be empty if - the module has been stripped with - <seealso marker="stdlib:beam_lib#strip/1">beam_lib(3)</seealso>.</p> + <p>Returns a list of tuples with information about + how the module was compiled. This list is empty if + the module has been stripped with the + <seealso marker="stdlib:beam_lib#strip/1">beam_lib(3)</seealso> + module (in STDLIB).</p> </item> <tag><c>md5</c></tag> <item> - <p>Return a binary representing the MD5 checksum of the module.</p> + <p>Returns a binary representing the MD5 checksum of the module.</p> </item> <tag><c>exports</c></tag> <item> - <p>Return a list of <c>{Name,Arity}</c> tuples with + <p>Returns a list of <c>{Name,Arity}</c> tuples with all exported functions in the module.</p> </item> <tag><c>functions</c></tag> <item> - <p>Return a list of <c>{Name,Arity}</c> tuples with + <p>Returns a list of <c>{Name,Arity}</c> tuples with all functions in the module.</p> </item> </taglist> diff --git a/system/doc/reference_manual/patterns.xml b/system/doc/reference_manual/patterns.xml index 1611002fa1..2163583636 100644 --- a/system/doc/reference_manual/patterns.xml +++ b/system/doc/reference_manual/patterns.xml @@ -40,7 +40,7 @@ <seealso marker="expressions#term">term</seealso>. If the matching succeeds, any unbound variables in the pattern become bound. If the matching fails, a run-time error occurs.</p> - <p>Examples:</p> + <p><em>Examples:</em></p> <pre> 1> <input>X.</input> ** 1: variable 'X' is unbound ** diff --git a/system/doc/reference_manual/ports.xml b/system/doc/reference_manual/ports.xml index 621af10624..e5dc99641b 100644 --- a/system/doc/reference_manual/ports.xml +++ b/system/doc/reference_manual/ports.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2013</year> + <year>2004</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,9 +28,12 @@ <rev></rev> <file>ports.xml</file> </header> - <p>Examples of how to use ports and port drivers can be found in - <em>Interoperability Tutorial</em>. The BIFs mentioned are as usual - documented in <c>erlang(3)</c>.</p> + <p>Examples of how to use ports and port drivers are provided in + <seealso marker="doc/tutorial:introduction#interoperability tutorial"> + Interoperability Tutorial</seealso>. + For information about the BIFs mentioned, see the + <seealso marker="erts:erlang">erlang(3)</seealso> manual + page in ERTS.</p> <section> <title>Ports</title> @@ -39,29 +42,34 @@ provide a byte-oriented interface to an external program. When a port has been created, Erlang can communicate with it by sending and receiving lists of bytes, including binaries.</p> - <p>The Erlang process which creates a port is said to be + <p>The Erlang process creating a port is said to be the <em>port owner</em>, or the <em>connected process</em> of - the port. All communication to and from the port should go via - the port owner. If the port owner terminates, so will the port + the port. All communication to and from the port must go through + the port owner. If the port owner terminates, so does the port (and the external program, if it is written correctly).</p> <p>The external program resides in another OS process. By default, - it should read from standard input (file descriptor 0) and write + it reads from standard input (file descriptor 0) and writes to standard output (file descriptor 1). The external program - should terminate when the port is closed.</p> + is to terminate when the port is closed.</p> </section> <section> <title>Port Drivers</title> - <p>It is also possible to write a driver in C according to certain + <p>It is possible to write a driver in C according to certain principles and dynamically link it to the Erlang runtime system. The linked-in driver looks like a port from the Erlang programmer's point of view and is called a <em>port driver</em>.</p> <warning> - <p>An erroneous port driver will cause the entire Erlang runtime + <p>An erroneous port driver causes the entire Erlang runtime system to leak memory, hang or crash.</p> </warning> - <p>Port drivers are documented in <c>erl_driver(4)</c>, - <c>driver_entry(1)</c> and <c>erl_ddll(3)</c>.</p> + <p>For information about port drivers, see the + <seealso marker="erts:erl_driver">erl_driver(4)</seealso> + manual page in ERTS, + <seealso marker="erts:driver_entry">driver_entry(1)</seealso> + manual page in ERTS, and + <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso> + manual page in Kernel.</p> </section> <section> @@ -70,53 +78,74 @@ <table> <row> <cell align="left" valign="middle"><c>open_port(PortName, PortSettings</c></cell> - <cell align="left" valign="middle">Returns a port identifier <c>Port</c>as the result of opening a new Erlang port. Messages can be sent to and received from a port identifier, just like a pid. Port identifiers can also be linked to or registered under a name using <c>link/1</c>and <c>register/2</c>.</cell> + <cell align="left" valign="middle">Returns a port identifier + <c>Port</c> as the result of opening a new Erlang port. + Messages can be sent to, and received from, a port identifier, + just like a pid. Port identifiers can also be linked to + using <c>link/1</c>, or registered under a name using + <c>register/2</c>.</cell> </row> - <tcaption>Port Creation BIF.</tcaption> + <tcaption>Port Creation BIF</tcaption> </table> <p><c>PortName</c> is usually a tuple <c>{spawn,Command}</c>, where the string <c>Command</c> is the name of the external program. - The external program runs outside the Erlang workspace unless a - port driver with the name <c>Command</c> is found. If found, that - driver is started.</p> + The external program runs outside the Erlang workspace, unless a + port driver with the name <c>Command</c> is found. If <c>Command</c> + is found, that driver is started.</p> <p><c>PortSettings</c> is a list of settings (options) for the port. - The list typically contains at least a tuple <c>{packet,N}</c> + The list typically contains at least a tuple <c>{packet,N}</c>, which specifies that data sent between the port and the external program are preceded by an N-byte length indicator. Valid values - for N are 1, 2 or 4. If binaries should be used instead of lists + for N are 1, 2, or 4. If binaries are to be used instead of lists of bytes, the option <c>binary</c> must be included.</p> <p>The port owner <c>Pid</c> can communicate with the port <c>Port</c> by sending and receiving messages. (In fact, any process can send the messages to the port, but the port owner must be identified in the message).</p> - <p>As of OTP-R16 messages sent to ports are delivered truly + <p>As of Erlang/OTP R16, messages sent to ports are delivered truly asynchronously. The underlying implementation previously delivered messages to ports synchronously. Message passing has - however always been documented as an asynchronous operation, so - this should not be an issue for an Erlang program communicating - with ports, unless false assumptions about ports has been made.</p> - <p>Below, <c>Data</c> must be an I/O list. An I/O list is a binary - or a (possibly deep) list of binaries or integers in the range - 0..255.</p> + however always been documented as an asynchronous operation. Hence, + this is not to be an issue for an Erlang program communicating + with ports, unless false assumptions about ports have been made.</p> + <p>In the following tables of examples, <c>Data</c> must be an I/O list. + An I/O list is a binary or a (possibly deep) list of binaries + or integers in the range 0..255:</p> <table> <row> + <cell align="left" valign="middle"><em>Message</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>{Pid,{command,Data}}</c></cell> - <cell align="left" valign="middle">Sends <c>Data</c>to the port.</cell> + <cell align="left" valign="middle">Sends <c>Data</c> to the port.</cell> </row> <row> <cell align="left" valign="middle"><c>{Pid,close}</c></cell> - <cell align="left" valign="middle">Closes the port. Unless the port is already closed, the port replies with <c>{Port,closed}</c>when all buffers have been flushed and the port really closes.</cell> + <cell align="left" valign="middle">Closes the port. Unless the + port is already closed, the port replies with + <c>{Port,closed}</c> when all buffers have been flushed + and the port really closes.</cell> </row> <row> <cell align="left" valign="middle"><c>{Pid,{connect,NewPid}}</c></cell> - <cell align="left" valign="middle">Sets the port owner of <c>Port</c>to <c>NewPid</c>. Unless the port is already closed, the port replies with<c>{Port,connected}</c>to the old port owner. Note that the old port owner is still linked to the port, but the new port owner is not.</cell> + <cell align="left" valign="middle">Sets the port owner of + <c>Port</c>to <c>NewPid</c>. Unless the port is already closed, + the port replies with<c>{Port,connected}</c> to the old + port owner. Note that the old port owner is still linked + to the port, but the new port owner is not.</cell> </row> - <tcaption>Messages Sent To a Port.</tcaption> + <tcaption>Messages Sent To a Port</tcaption> </table> + <p></p> <table> <row> + <cell align="left" valign="middle"><em>Message</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>{Port,{data,Data}}</c></cell> - <cell align="left" valign="middle"><c>Data</c>is received from the external program.</cell> + <cell align="left" valign="middle"><c>Data</c> is received from the external program.</cell> </row> <row> <cell align="left" valign="middle"><c>{Port,closed}</c></cell> @@ -124,20 +153,24 @@ </row> <row> <cell align="left" valign="middle"><c>{Port,connected}</c></cell> - <cell align="left" valign="middle">Reply to <c>Port ! {Pid,{connect,NewPid}}</c></cell> + <cell align="left" valign="middle">Reply to <c>Port ! {Pid,{connect,NewPid}}</c>.</cell> </row> <row> <cell align="left" valign="middle"><c>{'EXIT',Port,Reason}</c></cell> <cell align="left" valign="middle">If the port has terminated for some reason.</cell> </row> - <tcaption>Messages Received From a Port.</tcaption> + <tcaption>Messages Received From a Port</tcaption> </table> <p>Instead of sending and receiving messages, there are also a - number of BIFs that can be used.</p> + number of BIFs that can be used:</p> <table> <row> + <cell align="left" valign="middle"><em>Port BIF</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>port_command(Port,Data)</c></cell> - <cell align="left" valign="middle">Sends <c>Data</c>to the port.</cell> + <cell align="left" valign="middle">Sends <c>Data</c> to the port.</cell> </row> <row> <cell align="left" valign="middle"><c>port_close(Port)</c></cell> @@ -145,7 +178,10 @@ </row> <row> <cell align="left" valign="middle"><c>port_connect(Port,NewPid)</c></cell> - <cell align="left" valign="middle">Sets the port owner of <c>Port</c>to <c>NewPid</c>. The old port owner <c>Pid</c>stays linked to the port and have to call <c>unlink(Port)</c>if this is not desired.</cell> + <cell align="left" valign="middle">Sets the port owner of + <c>Port</c>to <c>NewPid</c>. The old port owner <c>Pid</c> + stays linked to the port and must call <c>unlink(Port)</c> + if this is not desired.</cell> </row> <row> <cell align="left" valign="middle"><c>erlang:port_info(Port,Item)</c></cell> @@ -155,9 +191,9 @@ <cell align="left" valign="middle"><c>erlang:ports()</c></cell> <cell align="left" valign="middle">Returns a list of all ports on the current node.</cell> </row> - <tcaption>Port BIFs.</tcaption> + <tcaption>Port BIFs</tcaption> </table> - <p>There are some additional BIFs that only apply to port drivers: + <p>Some additional BIFs that apply to port drivers: <c>port_control/3</c> and <c>erlang:port_call/3</c>.</p> </section> </chapter> diff --git a/system/doc/reference_manual/processes.xml b/system/doc/reference_manual/processes.xml index 95ae0672ec..d8474c163c 100644 --- a/system/doc/reference_manual/processes.xml +++ b/system/doc/reference_manual/processes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,8 +32,8 @@ <section> <title>Processes</title> <p>Erlang is designed for massive concurrency. Erlang processes are - light-weight (grow and shrink dynamically) with small memory - footprint, fast to create and terminate and the scheduling + lightweight (grow and shrink dynamically) with small memory + footprint, fast to create and terminate, and the scheduling overhead is low.</p> </section> @@ -46,10 +46,10 @@ spawn(Module, Name, Args) -> pid() Args = [Arg1,...,ArgN] ArgI = term()</pre> <p><c>spawn</c> creates a new process and returns the pid.</p> - <p>The new process will start executing in - <c>Module:Name(Arg1,...,ArgN)</c> where the arguments is + <p>The new process starts executing in + <c>Module:Name(Arg1,...,ArgN)</c> where the arguments are the elements of the (possible empty) <c>Args</c> argument list.</p> - <p>There exist a number of other <c>spawn</c> BIFs, for example + <p>There exist a number of other <c>spawn</c> BIFs, for example, <c>spawn/4</c> for spawning a process at another node.</p> </section> @@ -60,18 +60,25 @@ spawn(Module, Name, Args) -> pid() atom and is automatically unregistered if the process terminates:</p> <table> <row> + <cell align="left" valign="middle"><em>BIF</em></cell> + <cell align="left" valign="middle"><em>Description</em></cell> + </row> + <row> <cell align="left" valign="middle"><c>register(Name, Pid)</c></cell> <cell align="left" valign="middle">Associates the name <c>Name</c>, an atom, with the process <c>Pid</c>.</cell> </row> <row> <cell align="left" valign="middle"><c>registered()</c></cell> - <cell align="left" valign="middle">Returns a list of names which have been registered using<c>register/2</c>.</cell> + <cell align="left" valign="middle">Returns a list of names that + have been registered using <c>register/2</c>.</cell> </row> <row> <cell align="left" valign="middle"><c>whereis(Name)</c></cell> - <cell align="left" valign="middle">Returns the pid registered under <c>Name</c>, or<c>undefined</c>if the name is not registered.</cell> + <cell align="left" valign="middle">Returns the pid registered + under <c>Name</c>, or <c>undefined </c>if the name is not + registered.</cell> </row> - <tcaption>Name Registration BIFs.</tcaption> + <tcaption>Name Registration BIFs</tcaption> </table> </section> @@ -79,22 +86,27 @@ spawn(Module, Name, Args) -> pid() <marker id="term"></marker> <title>Process Termination</title> <p>When a process terminates, it always terminates with an - <em>exit reason</em>. The reason may be any term.</p> + <em>exit reason</em>. The reason can be any term.</p> <p>A process is said to terminate <em>normally</em>, if the exit reason is the atom <c>normal</c>. A process with no more code to execute terminates normally.</p> - <p>A process terminates with exit reason <c>{Reason,Stack}</c> + <p>A process terminates with an exit reason <c>{Reason,Stack}</c> when a run-time error occurs. See - <seealso marker="errors#exit_reasons">Error and Error Handling</seealso>.</p> - <p>A process can terminate itself by calling one of the BIFs - <c>exit(Reason)</c>, - <c>erlang:error(Reason)</c>, <c>erlang:error(Reason, Args)</c>, - <c>erlang:fault(Reason)</c> or <c>erlang:fault(Reason, Args)</c>. - The process then terminates with reason <c>Reason</c> for + <seealso marker="errors#exit_reasons">Exit Reasons</seealso>.</p> + <p>A process can terminate itself by calling one of the + following BIFs:</p> + <list type="bulleted"> + <item><c>exit(Reason)</c></item> + <item><c>erlang:error(Reason)</c></item> + <item><c>erlang:error(Reason, Args)</c></item> + <item><c>erlang:fault(Reason)</c></item> + <item><c>erlang:fault(Reason, Args)</c></item> + </list> + <p>The process then terminates with reason <c>Reason</c> for <c>exit/1</c> or <c>{Reason,Stack} for the others</c>.</p> - <p>A process may also be terminated if it receives an exit signal + <p>A process can also be terminated if it receives an exit signal with another exit reason than <c>normal</c>, see - <seealso marker="#errors">Error Handling</seealso> below.</p> + <seealso marker="#errors">Error Handling</seealso>.</p> </section> <section> @@ -113,35 +125,39 @@ spawn(Module, Name, Args) -> pid() <title>Links</title> <p>Two processes can be <em>linked</em> to each other. A link between two processes <c>Pid1</c> and <c>Pid2</c> is created - by <c>Pid1</c> calling the BIF <c>link(Pid2)</c> (or vice versa). + by <c>Pid1</c> calling the BIF <c>link(Pid2)</c> (or conversely). There also exist a number of <c>spawn_link</c> BIFs, which spawn and link to a process in one operation.</p> <p>Links are bidirectional and there can only be one link between two processes. Repeated calls to <c>link(Pid)</c> have no effect.</p> <p>A link can be removed by calling the BIF <c>unlink(Pid)</c>.</p> <p>Links are used to monitor the behaviour of other processes, see - <seealso marker="#errors">Error Handling</seealso> below.</p> + <seealso marker="#errors">Error Handling</seealso>.</p> </section> <section> <marker id="errors"></marker> <title>Error Handling</title> <p>Erlang has a built-in feature for error handling between - processes. Terminating processes will emit exit signals to all - linked processes, which may terminate as well or handle the exit + processes. Terminating processes emit exit signals to all + linked processes, which can terminate as well or handle the exit in some way. This feature can be used to build hierarchical program structures where some processes are supervising other - processes, for example restarting them if they terminate + processes, for example, restarting them if they terminate abnormally.</p> - <p>Refer to OTP Design Principles for more information about - OTP supervision trees, which uses this feature.</p> + <p>See <seealso marker= + "doc/design_principles:des_princ#otp design principles"> + OTP Design Principles</seealso> for more information about + OTP supervision trees, which use this feature.</p> <section> <title>Emitting Exit Signals</title> - <p>When a process terminates, it will terminate with an <em>exit reason</em> as explained in <seealso marker="#term">Process Termination</seealso> above. This exit reason is emitted in + <p>When a process terminates, it terminates with an + <em>exit reason</em> as explained in <seealso marker="#term"> + Process Termination</seealso>. This exit reason is emitted in an <em>exit signal</em> to all linked processes.</p> <p>A process can also call the function <c>exit(Pid,Reason)</c>. - This will result in an exit signal with exit reason + This results in an exit signal with exit reason <c>Reason</c> being emitted to <c>Pid</c>, but does not affect the calling process.</p> </section> @@ -156,14 +172,14 @@ spawn(Module, Name, Args) -> pid() <p>A process can be set to trap exit signals by calling:</p> <pre> process_flag(trap_exit, true)</pre> - <p>When a process is trapping exits, it will not terminate when + <p>When a process is trapping exits, it does not terminate when an exit signal is received. Instead, the signal is transformed - into a message <c>{'EXIT',FromPid,Reason}</c> which is put into - the mailbox of the process just like a regular message.</p> + into a message <c>{'EXIT',FromPid,Reason}</c>, which is put into + the mailbox of the process, just like a regular message.</p> <p>An exception to the above is if the exit reason is <c>kill</c>, - that is if <c>exit(Pid,kill)</c> has been called. This will - unconditionally terminate the process, regardless of if it is - trapping exit signals or not.</p> + that is if <c>exit(Pid,kill)</c> has been called. This + unconditionally terminates the process, regardless of if it is + trapping exit signals.</p> </section> </section> @@ -180,12 +196,12 @@ process_flag(trap_exit, true)</pre> <p>If <c>Pid2</c> does not exist, the 'DOWN' message is sent immediately with <c>Reason</c> set to <c>noproc</c>.</p> <p>Monitors are unidirectional. Repeated calls to - <c>erlang:monitor(process, Pid)</c> will create several, - independent monitors and each one will send a 'DOWN' message when + <c>erlang:monitor(process, Pid)</c> creates several + independent monitors, and each one sends a 'DOWN' message when <c>Pid</c> terminates.</p> <p>A monitor can be removed by calling <c>erlang:demonitor(Ref)</c>.</p> - <p>It is possible to create monitors for processes with registered + <p>Monitors can be created for processes with registered names, also at other nodes.</p> </section> diff --git a/system/doc/reference_manual/records.xml b/system/doc/reference_manual/records.xml index 04766531df..72c56b693d 100644 --- a/system/doc/reference_manual/records.xml +++ b/system/doc/reference_manual/records.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,16 +32,19 @@ elements. It has named fields and is similar to a struct in C. Record expressions are translated to tuple expressions during compilation. Therefore, record expressions are not understood by - the shell unless special actions are taken. See <c>shell(3)</c> - for details.</p> - <p>More record examples can be found in <em>Programming Examples</em>.</p> + the shell unless special actions are taken. For details, see the + <seealso marker="stdlib:shell">shell(3)</seealso> + manual page in STDLIB.</p> + <p>More examples are provided in + <seealso marker="doc/programming_examples:records"> + Programming Examples</seealso>.</p> <section> <title>Defining Records</title> <p>A record definition consists of the name of the record, followed by the field names of the record. Record and field names must be atoms. Each field can be given an optional default value. - If no default value is supplied, <c>undefined</c> will be used.</p> + If no default value is supplied, <c>undefined</c> is used.</p> <pre> -record(Name, {Field1 [= Value1], ... @@ -60,17 +63,18 @@ the corresponding expression <c>ExprI</c>:</p> <pre> #Name{Field1=Expr1,...,FieldK=ExprK}</pre> - <p>The fields may be in any order, not necessarily the same order as + <p>The fields can be in any order, not necessarily the same order as in the record definition, and fields can be omitted. Omitted - fields will get their respective default value instead.</p> - <p>If several fields should be assigned the same value, + fields get their respective default value instead.</p> + <p>If several fields are to be assigned the same value, the following construction can be used:</p> <pre> #Name{Field1=Expr1,...,FieldK=ExprK, _=ExprL}</pre> - <p>Omitted fields will then get the value of evaluating <c>ExprL</c> + <p>Omitted fields then get the value of evaluating <c>ExprL</c> instead of their default values. This feature was added in Erlang 5.1/OTP R8 and is primarily intended to be used to create - patterns for ETS and Mnesia match functions. Example:</p> + patterns for ETS and Mnesia match functions.</p> + <p><em>Example:</em></p> <pre> -record(person, {name, phone, address}). @@ -84,13 +88,13 @@ lookup(Name, Tab) -> <title>Accessing Record Fields</title> <pre> Expr#Name.Field</pre> - <p>Returns the value of the specified field. <c>Expr</c> should + <p>Returns the value of the specified field. <c>Expr</c> is to evaluate to a <c>Name</c> record.</p> <p>The following expression returns the position of the specified field in the tuple representation of the record:</p> <pre> #Name.Field</pre> - <p>Example:</p> + <p><em>Example:</em></p> <pre> -record(person, {name, phone, address}). @@ -104,8 +108,8 @@ lookup(Name, List) -> <title>Updating Records</title> <pre> Expr#Name{Field1=Expr1,...,FieldK=ExprK}</pre> - <p><c>Expr</c> should evaluate to a <c>Name</c> record. Returns a - copy of this record, with the value of each specified field + <p><c>Expr</c> is to evaluate to a <c>Name</c> record. A + copy of this record is returned, with the value of each specified field <c>FieldI</c> changed to the value of evaluating the corresponding expression <c>ExprI</c>. All other fields retain their old values.</p> @@ -116,17 +120,17 @@ Expr#Name{Field1=Expr1,...,FieldK=ExprK}</pre> <title>Records in Guards</title> <p>Since record expressions are expanded to tuple expressions, creating records and accessing record fields are allowed in - guards. However all subexpressions, for example for field - initiations, must of course be valid guard expressions as well. - Examples:</p> + guards. However all subexpressions, for example, for field + initiations, must be valid guard expressions as well.</p> + <p><em>Examples:</em></p> <code type="none"> handle(Msg, State) when Msg==#msg{to=void, no=3} -> ... handle(Msg, State) when State#state.running==true -> ...</code> - <p>There is also a type test BIF <c>is_record(Term, RecordTag)</c>. - Example:</p> + <p>There is also a type test BIF <c>is_record(Term, RecordTag)</c>.</p> + <p><em>Example:</em></p> <pre> is_person(P) when is_record(P, person) -> true; @@ -136,18 +140,18 @@ is_person(_P) -> <section> <title>Records in Patterns</title> - <p>A pattern that will match a certain record is created the same + <p>A pattern that matches a certain record is created in the same way as a record is created:</p> <pre> #Name{Field1=Expr1,...,FieldK=ExprK}</pre> - <p>In this case, one or more of <c>Expr1</c>...<c>ExprK</c> may be + <p>In this case, one or more of <c>Expr1</c>...<c>ExprK</c> can be unbound variables.</p> </section> <section> - <title>Nested records</title> - <p>Beginning with R14 parentheses when accessing or updating nested - records can be omitted. Assuming we have the following record + <title>Nested Records</title> + <p>Beginning with Erlang/OTP R14, parentheses when accessing or updating nested + records can be omitted. Assume the following record definitions:</p> <pre> -record(nrec0, {name = "nested0"}). @@ -156,12 +160,12 @@ is_person(_P) -> N2 = #nrec2{}, </pre> - <p>Before R14 you would have needed to use parentheses as following:</p> + <p>Before R14, parentheses were needed as follows:</p> <pre> "nested0" = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0.name, N0n = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0{name = "nested0a"}, </pre> - <p>Since R14 you can also write:</p> + <p>Since R14, the following can also be written:</p> <pre> "nested0" = N2#nrec2.nrec1#nrec1.nrec0#nrec0.name, N0n = N2#nrec2.nrec1#nrec1.nrec0#nrec0{name = "nested0a"},</pre> @@ -170,23 +174,23 @@ N0n = N2#nrec2.nrec1#nrec1.nrec0#nrec0{name = "nested0a"},</pre> <section> <title>Internal Representation of Records</title> <p>Record expressions are translated to tuple expressions during - compilation. A record defined as</p> + compilation. A record defined as:</p> <pre> -record(Name, {Field1,...,FieldN}).</pre> - <p>is internally represented by the tuple</p> + <p>is internally represented by the tuple:</p> <pre> {Name,Value1,...,ValueN}</pre> - <p>where each <c>ValueI</c> is the default value for <c>FieldI</c>.</p> + <p>Here each <c>ValueI</c> is the default value for <c>FieldI</c>.</p> <p>To each module using records, a pseudo function is added during compilation to obtain information about records:</p> <pre> record_info(fields, Record) -> [Field] record_info(size, Record) -> Size</pre> - <p><c>Size</c> is the size of the tuple representation, that is + <p><c>Size</c> is the size of the tuple representation, that is, one more than the number of fields.</p> <p>In addition, <c>#Record.Name</c> returns the index in the tuple - representation of <c>Name</c> of the record <c>Record</c>. - <c>Name</c> must be an atom.</p> + representation of <c>Name</c> of the record <c>Record</c>.</p> + <p><c>Name</c> must be an atom.</p> </section> </chapter> diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index d1584d2b98..0891dbaa9b 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -34,43 +34,46 @@ <p> Erlang is a dynamically typed language. Still, it comes with a notation for declaring sets of Erlang terms to form a particular - type, effectively forming a specific sub-type of the set of all + type. This effectively forms specific subtypes of the set of all Erlang terms. </p> <p> Subsequently, these types can be used to specify types of record fields - and the argument and return types of functions. - </p> - <p> - Type information can be used to document function interfaces, - provide more information for bug detection tools such as <c>Dialyzer</c>, - and can be exploited by documentation tools such as <c>Edoc</c> for - generating program documentation of various forms. - It is expected that the type language described in this document will - supersede and replace the purely comment-based <c>@type</c> and - <c>@spec</c> declarations used by <c>Edoc</c>. - </p> + and also the argument and return types of functions. + </p> + <p> + Type information can be used for the following:</p> + <list type="bulleted"> + <item>To document function interfaces</item> + <item>To provide more information for bug detection tools, + such as <c>Dialyzer</c></item> + <item>To be exploited by documentation tools, such as EDoc, for + generating program documentation of various forms</item> + </list> + <p>It is expected that the type language described in this section + supersedes and replaces the purely comment-based <c>@type</c> and + <c>@spec</c> declarations used by EDoc.</p> </section> <section> <marker id="syntax"></marker> <title>Types and their Syntax</title> <p> Types describe sets of Erlang terms. - Types consist and are built from a set of predefined types - (e.g. <c>integer()</c>, <c>atom()</c>, <c>pid()</c>, ...) - described below. - Predefined types represent a typically infinite set of Erlang terms which + Types consist of, and are built from, a set of predefined types, + for example, <c>integer()</c>, <c>atom()</c>, and <c>pid()</c>. + Predefined types represent a typically infinite set of Erlang terms that belong to this type. For example, the type <c>atom()</c> stands for the set of all Erlang atoms. </p> <p> - For integers and atoms, we allow for singleton types (e.g. the integers - <c>-1</c> and <c>42</c> or the atoms <c>'foo'</c> and <c>'bar'</c>). + For integers and atoms, it is allowed for singleton types; for example, + the integers + <c>-1</c> and <c>42</c>, or the atoms <c>'foo'</c> and <c>'bar'</c>). All other types are built using unions of either predefined types or singleton types. In a type union between a type and one - of its sub-types the sub-type is absorbed by the super-type and - the union is subsequently treated as if the sub-type was not a + of its subtypes, the subtype is absorbed by the supertype. Thus, + the union is then treated as if the subtype was not a constituent of the union. For example, the type union: </p> <pre> atom() | 'bar' | integer() | 42</pre> @@ -79,13 +82,13 @@ </p> <pre> atom() | integer()</pre> <p> - Because of sub-type relations that exist between types, types - form a lattice where the topmost element, <c>any()</c>, denotes + Because of subtype relations that exist between types, types + form a lattice where the top-most element, <c>any()</c>, denotes the set of all Erlang terms and the bottom-most element, <c>none()</c>, denotes the empty set of terms. </p> <p> - The set of predefined types and the syntax for types is given below: + The set of predefined types and the syntax for types follows: </p> <pre><![CDATA[ Type :: any() %% The top type, the set of all Erlang terms @@ -103,7 +106,7 @@ | Map | Tuple | Union - | UserDefined %% described in Section 7.3 + | UserDefined %% described in Type Declarations of User-Defined Types Atom :: atom() | Erlang_Atom %% 'foo', 'bar', ... @@ -146,22 +149,22 @@ <p> The general form of bitstrings is <c><<_:M, _:_*N>></c>, where <c>M</c> and <c>N</c> are positive integers. It denotes a - bitstring that is <c>M + (k*N)</c> bits long (i.e., a bitstring that + bitstring that is <c>M + (k*N)</c> bits long (that is, a bitstring that starts with <c>M</c> bits and continues with <c>k</c> segments of <c>N</c> bits each, where <c>k</c> is also a positive integer). The notations <c><<_:_*N>></c>, <c><<_:M>></c>, and <c><<>></c> are convenient shorthands for the cases - that <c>M</c>, <c>N</c>, or both, respectively, are zero. + that <c>M</c> or <c>N</c>, or both, are zero. </p> <p> Because lists are commonly used, they have shorthand type notations. The types <c>list(T)</c> and <c>nonempty_list(T)</c> have the shorthands <c>[T]</c> and <c>[T,...]</c>, respectively. - The only difference between the two shorthands is that <c>[T]</c> may be an - empty list but <c>[T,...]</c> may not. + The only difference between the two shorthands is that <c>[T]</c> can be an + empty list but <c>[T,...]</c> cannot. </p> <p> - Notice that the shorthand for <c>list()</c>, i.e. the list of + Notice that the shorthand for <c>list()</c>, that is, the list of elements of unknown type, is <c>[_]</c> (or <c>[any()]</c>), not <c>[]</c>. The notation <c>[]</c> specifies the singleton type for the empty list. </p> @@ -172,7 +175,7 @@ </p> <table> <row> - <cell><b>Built-in type</b></cell><cell><b>Defined as</b></cell> + <cell><em>Built-in type</em></cell><cell><em>Defined as</em></cell> </row> <row> <cell><c>term()</c></cell><cell><c>any()</c></cell> @@ -237,6 +240,7 @@ <row> <cell><c>no_return()</c></cell><cell><c>none()</c></cell> </row> + <tcaption>Built-in types, predefined aliases</tcaption> </table> <p> In addition, the following three built-in types exist and can be @@ -245,7 +249,8 @@ </p> <table> <row> - <cell><b>Built-in type</b></cell><cell><b>Could be thought defined by the syntax</b></cell> + <cell><em>Built-in type</em></cell><cell><em> + Can be thought defined by the syntax</em></cell> </row> <row> <cell><c>non_neg_integer()</c></cell><cell><c>0..</c></cell> @@ -256,6 +261,7 @@ <row> <cell><c>neg_integer()</c></cell><cell><c>..-1</c></cell> </row> + <tcaption>Additional built-in types</tcaption> </table> <p> @@ -278,109 +284,118 @@ define the set of Erlang terms one would expect. </p> <p> - Also for convenience, we allow for record notation to be used. - Records are just shorthands for the corresponding tuples. + Also for convenience, record notation is allowed to be used. + Records are shorthands for the corresponding tuples: </p> <pre> Record :: #Erlang_Atom{} | #Erlang_Atom{Fields}</pre> <p> - Records have been extended to possibly contain type information. - This is described in the sub-section <seealso marker="#typeinrecords">"Type information in record declarations"</seealso> below. + Records are extended to possibly contain type information. + This is described in <seealso marker="#typeinrecords"> + Type Information in Record Declarations</seealso>. </p> <note> - <p>Map types, both <c>map()</c> and <c>#{ ... }</c>, are considered experimental during OTP 17.</p> - <p>No type information of maps pairs, only the containing map types, are used by Dialyzer in OTP 17.</p> + <p>Map types, both <c>map()</c> and <c>#{...}</c>, + are considered experimental during OTP 17.</p> + <p>No type information of maps pairs, only the containing map types, + are used by Dialyzer in OTP 17.</p> </note> </section> - + <section> - <title>Type declarations of user-defined types</title> + <title>Type Declarations of User-Defined Types</title> <p> As seen, the basic syntax of a type is an atom followed by closed - parentheses. New types are declared using '-type' and '-opaque' + parentheses. New types are declared using <c>-type</c> and <c>-opaque</c> compiler attributes as in the following: </p> <pre> -type my_struct_type() :: Type. -opaque my_opaq_type() :: Type.</pre> <p> - where the type name is an atom (<c>'my_struct_type'</c> in the above) - followed by parentheses. Type is a type as defined in the + The type name is the atom <c>my_struct_type</c>, + followed by parentheses. <c>Type</c> is a type as defined in the previous section. - A current restriction is that Type can contain only predefined types, - or user-defined types which are either module-local (i.e., with a - definition that is present in the code of the module) or are remote - types (i.e., types defined in and exported by other modules; see below). - For module-local types, the restriction that their definition + A current restriction is that <c>Type</c> can contain + only predefined types, + or user-defined types which are either of the following: + </p> + <list type="bulleted"> + <item>Module-local type, that is, with a + definition that is present in the code of the module</item> + <item>Remote type, that is, type defined in, and exported by, + other modules; more about this soon.</item> + </list> + <p>For module-local types, the restriction that their definition exists in the module is enforced by the compiler and results in a - compilation error. (A similar restriction currently exists for records.) - </p> + compilation error. (A similar restriction currently exists for records.) </p> <p> Type declarations can also be parameterized by including type variables between the parentheses. The syntax of type variables is the same as - Erlang variables (starts with an upper case letter). - Naturally, these variables can - and should - appear on the RHS of the - definition. A concrete example appears below: + Erlang variables, that is, starts with an upper-case letter. + Naturally, these variables can - and is to - appear on the RHS of the + definition. A concrete example follows: </p> <pre> -type orddict(Key, Val) :: [{Key, Val}].</pre> <p> - A module can export some types in order to declare that other modules + A module can export some types to declare that other modules are allowed to refer to them as <em>remote types</em>. - This declaration has the following form: + This declaration has the following form:</p> <pre> -export_type([T1/A1, ..., Tk/Ak]).</pre> - where the Ti's are atoms (the name of the type) and the Ai's are their - arguments. An example is given below: + <p>Here the Ti's are atoms (the name of the type) and the Ai's are their + arguments</p> + <p><em>Example:</em></p> <pre> -export_type([my_struct_type/0, orddict/2]).</pre> - Assuming that these types are exported from module <c>'mod'</c> then - one can refer to them from other modules using remote type expressions - like those below: + <p>Assuming that these types are exported from module <c>'mod'</c>, + you can refer to them from other modules using remote type expressions + like the following:</p> <pre> mod:my_struct_type() mod:orddict(atom(), term())</pre> - One is not allowed to refer to types which are not declared as exported. + <p>It is not allowed to refer to types that are not declared as exported. </p> <p> Types declared as <c>opaque</c> represent sets of terms whose - structure is not supposed to be visible in any way outside of - their defining module (i.e., only the module defining them is - allowed to depend on their term structure). Consequently, such + structure is not supposed to be visible from outside of + their defining module. That is, only the module defining them + is allowed to depend on their term structure. Consequently, such types do not make much sense as module local - module local - types are not accessible by other modules anyway - and should - always be exported. + types are not accessible by other modules anyway - and is + always to be exported. </p> </section> - - <marker id="typeinrecords"/> + <section> - <title>Type information in record declarations</title> + <marker id="typeinrecords"/> + <title>Type Information in Record Declarations</title> <p> - The types of record fields can be specified in the declaration of the - record. The syntax for this is: + The types of record fields can be specified in the declaration of the + record. The syntax for this is as follows: </p> <pre> -record(rec, {field1 :: Type1, field2, field3 :: Type3}).</pre> <p> For fields without type annotations, their type defaults to any(). - I.e., the above is a shorthand for: + That is, the previous example is a shorthand for the following: </p> <pre> -record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).</pre> <p> In the presence of initial values for fields, - the type must be declared after the initialization as in the following: + the type must be declared after the initialization, as follows: </p> <pre> -record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3}).</pre> <p> - Naturally, the initial values for fields should be compatible - with (i.e. a member of) the corresponding types. - This is checked by the compiler and results in a compilation error - if a violation is detected. For fields without initial values, - the singleton type <c>'undefined'</c> is added to all declared types. + The initial values for fields are to be compatible + with (that is, a member of) the corresponding types. + This is checked by the compiler and results in a compilation error + if a violation is detected. For fields without initial values, + the singleton type <c>'undefined'</c> is added to all declared types. In other words, the following two record declarations have identical effects: </p> @@ -398,13 +413,13 @@ </p> <p> Any record, containing type information or not, once defined, - can be used as a type using the syntax: + can be used as a type using the following syntax: </p> <pre> #rec{}</pre> <p> In addition, the record fields can be further specified when using - a record type by adding type information about the field in - the following manner: + a record type by adding type information about the field + as follows: </p> <pre> #rec{some_field :: Type}</pre> <p> @@ -414,16 +429,16 @@ </section> <section> - <title>Specifications for functions</title> + <title>Specifications for Functions</title> <p> A specification (or contract) for a function is given using the new - compiler attribute <c>'-spec'</c>. The general format is as follows: + compiler attribute <c>-spec</c>. The general format is as follows: </p> <pre> -spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType.</pre> <p> - The arity of the function has to match the number of arguments, - or else a compilation error occurs. + The arity of the function must match the number of arguments, + else a compilation error occurs. </p> <p> This form can also be used in header files (.hrl) to declare type @@ -432,7 +447,7 @@ explicitly) import these functions. </p> <p> - For most uses within a given module, the following shorthand suffices: + Within a given module, the following shorthand suffice in most cases: </p> <pre> -spec Function(ArgType1, ..., ArgTypeN) -> ReturnType.</pre> @@ -450,8 +465,8 @@ ; (T4, T5) -> T6.</pre> <p> A current restriction, which currently results in a warning - (OBS: not an error) by the compiler, is that the domains of - the argument types cannot be overlapping. + (not an error) by the compiler, is that the domains of + the argument types cannot overlap. For example, the following specification results in a warning: </p> <pre> @@ -466,41 +481,43 @@ <pre> -spec id(X) -> X.</pre> <p> - However, note that the above specification does not restrict the input - and output type in any way. - We can constrain these types by guard-like subtype constraints + Notice that the above specification does not restrict the input + and output type in any way. + These types can be constrained by guard-like subtype constraints and provide bounded quantification: </p> <pre> -spec id(X) -> X when X :: tuple().</pre> <p> Currently, the <c>::</c> constraint (read as <c>is_subtype</c>) is - the only guard constraint which can be used in the <c>'when'</c> + the only guard constraint that can be used in the <c>'when'</c> part of a <c>'-spec'</c> attribute. </p> <note> <p> - The above function specification, using multiple occurrences of - the same type variable, provides more type information than the - function specification below where the type variables are missing: + The above function specification uses multiple occurrences of + the same type variable. That provides more type information than the + following function specification, where the type variables are missing: </p> <pre> -spec id(tuple()) -> tuple().</pre> <p> The latter specification says that the function takes some tuple - and returns some tuple, while the one with the <c>X</c> type + and returns some tuple. The specification with the <c>X</c> type variable specifies that the function takes a tuple and returns <em>the same</em> tuple. </p> <p> - However, it's up to the tools that process the specs to choose - whether to take this extra information into account or ignore it. + However, it is up to the tools that process the specificationss + to choose whether to take this extra information into account + or not. </p> </note> <p> - The scope of an <c>::</c> constraint is the - <c>(...) -> RetType</c> - specification after which it appears. To avoid confusion, - we suggest that different variables are used in different - constituents of an overloaded contract as in the example below: + The scope of a <c>::</c> constraint is the + <c>(...) -> RetType</c> + specification after which it appears. To avoid confusion, + it is suggested that different variables are used in different + constituents of an overloaded contract, as shown in the + following example: </p> <pre> -spec foo({X, integer()}) -> X when X :: atom() @@ -511,19 +528,20 @@ </p> <pre> -spec id(X) -> X when is_subtype(X, tuple()).</pre> <p> - but its use is discouraged. It will be taken out in a future + but its use is discouraged. It will be removed in a future Erlang/OTP release. </p> </note> <p> Some functions in Erlang are not meant to return; either because they define servers or because they are used to - throw exceptions as the function below: + throw exceptions, as in the following function: </p> <pre> my_error(Err) -> erlang:throw({error, Err}).</pre> <p> - For such functions we recommend the use of the special <c>no_return()</c> - type for their "return", via a contract of the form: + For such functions, it is recommended to use the special + <c>no_return()</c> type for their "return", through a contract + of the following form: </p> <pre> -spec my_error(term()) -> no_return().</pre> </section> diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc index a8ee2d1245..7c566229ac 100644 --- a/system/doc/system_principles/create_target.xmlsrc +++ b/system/doc/system_principles/create_target.xmlsrc @@ -31,55 +31,54 @@ <rev>A</rev> <file>create_target.xml</file> </header> + <marker id="creating upgrading target system"></marker> - <section> - <title>Introduction</title> - <p>When creating a system using Erlang/OTP, the most simple way is - to install Erlang/OTP somewhere, install the application specific + <p>When creating a system using Erlang/OTP, the simplest way is + to install Erlang/OTP somewhere, install the application-specific code somewhere else, and then start the Erlang runtime system, - making sure the code path includes the application specific code.</p> - <p>Often it is not desirable to use an Erlang/OTP system as is. A - developer may create new Erlang/OTP compliant applications for a + making sure the code path includes the application-specific code.</p> + <p>It is often not desirable to use an Erlang/OTP system as is. A + developer can create new Erlang/OTP-compliant applications for a particular purpose, and several original Erlang/OTP applications - may be irrelevant for the purpose in question. Thus, there is a + can be irrelevant for the purpose in question. Thus, there is a need to be able to create a new system based on a given - Erlang/OTP system, where dispensable applications are removed, - and a set of new applications are included. Documentation and + Erlang/OTP system, where dispensable applications are removed + and new applications are included. Documentation and source code is irrelevant and is therefore not included in the new system.</p> - <p>This chapter is about creating such a system, which we call a + <p>This chapter is about creating such a system, which is called a <em>target system</em>.</p> - <p>In the following sections we consider creating target systems with - different requirements of functionality:</p> + <p>The following sections deal with target systems + with different requirements of functionality:</p> <list type="bulleted"> - <item>a <em>basic target system</em> that can be started by - calling the ordinary <c>erl</c> script, </item> - <item>a <em>simple target system</em> where also code - replacement in run-time can be performed, and</item> - <item>an <em>embedded target system</em> where there is also + <item>A <em>basic target system</em> that can be started by + calling the ordinary <c>erl</c> script.</item> + <item>A <em>simple target system</em> where also code + replacement in runtime can be performed.</item> + <item>An <em>embedded target system</em> where there is also support for logging output from the system to file for later inspection, and where the system can be started automatically - at boot time. </item> + at boot time.</item> </list> - <p>We only consider the case when Erlang/OTP is running on a UNIX - system.</p> - <p>In the <c>sasl</c> application there is an example Erlang - module <c>target_system.erl</c> that contains functions for - creating and installing a target system. This module is used in - the examples below, and the source code of the module is listed - at the end of this chapter.</p> - </section> + <p>Here is only considered the case when Erlang/OTP is running on a + UNIX system.</p> + <p>The <c>sasl</c> application includes the example Erlang + module <c>target_system.erl</c>, which contains functions for + creating and installing a target system. This module is used in + the following examples. The source code of the module is listed + in <seealso marker="#listing of target system"> + Listing of target_system.erl</seealso></p> <section> <marker id="create"/> <title>Creating a Target System</title> <p>It is assumed that you have a working Erlang/OTP system structured - according to the OTP Design Principles.</p> - <p><em>Step 1.</em> First create a <c>.rel</c> file (see <seealso - marker="sasl:rel">rel(4)</seealso>) that specifies the <c>erts</c> - version and lists all applications that should be included in the - new basic target system. An example is the following - <c>mysystem.rel</c> file:</p> + according to the OTP design principles.</p> + <p><em>Step 1.</em> Create a <c>.rel</c> file (see the + <seealso marker="sasl:rel">rel(4)</seealso> manual page in + SASL), which specifies the ERTS version and lists + all applications that are to be included in the new basic target + system. An example is the following <c>mysystem.rel</c> file:</p> <code type="none"> %% mysystem.rel {release, @@ -91,23 +90,23 @@ {pea, "1.0"}]}.</code> <p>The listed applications are not only original Erlang/OTP applications but possibly also new applications that you have - written yourself (here exemplified by the application - <c>pea</c>). </p> - <p><em>Step 2.</em> From the directory where the <c>mysystem.rel</c> - file reside, start the Erlang/OTP system:</p> + written (here exemplified by the application Pea (<c>pea</c>)).</p> + <p><em>Step 2.</em> Start Erlang/OTP from the directory where + the <c>mysystem.rel</c> file resides:</p> <pre> os> <input>erl -pa /home/user/target_system/myapps/pea-1.0/ebin</input></pre> - <p>where also the path to the <c>pea-1.0</c> ebin directory is - provided. </p> - <p><em>Step 3.</em> Now create the target system: </p> + <p>Here also the path to the <c>pea-1.0</c> ebin directory is + provided.</p> + <p><em>Step 3.</em> Create the target system:</p> <pre> 1> <input>target_system:create("mysystem").</input></pre> - <p>The <c>target_system:create/1</c> function does the following:</p> + <p>The function <c>target_system:create/1</c> performs the + following:</p> <list type="ordered"> - <item>Reads the <c>mysystem.rel</c> file, and creates a new file - <c>plain.rel</c> which is identical to former, except that it - only lists the <c>kernel</c> and <c>stdlib</c> applications. </item> - <item>From the <c>mysystem.rel</c> and <c>plain.rel</c> files + <item>Reads the file <c>mysystem.rel</c> and creates a new file + <c>plain.rel</c> that is identical to the former, except that it + only lists the Kernel and STDLIB applications.</item> + <item>From the files <c>mysystem.rel</c> and <c>plain.rel</c> creates the files <c>mysystem.script</c>, <c>mysystem.boot</c>, <c>plain.script</c>, and <c>plain.boot</c> through a call to @@ -124,26 +123,26 @@ releases/mysystem.rel lib/kernel-2.16.4/ lib/stdlib-1.19.4/ lib/sasl-2.3.4/ -lib/pea-1.0/ </code> +lib/pea-1.0/</code> <p>The file <c>releases/FIRST/start.boot</c> is a copy of our <c>mysystem.boot</c></p> <p>The release resource file <c>mysystem.rel</c> is duplicated in the tar file. Originally, this file was only stored in - the <c>releases</c> directory in order to make it possible + the <c>releases</c> directory to make it possible for the <c>release_handler</c> to extract this file separately. After unpacking the tar file, <c>release_handler</c> would automatically copy the file to <c>releases/FIRST</c>. However, sometimes the tar file is unpacked without involving - the <c>release_handler</c> (e.g. when unpacking the first - target system) and therefore the file is now instead + the <c>release_handler</c> (for example, when unpacking the + first target system). The file is therefore now instead duplicated in the tar file so no manual copying is - necessary.</p> + needed.</p> </item> - <item>Creates the temporary directory <c>tmp</c> and extracts the tar file - <c>mysystem.tar.gz</c> into that directory. </item> - <item>Deletes the <c>erl</c> and <c>start</c> files from - <c>tmp/erts-5.10.4/bin</c>. These files will be created again from + <item>Creates the temporary directory <c>tmp</c> and extracts + the tar file <c>mysystem.tar.gz</c> into that directory.</item> + <item>Deletes the files <c>erl</c> and <c>start</c> from + <c>tmp/erts-5.10.4/bin</c>. These files are created again from source when installing the release.</item> <item>Creates the directory <c>tmp/bin</c>.</item> <item>Copies the previously created file <c>plain.boot</c> to @@ -151,31 +150,31 @@ lib/pea-1.0/ </code> <item>Copies the files <c>epmd</c>, <c>run_erl</c>, and <c>to_erl</c> from the directory <c>tmp/erts-5.10.4/bin</c> to the directory <c>tmp/bin</c>.</item> - <item>Creates the directory <c>tmp/log</c>, which will be used + <item>Creates the directory <c>tmp/log</c>, which is used if the system is started as embedded with the <c>bin/start</c> script.</item> <item>Creates the file <c>tmp/releases/start_erl.data</c> with the contents "5.10.4 FIRST". This file is to be passed as data - file to the <c>start_erl</c> script. - </item> + file to the <c>start_erl</c> script.</item> <item>Recreates the file <c>mysystem.tar.gz</c> from the directories - in the directory <c>tmp</c>, and removes <c>tmp</c>.</item> + in the directory <c>tmp</c> and removes <c>tmp</c>.</item> </list> </section> <section> <title>Installing a Target System</title> <p><em>Step 4.</em> Install the created target system in a - suitable directory. </p> + suitable directory.</p> <pre> 2> <input>target_system:install("mysystem", "/usr/local/erl-target").</input></pre> - <p>The function <c>target_system:install/2</c> does the following: + <p>The function <c>target_system:install/2</c> performs the following: </p> <list type="ordered"> <item>Extracts the tar file <c>mysystem.tar.gz</c> into the target directory <c>/usr/local/erl-target</c>.</item> - <item>In the target directory reads the file <c>releases/start_erl.data</c> - in order to find the Erlang runtime system version ("5.10.4").</item> + <item>In the target directory reads the file + <c>releases/start_erl.data</c> to find the Erlang runtime system + version ("5.10.4").</item> <item>Substitutes <c>%FINAL_ROOTDIR%</c> and <c>%EMU%</c> for <c>/usr/local/erl-target</c> and <c>beam</c>, respectively, in the files <c>erl.src</c>, <c>start.src</c>, and @@ -184,97 +183,102 @@ lib/pea-1.0/ </code> <c>start</c>, and <c>run_erl</c> in the target <c>bin</c> directory.</item> <item>Finally the target <c>releases/RELEASES</c> file is created - from data in the <c>releases/mysystem.rel</c> file.</item> + from data in the file <c>releases/mysystem.rel</c>.</item> </list> </section> <section> <marker id="start"/> <title>Starting a Target System</title> - <p>Now we have a target system that can be started in various ways.</p> - <p>We start it as a <em>basic target system</em> by invoking</p> + <p>Now we have a target system that can be started in various ways. + We start it as a <em>basic target system</em> by invoking:</p> <pre> os> <input>/usr/local/erl-target/bin/erl</input></pre> - <p>where only the <c>kernel</c> and <c>stdlib</c> applications are - started, i.e. the system is started as an ordinary development - system. There are only two files needed for all this to work: - <c>bin/erl</c> file (obtained from <c>erts-5.10.4/bin/erl.src</c>) - and the <c>bin/start.boot</c> file (a copy of <c>plain.boot</c>).</p> + <p>Here only the Kernel and STDLIB applications are + started, that is, the system is started as an ordinary development + system. Only two files are needed for all this to work:</p> + <list type="ordered"> + <item><c>bin/erl</c> (obtained from + <c>erts-5.10.4/bin/erl.src</c>)</item> + <item><c>bin/start.boot</c> (a copy of + <c>plain.boot</c>)</item> + </list> <p>We can also start a distributed system (requires <c>bin/epmd</c>).</p> <p>To start all applications specified in the original - <c>mysystem.rel</c> file, use the <c>-boot</c> flag as follows:</p> + <c>mysystem.rel</c> file, use flag <c>-boot</c> as follows:</p> <pre> os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FIRST/start</input></pre> - <p>We start a <em>simple target system</em> as above. The only difference - is that also the file <c>releases/RELEASES</c> is present for - code replacement in run-time to work.</p> - <p>To start an <em>embedded target system</em> the shell script - <c>bin/start</c> is used. That shell script calls + <p>We start a <em>simple target system</em> as above. The only + difference is that also the file <c>releases/RELEASES</c> is + present for code replacement in runtime to work.</p> + <p>To start an <em>embedded target system</em>, the shell script + <c>bin/start</c> is used. The script calls <c>bin/run_erl</c>, which in turn calls <c>bin/start_erl</c> (roughly, <c>start_erl</c> is an embedded variant of - <c>erl</c>). </p> + <c>erl</c>).</p> <p>The shell script <c>start</c>, which is generated from erts-5.10.4/bin/start.src during installation, is only an - example. You should edit it to suite your needs. Typically it is + example. Edit it to suite your needs. Typically it is executed when the UNIX system boots.</p> <p><c>run_erl</c> is a wrapper that provides logging of output from - the run-time system to file. It also provides a simple mechanism + the runtime system to file. It also provides a simple mechanism for attaching to the Erlang shell (<c>to_erl</c>).</p> - <p><c>start_erl</c> requires the root directory - (<c>"/usr/local/erl-target"</c>), the releases directory - (<c>"/usr/local/erl-target/releases"</c>), and the location of - the <c>start_erl.data</c> file. It reads the run-time system - version (<c>"5.10.4"</c>) and release version (<c>"FIRST"</c>) from - the <c>start_erl.data</c> file, starts the run-time system of the - version found, and provides <c>-boot</c> flag specifying the boot - file of the release version found - (<c>"releases/FIRST/start.boot"</c>).</p> - <p><c>start_erl</c> also assumes that there is <c>sys.config</c> in - release version directory (<c>"releases/FIRST/sys.config"</c>). That - is the topic of the next section (see below).</p> - <p>The <c>start_erl</c> shell script should normally not be + <p><c>start_erl</c> requires:</p> + <list type="ordered"> + <item>The root directory (<c>"/usr/local/erl-target"</c>)</item> + <item>The releases directory + (<c>"/usr/local/erl-target/releases"</c></item> + <item>The location of the file <c>start_erl.data</c></item> + </list> + <p>It performs the following:</p> + <list type="ordered"> + <item>Reads the runtime system version (<c>"5.10.4"</c>) and + release version (<c>"FIRST"</c>) from the file + <c>start_erl.data</c>.</item> + <item>Starts the runtime system of the version found.</item> + <item>Provides the flag <c>-boot</c> specifying the boot + file of the release version found + (<c>"releases/FIRST/start.boot"</c>).</item> + </list> + <p><c>start_erl</c> also assumes that there is <c>sys.config</c> + in the release version directory (<c>"releases/FIRST/sys.config"</c>). + That is the topic of the next section.</p> + <p>The <c>start_erl</c> shell script is normally not to be altered by the user.</p> </section> <section> <title>System Configuration Parameters</title> - <p>As was pointed out above <c>start_erl</c> requires a - <c>sys.config</c> in the release version directory - (<c>"releases/FIRST/sys.config"</c>). If there is no such a - file, the system start will fail. Hence such a file has to - be added as well.</p> - <p></p> - <p>If you have system configuration data that are neither file - location dependent nor site dependent, it may be convenient to - create the <c>sys.config</c> early, so that it becomes a part of + <p>As was mentioned in the previous section, <c>start_erl</c> + requires a <c>sys.config</c> in the release version directory + (<c>"releases/FIRST/sys.config"</c>). If there is no such + file, the system start fails. Such a file must therefore + also be added.</p> + <p>If you have system configuration data that is neither + file-location-dependent nor site-dependent, it can be convenient + to create <c>sys.config</c> early, so it becomes part of the target system tar file created by - <c>target_system:create/1</c>. In fact, if you create, in the - current directory, not only the <c>mysystem.rel</c> file, but - also a <c>sys.config</c> file, that latter file will be tacitly + <c>target_system:create/1</c>. In fact, if you in the + current directory create not only the file <c>mysystem.rel</c>, + but also file <c>sys.config</c>, the latter file is tacitly put in the appropriate directory.</p> </section> <section> - <title>Differences from the Install Script</title> - <p>The above <c>install/2</c> procedure differs somewhat from that + <title>Differences From the Install Script</title> + <p>The previous <c>install/2</c> procedure differs somewhat from that of the ordinary <c>Install</c> shell script. In fact, <c>create/1</c> makes the release package as complete as possible, and leave to the - <c>install/2</c> procedure to finish by only considering location - dependent files.</p> + <c>install/2</c> procedure to finish by only considering + location-dependent files.</p> </section> <section> <title>Creating the Next Version</title> - - <p> - In this example the <c>pea</c> application has been changed, and - so are <c>erts</c>, <c>kernel</c>, <c>stdlib</c> and - <c>sasl</c>. - </p> - - <p> - <em>Step 1.</em> Create the <c>.rel</c> file: - </p> + <p>In this example the Pea application has been changed, and + so are the applications ERTS, Kernel, STDLIB + and SASL.</p> + <p><em>Step 1.</em> Create the file <c>.rel</c>:</p> <code type="none"> %% mysystem2.rel {release, @@ -284,65 +288,49 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI {stdlib, "2.0"}, {sasl, "2.4"}, {pea, "2.0"}]}.</code> - <p> - <em>Step 2.</em> Create the application upgrade file (see - <seealso marker="sasl:appup">appup(4)</seealso>) for <c>pea</c>, - for example: - </p> + <p><em>Step 2.</em> Create the application upgrade file (see the + <seealso marker="sasl:appup">appup(4)</seealso> manual page in + SASL) for Pea, for example:</p> <code type="none"> %% pea.appup {"2.0", [{"1.0",[{load_module,pea_lib}]}], [{"1.0",[{load_module,pea_lib}]}]}.</code> - <p> - <em>Step 3.</em> From the directory where the - <c>mysystem2.rel</c> file reside, start the Erlang/OTP system: - </p> + <p><em>Step 3.</em> From the directory where the file + <c>mysystem2.rel</c> resides, start the Erlang/OTP system, + giving the path to the new version of Pea:</p> <pre> os> <input>erl -pa /home/user/target_system/myapps/pea-2.0/ebin</input></pre> - <p>giving the path to the new version of <c>pea</c>. </p> - - <p> - <em>Step 4.</em> Create the release upgrade file (see <seealso - marker="sasl:relup">relup(4)</seealso>): - </p> + <p><em>Step 4.</em> Create the release upgrade file (see the + <seealso marker="sasl:relup">relup(4)</seealso> manual page in + SASL):</p> <pre> -1> <input>systools:make_relup("mysystem2",["mysystem"],["mysystem"],[{path,["/home/user/target_system/myapps/pea-1.0/ebin","/my/old/erlang/lib/*/ebin"]}]).</input></pre> - <p> - where <c>"mysystem"</c> is the base release and - <c>"mysystem2"</c> is the release to upgrade to. - </p> - <p> - Note that the <c>path</c> option is used for pointing out the +1> <input>systools:make_relup("mysystem2",["mysystem"],["mysystem"], + [{path,["/home/user/target_system/myapps/pea-1.0/ebin", + "/my/old/erlang/lib/*/ebin"]}]).</input></pre> + <p>Here <c>"mysystem"</c> is the base release and + <c>"mysystem2"</c> is the release to upgrade to.</p> + <p>The <c>path</c> option is used for pointing out the old version of all applications. (The new versions are already - in the code path - assuming of course that the erlang node on + in the code path - assuming of course that the Erlang node on which this is executed is running the correct version of - Erlang/OTP.) - </p> - <p> - <em>Step 5.</em> Create the new release: - </p> + Erlang/OTP.)</p> + <p><em>Step 5.</em> Create the new release:</p> <pre> 2> <input>target_system:create("mysystem2").</input></pre> - <p> - Given that the <c>relup</c> file generated in step 4 above is - now located in the current directory, it will automatically be - included in the release package. - </p> + <p>Given that the file <c>relup</c> generated in Step 4 is + now located in the current directory, it is automatically + included in the release package.</p> </section> <section> <title>Upgrading the Target System</title> - <p> - This part is done on the target node, and for this example we + <p>This part is done on the target node, and for this example we want the node to be running as an embedded system with the - <c>-heart</c> option, allowing automatic restart of the - node. See <seealso marker="#start">Starting a Target - System</seealso> above for more information. - </p> - <p> - We add <c>-heart</c> to <c>bin/start</c>: - </p> + <c>-heart</c> option, allowing automatic restart of the node. + For more information, see <seealso marker="#start"> + Starting a Target System</seealso>.</p> + <p>We add <c>-heart</c> to <c>bin/start</c>:</p> <code type="none"> #!/bin/sh ROOTDIR=/usr/local/erl-target/ @@ -354,36 +342,27 @@ fi START_ERL_DATA=${1:-$RELDIR/start_erl.data} -$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart</code> - <p> - And we use the simplest possible <c>sys.config</c>, which we - store in <c>releases/FIRST</c>: - </p> +$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR\ +$RELDIR $START_ERL_DATA -heart</code> + <p>We use the simplest possible <c>sys.config</c>, which we + store in <c>releases/FIRST</c>:</p> <code type="none"> %% sys.config [].</code> - <p> - Finally, in order to prepare the upgrade, we need to put the new + <p>Finally, to prepare the upgrade, we must put the new release package in the <c>releases</c> directory of the first - target system: - </p> + target system:</p> <pre> os> <input>cp mysystem2.tar.gz /usr/local/erl-target/releases</input></pre> - <p> - And assuming that the node has been started like this: - </p> + <p>Assuming that the node has been started as follows:</p> <pre> os> <input>/usr/local/erl-target/bin/start</input></pre> - <p> - it can be accessed like this: - </p> + <p>It can be accessed as follows:</p> <pre> os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.1</input></pre> - <p> - Also note that logs can be found in + <p>Logs can be found in <c>/usr/local/erl-target/log</c>. This directory is specified as - an argument to <c>run_erl</c>in the start script listed above. - </p> + an argument to <c>run_erl</c>in the start script listed above.</p> <p> <em>Step 1.</em> Unpack the release: </p> @@ -402,18 +381,19 @@ heart: Tue Apr 1 12:15:11 2014: Executed "/usr/local/erl-target/bin/start /usr/ The above return value and output after the call to <c>release_handler:install_release/1</c> means that the <c>release_handler</c> has restarted the node by using - <c>heart</c>. This will always be done when the upgrade involves - a change of <c>erts</c>, <c>kernel</c>, <c>stdlib</c> or - <c>sasl</c>. See <seealso marker="upgrade">Upgrade when - Erlang/OTP has Changed</seealso> for more infomation about this. + <c>heart</c>. This is always done when the upgrade involves + a change of the applications ERTS, Kernel, + STDLIB, or SASL. For more information, see + <seealso marker="upgrade"> + Upgrade when Erlang/OTP has Changed</seealso>. </p> <p> - The node will be accessible via a new pipe: + The node is accessible through a new pipe: </p> <pre> os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.2</input></pre> <p> - Let's see which releases we have in our system: + Check which releases there are in the system: </p> <pre> 1> <input>release_handler:which_releases().</input> @@ -426,7 +406,7 @@ os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.2</input></pre> <p> Our new release, "SECOND", is now the current release, but we can also see that our "FIRST" release is still permanent. This - means that if the node would be restarted at this point, it + means that if the node would be restarted now, it would come up running the "FIRST" release again. </p> <p> @@ -434,11 +414,9 @@ os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.2</input></pre> </p> <pre> 2> <input>release_handler:make_permanent("SECOND").</input></pre> - <p> - Now look at the releases again: + Check the releases again: </p> - <pre> 3> <input>release_handler:which_releases().</input> <output>[{"MYSYSTEM","SECOND", @@ -447,19 +425,16 @@ os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.2</input></pre> {"MYSYSTEM","FIRST", ["kernel-2.16.4","stdlib-1.19.4","sasl-2.3.4","pea-1.0"], old}]</output></pre> - <p> - Here we see that the new release version is <c>permanent</c>, so - it would be safe to restart the node. - </p> - + We see that the new release version is <c>permanent</c>, so + it would be safe to restart the node.</p> </section> <section> + <marker id="listing of target system"/> <title>Listing of target_system.erl</title> <p>This module can also be found in the <c>examples</c> directory - of the <c>sasl</c> application.</p> + of the SASL application.</p> <codeinclude file="../../../lib/sasl/examples/src/target_system.erl" tag="%module" type="erl"></codeinclude> - </section> </chapter> diff --git a/system/doc/system_principles/error_logging.xml b/system/doc/system_principles/error_logging.xml index 80d5211323..3a82f4e0e0 100644 --- a/system/doc/system_principles/error_logging.xml +++ b/system/doc/system_principles/error_logging.xml @@ -28,41 +28,43 @@ <rev></rev> <file>error_logging.xml</file> </header> + <marker id="error logging"></marker> <section> <title>Error Information From the Runtime System</title> <p>Error information from the runtime system, that is, information - about a process terminating due to an uncaught error exception, + about a process terminating because of an uncaught error exception, is by default written to terminal (tty):</p> <code type="none"><![CDATA[ =ERROR REPORT==== 9-Dec-2003::13:25:02 === Error in process <0.27.0> with exit value: {{badmatch,[1,2,3]},[{m,f,1},{shell,eval_loop,2}]}]]></code> <p>The error information is handled by the <em>error logger</em>, a system process registered as <c>error_logger</c>. This process - receives all error messages from the Erlang runtime system and - also from the standard behaviours and different Erlang/OTP + receives all error messages from the Erlang runtime system as + well as from the standard behaviours and different Erlang/OTP applications.</p> - <p>The exit reasons (such as <c>badarg</c> above) used by + <p>The exit reasons (such as <c>badarg</c>) used by the runtime system are described in - <seealso marker="doc/reference_manual:errors#exit_reasons">Errors and Error Handling</seealso> - in the Erlang Reference Manual.</p> - <p>The process <c>error_logger</c> and its user interface (with - the same name) are described in - <seealso marker="kernel:error_logger">error_logger(3)</seealso>. - It is possible to configure the system so that error information - is written to file instead/as well as tty. Also, it is possible - for user defined applications to send and format error - information using <c>error_logger</c>.</p> + <seealso marker="doc/reference_manual:errors#exit_reasons"> + Errors and Error Handling</seealso>.</p> + <p>For information about the process <c>error_logger</c> and its user + interface (with the same name), see the + <seealso marker="kernel:error_logger">error_logger(3)</seealso> + manual page in Kernel. The system can be configured so that + error information + is written to file or to tty, or both. In addition, user-defined + applications can send and format error information using + <c>error_logger</c>.</p> </section> <section> <title>SASL Error Logging</title> - <p>The standard behaviors (<c>supervisor</c>, <c>gen_server</c>, - etc.) sends progress and error information to <c>error_logger</c>. - If the SASL application is started, this information is written - to tty as well. See + <p>The standard behaviours (<c>supervisor</c>, <c>gen_server</c>, + and so on) send progress and error information to <c>error_logger</c>. + If the SASL application is started, this information is + written to tty as well. For more information, see <seealso marker="sasl:error_logging">SASL Error Logging</seealso> - in the SASL User's Guide for further information.</p> + in the SASL User's Guide.</p> <pre> % <input>erl -boot start_sasl</input> Erlang (BEAM) emulator version 5.4.13 [hipe] [threads:0] [kernel-poll] diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml index 79ed86cd9f..70bb3cd441 100644 --- a/system/doc/system_principles/system_principles.xml +++ b/system/doc/system_principles/system_principles.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1996</year><year>2014</year> + <year>1996</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,35 +28,41 @@ <rev></rev> <file>system_principles.xml</file> </header> + <marker id="system principles"></marker> <section> <title>Starting the System</title> - <p>An Erlang runtime system is started with the command <c>erl</c>:</p> + <p>An Erlang runtime system is started with command <c>erl</c>:</p> <pre> % <input>erl</input> Erlang/OTP 17 [erts-6.0] [hipe] [smp:8:8] Eshell V6.0 (abort with ^G) 1> </pre> - <p><c>erl</c> understands a number of command line arguments, see - <c>erl(1)</c>. A number of them are also described in this chapter.</p> - <p>Application programs can access the values of the command line - arguments by calling one of the functions - <c>init:get_argument(Key)</c>, or <c>init:get_arguments()</c>. - See <c>init(3)</c>.</p> + <p><c>erl</c> understands a number of command-line arguments, see + the <seealso marker="erts:erl">erl(1)</seealso> manual page in + ERTS. Some of them are also described in this chapter.</p> + <p>Application programs can access the values of the command-line + arguments by calling the function <c>init:get_argument(Key)</c> + or <c>init:get_arguments()</c>. See the + <seealso marker="erts:init">init(3)</seealso> manual page in + ERTS.</p> </section> <section> <title>Restarting and Stopping the System</title> - <p>The runtime system can be halted by calling <c>halt/0,1</c>. - See <c>erlang(3)</c>.</p> - <p>The module <c>init</c> contains function for restarting, - rebooting and stopping the runtime system. See <c>init(3)</c>.</p> + <p>The runtime system is halted by calling <c>halt/0,1</c>. For + details, see the <seealso marker="erts:erlang">erlang(3)</seealso> + manual page in ERTS.</p> + <p>The module <c>init</c> contains functions for restarting, + rebooting, and stopping the runtime system:</p> <pre> init:restart() init:reboot() init:stop()</pre> - <p>Also, the runtime system will terminate if the Erlang shell is + <p>For details, see the <seealso marker="erts:init">init(3)</seealso> + manual page in ERTS.</p> + <p>The runtime system terminates if the Erlang shell is terminated.</p> </section> @@ -69,14 +75,15 @@ init:stop()</pre> <p>A boot script file has the extension <c>.script</c>. The runtime system uses a binary version of the script. This <em>binary boot script</em> file has the extension <c>.boot</c>.</p> - <p>Which boot script to use is specified by the command line flag - <c>-boot</c>. The extension <c>.boot</c> should be omitted. - Example, using the boot script <c>start_all.boot</c>:</p> + <p>Which boot script to use is specified by the command-line flag + <c>-boot</c>. The extension <c>.boot</c> is to be omitted. + For example, using the boot script <c>start_all.boot</c>:</p> <pre> % <input>erl -boot start_all</input></pre> <p>If no boot script is specified, it defaults to - <c>ROOT/bin/start</c>, see Default Boot Scripts below.</p> - <p>The command line flag <c>-init_debug</c> makes the <c>init</c> + <c>ROOT/bin/start</c>, see <seealso marker="#default_boot_scripts"> + Default Boot Scripts</seealso>.</p> + <p>The command-line flag <c>-init_debug</c> makes the <c>init</c> process write some debug information while interpreting the boot script:</p> <pre> @@ -87,59 +94,55 @@ init:stop()</pre> {start,heart} {start,error_logger} ...</pre> - <p>See <c>script(4)</c> for a detailed description of the syntax - and contents of the boot script.</p> + <p>For a detailed description of the syntax and contents of the + boot script, see the <c>script(4)</c> manual page in SASL.</p> <section> + <marker id="default_boot_scripts"></marker> <title>Default Boot Scripts</title> - <p>Erlang/OTP comes with two boot scripts:</p> - <taglist> - <tag><c>start_clean.boot</c></tag> - <item> - <p>Loads the code for and starts the applications Kernel and - STDLIB.</p> - </item> - <tag><c>start_sasl.boot</c></tag> - <item> - <p>Loads the code for and starts the applications Kernel, - STDLIB and SASL.</p> - </item> - <tag><c>no_dot_erlang.boot</c></tag> - <item> - <p>Loads the code for and starts the applications Kernel and - STDLIB, skips loading the <c>.erlang</c> file. - Useful for scripts and other tools that should be behave the - same regardless of user preferences. - </p> - </item> - </taglist> + <p>Erlang/OTP comes with these boot scripts:</p> + <list type="bulleted"> + <item><c>start_clean.boot</c> - Loads the code for and starts + the applications Kernel and STDLIB.</item> + <item><c>start_sasl.boot</c> - Loads the code for and starts + the applications Kernel, STDLIB, and + SASL).</item> + <item><c>no_dot_erlang.boot</c> - Loads the code for and + starts the applications Kernel and STDLIB. + Skips loading the file <c>.erlang</c>. Useful for scripts and + other tools that are to behave the same irrespective of user + preferences.</item> + </list> <p>Which of <c>start_clean</c> and <c>start_sasl</c> to use as default is decided by the user when installing Erlang/OTP using <c>Install</c>. The user is asked "Do you want to use a minimal system startup instead of the SASL startup". If the answer is yes, then <c>start_clean</c> is used, otherwise - <c>start_sasl</c> is used. A copy of the selected boot script - is made, named <c>start.boot</c> and placed in - the <c>ROOT/bin</c> directory.</p> + <c>start_sasl</c> is used. A copy of the selected boot script is + made, named <c>start.boot</c> and placed in directory + <c>ROOT/bin</c>.</p> </section> <section> <title>User-Defined Boot Scripts</title> <p>It is sometimes useful or necessary to create a user-defined boot script. This is true especially when running Erlang in - embedded mode, see <seealso marker="#code_loading">Code Loading Strategy</seealso>.</p> - <p>It is possible to write a boot script manually. - The recommended way to create a boot script, however, is to - generate the boot script from a release resource file - <c>Name.rel</c>, using the function + embedded mode, see <seealso marker="#code_loading"> + Code Loading Strategy</seealso>.</p> + <p>A boot script can be written manually. However, it is + recommended to create a boot script by generating it from a + release resource file <c>Name.rel</c>, using the function <c>systools:make_script/1,2</c>. This requires that the source code is structured as applications according to the OTP design principles. (The program does not have to be started in terms of - OTP applications but can be plain Erlang).</p> - <p>Read more about <c>.rel</c> files in OTP Design Principles and - <c>rel(4)</c>.</p> + OTP applications, but can be plain Erlang).</p> + <p>For more information about <c>.rel</c> files, see + <seealso marker="doc/design_principles:release_handling"> + OTP Design Principles</seealso> and the + <seealso marker="sasl:rel">rel(4)</seealso> manual page in + SASL.</p> <p>The binary boot script file <c>Name.boot</c> is generated from - the boot script file <c>Name.script</c> using the function + the boot script file <c>Name.script</c>, using the function <c>systools:script2boot(File)</c>.</p> </section> </section> @@ -148,16 +151,17 @@ init:stop()</pre> <marker id="code_loading"></marker> <title>Code Loading Strategy</title> <p>The runtime system can be started in either <em>embedded</em> or - <em>interactive</em> mode. Which one is decided by the command - line flag <c>-mode</c>.</p> + <em>interactive</em> mode. Which one is decided by the + command-line flag <c>-mode</c>.</p> <pre> % <input>erl -mode embedded</input></pre> <p>Default mode is <c>interactive</c>.</p> + <p>The mode properties are as follows:</p> <list type="bulleted"> - <item>In embedded mode, all code is loaded during system start-up + <item>In embedded mode, all code is loaded during system startup according to the boot script. (Code can also be loaded later - by explicitly ordering the code server to do so).</item> - <item>In interactive mode, code is dynamically loaded when first + by explicitly ordering the code server to do so.)</item> + <item>In interactive mode, the code is dynamically loaded when first referenced. When a call to a function in a module is made, and the module is not loaded, the code server searches the code path and loads the module into the system.</item> @@ -165,21 +169,21 @@ init:stop()</pre> <p>Initially, the code path consists of the current working directory and all object code directories under <c>ROOT/lib</c>, where <c>ROOT</c> is the installation directory - of Erlang/OTP. Directories can be named <c>Name[-Vsn]</c> and - the code server, by default, chooses the directory with + of Erlang/OTP. Directories can be named <c>Name[-Vsn]</c>. The + code server, by default, chooses the directory with the highest version number among those which have the same <c>Name</c>. The <c>-Vsn</c> suffix is optional. If an <c>ebin</c> directory exists under the <c>Name[-Vsn]</c> - directory, it is this directory which is added to the code path.</p> - <p>The code path can be extended by using the command line flags - <c>-pa Directories</c> and <c>-pz Directories</c>. These will add - <c>Directories</c> to the head or end of the code path, - respectively. Example</p> + directory, this directory is added to the code path.</p> + <p>The code path can be extended by using the command-line flags + <c>-pa Directories</c> and <c>-pz Directories</c>. These add + <c>Directories</c> to the head or the end of the code path, + respectively. Example:</p> <pre> % <input>erl -pa /home/arne/mycode</input></pre> <p>The code server module <c>code</c> contains a number of - functions for modifying and checking the search path, see - <c>code(3)</c>.</p> + functions for modifying and checking the search path, see the + <c>code(3)</c> manual page in Kernel.</p> </section> <section> @@ -192,49 +196,65 @@ init:stop()</pre> <cell align="left" valign="middle"><em>Documented in</em></cell> </row> <row> - <cell align="left" valign="middle">module</cell> + <cell align="left" valign="middle">Module</cell> <cell align="left" valign="middle"><c>.erl</c></cell> - <cell align="left" valign="middle">Erlang Reference Manual</cell> + <cell align="left" valign="middle"> + <seealso marker="doc/reference_manual:modules"> + Erlang Reference Manual</seealso></cell> </row> <row> - <cell align="left" valign="middle">include file</cell> + <cell align="left" valign="middle">Include file</cell> <cell align="left" valign="middle"><c>.hrl</c></cell> - <cell align="left" valign="middle">Erlang Reference Manual</cell> + <cell align="left" valign="middle"> + <seealso marker="doc/reference_manual:modules"> + Erlang Reference Manual</seealso></cell> </row> <row> - <cell align="left" valign="middle">release resource file</cell> + <cell align="left" valign="middle">Release resource file</cell> <cell align="left" valign="middle"><c>.rel</c></cell> - <cell align="left" valign="middle"><c>rel(4)</c></cell> + <cell align="left" valign="middle"> + <seealso marker="sasl:rel">rel(4)</seealso> + manual page in SASL</cell> </row> <row> - <cell align="left" valign="middle">application resource file</cell> + <cell align="left" valign="middle">Application resource file</cell> <cell align="left" valign="middle"><c>.app</c></cell> - <cell align="left" valign="middle"><c>app(4)</c></cell> + <cell align="left" valign="middle"> + <seealso marker="kernel:app">app(4)</seealso> + manual page in Kernel</cell> </row> <row> - <cell align="left" valign="middle">boot script</cell> + <cell align="left" valign="middle">Boot script</cell> <cell align="left" valign="middle"><c>.script</c></cell> - <cell align="left" valign="middle"><c>script(4)</c></cell> + <cell align="left" valign="middle"> + <seealso marker="sasl:script">script(4)</seealso> + manual page in SASL</cell> </row> <row> - <cell align="left" valign="middle">binary boot script</cell> + <cell align="left" valign="middle">Binary boot script</cell> <cell align="left" valign="middle"><c>.boot</c></cell> <cell align="left" valign="middle">-</cell> </row> <row> - <cell align="left" valign="middle">configuration file</cell> + <cell align="left" valign="middle">Configuration file</cell> <cell align="left" valign="middle"><c>.config</c></cell> - <cell align="left" valign="middle"><c>config(4)</c></cell> + <cell align="left" valign="middle"> + <seealso marker="kernel:config">config(4)</seealso> + manual page in Kernel</cell> </row> <row> - <cell align="left" valign="middle">application upgrade file</cell> + <cell align="left" valign="middle">Application upgrade file</cell> <cell align="left" valign="middle"><c>.appup</c></cell> - <cell align="left" valign="middle"><c>appup(4)</c></cell> + <cell align="left" valign="middle"> + <seealso marker="sasl:appup">appup(4)</seealso> + manual page in SASL</cell> </row> <row> - <cell align="left" valign="middle">release upgrade file</cell> + <cell align="left" valign="middle">Release upgrade file</cell> <cell align="left" valign="middle"><c>relup</c></cell> - <cell align="left" valign="middle"><c>relup(4)</c></cell> + <cell align="left" valign="middle"> + <seealso marker="sasl:relup">relup(4)</seealso> + manual page in SASL</cell> </row> <tcaption>File Types</tcaption> </table> diff --git a/system/doc/system_principles/upgrade.xml b/system/doc/system_principles/upgrade.xml index 68e48da0b8..83e8128f94 100644 --- a/system/doc/system_principles/upgrade.xml +++ b/system/doc/system_principles/upgrade.xml @@ -31,88 +31,72 @@ <rev></rev> <file>upgrade.xml</file> </header> + <marker id="upgrade section"></marker> <section> <title>Introduction</title> - <p> - As of Erlang/OTP 17, most applications deliver a valid - application upgrade (<c>appup</c>) file. In earlier releases, a + <marker id="upgrade"></marker> + <p>As of Erlang/OTP 17, most applications deliver a valid + application upgrade file (<c>appup</c>). In earlier releases, a majority of the applications in Erlang/OTP did not support - upgrade at all. Many of the applications use the + upgrade. Many of the applications use the <c>restart_application</c> instruction. These are applications for which it is not crucial to support real soft upgrade, for - instance tools and library applications. The + example, tools and library applications. The <c>restart_application</c> instruction ensures that all modules in the application are reloaded and - thereby running the new code. - </p> + thereby running the new code.</p> </section> <section> - <title>Upgrade of core applications</title> - <p> - The core applications ERTS, Kernel, STDLIB + <title>Upgrade of Core Applications</title> + <p>The core applications ERTS, Kernel, STDLIB, and SASL never allow real soft upgrade, but require the Erlang emulator to be restarted. This is indicated to the <c>release_handler</c> by the upgrade instruction - <c>restart_new_emulator</c>. This instruction will always be the - very first instruction executed, and it will restart the + <c>restart_new_emulator</c>. This instruction is always the + very first instruction executed, and it restarts the emulator with the new versions of the above mentioned core - applications and the old versions of all other - applications. When the node is back up all other upgrade instructions are + applications and the old versions of all other applications. + When the node is back up, all other upgrade instructions are executed, making sure each application is finally running its - new version. - </p> - - <p> - It might seem strange to do a two-step upgrade instead of + new version.</p> + <p>It might seem strange to do a two-step upgrade instead of just restarting the emulator with the new version of all applications. The reason for this design decision is to allow - <c>code_change</c> functions to have side effects, for example changing - data on disk. It also makes sure that the upgrade mechanism for - non-core applications does not differ depending on whether or not - core applications are changed at the same time. - </p> - - <p> - If, however, the more brutal variant is preferred, it is - possible to handwrite the release upgrade file using only the + <c>code_change</c> functions to have side effects, for example, + changing data on disk. It also guarantees that the upgrade + mechanism for non-core applications does not differ depending + on whether or not core applications are changed at the same time.</p> + <p>If, however, the more brutal variant is preferred, the + the release upgrade file can be handwritten using only the single upgrade instruction <c>restart_emulator</c>. This - instruction, in contrast to <c>restart_new_emulator</c>, will - cause the emulator to restart with the new versions of - <em>all</em> applications. - </p> - - <p> - <em>Note</em> that if other instructions are included before + instruction, in contrast to <c>restart_new_emulator</c>, + causes the emulator to restart with the new versions of + <em>all</em> applications.</p> + <p><em>Note:</em> If other instructions are included before <c>restart_emulator</c> in the handwritten <c>relup</c> file, - they will be executed in the old emulator. This is a big risk + they are executed in the old emulator. This is a big risk since there is no guarantee that new beam code can be loaded into the old emulator. Adding instructions after <c>restart_emulator</c> has no effect as the - <c>release_handler</c> will not do any attempt at executing - them. - </p> - - <p> - See <seealso marker="sasl:relup">relup(4)</seealso> for - information about the release upgrade file, and <seealso - marker="sasl:appup">appup(4)</seealso> for further information - about upgrade instructions. - </p> + <c>release_handler</c> will not execute them.</p> + <p>For information about the release upgrade file, see the + <seealso marker="sasl:relup">relup(4)</seealso> manual page + in SASL. + For more information about upgrade instructions, see the + <seealso marker="sasl:appup">appup(4)</seealso> manual page + in SASL.</p> </section> <section> - <title>Applications that still do not allow code upgrade</title> - <p> - A few applications, for instance HiPE do not support - upgrade at all. This is indicated by an application upgrade file - containing only <c>{Vsn,[],[]}</c>. Any attempt at creating a release - upgrade file with such input will fail. - The only way to force an upgrade involving applications like this is to - handwrite the <c>relup</c> file, preferably as described above - with only the <c>restart_emulator</c> instruction. - </p> - + <title>Applications that Still do Not Allow Code Upgrade</title> + <p>A few applications, such as HiPE, do not support upgrade. + This is indicated by an application upgrade file containing only + <c>{Vsn,[],[]}</c>. Any attempt at creating a release upgrade file + with such input fails. The only way to force an upgrade involving + applications like this is to + handwrite the file <c>relup</c>, preferably as described above + with only the <c>restart_emulator</c> instruction.</p> </section> </chapter> diff --git a/system/doc/system_principles/versions.xml b/system/doc/system_principles/versions.xml index ff042f4a3b..a0f13774ce 100644 --- a/system/doc/system_principles/versions.xml +++ b/system/doc/system_principles/versions.xml @@ -31,93 +31,100 @@ <rev></rev> <file>versions.xml</file> </header> - <section><title>OTP Version</title> + <marker id="versions section"></marker> + + <section> + <title>OTP Version</title> <p>As of OTP release 17, the OTP release number corresponds to the major part of the OTP version. The OTP version as a concept was - introduced in OTP 17. The <seealso marker="#version_scheme">version - scheme</seealso> used is described in more detail below.</p> - + introduced in OTP 17. The version scheme used is described in detail in + <seealso marker="#version_scheme">Version Scheme</seealso>.</p> <p>OTP of a specific version is a set of applications of specific versions. The application versions identified by an OTP version corresponds to application versions that have been tested together - by the Erlang/OTP team at Ericsson AB. An OTP system can however be + by the Erlang/OTP team at Ericsson AB. An OTP system can, however, be put together with applications from different OTP versions. Such a combination of application versions has not been tested by the Erlang/OTP team. It is therefore <em>always preferred to use OTP applications from one single OTP version</em>.</p> - <p>Release candidates have an <c>-rc<N></c> - suffix. The suffix <c>-rc0</c> will be used during development up to + suffix. The suffix <c>-rc0</c> is used during development up to the first release candidate.</p> - <section><title>Retrieving Current OTP Version</title> - <p>In an OTP source code tree, the OTP version can be read from - the text file <c><OTP source root>/OTP_VERSION</c>. The - absolute path to the file can be constructed by calling - <c>filename:join([<seealso marker="kernel:code#root_dir/0">code:root_dir()</seealso>, "OTP_VERSION"])</c>.</p> - <p>In an installed OTP development system, the OTP version can be read - from the text file <c><OTP installation root>/releases/<OTP release number>/OTP_VERSION</c>. - The absolute path to the file can by constructed by calling - <c>filename:join([<seealso marker="kernel:code#root_dir/0">code:root_dir()</seealso>, "releases", <seealso marker="erts:erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>, "OTP_VERSION"]).</c></p> - <p>If the version read from the <c>OTP_VERSION</c> file in a - development system has a <c>**</c> suffix, the system has been - patched using the <c>otp_patch_apply</c> tool available to - licensed customers. In this case, the system consists of application - versions from multiple OTP versions. The version preceding the <c>**</c> - suffix corresponds to the OTP version of the base system that - has been patched. Note that if a development system is updated by - other means than <c>otp_patch_apply</c>, the <c>OTP_VERSION</c> file - may identify an incorrect OTP version.</p> - - <p>No <c>OTP_VERSION</c> file will be placed in a - <seealso marker="create_target">target system</seealso> created - by OTP tools. This since one easily can create a target system - where it is hard to even determine the base OTP version. You may, - however, place such a file there yourself if you know the OTP - version.</p> + <section> + <title>Retrieving Current OTP Version</title> + <p>In an OTP source code tree, the OTP version can be read from + the text file <c><OTP source root>/OTP_VERSION</c>. The + absolute path to the file can be constructed by calling + <c>filename:join([<seealso marker="kernel:code#root_dir/0">code:root_dir()</seealso>, "OTP_VERSION"])</c>.</p> + <p>In an installed OTP development system, the OTP version can be read + from the text file <c><OTP installation root>/releases/<OTP release number>/OTP_VERSION</c>. + The absolute path to the file can by constructed by calling + <c>filename:join([<seealso marker="kernel:code#root_dir/0">code:root_dir()</seealso>, "releases", <seealso marker="erts:erlang#system_info_otp_release"> erlang:system_info(otp_release)</seealso>, "OTP_VERSION"]).</c></p> + <p>If the version read from the <c>OTP_VERSION</c> file in a + development system has a <c>**</c> suffix, the system has been + patched using the + <seealso marker="../installation_guide/OTP-PATCH-APPLY"><c>otp_patch_apply</c></seealso> + tool. In this case, the system consists of application + versions from multiple OTP versions. The version preceding the <c>**</c> + suffix corresponds to the OTP version of the base system that + has been patched. Notice that if a development system is updated by + other means than <c>otp_patch_apply</c>, the file <c>OTP_VERSION</c> + can identify an incorrect OTP version.</p> + <p>No <c>OTP_VERSION</c> file is placed in a + <seealso marker="create_target">target system</seealso> created + by OTP tools. This since one easily can create a target system + where it is hard to even determine the base OTP version. You can, + however, place such a file there if you know the OTP version.</p> </section> - <section><title>OTP Versions Table</title> - <p>The text file <c><OTP source root>/otp_versions.table</c> that - is part of the source code contains information about all OTP versions - from OTP 17.0 up to current OTP version. Each line contains information - about application versions that are part of a specific OTP version, and - is on the format:</p> -<pre> -<OtpVersion> : <ChangedAppVersions> # <UnchangedAppVersions> : -</pre> - <p><c><OtpVersion></c> is on the format <c>OTP-<VSN></c>, i.e., - the same as the git tag used to identify the source. - <c><ChangedAppVersions></c> and <c><UnchangedAppVersions></c> - are space separated lists of application versions on the - format <c><application>-<vsn></c>. - <c><ChangedAppVersions></c> corresponds to changed applications - with new version numbers in this OTP version, and - <c><UnchangedAppVersions></c> corresponds to unchanged application - versions in this OTP version. Both of them might be empty, although - not at the same time. If <ChangedAppVersions> is empty, no changes - has been made that change the build result of any application. This could - for example be a pure bug fix of the build system. The order of lines - is undefined. All white space characters in this file are either space - (character 32) or line-break (character 10).</p> - <p>Using ordinary UNIX tools like <c>sed</c> and <c>grep</c> one - can easily find answers to various questions like:</p> - <taglist> - <tag>Which OTP versions are <c>kernel-3.0</c> part of?</tag> - <item><p><c> $ grep ' kernel-3\.0 ' otp_versions.table</c></p></item> - <tag>In which OTP version was <c>kernel-3.0</c> introduced?</tag> - <item><p><c> $ sed 's/#.*//;/ kernel-3\.0 /!d' otp_versions.table</c></p></item> - </taglist> - <p>The above commands give a bit more information than the exact answers, - but adequate information when manually searching for answers to these - questions.</p> - <warning><p>The format of the <c>otp_versions.table</c> might be subject - to changes during the OTP 17 release.</p></warning> - </section> + <section> + <title>OTP Versions Table</title> + <p>The text file <c><OTP source root>/otp_versions.table</c>, + which is part of the source code, contains information about all + OTP versions from OTP 17.0 up to the current OTP version. Each line + contains information about application versions that are part of a + specific OTP version, and has the following format:</p> + <pre> +<OtpVersion> : <ChangedAppVersions> # <UnchangedAppVersions> :</pre> + <p><c><OtpVersion></c> has the format <c>OTP-<VSN></c>, + that is, the same as the git tag used to identify the source.</p> + <p><c><ChangedAppVersions></c> and + <c><UnchangedAppVersions></c> are space-separated lists of + application versions and has the format + <c><application>-<vsn></c>.</p> + <list type="bulleted"> + <item><c><ChangedAppVersions></c> corresponds to changed + applications with new version numbers in this OTP version.</item> + <item><c><UnchangedAppVersions></c> corresponds to unchanged + application versions in this OTP version.</item> + </list> + <p>Both of them can be empty, but not at the same time. + If <c><ChangedAppVersions></c> is empty, no changes have + been made that change the build result of any application. This could, + for example, be a pure bug fix of the build system. The order of lines + is undefined. All white-space characters in this file are either space + (character 32) or line-break (character 10).</p> + <p>By using ordinary UNIX tools like <c>sed</c> and <c>grep</c> one + can easily find answers to various questions like:</p> + <list type="bulleted"> + <item><p>Which OTP versions are <c>kernel-3.0</c> part of?</p> + <p><c>$ grep ' kernel-3\.0 ' otp_versions.table</c> </p></item> + <item><p>In which OTP version was <c>kernel-3.0</c> introduced?</p> + <p><c>$ sed 's/#.*//;/ kernel-3\.0 /!d' otp_versions.table</c> + </p></item> + </list> + <p>The above commands give a bit more information than the exact + answers, but adequate information when manually searching for answers + to these questions.</p> + <warning><p>The format of the <c>otp_versions.table</c> might be + subject to changes during the OTP 17 release.</p></warning> </section> + </section> - <section><title>Application Version</title> - <p>As of OTP 17.0 application versions will use the same + <section> + <title>Application Version</title> + <p>As of OTP 17.0 application versions use the same <seealso marker="#version_scheme">version scheme</seealso> as the OTP version. Application versions part of a release candidate will however not have an <c>-rc<N></c> suffix as the OTP version. @@ -125,88 +132,88 @@ necessarily imply a major increment of the OTP version. This depends on whether the major change in the application is considered as a major change for OTP as a whole or not.</p> - </section> + </section> + <section> + <title>Version Scheme</title> <marker id="version_scheme"/> - <section><title>Version Scheme</title> - <note>Note that the version scheme was changed as of OTP 17.0. This implies + <note><p>The version scheme was changed as of OTP 17.0. This implies that application versions used prior to OTP 17.0 do not adhere to this version scheme. <seealso marker="#otp_17_0_app_versions">A list of - application versions used in OTP 17.0</seealso> can be found - at the end of this document.</note> - - <p>In the normal case, a version will be constructed as - <c><Major>.<Minor>.<Patch></c> where <c><Major></c> - is the most significant part. However, more dot separated parts than - this may exist. The dot separated parts consists of non-negative integers. - If all parts less significant than <c><Minor></c> equals <c>0</c>, - they are omitted. The three normal parts - <c><Major>.<Minor>.<Patch></c> will be changed as + application versions used in OTP 17.0</seealso> is included at the + end of this section</p></note> + <p>In the normal case, a version is constructed as + <c><Major>.<Minor>.<Patch></c>, + where <c><Major></c> is the most significant part.</p> + <p>However, more dot-separated parts than this can exist. + The dot-separated parts consist of non-negative integers. If + all parts less significant than <c><Minor></c> equals + <c>0</c>, they are omitted. The three normal parts + <c><Major>.<Minor>.<Patch></c> are changed as follows:</p> - <taglist> - <tag><c><Major></c></tag><item>Increased when major changes, - including incompatibilities, have been made.</item> - <tag><c><Minor></c></tag><item>Increased when new functionality - has been added.</item> - <tag><c><Patch></c></tag><item>Increased when pure bug fixes - have been made.</item> - </taglist> - <p>When a part in the version number is increased, all less significant + <list type="bulleted"> + <item><c><Major></c> - Increases when major changes, + including incompatibilities, are made.</item> + <item><c><Minor></c> - Increases when new + functionality is added.</item> + <item><c><Patch></c> - Increases when pure bug fixes + are made.</item> + </list> + <p>When a part in the version number increases, all less significant parts are set to <c>0</c>.</p> - <p>An application version or an OTP version identifies source code - versions. That is, it does not imply anything about how the application + versions. That is, it implies nothing about how the application or OTP has been built.</p> - <section><title>Order of Versions</title> - <p>Version numbers in general are only partially ordered. However, - normal version numbers (with three parts) as of OTP 17.0 have a total - or linear order. This applies both to normal OTP versions and - normal application versions.</p> - - <p>When comparing two version numbers that have an order, one - compare each part as ordinary integers from the most - significant part towards less significant parts. The order is - defined by the first parts of the same significance that - differ. An OTP version with a larger version include all - changes that that are part of a smaller OTP version. The same - goes for application versions.</p> - - <p>In the general case, versions may have more than three parts. In - this case the versions are only partially ordered. Note that such - versions are only used in exceptional cases. When an extra - part (out of the normal three parts) is added to a version number, - a new branch of versions is made. The new branch has a linear - order against the base version. However, versions on different - branches have no order. Since they have no order, we - only know that they all include what is included in their - closest common ancestor. When branching multiple times from the - same base version, <c>0</c> parts are added between the base - version and the least significant <c>1</c> part until a unique - version is found. Versions that have an order can be compared - as described in the paragraph above.</p> - - <p>An example of branched versions: The version <c>6.0.2.1</c> - is a branched version from the base version <c>6.0.2</c>. - Versions on the form <c>6.0.2.<X></c> can be compared - with normal versions smaller than or equal to <c>6.0.2</c>, - and other versions on the form <c>6.0.2.<X></c>. The - version <c>6.0.2.1</c> will include all changes in - <c>6.0.2</c>. However, <c>6.0.3</c> will most likely - <em>not</em> include all changes in <c>6.0.2.1</c> (note that - these versions have no order). A second branched version from the base - version <c>6.0.2</c> will be version <c>6.0.2.0.1</c>, and a - third branched version will be <c>6.0.2.0.0.1</c>.</p> - </section> + <section> + <title>Order of Versions</title> + <p>Version numbers in general are only partially ordered. However, + normal version numbers (with three parts) as of OTP 17.0 have a total + or linear order. This applies both to normal OTP versions and + normal application versions.</p> + <p>When comparing two version numbers that have an order, one + compare each part as ordinary integers from the most + significant part to less significant parts. The order is + defined by the first parts of the same significance that + differ. An OTP version with a larger version includes all + changes that are part of a smaller OTP version. The same + goes for application versions.</p> + <p>In general, versions can have more than three parts. + The versions are then only partially ordered. Such + versions are only used in exceptional cases. When an extra + part (out of the normal three parts) is added to a version number, + a new branch of versions is made. The new branch has a linear + order against the base version. However, versions on different + branches have no order, and therefore one can only conclude + that they all include what is included in their + closest common ancestor. When branching multiple times from the + same base version, <c>0</c> parts are added between the base + version and the least significant <c>1</c> part until a unique + version is found. Versions that have an order can be compared + as described in the previous paragraph.</p> + <p>An example of branched versions: The version <c>6.0.2.1</c> + is a branched version from the base version <c>6.0.2</c>. + Versions on the form <c>6.0.2.<X></c> can be compared + with normal versions smaller than or equal to <c>6.0.2</c>, + and other versions on the form <c>6.0.2.<X></c>. The + version <c>6.0.2.1</c> will include all changes in + <c>6.0.2</c>. However, <c>6.0.3</c> will most likely + <em>not</em> include all changes in <c>6.0.2.1</c> (note that + these versions have no order). A second branched version from the base + version <c>6.0.2</c> will be version <c>6.0.2.0.1</c>, and a + third branched version will be <c>6.0.2.0.0.1</c>.</p> </section> + </section> + <section> + <title>OTP 17.0 Application Versions</title> <marker id="otp_17_0_app_versions"/> - <section><title>OTP 17.0 Application Versions</title> - <p>The following application versions were part of OTP 17.0. If - the normal part of an applications version number compares - as smaller than the corresponding application version in this list, + <p>The following list details the application versions that + were part of OTP 17.0. If + the normal part of an application version number compares + as smaller than the corresponding application version in the list, the version number does not adhere to the version scheme introduced - in OTP 17.0 and should be considered as not having an order against + in OTP 17.0 and is to be considered as not having an order against versions used as of OTP 17.0.</p> <list> <item><c>asn1-3.0</c></item> @@ -262,6 +269,6 @@ <item><c>wx-1.2</c></item> <item><c>xmerl-1.3.7</c></item> </list> - </section> + </section> </chapter> diff --git a/system/doc/tutorial/c_port.xmlsrc b/system/doc/tutorial/c_port.xmlsrc index 8579da8520..0631293237 100644 --- a/system/doc/tutorial/c_port.xmlsrc +++ b/system/doc/tutorial/c_port.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,16 +28,34 @@ <rev></rev> <file>c_port.xml</file> </header> - <p>This is an example of how to solve the <seealso marker="example">example problem</seealso> by using a port.</p> + <p>This section outlines an example of how to solve the example + problem in the <seealso marker="example">previous section</seealso> + by using a port.</p> + <p>The scenario is illustrated in the following figure:</p> <image file="../tutorial/port.gif"> - <icaption>Port Communication.</icaption> + <icaption>Port Communication</icaption> </image> <section> <title>Erlang Program</title> - <p>First of all communication between Erlang and C must be established by creating the port. The Erlang process which creates a port is said to be <em>the connected process</em> of the port. All communication to and from the port should go via the connected process. If the connected process terminates, so will the port (and the external program, if it is written correctly).</p> - <p>The port is created using the BIF <c>open_port/2</c> with <c>{spawn,ExtPrg}</c> as the first argument. The string <c>ExtPrg</c> is the name of the external program, including any command line arguments. The second argument is a list of options, in this case only <c>{packet,2}</c>. This option says that a two byte length indicator will be used to simplify the communication between C and Erlang. Adding the length indicator will be done automatically by the Erlang port, but must be done explicitly in the external C program.</p> - <p>The process is also set to trap exits which makes it possible to detect if the external program fails.</p> + <p>All communication between Erlang and C must be established by + creating the port. The Erlang process that creates a port is + said to be <em>the connected process</em> of the port. All + communication to and from the port must go through the connected + process. If the connected process terminates, the port also + terminates (and the external program, if it is written + properly).</p> + <p>The port is created using the BIF <c>open_port/2</c> with + <c>{spawn,ExtPrg}</c> as the first argument. The string + <c>ExtPrg</c> is the name of the external program, including any + command line arguments. The second argument is a list of + options, in this case only <c>{packet,2}</c>. This option says + that a 2 byte length indicator is to be used to simplify the + communication between C and Erlang. The Erlang port + automatically adds the length indicator, but this must be done + explicitly in the external C program.</p> + <p>The process is also set to trap exits, which enables detection + of failure of the external program:</p> <pre> -module(complex1). -export([start/1, init/1]). @@ -50,7 +68,9 @@ init(ExtPrg) -> process_flag(trap_exit, true), Port = open_port({spawn, ExtPrg}, [{packet, 2}]), loop(Port).</pre> - <p>Now it is possible to implement <c>complex1:foo/1</c> and <c>complex1:bar/1</c>. They both send a message to the <c>complex</c> process and receive the reply.</p> + <p>Now <c>complex1:foo/1</c> and <c>complex1:bar/1</c> can be + implemented. Both send a message to the <c>complex</c> process + and receive the following replies:</p> <pre> foo(X) -> call_port({foo, X}). @@ -63,7 +83,14 @@ call_port(Msg) -> {complex, Result} -> Result end.</pre> - <p>The <c>complex</c> process encodes the message into a sequence of bytes, sends it to the port, waits for a reply, decodes the reply and sends it back to the caller.</p> + <p>The <c>complex</c> process does the following:</p> + <list type="bulleted"> + <item>Encodes the message into a sequence of bytes.</item> + <item>Sends it to the port.</item> + <item>Waits for a reply.</item> + <item>Decodes the reply.</item> + <item>Sends it back to the caller:</item> + </list> <pre> loop(Port) -> receive @@ -75,37 +102,52 @@ loop(Port) -> end, loop(Port) end.</pre> - <p>Assuming that both the arguments and the results from the C functions will be less than 256, a very simple encoding/decoding scheme is employed where <c>foo</c> is represented by the byte 1, <c>bar</c> is represented by 2, and the argument/result is represented by a single byte as well.</p> + <p>Assuming that both the arguments and the results from the C + functions are less than 256, a simple encoding/decoding scheme + is employed. In this scheme, <c>foo</c> is represented by byte + 1, <c>bar</c> is represented by 2, and the argument/result is + represented by a single byte as well:</p> <pre> encode({foo, X}) -> [1, X]; encode({bar, Y}) -> [2, Y]. - + decode([Int]) -> Int.</pre> - <p>The resulting Erlang program, including functionality for stopping the port and detecting port failures is shown below. + <p>The resulting Erlang program, including functionality for + stopping the port and detecting port failures, is as follows: </p> <codeinclude file="complex1.erl" type="erl"/> </section> <section> <title>C Program</title> - <p>On the C side, it is necessary to write functions for receiving and sending - data with two byte length indicators from/to Erlang. By default, the C program - should read from standard input (file descriptor 0) and write to standard output - (file descriptor 1). Examples of such functions, <c>read_cmd/1</c> and - <c>write_cmd/2</c>, are shown below.</p> + <p>On the C side, it is necessary to write functions for receiving + and sending data with 2 byte length indicators from/to Erlang. + By default, the C program is to read from standard input (file + descriptor 0) and write to standard output (file descriptor 1). + Examples of such functions, <c>read_cmd/1</c> and + <c>write_cmd/2</c>, follows:</p> <codeinclude file="erl_comm.c" type="erl"/> - <p>Note that <c>stdin</c> and <c>stdout</c> are for buffered input/output and should not be used for the communication with Erlang!</p> - <p>In the <c>main</c> function, the C program should listen for a message from Erlang and, according to the selected encoding/decoding scheme, use the first byte to determine which function to call and the second byte as argument to the function. The result of calling the function should then be sent back to Erlang.</p> + <p>Notice that <c>stdin</c> and <c>stdout</c> are for buffered + input/output and must <em>not</em> be used for the communication + with Erlang.</p> + <p>In the <c>main</c> function, the C program is to listen for a + message from Erlang and, according to the selected + encoding/decoding scheme, use the first byte to determine which + function to call and the second byte as argument to the + function. The result of calling the function is then to be sent + back to Erlang:</p> <codeinclude file="port.c" tag="" type="none"></codeinclude> - <p>Note that the C program is in a <c>while</c>-loop checking for the return value of <c>read_cmd/1</c>. The reason for this is that the C program must detect when the port gets closed and terminate.</p> + <p>Notice that the C program is in a <c>while</c>-loop, checking + for the return value of <c>read_cmd/1</c>. This is because the C + program must detect when the port closes and terminates.</p> </section> <section> <title>Running the Example</title> - <p>1. Compile the C code.</p> + <p><em>Step 1.</em> Compile the C code:</p> <pre> unix> <input>gcc -o extprg complex.c erl_comm.c port.c</input></pre> - <p>2. Start Erlang and compile the Erlang code.</p> + <p><em>Step 2.</em> Start Erlang and compile the Erlang code:</p> <pre> unix> <input>erl</input> Erlang (BEAM) emulator version 4.9.1.2 @@ -113,7 +155,7 @@ Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) 1> <input>c(complex1).</input> {ok,complex1}</pre> - <p>3. Run the example.</p> + <p><em>Step 3.</em> Run the example:</p> <pre> 2> <input>complex1:start("extprg").</input> <0.34.0> diff --git a/system/doc/tutorial/c_portdriver.xmlsrc b/system/doc/tutorial/c_portdriver.xmlsrc index 2fd6fb0aac..61187703a4 100644 --- a/system/doc/tutorial/c_portdriver.xmlsrc +++ b/system/doc/tutorial/c_portdriver.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,46 +21,45 @@ </legalnotice> - <title>Port drivers</title> + <title>Port Drivers</title> <prepared></prepared> <docno></docno> <date></date> <rev></rev> <file>c_portdriver.xml</file> </header> - <p>This is an example of how to solve the <seealso marker="example">example problem</seealso> by using a linked in port driver.</p> + <p>This section outlines an example of how to solve the example problem + in <seealso marker="example">Problem Example</seealso> + by using a linked-in port driver.</p> + <p>A port driver is a linked-in driver that is accessible as a port + from an Erlang program. It is a shared library (SO in UNIX, DLL in + Windows), with special entry points. The Erlang runtime system + calls these entry points when the driver is started and when data + is sent to the port. The port driver can also send data to + Erlang.</p> + <p>As a port driver is dynamically linked into the emulator process, + this is the fastest way of calling C-code from Erlang. Calling + functions in the port driver requires no context switches. But it + is also the least safe way, because a crash in the port driver + brings the emulator down too.</p> + <p>The scenario is illustrated in the following figure:</p> <image file="../tutorial/port_driver.gif"> - <icaption>Port Driver Communication.</icaption> + <icaption>Port Driver Communication</icaption> </image> <section> - <title>Port Drivers</title> - <p>A port driver is a linked in driver that is accessible as a - port from an Erlang program. It is a shared library (SO in Unix, - DLL in Windows), with special entry points. The Erlang runtime - calls these entry points, when the driver is started and when - data is sent to the port. The port driver can also send data to - Erlang.</p> - <p>Since a port driver is dynamically linked into the emulator - process, this is the fastest way of calling C-code from Erlang. - Calling functions in the port driver requires no context - switches. But it is also the least safe, because a crash in the - port driver brings the emulator down too.</p> - </section> - - <section> <title>Erlang Program</title> - <p>Just as with a port program, the port communicates with a Erlang + <p>Like a port program, the port communicates with an Erlang process. All communication goes through one Erlang process that is the <em>connected process</em> of the port driver. Terminating this process closes the port driver.</p> <p>Before the port is created, the driver must be loaded. This is done with the function <c>erl_dll:load_driver/1</c>, with the name of the shared library as argument.</p> - <p>The port is then created using the BIF <c>open_port/2</c> with + <p>The port is then created using the BIF <c>open_port/2</c>, with the tuple <c>{spawn, DriverName}</c> as the first argument. The string <c>SharedLib</c> is the name of the port driver. The second - argument is a list of options, none in this case.</p> + argument is a list of options, none in this case:</p> <pre> -module(complex5). -export([start/1, init/1]). @@ -77,9 +76,9 @@ init(SharedLib) -> register(complex, self()), Port = open_port({spawn, SharedLib}, []), loop(Port).</pre> - <p>Now it is possible to implement <c>complex5:foo/1</c> and - <c>complex5:bar/1</c>. They both send a message to the - <c>complex</c> process and receive the reply.</p> + <p>Now <c>complex5:foo/1</c> and <c>complex5:bar/1</c> + can be implemented. Both send a message to the + <c>complex</c> process and receive the following reply:</p> <pre> foo(X) -> call_port({foo, X}). @@ -92,10 +91,14 @@ call_port(Msg) -> {complex, Result} -> Result end.</pre> - <p>The <c>complex</c> process encodes the message into a sequence - of bytes, sends it to the port, waits for a reply, decodes the - reply and sends it back to the caller. - </p> + <p>The <c>complex</c> process performs the following:</p> + <list type="bulleted"> + <item>Encodes the message into a sequence of bytes.</item> + <item>Sends it to the port.</item> + <item>Waits for a reply.</item> + <item>Decodes the reply.</item> + <item>Sends it back to the caller:</item> + </list> <pre> loop(Port) -> receive @@ -108,59 +111,58 @@ loop(Port) -> loop(Port) end.</pre> <p>Assuming that both the arguments and the results from the C - functions will be less than 256, a very simple encoding/decoding - scheme is employed where <c>foo</c> is represented by the byte + functions are less than 256, a simple encoding/decoding scheme + is employed. In this scheme, <c>foo</c> is represented by byte 1, <c>bar</c> is represented by 2, and the argument/result is - represented by a single byte as well. - </p> + represented by a single byte as well:</p> <pre> encode({foo, X}) -> [1, X]; encode({bar, Y}) -> [2, Y]. - + decode([Int]) -> Int.</pre> - <p>The resulting Erlang program, including functionality for - stopping the port and detecting port failures is shown below.</p> + <p>The resulting Erlang program, including functions for stopping + the port and detecting port failures, is as follows:</p> <codeinclude file="complex5.erl" type="erl"/> </section> <section> <title>C Driver</title> <p>The C driver is a module that is compiled and linked into a - shared library. It uses a driver structure, and includes the + shared library. It uses a driver structure and includes the header file <c>erl_driver.h</c>.</p> <p>The driver structure is filled with the driver name and function pointers. It is returned from the special entry point, declared with the macro <c><![CDATA[DRIVER_INIT(<driver_name>)]]></c>.</p> - <p>The functions for receiving and sending data, are combined into + <p>The functions for receiving and sending data are combined into a function, pointed out by the driver structure. The data sent - into the port is given as arguments, and the data the port - sends back is sent with the C-function <c>driver_output</c>.</p> - <p>Since the driver is a shared module, not a program, no main - function should be present. All function pointers are not used - in our example, and the corresponding fields in the + into the port is given as arguments, and the replied data is sent + with the C-function <c>driver_output</c>.</p> + <p>As the driver is a shared module, not a program, no main + function is present. All function pointers are not used + in this example, and the corresponding fields in the <c>driver_entry</c> structure are set to NULL.</p> - <p>All functions in the driver, takes a handle (returned from - <c>start</c>), that is just passed along by the erlang + <p>All functions in the driver takes a handle (returned from + <c>start</c>) that is just passed along by the Erlang process. This must in some way refer to the port driver instance.</p> - <p>The example_drv_start, is the only function that is called with - a handle to the port instance, so we must save this. It is - customary to use a allocated driver-defined structure for this - one, and pass a pointer back as a reference.</p> - <p>It is not a good idea to use a global variable; since the port - driver can be spawned by multiple Erlang processes, this - driver-structure should be instantiated multiple times. + <p>The <c>example_drv_start</c>, is the only function that is called with + a handle to the port instance, so this must be saved. It is + customary to use an allocated driver-defined structure for this + one, and to pass a pointer back as a reference.</p> + <p>It is not a good idea to use a global variable as the port + driver can be spawned by multiple Erlang processes. This + driver-structure is to be instantiated multiple times: </p> <codeinclude file="port_driver.c" tag="" type="none"></codeinclude> </section> <section> <title>Running the Example</title> - <p>1. Compile the C code.</p> + <p><em>Step 1.</em> Compile the C code:</p> <pre> unix> <input>gcc -o exampledrv -fpic -shared complex.c port_driver.c</input> windows> <input>cl -LD -MD -Fe exampledrv.dll complex.c port_driver.c</input></pre> - <p>2. Start Erlang and compile the Erlang code.</p> + <p><em>Step 2.</em> Start Erlang and compile the Erlang code:</p> <pre> > <input>erl</input> Erlang (BEAM) emulator version 5.1 @@ -168,7 +170,7 @@ Erlang (BEAM) emulator version 5.1 Eshell V5.1 (abort with ^G) 1> <input>c(complex5).</input> {ok,complex5}</pre> - <p>3. Run the example.</p> + <p><em>Step 3.</em> Run the example:</p> <pre> 2> <input>complex5:start("example_drv").</input> <0.34.0> diff --git a/system/doc/tutorial/cnode.xmlsrc b/system/doc/tutorial/cnode.xmlsrc index 293406160f..bcdd1298de 100644 --- a/system/doc/tutorial/cnode.xmlsrc +++ b/system/doc/tutorial/cnode.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,19 +28,39 @@ <rev></rev> <file>cnode.xml</file> </header> - <p>This is an example of how to solve the <seealso marker="example">example problem</seealso> by using a C node. Note that a C node would not typically be used for solving a simple problem like this, a port would suffice.</p> + <p>This section outlines an example of how to solve the example + problem in <seealso marker="example">Problem Example</seealso> + by using a C node. Notice that a C node is not typically + used for solving simple problems like this, a port is + sufficient.</p> <section> <title>Erlang Program</title> - <p>From Erlang's point of view, the C node is treated like a normal Erlang node. Therefore, calling the functions <c>foo</c> and <c>bar</c> only involves sending a message to the C node asking for the function to be called, and receiving the result. Sending a message requires a recipient; a process which can be defined using either a pid or a tuple consisting of a registered name and a node name. In this case a tuple is the only alternative as no pid is known.</p> + <p>From Erlang's point of view, the C node is treated like a + normal Erlang node. Thus, calling the functions <c>foo</c> and + <c>bar</c> only involves sending a message to the C node asking + for the function to be called, and receiving the result. Sending + a message requires a recipient, that is, a process that can be + defined using either a pid or a tuple, consisting of a + registered name and a node name. In this case, a tuple is the + only alternative as no pid is known:</p> <pre> {RegName, Node} ! Msg</pre> - <p>The node name <c>Node</c> should be the name of the C node. If short node names are used, the plain name of the node will be <c>cN</c> where <c>N</c> is an integer. If long node names are used, there is no such restriction. An example of a C node name using short node names is thus <c>c1@idril</c>, an example using long node names is <c>[email protected]</c>.</p> - <p>The registered name <c>RegName</c> could be any atom. The name can be ignored by the C code, or it could be used for example to distinguish between different types of messages. Below is an example of what the Erlang code could look like when using short node names. + <p>The node name <c>Node</c> is to be the name of the C node. If + short node names are used, the plain name of the node is + <c>cN</c>, where <c>N</c> is an integer. If long node names are + used, there is no such restriction. An example of a C node name + using short node names is thus <c>c1@idril</c>, an example using + long node names is <c>[email protected]</c>.</p> + <p>The registered name, <c>RegName</c>, can be any atom. The name + can be ignored by the C code, or, for example, be used to + distinguish between different types of messages. An example of + Erlang code using short node names follows: </p> <codeinclude file="complex3.erl" tag="" type="erl"></codeinclude> <p> - When using long node names the code is slightly different as shown in the following example: + When using long node names, the code is slightly different as + shown in the following example: </p> <codeinclude file="complex4.erl" tag="" type="erl"></codeinclude> @@ -50,39 +70,77 @@ <title>C Program</title> <section> - <title>Setting Up the Communication</title> - <p>Before calling any other Erl_Interface function, the memory handling must be initiated.</p> + <title>Setting Up Communication</title> + <p>Before calling any other function in Erl_Interface, the + memory handling must be initiated:</p> <pre> erl_init(NULL, 0);</pre> - <p>Now the C node can be initiated. If short node names are used, this is done by calling <c>erl_connect_init()</c>.</p> + <p>Now the C node can be initiated. If short node names are + used, this is done by calling <c>erl_connect_init()</c>:</p> <pre> erl_connect_init(1, "secretcookie", 0);</pre> - <p>The first argument is the integer which is used to construct the node name. In the example the plain node name will be <c>c1</c>. <br></br> - - The second argument is a string defining the magic cookie. <br></br> - - The third argument is an integer which is used to identify a particular instance of a C node.</p> - <p>If long node node names are used, initiation is done by calling <c>erl_connect_xinit()</c>.</p> + <p>Here:</p> + <list type="bulleted"> + <item>The first argument is the integer used to construct the node name. + <p>In the example, the plain node name is <c>c1</c>.</p></item> + <item>The second argument is a string defining the magic cookie.</item> + <item>The third argument is an integer that is used to identify + a particular instance of a C node.</item> + </list> + <p>If long node node names are used, initiation is done by + calling <c>erl_connect_xinit()</c>:</p> <pre> erl_connect_xinit("idril", "cnode", "[email protected]", &addr, "secretcookie", 0);</pre> - <p>The first three arguments are the host name, the plain node name, and the full node name. The fourth argument is a pointer to an <c>in_addr</c> struct with the IP address of the host, and the fifth and sixth arguments are the magic cookie and instance number.</p> - <p>The C node can act as a server or a client when setting up the communication Erlang-C. If it acts as a client, it connects to an Erlang node by calling <c>erl_connect()</c>, which will return an open file descriptor at success.</p> + <p>Here:</p> + <list type="bulleted"> + <item>The first argument is the host name.</item> + <item>The second argument is the plain node name.</item> + <item>The third argument is the full node name.</item> + <item>The fourth argument is a pointer to an <c>in_addr</c> + struct with the IP address of the host.</item> + <item>The fifth argument is the magic cookie.</item> + <item>The sixth argument is the instance number.</item> + </list> + <p>The C node can act as a server or a client when setting up + the Erlang-C communication. If it acts as a client, it + connects to an Erlang node by calling <c>erl_connect()</c>, + which returns an open file descriptor at success:</p> <pre> fd = erl_connect("e1@idril");</pre> - <p>If the C node acts as a server, it must first create a socket (call <c>bind()</c> and <c>listen()</c>) listening to a certain port number <c>port</c>. It then publishes its name and port number with <c>epmd</c> (the Erlang port mapper daemon, see the man page for <c>epmd</c>).</p> + <p>If the C node acts as a server, it must first create a socket + (call <c>bind()</c> and <c>listen()</c>) listening to a + certain port number <c>port</c>. It then publishes its name + and port number with <c>epmd</c>, the Erlang port mapper + daemon. For details, see the <seealso + marker="erts:epmd">epmd</seealso> manual page in ERTS:</p> <pre> erl_publish(port);</pre> - <p>Now the C node server can accept connections from Erlang nodes.</p> + <p>Now the C node server can accept connections from Erlang nodes:</p> <pre> fd = erl_accept(listen, &conn);</pre> - <p>The second argument to <c>erl_accept</c> is a struct <c>ErlConnect</c> that will contain useful information when a connection has been established; for example, the name of the Erlang node.</p> + <p>The second argument to <c>erl_accept</c> is a struct + <c>ErlConnect</c> which contains useful information when a + connection has been established, for example, the name of the + Erlang node.</p> </section> <section> <title>Sending and Receiving Messages</title> - <p>The C node can receive a message from Erlang by calling <c>erl_receive msg()</c>. This function reads data from the open file descriptor <c>fd</c> into a buffer and puts the result in an <c>ErlMessage</c> struct <c>emsg</c>. <c>ErlMessage</c> has a field <c>type</c> defining which kind of data was received. In this case the type of interest is <c>ERL_REG_SEND</c> which indicates that Erlang sent a message to a registered process at the C node. The actual message, an <c>ETERM</c>, will be in the <c>msg</c> field.</p> - <p>It is also necessary to take care of the types <c>ERL_ERROR</c> (an error occurred) and <c>ERL_TICK</c> (alive check from other node, should be ignored). Other possible types indicate process events such as link/unlink and exit.</p> + <p>The C node can receive a message from Erlang by calling + <c>erl_receive msg()</c>. This function reads data from the + open file descriptor <c>fd</c> into a buffer and puts the + result in an <c>ErlMessage</c> struct <c>emsg</c>. + <c>ErlMessage</c> has a field <c>type</c> defining what kind + of data is received. In this case, the type of interest is + <c>ERL_REG_SEND</c> which indicates that Erlang sent a message + to a registered process at the C node. The actual message, an + <c>ETERM</c>, is in the <c>msg</c> field.</p> + <p>It is also necessary to take care of the types + <c>ERL_ERROR</c> (an error occurred) and <c>ERL_TICK</c> + (alive check from other node, is to be ignored). Other + possible types indicate process events such as link, unlink, + and exit:</p> <pre> while (loop) { @@ -93,7 +151,16 @@ fd = erl_accept(listen, &conn);</pre> loop = 0; /* exit while loop */ } else { if (emsg.type == ERL_REG_SEND) {</pre> - <p>Since the message is an <c>ETERM</c> struct, Erl_Interface functions can be used to manipulate it. In this case, the message will be a 3-tuple (because that was how the Erlang code was written, see above). The second element will be the pid of the caller and the third element will be the tuple <c>{Function,Arg}</c> determining which function to call with which argument. The result of calling the function is made into an <c>ETERM</c> struct as well and sent back to Erlang using <c>erl_send()</c>, which takes the open file descriptor, a pid and a term as arguments.</p> + <p>As the message is an <c>ETERM</c> struct, Erl_Interface + functions can be used to manipulate it. In this case, the + message becomes a 3-tuple, because that is how the Erlang code + is written. The second element will be the pid of the caller + and the third element will be the tuple <c>{Function,Arg}</c> + determining which function to call, and with which argument. + The result of calling the function is made into an + <c>ETERM</c> struct as well and sent back to Erlang using + <c>erl_send()</c>, which takes the open file descriptor, a + pid, and a term as arguments:</p> <pre> fromp = erl_element(2, emsg.msg); tuplep = erl_element(3, emsg.msg); @@ -108,29 +175,30 @@ fd = erl_accept(listen, &conn);</pre> resp = erl_format("{cnode, ~i}", res); erl_send(fd, fromp, resp);</pre> - <p>Finally, the memory allocated by the <c>ETERM</c> creating functions (including <c>erl_receive_msg()</c> must be freed.</p> + <p>Finally, the memory allocated by the <c>ETERM</c> creating + functions (including <c>erl_receive_msg()</c> must be + freed:</p> <pre> erl_free_term(emsg.from); erl_free_term(emsg.msg); erl_free_term(fromp); erl_free_term(tuplep); erl_free_term(fnp); erl_free_term(argp); erl_free_term(resp);</pre> - <p>The resulting C programs can be found in looks like the following examples. First a C node server using short node names.</p> + <p>The following examples show the resulting C programs. + First a C node server using short node names:</p> <codeinclude file="cnode_s.c" type="c"/> - <p>Below follows a C node server using long node names.</p> + <p>A C node server using long node names:</p> <codeinclude file="cnode_s2.c" type="c"/> - <p>And finally we have the code for the C node client.</p> + <p>Finally, the code for the C node client:</p> <codeinclude file="cnode_c.c" type="c"/> </section> </section> <section> <title>Running the Example</title> - <p>1. Compile the C code, providing the paths to the Erl_Interface include files and libraries, and to the <c>socket</c> and <c>nsl</c> libraries.</p> - <p>In R5B and later versions of OTP, the <c>include</c> and <c>lib</c> directories are situated under <c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is the root directory of the OTP installation (<c>/usr/local/otp</c> in the example above) and <c>VSN</c> is the version of the <c>erl_interface</c> application (3.2.1 in the example above). <br></br> - - In R4B and earlier versions of OTP, <c>include</c> and <c>lib</c> are situated under <c>OTPROOT/usr</c>.</p> + <p><em>Step 1.</em> Compile the C code. This provides the paths to + the Erl_Interface include files and libraries, and to the + <c>socket</c> and <c>nsl</c> libraries:</p> <pre> - > <input>gcc -o cserver \\ </input> <input>-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </input> <input>-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input> @@ -148,11 +216,29 @@ unix> <input>gcc -o cclient \\ </input> <input>-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input> <input>complex.c cnode_c.c \\ </input> <input>-lerl_interface -lei -lsocket -lnsl</input></pre> - <p>2. Compile the Erlang code.</p> + <p>In Erlang/OTP R5B and later versions of OTP, the + <c>include</c> and <c>lib</c> directories are situated under + <c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is + the root directory of the OTP installation + (<c>/usr/local/otp</c> in the recent example) and <c>VSN</c> is + the version of the Erl_Interface application (3.2.1 in the + recent example).</p> + <p>In R4B and earlier versions of OTP, <c>include</c> and + <c>lib</c> are situated under <c>OTPROOT/usr</c>.</p> + <p><em>Step 2.</em> Compile the Erlang code:</p> <pre> unix> <input>erl -compile complex3 complex4</input></pre> - <p>3. Run the C node server example with short node names.</p> - <p>Start the C program <c>cserver</c> and Erlang in different windows. <c>cserver</c> takes a port number as argument and must be started before trying to call the Erlang functions. The Erlang node should be given the short name <c>e1</c> and must be set to use the same magic cookie as the C node, <c>secretcookie</c>.</p> + <p><em>Step 3.</em> Run the C node server example with short node names.</p> + <p>Do as follows:</p> + <list type="bulleted"> + <item>Start the C program <c>cserver</c> and Erlang in + different windows.</item> + <item><c>cserver</c> takes a port number as argument and must + be started before trying to call the Erlang functions.</item> + <item>The Erlang node is to be given the short name <c>e1</c> + and must be set to use the same magic cookie as the C node, + <c>secretcookie</c>:</item> + </list> <pre> unix> <input>cserver 3456</input> @@ -164,7 +250,9 @@ Eshell V4.9.1.2 (abort with ^G) 4 (e1@idril)2> <input>complex3:bar(5).</input> 10</pre> - <p>4. Run the C node client example. Terminate <c>cserver</c> but not Erlang and start <c>cclient</c>. The Erlang node must be started before the C node client is.</p> + <p><em>Step 4.</em> Run the C node client example. Terminate + <c>cserver</c>, but not Erlang, and start <c>cclient</c>. The + Erlang node must be started before the C node client:</p> <pre> unix> <input>cclient</input> @@ -172,7 +260,7 @@ unix> <input>cclient</input> 4 (e1@idril)4> <input>complex3:bar(5).</input> 10</pre> - <p>5. Run the C node server, long node names, example.</p> + <p><em>Step 5.</em> Run the C node server example with long node names:</p> <pre> unix> <input>cserver2 3456</input> diff --git a/system/doc/tutorial/erl_interface.xmlsrc b/system/doc/tutorial/erl_interface.xmlsrc index 0c4c5a99c2..5751a945d6 100644 --- a/system/doc/tutorial/erl_interface.xmlsrc +++ b/system/doc/tutorial/erl_interface.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,14 +28,29 @@ <rev></rev> <file>erl_interface.xml</file> </header> - <p>This is an example of how to solve the <seealso marker="example">example problem</seealso> by using a port and <c>erl_interface</c>. It is necessary to read the <seealso marker="c_port">port example</seealso> before reading this chapter.</p> + + <p>This section outlines an example of how to solve the example + problem in <seealso marker="example">Problem Example</seealso> by + using a port and Erl_Interface. It is necessary to read the port + example in <seealso marker="c_port">Ports</seealso> before reading + this section.</p> <section> <title>Erlang Program</title> - <p>The example below shows an Erlang program communicating with a C program over a plain port with home made encoding.</p> - <codeinclude file="complex1.erl" type="erl"/> - <p>Compared to the Erlang module - above used for the plain port, there are two differences when using Erl_Interface on the C side: Since Erl_Interface operates on the Erlang external term format the port must be set to use binaries and, instead of inventing an encoding/decoding scheme, the BIFs <c>term_to_binary/1</c> and <c>binary_to_term/1</c> should be used. That is:</p> + <p>The following example shows an Erlang program communicating + with a C program over a plain port with home made encoding:</p> + <codeinclude file="complex1.erl" type="erl"/> + <p>There are two differences when using Erl_Interface on the C + side compared to the example in <seealso marker="c_port"> + Ports</seealso>, using only the plain port:</p> + <list type="bulleted"> + <item>As Erl_Interface operates on the Erlang external term format, + the port must be set to use binaries.</item> + <item>Instead of inventing an encoding/decoding scheme, the + <c>term_to_binary/1</c> and <c>binary_to_term/1</c> BIFs are to + be used.</item> + </list> + <p>That is:</p> <pre> open_port({spawn, ExtPrg}, [{packet, 2}])</pre> <p>is replaced with:</p> @@ -55,69 +70,110 @@ receive {Port, {data, Data}} -> Caller ! {complex, binary_to_term(Data)} end</pre> - <p>The resulting Erlang program is shown below.</p> + <p>The resulting Erlang program is as follows:</p> <codeinclude file="complex2.erl" type="erl"/> - <p>Note that calling <c>complex2:foo/1</c> and <c>complex2:bar/1</c> will result in the tuple <c>{foo,X}</c> or <c>{bar,Y}</c> being sent to the <c>complex</c> process, which will code them as binaries and send them to the port. This means that the C program must be able to handle these two tuples.</p> + <p>Notice that calling <c>complex2:foo/1</c> and + <c>complex2:bar/1</c> results in the tuple <c>{foo,X}</c> or + <c>{bar,Y}</c> being sent to the <c>complex</c> process, which + codes them as binaries and sends them to the port. This means + that the C program must be able to handle these two tuples.</p> </section> <section> <title>C Program</title> - <p>The example below shows a C program communicating with an Erlang program over a plain port with home made encoding.</p> + <p>The following example shows a C program communicating with an + Erlang program over a plain port with home made encoding:</p> <codeinclude file="port.c" type="c"/> - <p>Compared to the C program above - used for the plain port the <c>while</c>-loop must be rewritten. Messages coming from the port will be on the Erlang external term format. They should be converted into an <c>ETERM</c> struct, a C struct similar to an Erlang term. The result of calling <c>foo()</c> or <c>bar()</c> must be converted to the Erlang external term format before being sent back to the port. But before calling any other <c>erl_interface</c> function, the memory handling must be initiated.</p> + <p>Compared to the C program in <seealso marker="c_port"> + Ports</seealso>, using only the plain port, the + <c>while</c>-loop must be rewritten. Messages coming from the + port is on the Erlang external term format. They must be + converted into an <c>ETERM</c> struct, which is a C struct + similar to an Erlang term. The result of calling <c>foo()</c> or + <c>bar()</c> must be converted to the Erlang external term + format before being sent back to the port. But before calling + any other Erl_Interface function, the memory handling must be + initiated:</p> <pre> erl_init(NULL, 0);</pre> - <p>For reading from and writing to the port the functions <c>read_cmd()</c> and <c>write_cmd()</c> from the erl_comm.c example below - can still be used. + <p>The following functions, <c>read_cmd()</c> and + <c>write_cmd()</c>, from the <c>erl_comm.c</c> example in + <seealso marker="c_port">Ports</seealso> can still be + used for reading from and writing to the port: </p> <codeinclude file="erl_comm.c" type="c"/> - <p>The function <c>erl_decode()</c> from <c>erl_marshal</c> will convert the binary into an <c>ETERM</c> struct.</p> + <p>The function <c>erl_decode()</c> from <c>erl_marshal</c> + converts the binary into an <c>ETERM</c> struct:</p> <pre> int main() { ETERM *tuplep; while (read_cmd(buf) > 0) { tuplep = erl_decode(buf);</pre> - <p>In this case <c>tuplep</c> now points to an <c>ETERM</c> struct representing a tuple with two elements; the function name (atom) and the argument (integer). By using the function <c>erl_element()</c> from <c>erl_eterm</c> it is possible to extract these elements, which also must be declared as pointers to an <c>ETERM</c> struct.</p> + <p>Here, <c>tuplep</c> points to an <c>ETERM</c> struct + representing a tuple with two elements; the function name (atom) + and the argument (integer). Using the function + <c>erl_element()</c> from <c>erl_eterm</c>, these elements can + be extracted, but they must also be declared as pointers to an + <c>ETERM</c> struct:</p> <pre> fnp = erl_element(1, tuplep); argp = erl_element(2, tuplep);</pre> - <p>The macros <c>ERL_ATOM_PTR</c> and <c>ERL_INT_VALUE</c> from <c>erl_eterm</c> can be used to obtain the actual values of the atom and the integer. The atom value is represented as a string. By comparing this value with the strings "foo" and "bar" it can be decided which function to call.</p> + <p>The macros <c>ERL_ATOM_PTR</c> and <c>ERL_INT_VALUE</c> from + <c>erl_eterm</c> can be used to obtain the actual values of the + atom and the integer. The atom value is represented as a string. + By comparing this value with the strings "foo" and "bar", it can + be decided which function to call:</p> <pre> if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) { res = foo(ERL_INT_VALUE(argp)); } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) { res = bar(ERL_INT_VALUE(argp)); }</pre> - <p>Now an <c>ETERM</c> struct representing the integer result can be constructed using the function <c>erl_mk_int()</c> from <c>erl_eterm</c>. It is also possible to use the function <c>erl_format()</c> from the module <c>erl_format</c>.</p> + <p>Now an <c>ETERM</c> struct that represents the integer result + can be constructed using the function <c>erl_mk_int()</c> from + <c>erl_eterm</c>. The function + <c>erl_format()</c> from the module <c>erl_format</c> can also + be used:</p> <pre> intp = erl_mk_int(res);</pre> - <p>The resulting <c>ETERM</c> struct is converted into the Erlang external term format using the function <c>erl_encode()</c> from <c>erl_marshal</c> and sent to Erlang using <c>write_cmd()</c>.</p> + <p>The resulting <c>ETERM</c> struct is converted into the Erlang + external term format using the function <c>erl_encode()</c> from + <c>erl_marshal</c> and sent to Erlang using + <c>write_cmd()</c>:</p> <pre> erl_encode(intp, buf); write_cmd(buf, erl_eterm_len(intp));</pre> - <p>Last, the memory allocated by the <c>ETERM</c> creating functions must be freed.</p> + <p>Finally, the memory allocated by the <c>ETERM</c> creating + functions must be freed:</p> <pre> erl_free_compound(tuplep); erl_free_term(fnp); erl_free_term(argp); erl_free_term(intp);</pre> - <p>The resulting C program is shown below:</p> + <p>The resulting C program is as follows:</p> <codeinclude file="ei.c" type="c"/> </section> <section> <title>Running the Example</title> - <p>1. Compile the C code, providing the paths to the include files <c>erl_interface.h</c> and <c>ei.h</c>, and to the libraries <c>erl_interface</c> and <c>ei</c>.</p> + <p><em>Step 1.</em> Compile the C code. This provides the paths to + the include files <c>erl_interface.h</c> and <c>ei.h</c>, and + also to the libraries <c>erl_interface</c> and <c>ei</c>:</p> <pre> unix> <input>gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </input> <input> -L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input> <input> complex.c erl_comm.c ei.c -lerl_interface -lei</input></pre> - <p>In R5B and later versions of OTP, the <c>include</c> and <c>lib</c> directories are situated under <c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is the root directory of the OTP installation (<c>/usr/local/otp</c> in the example above) and <c>VSN</c> is the version of the <c>erl_interface</c> application (3.2.1 in the example above). <br></br> - - In R4B and earlier versions of OTP, <c>include</c> and <c>lib</c> are situated under <c>OTPROOT/usr</c>.</p> - <p>2. Start Erlang and compile the Erlang code.</p> + <p>In Erlang/OTP R5B and later versions of OTP, the <c>include</c> + and <c>lib</c> directories are situated under + <c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is + the root directory of the OTP installation + (<c>/usr/local/otp</c> in the recent example) and <c>VSN</c> is + the version of the Erl_interface application (3.2.1 in the + recent example).</p> + <p>In R4B and earlier versions of OTP, <c>include</c> and <c>lib</c> + are situated under <c>OTPROOT/usr</c>.</p> + <p><em>Step 2.</em> Start Erlang and compile the Erlang code:</p> <pre> unix> <input>erl</input> Erlang (BEAM) emulator version 4.9.1.2 @@ -125,7 +181,7 @@ Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) 1> <input>c(complex2).</input> {ok,complex2}</pre> - <p>3. Run the example.</p> + <p><em>Step 3.</em> Run the example:</p> <pre> 2> <input>complex2:start("extprg").</input> <0.34.0> diff --git a/system/doc/tutorial/example.xmlsrc b/system/doc/tutorial/example.xmlsrc index f87eb217e9..e205ca189e 100644 --- a/system/doc/tutorial/example.xmlsrc +++ b/system/doc/tutorial/example.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,16 +31,25 @@ <section> <title>Description</title> - <p>A common interoperability situation is when there exists a piece of code solving some complex problem, and we would like to incorporate this piece of code in our Erlang program. Suppose for example we have the following C functions that we would like to be able to call from Erlang.</p> - <codeinclude file="complex.c" tag="" type="none"></codeinclude> - <p>(For the sake of keeping the example as simple as possible, the functions are not very complicated in this case).</p> - <p>Preferably we would like to able to call <c>foo</c> and <c>bar</c> without having to bother about them actually being C functions.</p> + <p>A common interoperability situation is when you want to incorporate + a piece of code, solving a complex problem, in your Erlang + program. Suppose for example, that you have the following C + functions that you would like to call from Erlang:</p> + <codeinclude file="complex.c" tag="" type="none"></codeinclude> + <p>The functions are deliberately kept as simple as possible, for + readability reasons.</p> + <p>From an Erlang perspektive, it is preferable to be able to call + <c>foo</c> and <c>bar</c> without having to bother about that + they are C functions:</p> <pre> % Erlang code ... Res = complex:foo(X), ...</pre> - <p>The communication with C is hidden in the implementation of <c>complex.erl</c>. In the following chapters it is shown how this module can be implemented using the different interoperability mechanisms.</p> + <p>Here, the communication with C is hidden in the implementation + of <c>complex.erl</c>. + In the following sections, it is shown how this module can be + implemented using the different interoperability mechanisms.</p> </section> </chapter> diff --git a/system/doc/tutorial/introduction.xml b/system/doc/tutorial/introduction.xml index ed86a00f76..dcf462e311 100644 --- a/system/doc/tutorial/introduction.xml +++ b/system/doc/tutorial/introduction.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,18 +28,34 @@ <rev></rev> <file>introduction.xml</file> </header> + <marker id="interoperability tutorial"></marker> + <p>This section informs on interoperability, that is, information + exchange, between Erlang and other programming languages. The + included examples mainly treat interoperability between Erlang and + C.</p> <section> <title>Purpose</title> - <p>The purpose of this tutorial is to give the reader an orientation of the different interoperability mechanisms that can be used when integrating a program written in Erlang with a program written in another programming language, from the Erlang programmer's point of view.</p> + <p>The purpose of this tutorial is to describe different + interoperability mechanisms that can be used when integrating a + program written in Erlang with a program written in another + programming language, from the Erlang programmer's + perspective.</p> </section> <section> <title>Prerequisites</title> - <p>It is assumed that the reader is a skilled Erlang programmer, familiar with concepts such as Erlang data types, processes, messages and error handling.</p> - <p>To illustrate the interoperability principles C programs running in a UNIX environment have been used. It is assumed that the reader has enough knowledge to be able to apply these principles to the relevant programming languages and platforms.</p> + <p>It is assumed that you are a skilled Erlang programmer, + familiar with concepts such as Erlang data types, processes, + messages, and error handling.</p> + <p>To illustrate the interoperability principles, C programs + running in a UNIX environment have been used. It is assumed that + you have enough knowledge to apply these principles to the + relevant programming languages and platforms.</p> <note> - <p>For the sake of readability, the example code has been kept as simple as possible. It does not include functionality such as error handling, which might be vital in a real-life system.</p> + <p>For readability, the example code is kept as simple as + possible. For example, it does not include error handling, + which might be vital in a real-life system.</p> </note> </section> </chapter> diff --git a/system/doc/tutorial/nif.xmlsrc b/system/doc/tutorial/nif.xmlsrc index 8ddad60f74..c79370e8c8 100644 --- a/system/doc/tutorial/nif.xmlsrc +++ b/system/doc/tutorial/nif.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,92 +28,105 @@ <rev></rev> <file>nif.xml</file> </header> - <p>This is an example of how to solve the <seealso marker="example">example problem</seealso> - by using NIFs. NIFs were introduced in R13B03 as an experimental - feature. It is a simpler and more efficient way of calling C-code - than using port drivers. NIFs are most suitable for synchronous functions like - <c>foo</c> and <c>bar</c> in the example, that does some - relatively short calculations without side effects and return the result.</p> - <section> - <title>NIFs</title> - <p>A NIF (Native Implemented Function) is a function that is - implemented in C instead of Erlang. NIFs appear as any other functions to - the callers. They belong to a module and are called like any other Erlang - functions. The NIFs of a module are compiled and linked into a dynamic - loadable shared library (SO in Unix, DLL in Windows). The NIF library must - be loaded in runtime by the Erlang code of the module.</p> - <p>Since a NIF library is dynamically linked into the emulator - process, this is the fastest way of calling C-code from Erlang (alongside - port drivers). Calling NIFs requires no context switches. But it is also - the least safe, because a crash in a NIF will bring the emulator down - too.</p> - </section> + <p>This section outlines an example of how to solve the example + problem in <seealso marker="example">Problem Example</seealso> + by using Native Implemented Functions (NIFs).</p> + <p>NIFs were introduced in Erlang/OTP R13B03 as an experimental + feature. It is a simpler and more efficient way of calling C-code + than using port drivers. NIFs are most suitable for synchronous + functions, such as <c>foo</c> and <c>bar</c> in the example, that + do some relatively short calculations without side effects and + return the result.</p> + <p>A NIF is a function that is implemented in C instead of Erlang. + NIFs appear as any other functions to the callers. They belong to + a module and are called like any other Erlang functions. The NIFs + of a module are compiled and linked into a dynamic loadable, + shared library (SO in UNIX, DLL in Windows). The NIF library must + be loaded in runtime by the Erlang code of the module.</p> + <p>As a NIF library is dynamically linked into the emulator process, + this is the fastest way of calling C-code from Erlang (alongside + port drivers). Calling NIFs requires no context switches. But it + is also the least safe, because a crash in a NIF brings the + emulator down too.</p> <section> <title>Erlang Program</title> - <p>Even if all functions of a module will be NIFs, you still need an Erlang - module for two reasons. First, the NIF library must be explicitly loaded - by Erlang code in the same module. Second, all NIFs of a module must have - an Erlang implementation as well. Normally these are minimal stub - implementations that throw an exception. But it can also be used as - fallback implementations for functions that do not have native - implemenations on some architectures.</p> - <p>NIF libraries are loaded by calling <c>erlang:load_nif/2</c>, with the - name of the shared library as argument. The second argument can be any - term that will be passed on to the library and used for - initialization.</p> + <p>Even if all functions of a module are NIFs, an Erlang + module is still needed for two reasons:</p> + <list type="bulleted"> + <item>The NIF library must be explicitly loaded by + Erlang code in the same module.</item> + <item>All NIFs of a module must have an Erlang implementation + as well.</item> + </list> + <p>Normally these are minimal stub implementations that throw an + exception. But they can also be used as fallback implementations + for functions that do not have native implemenations on some + architectures.</p> + <p>NIF libraries are loaded by calling <c>erlang:load_nif/2</c>, + with the name of the shared library as argument. The second + argument can be any term that will be passed on to the library + and used for initialization:</p> <codeinclude file="complex6.erl" tag="" type="none"></codeinclude> - <p>We use the directive <c>on_load</c> to get function <c>init</c> to be - automatically called when the module is loaded. If <c>init</c> - returns anything other than <c>ok</c>, such when the loading of - the NIF library fails in this example, the module will be - unloaded and calls to functions within it will fail.</p> - <p>Loading the NIF library will override the stub implementations + <p>Here, the directive <c>on_load</c> is used to get function + <c>init</c> to be automatically called when the module is + loaded. If <c>init</c> returns anything other than <c>ok</c>, + such when the loading of the NIF library fails in this example, + the module is unloaded and calls to functions within it, + fail.</p> + <p>Loading the NIF library overrides the stub implementations and cause calls to <c>foo</c> and <c>bar</c> to be dispatched to the NIF implementations instead.</p> </section> <section> - <title>NIF library code</title> + <title>NIF Library Code</title> <p>The NIFs of the module are compiled and linked into a shared library. Each NIF is implemented as a normal C function. The macro <c>ERL_NIF_INIT</c> together with an array of structures defines the names, - arity and function pointers of all the NIFs in the module. The header - file <c>erl_nif.h</c> must be included. Since the library is a shared - module, not a program, no main function should be present.</p> + arity, and function pointers of all the NIFs in the module. The header + file <c>erl_nif.h</c> must be included. As the library is a shared + module, not a program, no main function is to be present.</p> <p>The function arguments passed to a NIF appears in an array <c>argv</c>, - with <c>argc</c> as the length of the array and thus the arity of the + with <c>argc</c> as the length of the array, and thus the arity of the function. The Nth argument of the function can be accessed as <c>argv[N-1]</c>. NIFs also take an environment argument that serves as an opaque handle that is needed to be passed on to most API functions. The environment contains information about - the calling Erlang process.</p> + the calling Erlang process:</p> <codeinclude file="complex6_nif.c" tag="" type="none"></codeinclude> - <p>The first argument to <c>ERL_NIF_INIT</c> must be the name of the + <p>Here,<c>ERL_NIF_INIT</c> has the following arguments:</p> + <list type="bulleted"> + <item><p>The first argument must be the name of the Erlang module as a C-identifier. It will be stringified by the - macro. The second argument is the array of <c>ErlNifFunc</c> - structures containing name, arity and function pointer of - each NIF. The other arguments are pointers to callback functions - that can be used to initialize the library. We do not use them - in this simple example so we set them all to <c>NULL</c>.</p> + macro.</p> + </item> + <item>The second argument is the array of <c>ErlNifFunc</c> + structures containing name, arity, and function pointer of + each NIF.</item> + <item>The remaining arguments are pointers to callback functions + that can be used to initialize the library. They are not used + in this simple example, hence they are all set to <c>NULL</c>.</item> + </list> <p>Function arguments and return values are represented as values - of type <c>ERL_NIF_TERM</c>. We use functions like <c>enif_get_int</c> - and <c>enif_make_int</c> to convert between Erlang term and C-type. - If the function argument <c>argv[0]</c> is not an integer then - <c>enif_get_int</c> will return false, in which case we return + of type <c>ERL_NIF_TERM</c>. Here, functions like <c>enif_get_int</c> + and <c>enif_make_int</c> are used to convert between Erlang term + and C-type. + If the function argument <c>argv[0]</c> is not an integer, + <c>enif_get_int</c> returns false, in which case it returns by throwing a <c>badarg</c>-exception with <c>enif_make_badarg</c>.</p> </section> <section> <title>Running the Example</title> - <p>1. Compile the C code.</p> + <p><em>Step 1.</em> Compile the C code:</p> <pre> unix> <input>gcc -o complex6_nif.so -fpic -shared complex.c complex6_nif.c</input> windows> <input>cl -LD -MD -Fe complex6_nif.dll complex.c complex6_nif.c</input></pre> - <p>2. Start Erlang and compile the Erlang code.</p> + <p><em>Step 2:</em> Start Erlang and compile the Erlang code:</p> <pre> > <input>erl</input> Erlang R13B04 (erts-5.7.5) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false] @@ -121,7 +134,7 @@ Erlang R13B04 (erts-5.7.5) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-p Eshell V5.7.5 (abort with ^G) 1> <input>c(complex6).</input> {ok,complex6}</pre> - <p>3. Run the example.</p> + <p><em>Step 3:</em> Run the example:</p> <pre> 3> <input>complex6:foo(3).</input> 4 diff --git a/system/doc/tutorial/overview.xml b/system/doc/tutorial/overview.xml index 1fe1aad22b..3814a135b4 100644 --- a/system/doc/tutorial/overview.xml +++ b/system/doc/tutorial/overview.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,35 +31,90 @@ <section> <title>Built-In Mechanisms</title> - <p>There are two interoperability mechanisms built into the Erlang runtime system. One is <em>distributed Erlang</em> and the other one is <em>ports</em>. A variation of ports is <em>linked-in drivers</em>.</p> + <p>Two interoperability mechanisms are built into the Erlang + runtime system, <em>distributed Erlang</em> and <em>ports</em>. + A variation of ports is <em>linked-in drivers</em>.</p> <marker id="dist"></marker> <section> <title>Distributed Erlang</title> - <p>An Erlang runtime system is made into a distributed Erlang node by giving it a name. A distributed Erlang node can connect to and monitor other nodes, it is also possible to spawn processes at other nodes. Message passing and error handling between processes at different nodes are transparent. There exists a number of useful <c>stdlib</c> modules intended for use in a distributed Erlang system; for example, <c>global</c> which provides global name registration. The distribution mechanism is implemented using TCP/IP sockets.</p> - <p><em>When to use:</em> Distributed Erlang is primarily used for communication Erlang-Erlang. It can also be used for communication between Erlang and C, if the C program is implemented as a <seealso marker="#cnode">C node</seealso>, see below.</p> - <p><em>Where to read more:</em> Distributed Erlang and some distributed programming techniques are described in the Erlang book. <br></br> - - In the Erlang/OTP documentation there is a chapter about distributed Erlang in "Getting Started" (User's Guide). <br></br> - - Relevant man pages are <c>erlang</c> (describes the BIFs) and <c>global</c>, <c>net_adm</c>, <c>pg2</c>, <c>rpc</c>, <c>pool</c> and <c>slave</c>.</p> + <p>An Erlang runtime system is made a distributed Erlang node by + giving it a name. A distributed Erlang node can connect to, + and monitor, other nodes. It can also spawn processes at other + nodes. Message passing and error handling between processes at + different nodes are transparent. A number of useful STDLIB + modules are available in a distributed Erlang system. For + example, <c>global</c>, which provides global name + registration. The distribution mechanism is implemented using + TCP/IP sockets.</p> + <p><em>When to use:</em> Distributed Erlang is primarily used + for Erlang-Erlang communication. It can also be used for + communication between Erlang and C, if the C program is + implemented as a C node, see + <seealso marker="#cnode">C and Java Libraries</seealso>.</p> + <p><em>Where to read more:</em> Distributed Erlang and some distributed + programming techniques are described in the Erlang book.</p> + <p>For more information, see <seealso + marker="doc/getting_started:conc_prog#Distributed Programming"> + Distributed Programming.</seealso></p> + <p>Relevant manual pages are the following:</p> + <list type="bulleted"> + <item><seealso marker="erts:erlang">erlang</seealso> manual page in ERTS + (describes the BIFs)</item> + <item><seealso marker="kernel:global">global</seealso> manual page in Kernel</item> + <item><seealso marker="kernel:net_adm">net_adm</seealso> manual page in Kernel</item> + <item><seealso marker="kernel:pg2">pg2</seealso> manual page in Kernel</item> + <item><seealso marker="kernel:rpc">rpc</seealso> manual page in Kernel</item> + <item><seealso marker="stdlib:pool">pool</seealso> manual page in STDLIB</item> + <item><seealso marker="stdlib:slave">slave</seealso> manual page in STDLIB</item> + </list> </section> <section> <title>Ports and Linked-In Drivers</title> - <p>Ports provide the basic mechanism for communication with the external world, from Erlang's point of view. They provide a byte-oriented interface to an external program. When a port has been created, Erlang can communicate with it by sending and receiving lists of bytes (not Erlang terms). This means that the programmer may have to invent a suitable encoding and decoding scheme.</p> - <p>The actual implementation of the port mechanism depends on the platform. In the Unix case, pipes are used and the external program should as default read from standard input and write to standard output. Theoretically, the external program could be written in any programming language as long as it can handle the interprocess communication mechanism with which the port is implemented.</p> - <p>The external program resides in another OS process than the Erlang runtime system. In some cases this is not acceptable, consider for example drivers with very hard time requirements. It is therefore possible to write a program in C according to certain principles and dynamically link it to the Erlang runtime system, this is called a linked-in driver.</p> - <p><em>When to use:</em> Being the basic mechanism, ports can be used for all kinds of interoperability situations where the Erlang program and the other program runs on the same machine. Programming is fairly straight-forward. <br></br> - - Linked-in drivers involves writing certain call-back functions in C. Very good skills are required as the code is linked to the Erlang runtime system.</p> + <p>Ports provide the basic mechanism for communication with the + external world, from Erlang's point of view. The ports provide + a byte-oriented interface to an external program. When a port + is created, Erlang can communicate with it by sending and + receiving lists of bytes (not Erlang terms). This means that + the programmer might have to invent a suitable encoding and + decoding scheme.</p> + <p>The implementation of the port mechanism depends on the + platform. For UNIX, pipes are used and the external program is + assumed to read from standard input and write to standard + output. The external program can be written in any programming + language as long as it can handle the interprocess + communication mechanism with which the port is + implemented.</p> + <p>The external program resides in another OS process than the + Erlang runtime system. In some cases this is not acceptable. + Consider, for example, drivers with very hard time + requirements. It is therefore possible to write a program in C + according to certain principles, and dynamically link it to + the Erlang runtime system. This is called a <em>linked-in + driver</em>.</p> + <p><em>When to use:</em> Ports can be used for all kinds of + interoperability situations where the Erlang program and the + other program runs on the same machine. Programming is fairly + straight-forward.</p> + <p>Linked-in drivers involves writing certain call-back + functions in C. This requires very good skills as the code is + linked to the Erlang runtime system.</p> <warning> - <p>An erroneous linked-in driver will cause the entire Erlang runtime system to leak memory, hang or crash.</p> + <p>A faulty linked-in driver causes the entire Erlang runtime + system to leak memory, hang, or crash.</p> </warning> - <p><em>Where to read more:</em> Ports are described in the "Miscellaneous Items" chapter of the Erlang book. Linked-in drivers are described in Appendix E. <br></br> - - The BIF <c>open_port/2</c> is documented in the man page for <c>erlang</c>. For linked-in drivers, the programmer needs to read the information in the man page for <c>erl_ddll</c>.</p> - <p><em>Examples:</em><seealso marker="c_port">Port example</seealso>.</p> + <p><em>Where to read more:</em> Ports are described in section + "Miscellaneous Items" of the Erlang book. Linked-in drivers + are described in Appendix E.</p> + <p>The BIF <c>open_port/2</c> is documented in the + <seealso marker="erts:erlang">erlang</seealso> manual page in + ERTS.</p> + <p>For linked-in drivers, the programmer needs to read the + <seealso marker="kernel:erl_ddll">erl_ddll</seealso> manual + page in Kernel.</p> + <p><em>Examples:</em> Port example in <seealso marker="c_port"> + Ports</seealso>.</p> </section> </section> @@ -68,64 +123,152 @@ <section> <title>Erl_Interface</title> - <p>Very often the program at the other side of a port is a C program. To help the C programmer a library called Erl_Interface has been developed. It consists of five parts:</p> + <p>The program at the other side of a port is often a C program. + To help the C programmer, the Erl_Interface library + has been developed, including the following five parts:</p> <list type="bulleted"> - <item><c>erl_marshal</c>, <c>erl_eterm</c>, <c>erl_format</c>, <c>erl_malloc</c> Handling of the Erlang external term format.</item> - <item><c>erl_connect</c> Communication with distributed Erlang, see <seealso marker="#cnode">C nodes</seealso> below.</item> - <item><c>erl_error</c> Error print routines.</item> - <item><c>erl_global</c> Access globally registered names.</item> - <item><c>Registry</c> Store and backup of key-value pairs.</item> + <item> + <c>erl_marshal</c>, <c>erl_eterm</c>, <c>erl_format</c>, and + <c>erl_malloc</c>: Handling of the Erlang external term format</item> + <item> + <c>erl_connect</c>: + Communication with distributed Erlang, see <seealso + marker="#cnode">C nodes</seealso> below</item> + <item> + <c>erl_error</c>: + Error print routines</item> + <item> + <c>erl_global</c>: + Access globally registered names</item> + <item> + <c>Registry</c>: + Store and backup of key-value pairs</item> </list> - <p>The Erlang external term format is a representation of an Erlang term as a sequence of bytes, a binary. Conversion between the two representations is done using BIFs.</p> + <p>The Erlang external term format is a representation of an + Erlang term as a sequence of bytes, that is, a binary. + Conversion between the two representations is done using the + following BIFs:</p> <pre> Binary = term_to_binary(Term) Term = binary_to_term(Binary)</pre> - <p>A port can be set to use binaries instead of lists of bytes. It is then not necessary to invent any encoding/decoding scheme. Erl_Interface functions are used for unpacking the binary and convert it into a struct similar to an Erlang term. Such a struct can be manipulated in different ways and be converted to the Erlang external format and sent to Erlang.</p> + <p>A port can be set to use binaries instead of lists of bytes. + It is then not necessary to invent any encoding/decoding + scheme. Erl_Interface functions are used for unpacking the + binary and convert it into a struct similar to an Erlang term. + Such a struct can be manipulated in different ways, be + converted to the Erlang external format, and sent to + Erlang.</p> <p><em>When to use:</em> In C code, in conjunction with Erlang binaries.</p> - <p><em>Where to read more:</em> Read about the Erl_Interface User's Guide; Command Reference and Library Reference. In R5B and earlier versions the information can be found under the Kernel application.</p> - </section> - <p><em>Examples:</em><seealso marker="erl_interface">erl_interface example</seealso>.</p> + <p><em>Where to read more:</em> See the Erlang Interface User's + Guide, Command Reference, and Library Reference. In Erlang/OTP + R5B, and earlier versions, the information is part of the + Kernel application.</p> </section> + <p><em>Examples:</em> Erl_Interface example in + <seealso marker="erl_interface">Erl_Interface</seealso>.</p> <marker id="cnode"></marker> <section> <title>C Nodes</title> - <p>A C program which uses the Erl_Interface functions for setting up a connection to and communicating with a distributed Erlang node is called a <em>C node</em>, or a <em>hidden node</em>. The main advantage with a C node is that the communication from the Erlang programmer's point of view is extremely easy, since the C program behaves as a distributed Erlang node.</p> - <p><em>When to use:</em> C nodes can typically be used on device processors (as opposed to control processors) where C is a better choice than Erlang due to memory limitations and/or application characteristics.</p> - <p><em>Where to read more:</em> In the <c>erl_connect</c> part of the Erl_Interface documentation, see above. The programmer also needs to be familiar with TCP/IP sockets, see <seealso marker="#sockets">below</seealso>, and distributed Erlang, see <seealso marker="#dist">above</seealso>.</p> - <p><em>Examples:</em><seealso marker="cnode">C node example</seealso>.</p> + <p>A C program that uses the Erl_Interface functions for setting + up a connection to, and communicating with, a distributed + Erlang node is called a <em>C node</em>, or a <em>hidden + node</em>. The main advantage with a C node is that the + communication from the Erlang programmer's perspective is + extremely easy, as the C program behaves as a distributed + Erlang node.</p> + <p><em>When to use:</em> C nodes can typically be used on device + processors (as opposed to control processors) where C is a + better choice than Erlang due to memory limitations or + application characteristics, or both.</p> + <p><em>Where to read more:</em> See the <c>erl_connect</c> part + of the Erl_Interface documentation. The programmer also needs + to be familiar with TCP/IP sockets, see Sockets in <seealso + marker="#sockets">Standard + Protocols</seealso> and Distributed Erlang in <seealso + marker="#dist">Built-In Mechanisms</seealso>.</p> + <p><em>Example:</em> C node example in <seealso marker="cnode"> + C Nodes</seealso>.</p> </section> <section> <title>Jinterface</title> - <p>In Erlang/OTP R6B, a library similar to Erl_Interface for Java was added called <em>jinterface</em>.</p> + <p>In Erlang/OTP R6B, a library similar to Erl_Interface for + Java was added called <em>jinterface</em>. It provides a tool + for Java programs to communicate with Erlang nodes.</p> </section> </section> <section> <title>Standard Protocols</title> - <p>Sometimes communication between an Erlang program and another program using a standard protocol is desirable. Erlang/OTP currently supports TCP/IP and UDP <em>sockets</em>, SNMP, HTTP and IIOP (CORBA). Using one of the latter three requires good knowledge about the protocol and is not covered by this tutorial. Please refer to the documentation for the SNMP, Inets and Orber applications, respectively.</p> + <p>Sometimes communication between an Erlang program and another + program using a standard protocol is desirable. Erlang/OTP + currently supports TCP/IP and UDP <em>sockets</em>: as + follows:</p> + <list type="bulleted"> + <item>SNMP</item> + <item>HTTP</item> + <item>IIOP (CORBA)</item> + </list> + <p>Using one of the latter three requires good knowledge about the + protocol and is not covered by this tutorial. See the SNMP, + Inets, and Orber applications, respectively.</p> <marker id="sockets"></marker> <section> <title>Sockets</title> - <p>Simply put, connection-oriented socket communication (TCP/IP) consists of an initiator socket ("server") started at a certain host with a certain port number. A connector socket ("client") aware of the initiator's host name and port number can connect to it and data can be sent between them. Connection-less socket communication (UDP) consists of an initiator socket at a certain host with a certain port number and a connector socket sending data to it. For a detailed description of the socket concept, please refer to a suitable book about network programming. A suggestion is <em>UNIX Network Programming, Volume 1: Networking APIs - Sockets and XTI</em> by W. Richard Stevens, ISBN: 013490012X.</p> - <p>In Erlang/OTP, access to TCP/IP and UDP sockets is provided by the - Kernel modules <c>gen_tcp</c> and <c>gen_udp</c>. Both are easy to - use and do not require any deeper knowledge about the socket concept.</p> - <p><em>When to use:</em> For programs running on the same or on another machine than the Erlang program.</p> - <p><em>Where to read more:</em> The man pages for <c>gen_tcp</c> and <c>gen_udp</c>.</p> + <p>Simply put, connection-oriented socket communication (TCP/IP) + consists of an initiator socket ("server") started at a + certain host with a certain port number. A connector socket + ("client"), which is aware of the initiator host name and port + number, can connect to it and data can be sent between + them.</p> + <p>Connection-less socket communication (UDP) consists of an + initiator socket at a certain host with a certain port number + and a connector socket sending data to it.</p> + <p>For a detailed description of the socket concept, refer to a + suitable book about network programming. A suggestion is + <em>UNIX Network Programming, Volume 1: Networking APIs - + Sockets and XTI</em> by W. Richard Stevens, ISBN: + 013490012X.</p> + <p>In Erlang/OTP, access to TCP/IP and UDP sockets is provided + by the modules <c>gen_tcp</c> and <c>gen_udp</c> in + Kernel. Both are easy to use and do not require + detailed knowledge about the socket concept.</p> + <p><em>When to use:</em> For programs running on the same or on + another machine than the Erlang program.</p> + <p><em>Where to read more:</em> See the <seealso + marker="kernel:gen_tcp">gen_tcp</seealso> and the <seealso + marker="kernel:gen_udp">gen_udp</seealso> manual pages in + Kernel.</p> </section> </section> <section> <title>IC</title> - <p>IC (IDL Compiler) is an interface generator which given an IDL interface specification automatically generates stub code in Erlang, C or Java. Please refer to the IC User's Guide and IC Reference Manual.</p> + <p>IC (Erlang IDL Compiler) is an interface generator that, given + an IDL interface specification, automatically generates stub + code in Erlang, C, or Java. See the IC User's Guide and IC + Reference Manual.</p> + <p>For details, see the <seealso marker="ic:ic">ic</seealso> + manual page in IC.</p> </section> <section> <title>Old Applications</title> - <p>There are two old applications of interest when talking about interoperability: <em>IG</em> which was removed in Erlang/OTP R6B and <em>Jive</em> which was removed in Erlang/OTP R7B. Both applications have been replaced by IC and are mentioned here for reference only.</p> - <p>IG (Interface Generator) automatically generated code for port or socket communication between an Erlang program and a C program, given a C header file with certain keywords. Jive provided a simple interface between an Erlang program and a Java program.</p> + <p>Two old applications are of interest regarding + interoperability. Both have been replaced by IC and are + mentioned here for reference only:</p> + <list type="bulleted"> + <item><p>IG - Removed from Erlang/OTP R6B.</p> + <p>IG (Interface Generator) automatically generated code for + port or socket communication between an Erlang program and a + C program, given a C header file with certain keywords.</p> + </item> + <item><p>Jive - Removed from Erlang/OTP R7B.</p> + <p>Jive provided a simple interface between an Erlang program + and a Java program.</p> + </item> + </list> </section> </chapter> diff --git a/xcomp/erl-xcomp-arm-linux.conf b/xcomp/erl-xcomp-arm-linux.conf index 76912d25e0..6656c1a1aa 100644 --- a/xcomp/erl-xcomp-arm-linux.conf +++ b/xcomp/erl-xcomp-arm-linux.conf @@ -74,7 +74,8 @@ erl_xcomp_configure_flags="--disable-hipe" CC="arm-wrs-linux-gnueabi-gcc --sysroot=$ARM_SYSROOT" # * `CFLAGS' - C compiler flags. -#CFLAGS="-O@OPT_LEVEL@ -DSMALL_MEMORY" +CFLAGS="-O2 -DSMALL_MEMORY --sysroot=$ARM_SYSROOT -Wall -g" + # * `STATIC_CFLAGS' - Static C compiler flags. #STATIC_CFLAGS= @@ -87,19 +88,19 @@ CC="arm-wrs-linux-gnueabi-gcc --sysroot=$ARM_SYSROOT" CPP="arm-wrs-linux-gnueabi-cpp --sysroot=$ARM_SYSROOT" # * `CPPFLAGS' - C pre-processor flags. -#CPPFLAGS="--sysroot=$ARM_SYSROOT" +CPPFLAGS="--sysroot=$ARM_SYSROOT" # * `CXX' - C++ compiler. CXX="arm-wrs-linux-gnueabi-c++ --sysroot=$ARM_SYSROOT" # * `CXXFLAGS' - C++ compiler flags. -#CXXFLAGS= +CXXFLAGS="--sysroot=$ARM_SYSROOT" # * `LD' - Linker. -#LD= +LD="arm-wrs-linux-gnueabi-gcc" # * `LDFLAGS' - Linker flags. -#LDFLAGS= +LDFLAGS="--sysroot=$ARM_SYSROOT" # * `LIBS' - Libraries. #LIBS= @@ -109,14 +110,14 @@ CXX="arm-wrs-linux-gnueabi-c++ --sysroot=$ARM_SYSROOT" ## *NOTE*! Either set all or none of the `DED_LD*' variables. # * `DED_LD' - Linker for Dynamically loaded Erlang Drivers. -#DED_LD= +DED_LD="arm-wrs-linux-gnueabi-gcc" # * `DED_LDFLAGS' - Linker flags to use with `DED_LD'. -#DED_LDFLAGS= +DED_LDFLAGS="--sysroot=$ARM_SYSROOT -shared -Wl,-Bsymbolic" # * `DED_LD_FLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library # search path for shared libraries when linking with `DED_LD'. -#DED_LD_FLAG_RUNTIME_LIBRARY_PATH= +DED_LD_FLAG_RUNTIME_LIBRARY_PATH="-Wl,-R" ## -- Large File Support -- |